diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 488df7a8c..3938244c6 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,18 +1,6 @@ - - - + ## Expected Behavior diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..7f6e12135 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,46 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +## Describe the bug + +A clear and concise description of what the bug is. + +Please ensure to read https://github.com/Icinga/icinga2/blob/master/doc/15-troubleshooting.md first. Formatting tips: GitHub supports Markdown: https://guides.github.com/features/mastering-markdown/ + +## To Reproduce + +Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. Include configuration, logs, etc. to reproduce, if relevant. + +1. +2. +3. +4. + +## Expected behavior + +A clear and concise description of what you expected to happen. + +## Screenshots + +If applicable, add screenshots to help explain your problem. + +## Your Environment + +Include as many relevant details about the environment you experienced the problem in + +* Version used (`icinga2 --version`): +* Operating System and version: +* Enabled features (`icinga2 feature list`): +* Icinga Web 2 version and modules (System - About): +* Config validation (`icinga2 daemon -C`): +* If you run multiple Icinga 2 instances, the `zones.conf` file (or `icinga2 object list --type Endpoint` and `icinga2 object list --type Zone`) from all affected nodes. + +## Additional context + +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..1d19ccf8a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,24 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +## Is your feature request related to a problem? Please describe. + +A clear and concise description of what the problem is. Ex. I'm always using this feature but am missing [...] + +## Describe the solution you'd like + +A clear and concise description of what you want to happen. + +## Describe alternatives you've considered + +A clear and concise description of any alternative solutions or features you've considered. + +## Additional context + +Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore index 0f798cb79..b300b597d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,18 @@ -## Editors -.idea/ -*.komodoproject -.*.sw[op] -*~ +# Exclude all hidden files +.* -## C++ and Tools -*.patch -*.playground -.vagrant +# Except those related to git and vagrant +!.git* +!.puppet* +!.travis.yml +!.mailmap + +## Tools +*~ +tickets.pickle ## Build artifacts -build/ -build-debug/ -build-release/ -build32/ -build64/ +build*/ debug/ release/ cmake-build-debug diff --git a/.mailmap b/.mailmap index ab70551b8..e37a5aca9 100644 --- a/.mailmap +++ b/.mailmap @@ -6,12 +6,17 @@ Gunnar Beutner Michael Insel -Jean Flach -Jean Flach -Jean Flach Jean Flach +Diana Flach +Diana Flach +Diana Flach Jean Flach +Diana Flach Dolf Schimmel Markus Waldmüller Claudio Kuenzler +Carsten Köbke Carsten Koebke +Thomas Gelf +Michael Insel +Michael Insel @@ -32,3 +37,7 @@ Claudio Kuenzler Marianne Spiller Robin O'Brien +Jens Schanz +Jens Schanz Schanz, Jens +Henrik Triem Henrik Triem <43344334+htriem@users.noreply.github.com> +nemtrif diff --git a/.travis.yml b/.travis.yml index 89b52bdd8..03f43f032 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,51 +1,42 @@ -dist: trusty +dist: xenial sudo: false language: cpp cache: ccache -env: - global: - # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created - # via the "travis encrypt" command using the project repo's public key - - secure: "eOnFdiRhB7VUZY7Of4Ff0px93HRWGcD4fXCPiy8V2OC2ER98CYCVw7PKt2Is6i/yTveFTps1kObOo0T03aUT8y/xeBy/wMuJYk1d6mVgmSXOjxcxjQVTUh4J+xB+k/R6FoP2dirNDbvSayCj9Fi9toN9hQHMM8oAZOZfiKmYTJc=" - -before_install: - - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- - addons: - apt_packages: - - libboost-all-dev - - flex - - bison - - libssl-dev - - libpq-dev - - libmysqlclient-dev - - libedit-dev - - libyajl-dev - - libwxbase3.0-dev - - libwxgtk3.0-dev - coverity_scan: - project: - name: "Icinga/icinga2" - notification_email: icinga2@icinga.com - build_command_prepend: "cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/tmp/icinga2 -DICINGA2_PLUGINDIR=/tmp/icinga2/sbin -DICINGA2_UNITY_BUILD=ON" - build_command: "make -j 2" - branch_pattern: coverity_scan - + apt: + sources: + - sourceline: 'deb http://packages.icinga.com/ubuntu icinga-xenial main' + key_url: 'https://packages.icinga.com/icinga.key' + packages: + - libboost1.67-icinga-all-dev + - flex + - bison + - libssl-dev + - libpq-dev + - libmysqlclient-dev + - libedit-dev before_script: - - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then - mkdir build; - cd build; - cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/tmp/icinga2 -DICINGA2_PLUGINDIR=/tmp/icinga2/sbin; - fi + - arch=$(uname -m) + - mkdir build + - cd build + - > + cmake .. + -DCMAKE_BUILD_TYPE=Debug + -DICINGA2_UNITY_BUILD=Off + -DCMAKE_INSTALL_PREFIX=/tmp/icinga2 + -DICINGA2_PLUGINDIR=/tmp/icinga2/sbin + -DBoost_NO_BOOST_CMAKE=TRUE + -DBoost_NO_SYSTEM_PATHS=TRUE + -DBOOST_LIBRARYDIR=/usr/lib/${arch}-linux-gnu/icinga-boost + -DBOOST_INCLUDEDIR=/usr/include/icinga-boost + -DCMAKE_INSTALL_RPATH=/usr/lib/${arch}-linux-gnu/icinga-boost script: - - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then - make; - make test; - make install; - /tmp/icinga2/sbin/icinga2 --version; - /tmp/icinga2/sbin/icinga2 daemon -C -DRunAsUser=$(id -u -n) -DRunAsGroup=$(id -g -n); - fi + - make + - make test + - make install + - /tmp/icinga2/sbin/icinga2 --version + - /tmp/icinga2/sbin/icinga2 daemon -C -DRunAsUser=$(id -u -n) -DRunAsGroup=$(id -g -n) diff --git a/AUTHORS b/AUTHORS index 7d85ded0d..e13ab9ebb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,16 +1,27 @@ +Aaron Bishop Adam Bolte Adam James +Alan Jenkins +Alan Litster +Alex +Alex Merry Alexander A. Klimov Alexander Fuhr Alexander Schomburg +Alexander Stoll Alexander Wirt +Andrea Kao Andreas Scherbaum Andres Ivanov +Andrew Jaffie Andrew Meyer Andy Grunwald Arnd Hannemann Assaf Flatto +BarbUk +Bas Couwenberg Bastian Guse +Bauerheim, Marcus Benedikt Heine Bernd Erk Berthold Cogel @@ -29,6 +40,7 @@ Christian Jonak Christian Lehmann Christian Loos Christian Schmidt +Christopher Schirner Claudio Bilotta Claudio Kuenzler Conrad Clement @@ -36,7 +48,10 @@ Daniel Helgenberger Daniel Kesselberg Daniil Yaroslavtsev David Beck +David Lublink Denis +Dennis Lichtenthäler +Diana Flach Dinesh Majrekar Dirk Goetz Dirk Melchers @@ -44,10 +59,13 @@ Dolf Schimmel Edgar Fuß Eduard Güldner Edvin Seferovic +Elias Ohm Eric Lippmann Evgeni Golov Ewoud Kohl van Wijngaarden +Fabian Röhl Federico Cuello +Federico Pires Ferdi Gueran Francesco Colista Gaël Beaudoin @@ -56,41 +74,55 @@ Georg Haas Gerd von Egidy Gerhardt Roman Glauco Vinicius +Greg Hewgill Gunnar Beutner Hannes Happle Hannes Van de Vel +Harald Laabs Heike Jurzik Hendrik Röder +Henrik Triem Ian Kelling Ildar Hizbulin Irina Kaprizkina +Jack James Pharaoh Jan Andres Jan Beich Jan Wagner +Janne Heß Jason Young -Jean Flach Jean-Louis Dupond -Jens Schanz +Jens Link +Jens Schanz +Jeon Sang Wan Jeremy Armstrong Jesse Morgan Jo Goossens Johannes Meyer Jonas Meurer +Jordi van Scheijen Joseph L. Casale Julian Brost Jérôme Drouet +K0nne <34264690+K0nne@users.noreply.github.com> Kai Goller Konstantin Kelemen Kálmán Szalai - KAMI +Kálmán „KAMI” Szalai Lars Engels Lars Krüger +Leah Oswald Lee Clemens +Lee Garrett Lennart Betz -lihan +Leon Stringer Louis Sautier Luca Lesinigo +Lucas Bremgartner Lucas Fairchild-Madar +Luiz Amaral +Magnus Bäck Malte Rabenseifner Manuel Reiter Marcus van Dam @@ -98,37 +130,48 @@ MarcusCaepio Marianne Spiller Marius Bergmann Marius Sturm +Mark Leary Markus Frosch Markus Waldmüller +Martijn van Duren +Martin Neubert Martin Stiborsky Mathieu Arnold Mathieu Lutfy Matthaus Owens Matthias Schales Maurice Meyer +Max Deparade Max Rosin Max Zhang +Maximilian Falkenstein Mhd Sulhan Micha Ahrweiler Michael Friedrich Michael Insel -Michael Insel Michael Kraus Michael Newton +Michal Moravec +Michal Petko Mikesch-mp Mirco Bauer Mirko Nardin +Muhammad Mominul Huque +Nemanja Trifunovic Nicolai Nicolas Limage Nicole Lang Niflou Noah Hilverling +Obihörnchen +Oleg Artenii Pall Sigurdsson Paolo Schiro Patrick Huy Paul Richards Pawel Szafer Per von Zweigbergk +Peter Eckel Peter Eckel Petr Ruzicka Phil Hutchinson @@ -136,18 +179,23 @@ Philipp Dallig Ralph Breier Reto Zeder Ricardo Bartels +Robert Lindgren +Robert Scheck Robin O'Brien Roland Hopferwieser Roman Gerhardt Rudy Gevaert Rune Darrud Sam Kottler +Sascha Westermann Sebastian Brückner Sebastian Chrostek +Sebastian Eikenberg Sebastian Marsching Simon Murray Simon Ruderich Siyalrach Anton Thomas +Stefan Bethke Stefan Triep Stefar77 Stephan Platz @@ -156,7 +204,10 @@ Steve McMaster Strajan Sebastian Ioan Strix <660956+MrStrix@users.noreply.github.com> Sven Nierlein -Thomas Gelf +Sven Wegener +T. Mulyana +Thomas Forrer +Thomas Gelf Thomas Niedermeier Thomas Widhalm Tim Hardeck @@ -169,19 +220,33 @@ Uwe Ebel Valentin Hoebel Vytenis Darulis Wenger Florian +Will Frey Winfried Angele Wolfgang Nieder Yannick Charton Yohan Jarosz Zachary McGibbon Zoltan Nagy +akrus bascarsija +chrostek cstegm ctrlaltca +dh.harald +dominik-r-s <43005480+dominik-r-s@users.noreply.github.com> +fbachmann +fluxX04 gitmopp +htriem +jre3brg krishna +lihan +marxin mocruz +nemtrif noobahoi <20069422+noobahoi@users.noreply.github.com> pv2b ryanohnemus +sah +teclogi <27726999+teclogi@users.noreply.github.com> Élie Bouttier diff --git a/BUILD_WINDOWS.md b/BUILD_WINDOWS.md deleted file mode 100644 index 28e75259e..000000000 --- a/BUILD_WINDOWS.md +++ /dev/null @@ -1,103 +0,0 @@ -# Build Icinga 2 on Windows - -The Icinga Project is providing Windows MSI packages under https://packages.icinga.com/windows/ - -> **Note:** -> This is a developer documentation on how to build Icinga 2 on Windows! - -Also see [INSTALL.md](INSTALL.md) for Linux build instructions. - -## Requirements - -* 32 or 64-bit system -* Visual Studio >= 14 2015 -* CMake >= 2.6 -* OpenSSL >= 1.0.1 -* Flex and Bison - -## Install Requirements - -**Visual Studio** - -Download from [visualstudio.com](https://www.visualstudio.com/en/downloads/) - -The Community Edition is available for free, and is what we use to build. - -Workloads to install: -* C++ Desktop -* .NET Desktop - -**OpenSSL for Icinga** - -See our [openssl-windows GitHub project](https://github.com/Icinga/openssl-windows). - -You will need to install a binary dist version to 'C:\\Program Files\\OpenSSL'. - -There is a Powershell script to help you downloading: `.\tools\win32\download-openssl.ps1` - -**Chocolatey** - -A simple package manager for Windows, please see [install instructions](https://chocolatey.org/install). - -**Git** - -Best to use Chocolatey, see [package details](https://chocolatey.org/packages/git). - -``` -choco install git -``` - -**Flex / Bison** - -Best to use Chocolatey, see [package details](https://chocolatey.org/packages/winflexbison3). - -``` -choco install winflexbison3 -``` - -**CMake** - -Best to use Chocolatey, see [package details](https://chocolatey.org/packages/cmake) -or download from: [cmake.org](https://cmake.org/download/) - -``` -choco install cmake -``` - -**WIX** - -Best to use Chocolatey, see [package details](https://chocolatey.org/packages/wixtoolset). - -``` -choco install wixtoolset -``` - -**Boost** - -Download third party Windows binaries from: [boost.org](http://www.boost.org/users/download/) - -For example: `https://dl.bintray.com/boostorg/release/1.65.1/binaries/boost_1_65_1-msvc-14.1-64.exe` - -*Warnings:* -* Must match your Visual Studio version! -* CMake might not support the latest Boost version (we used CMake 3.10 and Boost 1_65_1) - -Run the installer exe. - -## Build Icinga 2 - -Run with VC Native x64 Command Prompt: - -``` -powershell .\tools\win32\configure.ps1 -powershell .\tools\win32\build.ps1 -powershell .\tools\win32\test.ps1 -``` - -See these scripts for details. - -## AppVeyor - -We are building [Icinga 2 with AppVeyor](https://ci.appveyor.com/project/icinga/icinga2) for testing and CI integration. - -Please check `appveyor.yml` for our instructions. diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a01c42c5..5070efec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,882 @@ -# Icinga 2.x CHANGELOG +# Icinga 2 CHANGELOG + +**The latest release announcements are available on [https://icinga.com/blog/](https://icinga.com/blog/).** + +Please read the [upgrading](https://icinga.com/docs/icinga2/latest/doc/16-upgrading-icinga-2/) +documentation before upgrading to a new release. + +Released closed milestones can be found on [GitHub](https://github.com/Icinga/icinga2/milestones?state=closed). + +## 2.11.0 (2019-09-19) + +[Issue and PRs](https://github.com/Icinga/icinga2/issues?utf8=%E2%9C%93&q=milestone%3A2.11.0) + +### Notes + +Upgrading docs: https://icinga.com/docs/icinga2/snapshot/doc/16-upgrading-icinga-2/ + +Thanks to all contributors: [Obihoernchen](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3AObihoernchen), [dasJ](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3AdasJ), [sebastic](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Asebastic), [waja](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Awaja), [BarbUk](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3ABarbUk), [alanlitster](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aalanlitster), [mcktr](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Amcktr), [KAMI911](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3AKAMI911), [peteeckel](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Apeteeckel), [breml](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Abreml), [episodeiv](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aepisodeiv), [Crited](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3ACrited), [robert-scheck](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Arobert-scheck), [west0rmann](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Awest0rmann), [Napsty](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3ANapsty), [Elias481](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3AElias481), [uubk](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Auubk), [miso231](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Amiso231), [neubi4](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aneubi4), [atj](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aatj), [mvanduren-itisit](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Amvanduren-itisit), [jschanz](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Ajschanz), [MaBauMeBad](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3AMaBauMeBad), [markleary](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Amarkleary), [leeclemens](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aleeclemens), [m4k5ym](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Am4k5ym) + +### Enhancements + +* Core + * Rewrite Network Stack (cluster, REST API) based on Boost Asio, Beast, Coroutines + * Technical concept: #7041 + * Requires package updates: Boost >1.66 (either from packages.icinga.com, EPEL or backports). SLES11 & Ubuntu 14 are EOL. + * Require TLS 1.2 and harden default cipher list + * Improved Reload Handling (umbrella process, now 3 processes at runtime) + * Support running Icinga 2 in (Docker) containers natively in foreground + * Quality: Use Modern JSON for C++ library instead of YAJL (dead project) + * Quality: Improve handling of invalid UTF8 strings +* API + * Fix crashes on Linux, Unix and Windows from Nessus scans #7431 + * Locks and stalled waits are fixed with the core rewrite in #7071 + * schedule-downtime action supports `all_services` for host downtimes + * Improve storage handling for runtime created objects in the `_api` package +* Cluster + * HA aware features & improvements for failover handling #2941 #7062 + * Improve cluster config sync with staging #6716 + * Fixed that same downtime/comment objects would be synced again in a cluster loop #7198 +* Checks & Notifications + * Ensure that notifications during a restart are sent + * Immediately notify about a problem after leaving a downtime and still NOT-OK + * Improve reload handling and wait for features/metrics + * Store notification command results and sync them in HA enabled zones #6722 +* DSL/Configuration + * Add getenv() function + * Fix TimePeriod range support over midnight + * `concurrent_checks` in the Checker feature has no effect, use the global MaxConcurrentChecks constant instead +* CLI + * Permissions: node wizard/setup, feature, api setup now run in the Icinga user context, not root + * `ca list` shows pending CSRs by default, `ca remove/restore` allow to delete signing requests +* ITL + * Add new commands and missing attributes +* Windows + * Update bundled NSClient++ to 0.5.2.39 + * Refine agent setup wizard & update requirements to .NET 4.6 +* Documentation + * Service Monitoring: How to create plugins by example, check commands and a modern version of the supported plugin API with best practices + * Features: Better structure on metrics, and supported features + * Technical Concepts: TLS Network IO, Cluster Feature HA, Cluster Config Sync + * Development: Rewritten for better debugging and development experience for contributors including a style guide. Add nightly build setup instructions. + * Packaging: INSTALL.md was integrated into the Development chapter, being available at https://icinga.com/docs too. + + + + +## 2.11.0 RC1 (2019-07-25) + +[Issue and PRs](https://github.com/Icinga/icinga2/issues?utf8=%E2%9C%93&q=milestone%3A2.11.0) + +### Notes + +**This is the first release candidate for 2.11.** + +Upgrading docs: https://icinga.com/docs/icinga2/snapshot/doc/16-upgrading-icinga-2/ + +Thanks to all contributors: [BarbUk](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3ABarbUk), [alanlitster](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aalanlitster), [mcktr](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Amcktr), [KAMI911](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3AKAMI911), [peteeckel](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Apeteeckel), [breml](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Abreml), [episodeiv](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aepisodeiv), [Crited](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3ACrited), [robert-scheck](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Arobert-scheck), [west0rmann](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Awest0rmann), [Napsty](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3ANapsty), [Elias481](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3AElias481), [uubk](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Auubk), [miso231](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Amiso231), [neubi4](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aneubi4), [atj](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aatj), [mvanduren-itisit](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Amvanduren-itisit), [jschanz](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Ajschanz), [MaBauMeBad](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3AMaBauMeBad), [markleary](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Amarkleary), [leeclemens](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aleeclemens), [m4k5ym](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Am4k5ym) + +### Enhancements + +* Core + * Rewrite Network Stack (cluster, REST API) based on Boost Asio, Beast, Coroutines + * Technical concept: #7041 + * Requires package updates: Boost >1.66 (either from packages.icinga.com, EPEL or backports). SLES11 & Ubuntu 14 are EOL. + * Require TLS 1.2 and harden default cipher list + * Improved Reload Handling (umbrella process, now 3 processes at runtime) + * Support running Icinga 2 in (Docker) containers natively in foreground + * Quality: Use Modern JSON for C++ library instead of YAJL (dead project) + * Quality: Improve handling of invalid UTF8 strings +* API + * Fix crashes and problems with permission filters from recent Namespace introduction #6785 (thanks Elias Ohm) #6874 (backported to 2.10.5) + * Locks and stalled waits are fixed with the core rewrite in #7071 + * schedule-downtime action supports `all_services` for host downtimes + * Improve storage handling for runtime created objects in the `_api` package +* Cluster + * HA aware features & improvements for failover handling #2941 #7062 + * Improve cluster config sync with staging #6716 +* Checks & Notifications + * Ensure that notifications during a restart are sent + * Immediately notify about a problem after leaving a downtime and still NOT-OK + * Improve reload handling and wait for features/metrics + * Store notification command results and sync them in HA enabled zones #6722 +* DSL/Configuration + * Add getenv() function + * Fix TimePeriod range support over midnight + * `concurrent_checks` in the Checker feature has no effect, use the global MaxConcurrentChecks constant instead +* CLI + * Permissions: node wizard/setup, feature, api setup now run in the Icinga user context, not root + * `ca list` shows pending CSRs by default, `ca remove/restore` allow to delete signing requests +* ITL + * Add new commands and missing attributes - thanks to all contributors! +* Windows + * Update bundled NSClient++ to 0.5.2.39 + * Update agent installer and OpenSSL +* Documentation + * Service Monitoring: How to create plugins by example, check commands and a modern version of the supported plugin API with best practices. + * Features: Better structure on metrics, and supported features. + * Basics: Rename `Custom Attributes` to `Custom Variables`. + * Basics: Refine explanation of command arguments. + * Distributed: Reword `Icinga client` into `Icinga agent` and add new images for scenarios and modes. + * Security: Add TLS v1.2+ requirement, hardened cipher lists + * Technical Concepts: TLS Network IO, Cluster Feature HA, Cluster Config Sync, Core Reload Handling. + * Development: Rewritten for better debugging and development experience for contributors including a style guide. Add nightly build setup instructions. + * Packaging: INSTALL.md was integrated into the Development chapter available at https://icinga.com/docs too. + + + + +## 2.10.5 (2019-05-23) + +[Issues and PRs](https://github.com/Icinga/icinga2/milestone/81?closed=1) + +### Bugfixes + +* Core + * Fix crashes with logrotate signals #6737 (thanks Elias Ohm) +* API + * Fix crashes and problems with permission filters from recent Namespace introduction #6785 (thanks Elias Ohm) #6874 (backported from 2.11) + * Reduce log spam with locked connections (real fix is the network stack rewrite in 2.11) #6877 +* Cluster + * Fix problems with replay log rotation and storage #6932 (thanks Peter Eckel) +* IDO DB + * Fix that reload shutdown deactivates hosts and hostgroups (introduced in 2.9) #7157 +* Documentation + * Improve the [REST API](https://icinga.com/docs/icinga2/latest/doc/12-icinga2-api/) chapter: Unix timestamp handling, filters, unify POST requests with filters in the body + * Better layout for the [features](https://icinga.com/docs/icinga2/latest/doc/14-features/) chapter, specifically metrics and events + * Split [object types](https://icinga.com/docs/icinga2/latest/doc/09-object-types/) into monitoring, runtime, features + * Add technical concepts for [cluster messages](https://icinga.com/docs/icinga2/latest/doc/19-technical-concepts/#json-rpc-message-api) + + +## 2.10.4 (2019-03-19) + +### Notes + +* Fix TLS connections in Influxdb/Elasticsearch features leaking file descriptors (#6989 #7018 ref/IP/12219) +* Fixes for delayed and one-time notifications (#5561 #6757) +* Improve performance for downtimes/comments added in HA clusters (#6885 ref/IP/9235) +* check_perfmon supports non-localized performance counter names (#5546 #6418) + +### Enhancement + +* [#6732](https://github.com/icinga/icinga2/issues/6732) (Windows, PR): Update Windows Agent with new design +* [#6729](https://github.com/icinga/icinga2/issues/6729) (Windows): Polish the Windows Agent design +* [#6418](https://github.com/icinga/icinga2/issues/6418) (Windows): check\_perfmon.exe: Add fallback support for localized performance counters + +### Bug + +* [#7020](https://github.com/icinga/icinga2/issues/7020) (Elasticsearch, PR): ElasticsearchWriter: don't leak sockets +* [#7018](https://github.com/icinga/icinga2/issues/7018) (Elasticsearch): ElasticsearchWriter not closing SSL connections on Icinga2 2.10.3.1 +* [#6991](https://github.com/icinga/icinga2/issues/6991) (CLI, PR): PkiUtility::NewCa\(\): just warn if the CA files already exist +* [#6990](https://github.com/icinga/icinga2/issues/6990) (InfluxDB, PR): InfluxdbWriter: don't leak sockets +* [#6989](https://github.com/icinga/icinga2/issues/6989) (InfluxDB): InfluxdbWriter not closing connections Icinga2 2.10.3 CentOS 7 +* [#6976](https://github.com/icinga/icinga2/issues/6976) (Cluster, PR): Don't require OS headers to provide SO\_REUSEPORT +* [#6896](https://github.com/icinga/icinga2/issues/6896) (Notifications, PR): Notification\#BeginExecuteNotification\(\): SetNextNotification\(\) correctly +* [#6885](https://github.com/icinga/icinga2/issues/6885) (API, Configuration, PR): Don't run UpdateObjectAuthority for Comments and Downtimes +* [#6800](https://github.com/icinga/icinga2/issues/6800) (Plugins, Windows, PR): Fix check\_perfmon to support non-localized names +* [#6757](https://github.com/icinga/icinga2/issues/6757) (Notifications, PR): Fix that no\_more\_notifications gets reset when Recovery notifications are filtered away +* [#5561](https://github.com/icinga/icinga2/issues/5561) (Notifications): Set the notification mode times.begin is not 0, the first notification has a delay +* [#5546](https://github.com/icinga/icinga2/issues/5546) (Plugins, Windows): check\_perfmon.exe doesn't support cyrillic names of perf counters + +### Documentation + +* [#7033](https://github.com/icinga/icinga2/issues/7033) (Documentation, PR): Docs: Update supported package repos in Getting Started chapter +* [#7028](https://github.com/icinga/icinga2/issues/7028) (Documentation, PR): Fix heading level in development chapter +* [#7001](https://github.com/icinga/icinga2/issues/7001) (Documentation, PR): Assignment operators doc: tell what the { } are for +* [#6995](https://github.com/icinga/icinga2/issues/6995) (Documentation, PR): Typo and link fix +* [#6979](https://github.com/icinga/icinga2/issues/6979) (Documentation, PR): Doc: write systemd lower-case +* [#6975](https://github.com/icinga/icinga2/issues/6975) (Documentation, PR): Fix nested hostgroup example +* [#6949](https://github.com/icinga/icinga2/issues/6949) (Documentation, PR): Doc fix: update check\_rbl parameter +* [#6708](https://github.com/icinga/icinga2/issues/6708) (Documentation, PR): Docs: Alpine needs 'edge/main' repository too +* [#5430](https://github.com/icinga/icinga2/issues/5430) (Documentation): Documentation about dictionaries and assignements + +### Support + +* [#7032](https://github.com/icinga/icinga2/issues/7032) (code-quality, PR): Backport Defer class for 2.10 +* [#7030](https://github.com/icinga/icinga2/issues/7030) (Packages, PR): SELinux: add unreserved\_port\_type attribute to icinga2\_port\_t +* [#7029](https://github.com/icinga/icinga2/issues/7029) (Packages): Add unreserved\_port\_type attribute to icinga2\_port\_t +* [#7002](https://github.com/icinga/icinga2/issues/7002) (Plugins, Windows, PR): check\_network -h: drop non-existent feature +* [#6987](https://github.com/icinga/icinga2/issues/6987) (Tests): base-base\_utility/comparepasswords\_issafe test fails on i386 +* [#6977](https://github.com/icinga/icinga2/issues/6977) (Tests, PR): Ignore failure of unit test base\_utility/comparepasswords\_issafe + +## 2.10.3 (2019-02-26) + +### Notes + +Bugfixes: + +- Stalled TLS connections on reload/Director deployments (#6816 #6898 ref/NC/588119) +- 'Connection: close' header leading to unstable instance, affects Ruby clients (#6799) +- Server time in the future breaks check result processing (#6797 ref/NC/595861) +- ScheduledDowntimes: Generate downtime objects only on one HA endpoint (#2844 ref/IC/9673 ref/NC/590167 ref/NC/591721) +- Improve activation & syncing for downtime objects generated from ScheduledDowntimes (#6826 ref/IC/9673 ref/NC/585559) +- Generate a runtime downtime object from already running ScheduledDowntime objects (#6704) +- DB IDO: Don't enqueue queries when the feature is paused in HA zones (#5876) +- Crashes with localtime_r errors (#6887) + +Documentation updates: + +- Ephemeral port range blocking on Windows agents (ref/NC/597307) +- Technical concepts for the check scheduler (#6775) +- DB IDO cleanup (#6791) +- Unified development docs (#6819) + +### Bug + +* [#6971](https://github.com/icinga/icinga2/issues/6971) (Notifications, PR): Activate downtimes before any checkable object +* [#6968](https://github.com/icinga/icinga2/issues/6968) (API, PR): Secure ApiUser::GetByAuthHeader\(\) against timing attacks +* [#6940](https://github.com/icinga/icinga2/issues/6940) (Plugins, Windows, PR): Fix check\_swap percentage calculation +* [#6925](https://github.com/icinga/icinga2/issues/6925) (Plugins, Windows, PR): Fix check\_swap formatting +* [#6924](https://github.com/icinga/icinga2/issues/6924) (PR): Fix double to long conversions +* [#6922](https://github.com/icinga/icinga2/issues/6922) (API, DB IDO): IDO MySQL fails on start if check\_interval is a float \(Icinga 2.9.2\) +* [#6920](https://github.com/icinga/icinga2/issues/6920) (PR): Downtime::AddDowntime\(\): place Downtimes in the same zone as the origin ScheduledDowntimes +* [#6917](https://github.com/icinga/icinga2/issues/6917) (Cluster, Log, PR): Cluster: Delete object message should log that +* [#6916](https://github.com/icinga/icinga2/issues/6916) (PR): Don't allow retry\_interval \<= 0 +* [#6914](https://github.com/icinga/icinga2/issues/6914) (Cluster, PR): ClusterEvents::AcknowledgementSet event should forward 'persistent' attribute +* [#6913](https://github.com/icinga/icinga2/issues/6913) (Plugins, Windows): check\_swap return value wrong when no swap file configured +* [#6901](https://github.com/icinga/icinga2/issues/6901) (API, PR): TcpSocket\#Bind\(\): also set SO\_REUSEPORT +* [#6899](https://github.com/icinga/icinga2/issues/6899) (PR): Log: Ensure not to pass negative values to localtime\(\) +* [#6898](https://github.com/icinga/icinga2/issues/6898) (API): API action restart-process fails on FreeBSD +* [#6894](https://github.com/icinga/icinga2/issues/6894) (Check Execution, PR): Fix checkresults from the future breaking checks +* [#6887](https://github.com/icinga/icinga2/issues/6887) (Check Execution, Windows): Icinga2 Windows Service does not start critical/checker: Exception occurred while checking 'hostname.tld' +* [#6883](https://github.com/icinga/icinga2/issues/6883) (Check Execution, PR): Allow Checkable\#retry\_interval to be 0 +* [#6871](https://github.com/icinga/icinga2/issues/6871): Icinga2 crashes after localtime\_r call +* [#6857](https://github.com/icinga/icinga2/issues/6857) (Plugins, Windows, PR): Url\#m\_Query: preserve order +* [#6826](https://github.com/icinga/icinga2/issues/6826) (Configuration, PR): Downtime\#HasValidConfigOwner\(\): wait for ScheduledDowntimes +* [#6821](https://github.com/icinga/icinga2/issues/6821) (Cluster, Configuration, PR): Don't delete downtimes in satellite zones +* [#6820](https://github.com/icinga/icinga2/issues/6820) (Cluster, PR): Only create downtimes from non-paused ScheduledDowntime objects in HA enabled cluster zones +* [#6817](https://github.com/icinga/icinga2/issues/6817) (API, PR): HttpServerConnection\#DataAvailableHandler\(\): be aware of being called multiple times concurrently +* [#6816](https://github.com/icinga/icinga2/issues/6816) (API, Cluster): Stalled TLS connections and lock waits in SocketEventEngine +* [#6814](https://github.com/icinga/icinga2/issues/6814) (API, PR): Restore 'Connection: close' behaviour in HTTP responses +* [#6811](https://github.com/icinga/icinga2/issues/6811) (Plugins, Windows, PR): Fix state conditions in check\_memory and check\_swap +* [#6810](https://github.com/icinga/icinga2/issues/6810) (Plugins, Windows): Windows check\_memory never gets critical +* [#6808](https://github.com/icinga/icinga2/issues/6808) (API, PR): Remove redundand check for object existence on creation via API +* [#6807](https://github.com/icinga/icinga2/issues/6807) (API): \[2.10.2\] Director deploy crashes the Icinga service \[FreeBSD\] +* [#6799](https://github.com/icinga/icinga2/issues/6799) (API): "Connection: close" header leads to unstable instance +* [#6797](https://github.com/icinga/icinga2/issues/6797) (Check Execution): Servertime in the future breaks check results processing +* [#6750](https://github.com/icinga/icinga2/issues/6750) (Configuration, PR): \#6749 Wrong operator on stride variable causing incorrect behaviour +* [#6749](https://github.com/icinga/icinga2/issues/6749) (Configuration): Stride is misinterpreted in multi-date legacydatetime +* [#6748](https://github.com/icinga/icinga2/issues/6748) (CLI, PR): Fix api setup to automatically create the conf.d directory +* [#6718](https://github.com/icinga/icinga2/issues/6718) (API, Cluster, PR): Call SSL\_shutdown\(\) at least twice +* [#6704](https://github.com/icinga/icinga2/issues/6704) (Notifications, PR): Put newly configured already running ScheduledDowntime immediately in effect +* [#6542](https://github.com/icinga/icinga2/issues/6542) (Configuration, Log): /var/log/icinga2/icinga2.log is growing very fast on satellites +* [#6536](https://github.com/icinga/icinga2/issues/6536) (Windows, help wanted): check\_nscp\_api: Query arguments are sorted on Url::Format\(\) +* [#4790](https://github.com/icinga/icinga2/issues/4790) (Notifications): Newly configured already running ScheduledDowntime not put into effect +* [#3937](https://github.com/icinga/icinga2/issues/3937) (API): Icinga2 API: PUT request fails at 0-byte file +* [#2844](https://github.com/icinga/icinga2/issues/2844) (Cluster): Duplicated scheduled downtimes created in cluster HA zone + +### Documentation + +* [#6956](https://github.com/icinga/icinga2/issues/6956) (Documentation, PR): Escape pipe symbol in api documentation +* [#6944](https://github.com/icinga/icinga2/issues/6944) (Documentation, PR): Troubleshooting: Add notes on ephemeral port range blocking on Windows agents +* [#6928](https://github.com/icinga/icinga2/issues/6928) (Documentation, PR): Doc: Add .NET 3.5 to the windows build stack +* [#6825](https://github.com/icinga/icinga2/issues/6825) (Documentation, PR): Document that retry\_interval is only used after an active check result +* [#6819](https://github.com/icinga/icinga2/issues/6819) (Documentation, PR): Enhance and unify development docs for debug, develop, package +* [#6791](https://github.com/icinga/icinga2/issues/6791) (Documentation, PR): Docs: Add a section for DB IDO Cleanup +* [#6776](https://github.com/icinga/icinga2/issues/6776) (Documentation, PR): Doc fix: update apache section +* [#6775](https://github.com/icinga/icinga2/issues/6775) (Documentation, PR): Add technical docs for the check scheduler \(general, initial check, offsets\) +* [#6751](https://github.com/icinga/icinga2/issues/6751) (Documentation, PR): Doc fix: documentation link for apt +* [#6743](https://github.com/icinga/icinga2/issues/6743) (Documentation, PR): Doc fix: error in example path. +* [#5341](https://github.com/icinga/icinga2/issues/5341) (Documentation): Enhance development documentation + +### Support + +* [#6972](https://github.com/icinga/icinga2/issues/6972) (PR): Fix formatting in development docs +* [#6958](https://github.com/icinga/icinga2/issues/6958) (code-quality, PR): Debug: Log calls to ConfigObject::Deactivate\(\) +* [#6897](https://github.com/icinga/icinga2/issues/6897) (PR): Validate Zone::GetLocalZone\(\) before using +* [#6872](https://github.com/icinga/icinga2/issues/6872) (Windows): 2.10 is unstable \(Windows Agent\) +* [#6843](https://github.com/icinga/icinga2/issues/6843) (Tests, Windows, PR): Improve AppVeyor builds +* [#6479](https://github.com/icinga/icinga2/issues/6479) (code-quality, PR): SocketEvents: inherit from Stream +* [#6477](https://github.com/icinga/icinga2/issues/6477) (code-quality): SocketEvents: inherit from Object + +## 2.10.2 (2018-11-14) + +### Bug + +* [#6770](https://github.com/icinga/icinga2/issues/6770) (PR): Fix deadlock in GraphiteWriter +* [#6769](https://github.com/icinga/icinga2/issues/6769) (Cluster): Hanging TLS connections +* [#6759](https://github.com/icinga/icinga2/issues/6759) (Log, PR): Fix possible double free in StreamLogger::BindStream\(\) +* [#6753](https://github.com/icinga/icinga2/issues/6753): Icinga2.service state is reloading in systemd after safe-reload until systemd time-out +* [#6740](https://github.com/icinga/icinga2/issues/6740) (DB IDO, PR): DB IDO: Don't enqueue queries when the feature is paused \(HA\) +* [#6738](https://github.com/icinga/icinga2/issues/6738) (API, Cluster, PR): Ensure that API/JSON-RPC messages in the same session are processed and not stalled +* [#6736](https://github.com/icinga/icinga2/issues/6736) (Crash): Stability issues with Icinga 2.10.x +* [#6717](https://github.com/icinga/icinga2/issues/6717) (API, PR): Improve error handling for invalid child\_options for API downtime actions +* [#6712](https://github.com/icinga/icinga2/issues/6712) (API): Downtime name not returned when error occurs +* [#6711](https://github.com/icinga/icinga2/issues/6711) (API, Cluster): Slow API \(TLS-Handshake\) +* [#6709](https://github.com/icinga/icinga2/issues/6709) (PR): Fix the Icinga2 version check for versions with more than 5 characters +* [#6707](https://github.com/icinga/icinga2/issues/6707) (Compat, PR): Fix regression for wrong objects.cache path overwriting icinga2.debug file +* [#6705](https://github.com/icinga/icinga2/issues/6705) (CLI, Compat, Configuration): Crash "icinga2 object list" command with 2.10.1-1 on CentOS 7 +* [#6703](https://github.com/icinga/icinga2/issues/6703): Check command 'icinga' breaks when vars.icinga\_min\_version is defined \(2.10.x\) +* [#6635](https://github.com/icinga/icinga2/issues/6635) (API): API TLS session connection closed after 2 requests +* [#5876](https://github.com/icinga/icinga2/issues/5876) (DB IDO): IDO Work queue on the inactive node growing when switching connection between redundant master servers + +### Documentation + +* [#6714](https://github.com/icinga/icinga2/issues/6714) (Documentation, PR): Docs: Add package related changes to the upgrading docs + +### Support + +* [#6773](https://github.com/icinga/icinga2/issues/6773) (Installation, Packages, PR): Initialize ICINGA2\_ERROR\_LOG inside the systemd environment +* [#6771](https://github.com/icinga/icinga2/issues/6771) (Tests, PR): Implement unit tests for Dictionary initializers +* [#6760](https://github.com/icinga/icinga2/issues/6760) (Packages, Tests, PR): armhf: Apply workaround for timer tests with std::bind callbacks +* [#6710](https://github.com/icinga/icinga2/issues/6710) (Packages): Crash when upgrading from 2.10.0 to 2.10.1 \(SELinux related\) + +## 2.10.1 (2018-10-18) + +### Bug + +* [#6696](https://github.com/icinga/icinga2/issues/6696) (PR): Remove default environment, regression from e678fa1aa5 +* [#6694](https://github.com/icinga/icinga2/issues/6694): v2.10.0 sets a default environment "production" in SNI +* [#6691](https://github.com/icinga/icinga2/issues/6691) (PR): Add missing shutdown/program state dumps for SIGUSR2 reload handler +* [#6689](https://github.com/icinga/icinga2/issues/6689): State file not updated on reload +* [#6685](https://github.com/icinga/icinga2/issues/6685) (API, PR): Fix regression with API permission filters and namespaces in v2.10 +* [#6682](https://github.com/icinga/icinga2/issues/6682) (API): API process-check-result fails in 2.10.0 +* [#6679](https://github.com/icinga/icinga2/issues/6679) (Windows, PR): Initialize Configuration::InitRunDir for Windows and writing the PID file +* [#6624](https://github.com/icinga/icinga2/issues/6624) (Check Execution): Master Reload Causes Passive Check State Change +* [#6592](https://github.com/icinga/icinga2/issues/6592): Reloads seem to reset the check atempt count. Also notifications go missing shortly after a reload. + +### Documentation + +* [#6701](https://github.com/icinga/icinga2/issues/6701) (Documentation, PR): Add GitHub release tag to README +* [#6700](https://github.com/icinga/icinga2/issues/6700) (Documentation, PR): Enhance the addon chapter in the docs +* [#6699](https://github.com/icinga/icinga2/issues/6699) (Documentation, PR): Update to https://icinga.com/ +* [#6692](https://github.com/icinga/icinga2/issues/6692) (Documentation, PR): Update release docs for Chocolatey +* [#6690](https://github.com/icinga/icinga2/issues/6690) (Documentation, PR): Extend 09-object-types.md with argument array +* [#6674](https://github.com/icinga/icinga2/issues/6674) (Documentation, PR): Add a note to the docs on \>2 endpoints in a zone +* [#6673](https://github.com/icinga/icinga2/issues/6673) (Documentation, PR): Update RELEASE docs +* [#6672](https://github.com/icinga/icinga2/issues/6672) (Documentation, PR): Extend upgrade docs +* [#6671](https://github.com/icinga/icinga2/issues/6671) (Documentation): Zone requirements changed in 2.10 - Undocumented Change + +### Support + +* [#6681](https://github.com/icinga/icinga2/issues/6681) (code-quality, PR): Fix spelling errors. +* [#6677](https://github.com/icinga/icinga2/issues/6677) (Packages, Windows): icinga does not start after Update to 2.10 + +## 2.10.0 (2018-10-11) + +### Notes + +* Support for namespaces, details in [this blogpost](https://icinga.com/2018/09/17/icinga-2-dsl-feature-namespaces-coming-in-v2-10/) +* Only send acknowledgement notification to users notified about a problem before, thanks for sponsoring to the [Max-Planck-Institut for Marine Mikrobiologie](https://www.mpi-bremen.de) +* More child options for scheduled downtimes +* Performance improvements and fixes for the TLS connections inside cluster/REST API +* Better logging for HTTP requests and less verbose object creation (e.g. downtimes via Icinga Web 2 & REST API) +* New configuration path constants, e.g. ConfigDir +* Fixed problem with dependencies rescheduling parent checks too fast +* Fixed problem with logging in systemd and syslog +* Improved vim syntax highlighting +* [Technical concepts docs](https://icinga.com/docs/icinga2/latest/doc/19-technical-concepts/) update with config compiler and TLS network IO + +### Enhancement + +* [#6663](https://github.com/icinga/icinga2/issues/6663) (API, Log, PR): Silence config compiler logging for runtime created objects +* [#6657](https://github.com/icinga/icinga2/issues/6657) (API, Log, PR): Enable the HTTP request body debug log entry for release builds +* [#6655](https://github.com/icinga/icinga2/issues/6655) (API, Log, PR): Improve logging for disconnected HTTP clients +* [#6651](https://github.com/icinga/icinga2/issues/6651) (Plugins, PR): Add 'used' feature to check\_swap +* [#6633](https://github.com/icinga/icinga2/issues/6633) (API, Cluster, PR): Use a dynamic thread pool for API connections +* [#6632](https://github.com/icinga/icinga2/issues/6632) (Cluster, PR): Increase the cluster reconnect frequency to 10s +* [#6616](https://github.com/icinga/icinga2/issues/6616) (API, Cluster, PR): Add ApiListener\#tls\_handshake\_timeout option +* [#6611](https://github.com/icinga/icinga2/issues/6611) (Notifications): Allow types = \[ Recovery \] to always send recovery notifications +* [#6595](https://github.com/icinga/icinga2/issues/6595) (API, Cluster, PR): Allow to configure anonymous clients limit inside the ApiListener object +* [#6532](https://github.com/icinga/icinga2/issues/6532) (Configuration, PR): Add child\_options to ScheduledDowntime +* [#6531](https://github.com/icinga/icinga2/issues/6531) (API, PR): Expose Zone\#all\_parents via API +* [#6527](https://github.com/icinga/icinga2/issues/6527) (Notifications, PR): Acknowledgment notifications should only be send if problem notification has been send +* [#6521](https://github.com/icinga/icinga2/issues/6521) (Configuration, PR): Implement references +* [#6512](https://github.com/icinga/icinga2/issues/6512) (Cluster, PR): Refactor environment for API connections +* [#6511](https://github.com/icinga/icinga2/issues/6511) (Cluster, PR): ApiListener: Add support for dynamic port handling +* [#6509](https://github.com/icinga/icinga2/issues/6509) (Configuration, PR): Implement support for namespaces +* [#6508](https://github.com/icinga/icinga2/issues/6508) (Configuration, PR): Implement the Dictionary\#clear script function +* [#6506](https://github.com/icinga/icinga2/issues/6506) (PR): Improve path handling in cmake and daemon +* [#6460](https://github.com/icinga/icinga2/issues/6460) (Log, help wanted): Feature suggestion: Do not log warnings when env elements are undefined in CheckCommand objects +* [#6455](https://github.com/icinga/icinga2/issues/6455) (Log, PR): Log something when the Filelogger has been started +* [#6379](https://github.com/icinga/icinga2/issues/6379) (Configuration, PR): Throw config error when using global zones as parent +* [#6356](https://github.com/icinga/icinga2/issues/6356) (Log, PR): Fix logging under systemd +* [#6339](https://github.com/icinga/icinga2/issues/6339) (Log, help wanted): On systemd, icinga2 floods the system log, and this cannot simply be opted out of +* [#6110](https://github.com/icinga/icinga2/issues/6110) (Configuration, PR): Implement support for optionally specifying the 'var' keyword in 'for' loops +* [#6047](https://github.com/icinga/icinga2/issues/6047) (Notifications): Acknowledgment notifications should only be sent if the user already received a problem notification +* [#4282](https://github.com/icinga/icinga2/issues/4282) (API, Log): Icinga should log HTTP bodies for API requests + +### Bug + +* [#6658](https://github.com/icinga/icinga2/issues/6658) (API, PR): Ensure that HTTP/1.0 or 'Connection: close' headers are properly disconnecting the client +* [#6652](https://github.com/icinga/icinga2/issues/6652) (Plugins, PR): Fix check\_memory thresholds in 'used' mode +* [#6647](https://github.com/icinga/icinga2/issues/6647) (CLI, PR): node setup: always respect --accept-config and --accept-commands +* [#6643](https://github.com/icinga/icinga2/issues/6643) (Check Execution, Notifications, PR): Fix that check\_timeout was used for Event/Notification commands too +* [#6639](https://github.com/icinga/icinga2/issues/6639) (Windows, PR): Ensure to \_unlink before renaming replay log on Windows +* [#6622](https://github.com/icinga/icinga2/issues/6622) (DB IDO, PR): Ensure to use UTC timestamps for IDO PgSQL cleanup queries +* [#6603](https://github.com/icinga/icinga2/issues/6603) (Check Execution, Cluster): CheckCommand 'icinga' seems to ignore retry interval via command\_endpoint +* [#6575](https://github.com/icinga/icinga2/issues/6575): LTO builds fail on Linux +* [#6566](https://github.com/icinga/icinga2/issues/6566) (Cluster): Master disconnects during signing process +* [#6546](https://github.com/icinga/icinga2/issues/6546) (API, CLI, PR): Overridden path constants not passed to config validation in /v1/config/stages API call +* [#6530](https://github.com/icinga/icinga2/issues/6530) (DB IDO, PR): IDO/MySQL: avoid empty queries +* [#6519](https://github.com/icinga/icinga2/issues/6519) (CLI, PR): Reset terminal on erroneous console exit +* [#6517](https://github.com/icinga/icinga2/issues/6517) (Cluster): Not all Endpoints can't reconnect due to "Client TLS handshake failed" error after "reload or restart" +* [#6514](https://github.com/icinga/icinga2/issues/6514) (API): API using "Connection: close" header results in infinite threads +* [#6507](https://github.com/icinga/icinga2/issues/6507) (Cluster): Variable name conflict in constants.conf / Problem with TLS verification, CN and Environment variable +* [#6503](https://github.com/icinga/icinga2/issues/6503) (Log, PR): Reduce the log level for missing env macros to debug +* [#6485](https://github.com/icinga/icinga2/issues/6485) (Log): Icinga logs discarding messages still as warning and not as notice +* [#6475](https://github.com/icinga/icinga2/issues/6475) (Compat, PR): lib-\>compat-\>statusdatawriter: fix notifications\_enabled +* [#6430](https://github.com/icinga/icinga2/issues/6430) (Log, PR): Fix negative 'empty in' value in WorkQueue log message +* [#6427](https://github.com/icinga/icinga2/issues/6427) (Configuration, Crash, PR): Improve error message for serializing objects with recursive references +* [#6409](https://github.com/icinga/icinga2/issues/6409) (Configuration, Crash): Assigning vars.x = vars causes Icinga 2 segfaults +* [#6408](https://github.com/icinga/icinga2/issues/6408) (PR): ObjectLock\#Unlock\(\): don't reset m\_Object-\>m\_LockOwner too early +* [#6386](https://github.com/icinga/icinga2/issues/6386) (Configuration, PR): Fix that TimePeriod segments are not cleared on restart +* [#6382](https://github.com/icinga/icinga2/issues/6382) (CLI, help wanted): icinga2 console breaks the terminal on errors +* [#6313](https://github.com/icinga/icinga2/issues/6313) (Plugins, Windows, PR): Fix wrong calculation of check\_swap windows plugin +* [#6304](https://github.com/icinga/icinga2/issues/6304) (Configuration, Notifications): Timeout defined in NotificationCommand is ignored and uses check\_timeout +* [#5815](https://github.com/icinga/icinga2/issues/5815) (Plugins, Windows): swap-windows check delivers wrong result +* [#5375](https://github.com/icinga/icinga2/issues/5375) (Check Execution, PR): Parents who are non-active should not be rescheduled +* [#5052](https://github.com/icinga/icinga2/issues/5052) (Cluster, Windows): Replay log not working with Windows client +* [#5022](https://github.com/icinga/icinga2/issues/5022) (Check Execution): Dependencies may reschedule passive checks, triggering freshness checks + +### ITL + +* [#6646](https://github.com/icinga/icinga2/issues/6646) (ITL, PR): Update ITL and Docs for memory-windows - show used +* [#6640](https://github.com/icinga/icinga2/issues/6640) (ITL): Update ITL and Docs for memory-windows - show used +* [#6563](https://github.com/icinga/icinga2/issues/6563) (ITL, PR): \[Feature\] Cloudera service health CheckCommand +* [#6561](https://github.com/icinga/icinga2/issues/6561) (ITL, PR): \[Feature\] Ceph health CheckCommand +* [#6504](https://github.com/icinga/icinga2/issues/6504) (ITL, PR): squashfs ignored +* [#6491](https://github.com/icinga/icinga2/issues/6491) (ITL, PR): Feature/itl vmware health +* [#6481](https://github.com/icinga/icinga2/issues/6481) (ITL): command-plugins.conf check\_disk exclude squashfs + +### Documentation + +* [#6670](https://github.com/icinga/icinga2/issues/6670) (Documentation, PR): Add technical concepts for the config compiler and daemon CLI command +* [#6665](https://github.com/icinga/icinga2/issues/6665) (Documentation, PR): Make the two modes of check\_http more obvious. +* [#6615](https://github.com/icinga/icinga2/issues/6615) (Documentation, PR): Update distributed monitoring docs for 2.10 +* [#6610](https://github.com/icinga/icinga2/issues/6610) (Documentation, PR): Add "TLS Network IO" into technical concepts docs +* [#6607](https://github.com/icinga/icinga2/issues/6607) (Documentation, PR): Enhance development docs with GDB backtrace and thread list +* [#6606](https://github.com/icinga/icinga2/issues/6606) (Documentation, PR): Enhance contributing docs +* [#6598](https://github.com/icinga/icinga2/issues/6598) (Documentation, PR): doc/09-object-types: states filter ignored for Acknowledgements +* [#6597](https://github.com/icinga/icinga2/issues/6597) (Documentation, PR): Add Fedora to development docs for debuginfo packages +* [#6593](https://github.com/icinga/icinga2/issues/6593) (Documentation, help wanted): Include CA Proxy in 3rd scenario in Distributed Monitoring docs +* [#6573](https://github.com/icinga/icinga2/issues/6573) (Documentation, PR): Fix operator precedence table +* [#6528](https://github.com/icinga/icinga2/issues/6528) (Documentation, PR): Document default of User\#enable\_notifications +* [#6502](https://github.com/icinga/icinga2/issues/6502) (Documentation, PR): Update 17-language-reference.md +* [#6501](https://github.com/icinga/icinga2/issues/6501) (Documentation, PR): Update 03-monitoring-basics.md +* [#6488](https://github.com/icinga/icinga2/issues/6488) (Documentation, ITL, PR): Fix typo with the CheckCommand cert + +### Support + +* [#6669](https://github.com/icinga/icinga2/issues/6669) (PR): Don't throw an error when namespace indexers don't find a valid key +* [#6668](https://github.com/icinga/icinga2/issues/6668) (Installation, PR): Enhance vim syntax highlighting for 2.10 +* [#6661](https://github.com/icinga/icinga2/issues/6661) (API, Log, code-quality, PR): Cache the peer address in the HTTP server +* [#6642](https://github.com/icinga/icinga2/issues/6642) (PR): Allow to override MaxConcurrentChecks constant +* [#6621](https://github.com/icinga/icinga2/issues/6621) (code-quality, PR): Remove unused timestamp function in DB IDO +* [#6618](https://github.com/icinga/icinga2/issues/6618) (PR): Silence compiler warning for nice\(\) +* [#6591](https://github.com/icinga/icinga2/issues/6591) (PR): Fix static initializer priority for namespaces in LTO builds +* [#6588](https://github.com/icinga/icinga2/issues/6588) (PR): Fix using full path in prepare-dirs/safe-reload scripts +* [#6586](https://github.com/icinga/icinga2/issues/6586) (PR): Fix non-unity builds on CentOS 7 with std::shared\_ptr +* [#6583](https://github.com/icinga/icinga2/issues/6583) (Documentation, Installation, PR): Update PostgreSQL library path variable in INSTALL.md +* [#6574](https://github.com/icinga/icinga2/issues/6574) (PR): Move new downtime constants into the Icinga namespace +* [#6570](https://github.com/icinga/icinga2/issues/6570) (Cluster, PR): Increase limit for simultaneously connected anonymous TLS clients +* [#6567](https://github.com/icinga/icinga2/issues/6567) (PR): ApiListener: Dump the state file port detail as number +* [#6556](https://github.com/icinga/icinga2/issues/6556) (Installation, Windows, PR): windows: Allow suppression of extra actions in the MSI package +* [#6544](https://github.com/icinga/icinga2/issues/6544) (code-quality, PR): Remove \#include for deprecated header file +* [#6539](https://github.com/icinga/icinga2/issues/6539) (PR): Build fix for CentOS 7 and non-unity builds +* [#6526](https://github.com/icinga/icinga2/issues/6526) (code-quality, PR): icinga::PackObject\(\): shorten conversion to string +* [#6510](https://github.com/icinga/icinga2/issues/6510) (Tests, Windows, PR): Update windows build scripts +* [#6494](https://github.com/icinga/icinga2/issues/6494) (Tests, PR): Test PackObject +* [#6489](https://github.com/icinga/icinga2/issues/6489) (code-quality, PR): Implement object packer for consistent hashing +* [#6484](https://github.com/icinga/icinga2/issues/6484) (Packages): Packages from https://packages.icinga.com are not Systemd Type=notify enabled? +* [#6469](https://github.com/icinga/icinga2/issues/6469) (Installation, Windows, PR): Fix Windows Agent resize behavior +* [#6458](https://github.com/icinga/icinga2/issues/6458) (code-quality, PR): Fix debug build log entry for ConfigItem activation priority +* [#6456](https://github.com/icinga/icinga2/issues/6456) (code-quality, PR): Keep notes for immediately log flushing +* [#6440](https://github.com/icinga/icinga2/issues/6440) (code-quality, PR): Fix typo +* [#6410](https://github.com/icinga/icinga2/issues/6410) (code-quality, PR): Remove unused code +* [#4959](https://github.com/icinga/icinga2/issues/4959) (Installation, Windows): Windows Agent Wizard Window resizes with screen, hiding buttons + +## 2.9.2 (2018-09-26) + +### Enhancement + +* [#6602](https://github.com/icinga/icinga2/issues/6602) (API, Cluster, PR): Improve TLS handshake exception logging +* [#6568](https://github.com/icinga/icinga2/issues/6568) (Configuration, PR): Ensure that config object types are committed in dependent load order +* [#6497](https://github.com/icinga/icinga2/issues/6497) (Configuration, PR): Improve error logging for match/regex/cidr\_match functions and unsupported dictionary usage + +### Bug + +* [#6596](https://github.com/icinga/icinga2/issues/6596) (Crash, PR): Fix crash on API queries with Fedora 28 hardening and GCC 8 +* [#6581](https://github.com/icinga/icinga2/issues/6581) (Configuration, PR): Shuffle items before config validation +* [#6569](https://github.com/icinga/icinga2/issues/6569) (DB IDO): Custom Vars not updated after upgrade +* [#6533](https://github.com/icinga/icinga2/issues/6533) (Crash): Icinga2 crashes after using some api-commands on Fedora 28 +* [#6505](https://github.com/icinga/icinga2/issues/6505) (Cluster, PR): Fix clusterzonecheck if not connected +* [#6498](https://github.com/icinga/icinga2/issues/6498) (Configuration, PR): Fix regression with MatchAny false conditions on match/regex/cidr\_match +* [#6496](https://github.com/icinga/icinga2/issues/6496) (Configuration): error with match and type matchany + +### Documentation + +* [#6590](https://github.com/icinga/icinga2/issues/6590) (DB IDO, Documentation, PR): Update workaround for custom vars +* [#6572](https://github.com/icinga/icinga2/issues/6572) (Documentation, PR): Add note about workaround for broken custom vars + +### Support + +* [#6540](https://github.com/icinga/icinga2/issues/6540) (Configuration): Evaluate a fixed config compiler commit order +* [#6486](https://github.com/icinga/icinga2/issues/6486) (Configuration): Configuration validation w/ ScheduledDowntimes performance decreased in 2.9 +* [#6442](https://github.com/icinga/icinga2/issues/6442) (Configuration): Error while evaluating "assign where match" expression: std::bad\_cast + +## 2.9.1 (2018-07-24) + +### Bug + +* [#6457](https://github.com/icinga/icinga2/issues/6457) (PR): Ensure that timer thread is initialized after Daemonize\(\) +* [#6449](https://github.com/icinga/icinga2/issues/6449): icinga r2.9.0-1 init.d script overrides PATH variable +* [#6445](https://github.com/icinga/icinga2/issues/6445): Problem with daemonize \(init scripts, -d\) on Debian 8 / CentOS 6 / Ubuntu 14 / SLES 11 in 2.9 +* [#6444](https://github.com/icinga/icinga2/issues/6444) (PR): SELinux: allow systemd notify +* [#6443](https://github.com/icinga/icinga2/issues/6443): selinux and 2.9 + +### Support + +* [#6470](https://github.com/icinga/icinga2/issues/6470) (code-quality, PR): Fix spelling errors. +* [#6467](https://github.com/icinga/icinga2/issues/6467) (Tests, PR): Start and stop the timer thread lazily +* [#6461](https://github.com/icinga/icinga2/issues/6461) (Tests): Broken tests with fix from \#6457 +* [#6451](https://github.com/icinga/icinga2/issues/6451) (Packages, PR): Fix initscripts +* [#6450](https://github.com/icinga/icinga2/issues/6450) (Packages): init script helpers - source: not found + +## 2.9.0 (2018-07-17) + +### Notes + +- Elasticsearch 6 Support +- icinga health check supports minimum version parameter, ido thresholds for query rate, dummy check is executed in-memory, avoids plugin call +- `ApplicationVersion` constant in the configuration +- Setup wizards: global zone, disable conf.d inclusion, unified parameter handling +- TTL support for check results, pretty formatting for REST API queries +- TLS support for IDO PostgreSQL +- Improvements for check scheduling, concurrent checks with command endpoints, downtime notification handling, scheduled downtimes and memory handling with many API requests + +### Enhancement + +* [#6400](https://github.com/icinga/icinga2/issues/6400) (Plugins, Windows, PR): Enhance debug logging for check\_nscp\_api +* [#6321](https://github.com/icinga/icinga2/issues/6321) (Log, PR): Update log message for skipped certificate renewal +* [#6305](https://github.com/icinga/icinga2/issues/6305) (PR): Introduce the 'Environment' variable +* [#6299](https://github.com/icinga/icinga2/issues/6299) (Check Execution, Log, PR): Change log level for failed event command execution +* [#6285](https://github.com/icinga/icinga2/issues/6285) (CLI, Log, PR): Add support for config validation log timestamps +* [#6270](https://github.com/icinga/icinga2/issues/6270) (Configuration, PR): Add activation priority for config object types +* [#6236](https://github.com/icinga/icinga2/issues/6236) (DB IDO, PR): Add TLS support for DB IDO PostgreSQL feature +* [#6219](https://github.com/icinga/icinga2/issues/6219) (Elasticsearch, PR): Add support for Elasticsearch 6 +* [#6211](https://github.com/icinga/icinga2/issues/6211) (DB IDO): IDO pgsql with TLS support +* [#6209](https://github.com/icinga/icinga2/issues/6209) (CLI, PR): Unify zone name settings in node setup/wizard; add connection-less mode for node setup +* [#6208](https://github.com/icinga/icinga2/issues/6208) (CLI): Add connection-less support for node setup CLI command +* [#6206](https://github.com/icinga/icinga2/issues/6206) (Configuration, PR): Add ApplicationVersion built-in constant +* [#6205](https://github.com/icinga/icinga2/issues/6205) (API, PR): API: Unify verbose error messages +* [#6194](https://github.com/icinga/icinga2/issues/6194) (Elasticsearch, Graylog, PR): Elasticsearch/GELF: Add metric unit to performance data fields +* [#6170](https://github.com/icinga/icinga2/issues/6170) (Configuration, Windows, PR): Add option to windows installer to add global zones +* [#6158](https://github.com/icinga/icinga2/issues/6158) (API, Log): Review API debugging: verboseErrors and diagnostic information +* [#6136](https://github.com/icinga/icinga2/issues/6136) (Check Execution, PR): Add counter for current concurrent checks to Icinga check +* [#6131](https://github.com/icinga/icinga2/issues/6131) (Log, PR): Log which ticket was invalid on the master +* [#6109](https://github.com/icinga/icinga2/issues/6109) (Plugins, PR): Add 'used' feature to check\_memory +* [#6090](https://github.com/icinga/icinga2/issues/6090) (Notifications, PR): Fixed URL encoding for HOSTNAME and SERVICENAME in mail notification +* [#6078](https://github.com/icinga/icinga2/issues/6078) (Check Execution, PR): Add more metrics and details to built-in 'random' check +* [#6039](https://github.com/icinga/icinga2/issues/6039) (Configuration, PR): Improve location info for some error messages +* [#6033](https://github.com/icinga/icinga2/issues/6033) (Compat): Deprecate StatusDataWriter +* [#6032](https://github.com/icinga/icinga2/issues/6032) (Compat): Deprecate CompatLogger +* [#6010](https://github.com/icinga/icinga2/issues/6010) (Cluster, PR): Move the endpoint list into a new line for the 'cluster' check +* [#5996](https://github.com/icinga/icinga2/issues/5996) (PR): Add systemd watchdog and adjust reload behaviour +* [#5985](https://github.com/icinga/icinga2/issues/5985) (DB IDO, PR): Add query thresholds for the 'ido' check: Rate and pending queries +* [#5979](https://github.com/icinga/icinga2/issues/5979) (CLI, PR): Add quit, exit and help +* [#5973](https://github.com/icinga/icinga2/issues/5973) (API, Check Execution, PR): Add 'ttl' support for check result freshness via REST API +* [#5959](https://github.com/icinga/icinga2/issues/5959) (API, PR): API: Add 'pretty' parameter for beautified JSON response bodies +* [#5905](https://github.com/icinga/icinga2/issues/5905) (Elasticsearch): Add support for Elasticsearch 6 +* [#5888](https://github.com/icinga/icinga2/issues/5888) (DB IDO, PR): FindMySQL: Support mariadbclient implementation +* [#5877](https://github.com/icinga/icinga2/issues/5877) (API): Add pretty format to REST API parameters \(for debugging\) +* [#5811](https://github.com/icinga/icinga2/issues/5811) (CLI, PR): Update NodeName/ZoneName constants with 'api setup' +* [#5767](https://github.com/icinga/icinga2/issues/5767) (CLI, PR): Implement ability to make global zones configurable during node wizard/setup +* [#5733](https://github.com/icinga/icinga2/issues/5733) (Plugins, Windows, PR): Make --perf-syntax also change short message +* [#5729](https://github.com/icinga/icinga2/issues/5729) (CLI, Cluster, PR): Correct node wizard output formatting +* [#5675](https://github.com/icinga/icinga2/issues/5675) (InfluxDB, PR): Add pdv unit to influxdbwriter if not empty + doc +* [#5627](https://github.com/icinga/icinga2/issues/5627) (InfluxDB, Metrics): InfluxDBWriter: Send metric unit \(perfdata\) +* [#5605](https://github.com/icinga/icinga2/issues/5605) (CLI, Cluster, Configuration): Disable conf.d inclusion in node setup wizards +* [#5509](https://github.com/icinga/icinga2/issues/5509) (Cluster, wishlist): Add metrics about communication between endpoints +* [#5444](https://github.com/icinga/icinga2/issues/5444) (Cluster): Display endpoints in the second line of the ClusterCheckTask output +* [#5426](https://github.com/icinga/icinga2/issues/5426) (CLI, Configuration, PR): Add the ability to disable the conf.d inclusion through the node wizard +* [#5418](https://github.com/icinga/icinga2/issues/5418) (Plugins, Windows): Feature request: check\_perfmon.exe - Change name of counter in output +* [#4966](https://github.com/icinga/icinga2/issues/4966) (CLI, Cluster): Unify setting of master zones name +* [#4508](https://github.com/icinga/icinga2/issues/4508) (CLI): node wizard/setup: allow to disable conf.d inclusion +* [#3455](https://github.com/icinga/icinga2/issues/3455) (API, Log): startup.log in stage dir has no timestamps +* [#3245](https://github.com/icinga/icinga2/issues/3245) (CLI, help wanted, wishlist): Add option to Windows installer to add global zone during setup +* [#2287](https://github.com/icinga/icinga2/issues/2287) (help wanted, wishlist): Please support systemd startup notification + +### Bug + +* [#6429](https://github.com/icinga/icinga2/issues/6429) (PR): Make HttpServerConnection\#m\_DataHandlerMutex a boost::recursive\_mutex +* [#6428](https://github.com/icinga/icinga2/issues/6428) (API): Director kickstart wizard querying the API results in TLS stream disconnected infinite loop +* [#6411](https://github.com/icinga/icinga2/issues/6411) (Plugins, Windows, PR): Windows: Conform to the Plugin API spec for performance label quoting +* [#6407](https://github.com/icinga/icinga2/issues/6407) (Windows, PR): Fix wrong UOM in check\_uptime windows plugin +* [#6405](https://github.com/icinga/icinga2/issues/6405) (Windows, PR): TcpSocket\#Bind\(\): reuse socket addresses on Windows, too +* [#6403](https://github.com/icinga/icinga2/issues/6403) (API, PR): Conform to RFC for CRLF in HTTP requests +* [#6401](https://github.com/icinga/icinga2/issues/6401) (Elasticsearch, InfluxDB, PR): Fix connection error handling in Elasticsearch and InfluxDB features +* [#6397](https://github.com/icinga/icinga2/issues/6397) (Plugins, Windows, PR): TlsStream\#IsEof\(\): fix false positive EOF indicator +* [#6394](https://github.com/icinga/icinga2/issues/6394) (Crash, Elasticsearch): Icinga will throw an exception, if ElasticSearch is not reachable +* [#6393](https://github.com/icinga/icinga2/issues/6393) (API, Elasticsearch, PR): Stream\#ReadLine\(\): fix false positive buffer underflow indicator +* [#6387](https://github.com/icinga/icinga2/issues/6387) (Configuration, Crash, Windows, PR): Remove ApiUser password\_hash functionality +* [#6383](https://github.com/icinga/icinga2/issues/6383) (API, CLI, PR): HttpRequest\#ParseBody\(\): indicate success on complete body +* [#6378](https://github.com/icinga/icinga2/issues/6378) (Windows): Analyze Windows reload behaviour +* [#6371](https://github.com/icinga/icinga2/issues/6371) (API, Cluster, PR): ApiListener\#NewClientHandlerInternal\(\): Explicitly close the TLS stream on any failure +* [#6368](https://github.com/icinga/icinga2/issues/6368) (CLI, PR): Fix program option parsing +* [#6365](https://github.com/icinga/icinga2/issues/6365) (CLI): Different behavior between `icinga2 -V` and `icinga2 --version` +* [#6355](https://github.com/icinga/icinga2/issues/6355) (API): HTTP header size too low: Long URLs and session cookies cause bad requests +* [#6354](https://github.com/icinga/icinga2/issues/6354) (Elasticsearch): ElasticsearchWriter not writing to ES +* [#6336](https://github.com/icinga/icinga2/issues/6336) (Log, PR): Fix unnecessary blank in log message +* [#6324](https://github.com/icinga/icinga2/issues/6324) (Crash, PR): Ensure that password hash generation from OpenSSL is atomic +* [#6319](https://github.com/icinga/icinga2/issues/6319) (Windows): Windows service restart fails and config validate runs forever +* [#6297](https://github.com/icinga/icinga2/issues/6297) (Cluster, PR): Execute event commands only on actively checked host/service objects in an HA zone +* [#6294](https://github.com/icinga/icinga2/issues/6294) (API, Configuration, PR): Ensure that group memberships on API object creation are unique +* [#6292](https://github.com/icinga/icinga2/issues/6292) (Notifications, PR): Fix problem with reminder notifications if the checkable is flapping +* [#6290](https://github.com/icinga/icinga2/issues/6290) (OpenTSDB, PR): Fixed opentsdb metric name with colon chars +* [#6282](https://github.com/icinga/icinga2/issues/6282) (Configuration): Issue when using excludes in TimePeriod Objects +* [#6279](https://github.com/icinga/icinga2/issues/6279) (Crash): segfault with sha1\_block\_data\_order\_avx of libcrypto +* [#6255](https://github.com/icinga/icinga2/issues/6255) (Configuration): On debian based systems /etc/default/icinga2 is not read/used +* [#6242](https://github.com/icinga/icinga2/issues/6242) (Plugins, Windows): Sporadic check\_nscp\_api timeouts +* [#6239](https://github.com/icinga/icinga2/issues/6239) (Plugins, Windows, PR): Fix Windows check\_memory rounding +* [#6231](https://github.com/icinga/icinga2/issues/6231) (Notifications): icinga2.8 - Notifications are sent even in downtime +* [#6218](https://github.com/icinga/icinga2/issues/6218) (PR): attempt to fix issue \#5277 +* [#6217](https://github.com/icinga/icinga2/issues/6217) (Check Execution, PR): Fix check behavior on restart +* [#6204](https://github.com/icinga/icinga2/issues/6204) (API, PR): API: Check if objects exists and return proper error message +* [#6195](https://github.com/icinga/icinga2/issues/6195) (API, Crash, PR): Fix crash in remote api console +* [#6193](https://github.com/icinga/icinga2/issues/6193) (Crash, Graylog, PR): GelfWriter: Fix crash on invalid performance data metrics +* [#6184](https://github.com/icinga/icinga2/issues/6184) (API): debug console with API connection sometimes hangs since 2.8.2 +* [#6125](https://github.com/icinga/icinga2/issues/6125) (Configuration, PR): Fix description of the NotificationComponent in notification.conf +* [#6077](https://github.com/icinga/icinga2/issues/6077) (API, PR): Allow to pass raw performance data in 'process-check-result' API action +* [#6057](https://github.com/icinga/icinga2/issues/6057) (Notifications): Icinga2 sends notifications without logging about it and despite having a downtime +* [#6020](https://github.com/icinga/icinga2/issues/6020) (CLI, PR): Fix crash when running 'icinga2 console' without HOME environment variable +* [#6019](https://github.com/icinga/icinga2/issues/6019): icinga2 console -r crashes when run without a HOME environment variable +* [#6016](https://github.com/icinga/icinga2/issues/6016) (Notifications, PR): Check notification state filters for problems only, not for Custom, etc. +* [#5988](https://github.com/icinga/icinga2/issues/5988) (Check Execution, Cluster, PR): Fix concurrent checks limit while using command\_endpoint +* [#5964](https://github.com/icinga/icinga2/issues/5964) (Metrics, OpenTSDB, PR): OpenTSDB writer - Fix function for escaping host tag chars. +* [#5963](https://github.com/icinga/icinga2/issues/5963) (Metrics, OpenTSDB): OpenTSDB writer is escaping wrong chars for host names. +* [#5952](https://github.com/icinga/icinga2/issues/5952) (Notifications): Custom notifications are filtered by object state +* [#5940](https://github.com/icinga/icinga2/issues/5940) (PR): Remove deprecated Chocolatey functions +* [#5928](https://github.com/icinga/icinga2/issues/5928) (PR): Fix build problem with MSVC +* [#5908](https://github.com/icinga/icinga2/issues/5908) (Windows): Icinga2 fails to build on Windows +* [#5901](https://github.com/icinga/icinga2/issues/5901) (PR): Do not replace colons in plugin output +* [#5885](https://github.com/icinga/icinga2/issues/5885) (PR): Workaround for GCC bug 61321 +* [#5884](https://github.com/icinga/icinga2/issues/5884): Icinga2 fails to build +* [#5872](https://github.com/icinga/icinga2/issues/5872) (PR): Replace incorrect fclose\(\) call with pclose\(\) +* [#5863](https://github.com/icinga/icinga2/issues/5863) (PR): Fix glob error handling +* [#5861](https://github.com/icinga/icinga2/issues/5861) (PR): Fix incorrect memory access +* [#5860](https://github.com/icinga/icinga2/issues/5860) (PR): Fix memory leaks in the unit tests +* [#5853](https://github.com/icinga/icinga2/issues/5853) (Plugins, Windows, PR): Fix missing space in check\_service output +* [#5840](https://github.com/icinga/icinga2/issues/5840) (Elasticsearch, PR): Fix newline terminator for bulk requests in ElasticsearchWriter +* [#5796](https://github.com/icinga/icinga2/issues/5796) (CLI, PR): Fix error reporting for 'icinga2 console -r' +* [#5795](https://github.com/icinga/icinga2/issues/5795) (Elasticsearch): ElasticsearchWriter gives "Unexpected response code 400" with Elasticsearch 6.x +* [#5763](https://github.com/icinga/icinga2/issues/5763) (API): "icinga2 api setup" should explicitly set the NodeName constant in constants.conf +* [#5753](https://github.com/icinga/icinga2/issues/5753) (API, Cluster, Metrics, PR): Fix that RingBuffer does not get updated and add metrics about communication between endpoints +* [#5718](https://github.com/icinga/icinga2/issues/5718) (API, PR): API: Fix http status codes +* [#5550](https://github.com/icinga/icinga2/issues/5550) (API): Verify error codes and returned log messages in API actions +* [#5277](https://github.com/icinga/icinga2/issues/5277) (Notifications): Flexible downtime is expired at end\_time, not trigger\_time+duration +* [#5095](https://github.com/icinga/icinga2/issues/5095) (API): Wrong HTTP status code when API request fails +* [#5083](https://github.com/icinga/icinga2/issues/5083) (Check Execution): Initial checks are not executed immediately +* [#4786](https://github.com/icinga/icinga2/issues/4786) (API): API: Command process-check-result fails if it contains performance data +* [#4785](https://github.com/icinga/icinga2/issues/4785) (Compat): Semicolons in plugin output are converted to colon +* [#4732](https://github.com/icinga/icinga2/issues/4732) (API, Configuration): Duplicate groups allowed when creating host +* [#4436](https://github.com/icinga/icinga2/issues/4436) (Check Execution): New objects not scheduled to check immediately +* [#4272](https://github.com/icinga/icinga2/issues/4272) (Cluster, Configuration): Duplicating downtime from ScheduledDowntime object on each restart +* [#3431](https://github.com/icinga/icinga2/issues/3431) (Cluster): Eventhandler trigger on all endpoints in high available zone + +### ITL + +* [#6389](https://github.com/icinga/icinga2/issues/6389) (ITL, PR): New ITL command nscp-local-tasksched +* [#6348](https://github.com/icinga/icinga2/issues/6348) (ITL, PR): Fix for catalogued locally databases. Fixes \#6338 +* [#6338](https://github.com/icinga/icinga2/issues/6338) (ITL): db2\_health not working with catalogued databases, as --hostname is always used +* [#6308](https://github.com/icinga/icinga2/issues/6308) (ITL, PR): Update lsi-raid ITL command +* [#6263](https://github.com/icinga/icinga2/issues/6263) (ITL, PR): ITL: Add default thresholds to windows check commands +* [#6139](https://github.com/icinga/icinga2/issues/6139) (ITL, PR): itl/disk: Ignore overlay and netfs filesystems +* [#6045](https://github.com/icinga/icinga2/issues/6045) (ITL, PR): Move the "passive" check command to command-icinga.conf +* [#6043](https://github.com/icinga/icinga2/issues/6043) (ITL): ITL "plugins" has an implicit dependency on "itl" +* [#6034](https://github.com/icinga/icinga2/issues/6034) (ITL, PR): ITL by\_ssh add -E parameter +* [#5958](https://github.com/icinga/icinga2/issues/5958) (ITL, PR): Add minimum version check to the built-in icinga command +* [#5954](https://github.com/icinga/icinga2/issues/5954) (ITL, PR): ITL: Add mongodb --authdb parameter support +* [#5951](https://github.com/icinga/icinga2/issues/5951) (ITL, PR): itl: Add command parameters for snmp-memory +* [#5921](https://github.com/icinga/icinga2/issues/5921) (ITL, PR): Add icingacli-director check to ITL +* [#5920](https://github.com/icinga/icinga2/issues/5920) (ITL): Add Check for Director Jobs to ITL +* [#5914](https://github.com/icinga/icinga2/issues/5914) (ITL, PR): Fix for wrong attribute in ITL mongodb CheckCommand +* [#5906](https://github.com/icinga/icinga2/issues/5906) (ITL, PR): Add check\_openmanage command to ITL. +* [#5902](https://github.com/icinga/icinga2/issues/5902) (ITL, PR): Add parameter --octetlength to snmp-storage command. +* [#5817](https://github.com/icinga/icinga2/issues/5817) (ITL): mongodb\_address vs mongodb\_host +* [#5812](https://github.com/icinga/icinga2/issues/5812) (ITL): Better way to check required parameters in notification scripts +* [#5805](https://github.com/icinga/icinga2/issues/5805) (ITL, PR): Add support for LD\_LIBRARY\_PATH env variable in oracle\_health ITL CheckCommand +* [#5792](https://github.com/icinga/icinga2/issues/5792) (ITL, PR): ITL: Add check\_rpc +* [#5787](https://github.com/icinga/icinga2/issues/5787) (Check Execution, ITL): random check should provide performance data metrics +* [#5744](https://github.com/icinga/icinga2/issues/5744) (Check Execution, ITL, PR): Implement DummyCheckTask and move dummy into embedded in-memory checks +* [#5717](https://github.com/icinga/icinga2/issues/5717) (ITL, PR): add order tags to disk check +* [#5714](https://github.com/icinga/icinga2/issues/5714) (ITL): disk check in icinga2/itl/command-plugins.conf lacks order tags +* [#5260](https://github.com/icinga/icinga2/issues/5260) (ITL): CheckCommand mongodb does not expose authdb option + +### Documentation + +* [#6436](https://github.com/icinga/icinga2/issues/6436) (Documentation, PR): Update tested Elasticsearch version +* [#6435](https://github.com/icinga/icinga2/issues/6435) (Documentation, PR): Add note on sysconfig shell variables for Systemd to the Upgrading docs +* [#6433](https://github.com/icinga/icinga2/issues/6433) (Documentation, PR): Docs: Fix typos in 03-monitoring-basics.md +* [#6426](https://github.com/icinga/icinga2/issues/6426) (Documentation, PR): Update 'Upgrading to 2.9' docs +* [#6413](https://github.com/icinga/icinga2/issues/6413) (Documentation, PR): Fix table in Livestatus Filters +* [#6391](https://github.com/icinga/icinga2/issues/6391) (Documentation, PR): Docs: Fix icinga.com link +* [#6390](https://github.com/icinga/icinga2/issues/6390) (Documentation, Windows, PR): Docs: Update Windows wizard images +* [#6375](https://github.com/icinga/icinga2/issues/6375) (Documentation, PR): some minor fixes in the flapping documentation +* [#6374](https://github.com/icinga/icinga2/issues/6374) (Documentation, PR): Docs: Add an additional note for VMWare timeouts on Ubuntu 16.04 LTS +* [#6373](https://github.com/icinga/icinga2/issues/6373) (Documentation, PR): Drop command template imports for versions \< 2.6 in the docs +* [#6372](https://github.com/icinga/icinga2/issues/6372) (Documentation, PR): Remove the import of 'legacy-timeperiod' in the docs +* [#6350](https://github.com/icinga/icinga2/issues/6350) (Documentation, PR): clarify the permision system of the api in the docs +* [#6344](https://github.com/icinga/icinga2/issues/6344) (Documentation, PR): README: Fix broken community link +* [#6330](https://github.com/icinga/icinga2/issues/6330) (Documentation, PR): Fix $ipaddress6$ attribute name typo in the docs +* [#6317](https://github.com/icinga/icinga2/issues/6317) (Documentation, PR): Add a note on Windows NSClient++ CPU checks to the docs +* [#6289](https://github.com/icinga/icinga2/issues/6289) (Documentation, PR): Update release documentation with git tag signing key configuration +* [#6286](https://github.com/icinga/icinga2/issues/6286) (Documentation): Update Windows wizard screenshots in the docs +* [#6283](https://github.com/icinga/icinga2/issues/6283) (Documentation, PR): edit Icinga license info so that GitHub recognizes it +* [#6271](https://github.com/icinga/icinga2/issues/6271) (Documentation, PR): Enhance advanced topics with \(scheduled\) downtimes +* [#6267](https://github.com/icinga/icinga2/issues/6267) (Documentation, PR): Update docs to reflect required user\* attributes for notification objects +* [#6265](https://github.com/icinga/icinga2/issues/6265) (Documentation): Notifications user/user\_groups required +* [#6264](https://github.com/icinga/icinga2/issues/6264) (Documentation, PR): Enhance "Getting Started" chapter +* [#6262](https://github.com/icinga/icinga2/issues/6262) (Documentation, PR): Enhance the environment variables chapter +* [#6254](https://github.com/icinga/icinga2/issues/6254) (Documentation, PR): Enhance release documentation +* [#6253](https://github.com/icinga/icinga2/issues/6253) (Documentation, PR): Doc: Add note for not fully supported Plugin collections +* [#6243](https://github.com/icinga/icinga2/issues/6243) (Documentation, PR): Update PostgreSQL documentation +* [#6226](https://github.com/icinga/icinga2/issues/6226) (Documentation, PR): Fix broken SELinux anchor in the documentation +* [#6224](https://github.com/icinga/icinga2/issues/6224) (Documentation, PR): Update volatile docs +* [#6216](https://github.com/icinga/icinga2/issues/6216) (Documentation): Volatile service explanation +* [#6180](https://github.com/icinga/icinga2/issues/6180) (Documentation, PR): Doc: fixed wrong information about defaulting +* [#6128](https://github.com/icinga/icinga2/issues/6128) (Documentation, PR): Adding documentation for configurable global zones during setup +* [#6067](https://github.com/icinga/icinga2/issues/6067) (Documentation, Windows, PR): Improve Windows builds and testing +* [#6022](https://github.com/icinga/icinga2/issues/6022) (Configuration, Documentation, PR): Update default config and documentation for the "library" keyword +* [#6018](https://github.com/icinga/icinga2/issues/6018) (Documentation): Move init configuration from getting-started +* [#6000](https://github.com/icinga/icinga2/issues/6000) (Documentation, PR): Add newline to COPYING to fix Github license detection +* [#5948](https://github.com/icinga/icinga2/issues/5948) (Documentation, PR): doc: Improve INSTALL documentation +* [#4958](https://github.com/icinga/icinga2/issues/4958) (Check Execution, Documentation): How to set the HOME environment variable + +### Support + +* [#6439](https://github.com/icinga/icinga2/issues/6439) (PR): Revert "Fix obsolete parameter in Systemd script" +* [#6423](https://github.com/icinga/icinga2/issues/6423) (PR): Fix missing next check update causing the scheduler to execute checks too often +* [#6421](https://github.com/icinga/icinga2/issues/6421) (Check Execution): High CPU load due to seemingly ignored check\_interval +* [#6412](https://github.com/icinga/icinga2/issues/6412) (Plugins, Windows, PR): Fix output formatting in windows plugins +* [#6402](https://github.com/icinga/icinga2/issues/6402) (Cluster, code-quality, PR): Use SSL\_pending\(\) for remaining TLS stream data +* [#6384](https://github.com/icinga/icinga2/issues/6384) (PR): Remove leftover for sysconfig file parsing +* [#6381](https://github.com/icinga/icinga2/issues/6381) (Packages, PR): Fix sysconfig not being handled correctly by sysvinit +* [#6377](https://github.com/icinga/icinga2/issues/6377) (code-quality, PR): Fix missing name for workqueue while creating runtime objects via API +* [#6364](https://github.com/icinga/icinga2/issues/6364) (code-quality): lib/base/workqueue.cpp:212: assertion failed: !m\_Name.IsEmpty\(\) +* [#6361](https://github.com/icinga/icinga2/issues/6361) (API, Cluster): Analyse socket IO handling with HTTP/JSON-RPC +* [#6359](https://github.com/icinga/icinga2/issues/6359) (Configuration, PR): Fix ScheduledDowntimes replicating on restart +* [#6357](https://github.com/icinga/icinga2/issues/6357) (API, PR): Increase header size to 8KB for HTTP requests +* [#6347](https://github.com/icinga/icinga2/issues/6347) (Packages, PR): SELinux: Allow notification plugins to read local users +* [#6343](https://github.com/icinga/icinga2/issues/6343) (Check Execution, Cluster, PR): Fix that checks with command\_endpoint don't return any check results +* [#6337](https://github.com/icinga/icinga2/issues/6337): Checks via command\_endpoint are not executed \(snapshot packages only\) +* [#6328](https://github.com/icinga/icinga2/issues/6328) (Installation, Packages, PR): Rework sysconfig file/startup environment +* [#6320](https://github.com/icinga/icinga2/issues/6320) (PR): Ensure that icinga\_min\_version parameter is optional +* [#6309](https://github.com/icinga/icinga2/issues/6309) (PR): Fix compiler warning in checkercomponent.ti +* [#6306](https://github.com/icinga/icinga2/issues/6306) (code-quality, PR): Adjust message for CheckResultReader deprecation +* [#6301](https://github.com/icinga/icinga2/issues/6301) (Documentation, code-quality, PR): Adjust deprecation removal for compat features +* [#6295](https://github.com/icinga/icinga2/issues/6295) (Compat, PR): Deprecate compatlog feature +* [#6238](https://github.com/icinga/icinga2/issues/6238) (Notifications, PR): Implement better way to check parameters in notification scripts +* [#6233](https://github.com/icinga/icinga2/issues/6233) (Check Execution): Verify next check execution on daemon reload +* [#6229](https://github.com/icinga/icinga2/issues/6229) (Packages, PR): Don't use shell variables in sysconfig +* [#6214](https://github.com/icinga/icinga2/issues/6214) (Packages): Reload-internal with unresolved shell variable +* [#6201](https://github.com/icinga/icinga2/issues/6201) (Windows, PR): Handle exceptions from X509Certificate2 +* [#6199](https://github.com/icinga/icinga2/issues/6199) (API, PR): Return 500 when no api action is successful +* [#6198](https://github.com/icinga/icinga2/issues/6198) (Compat, PR): Deprecate Statusdatawriter +* [#6187](https://github.com/icinga/icinga2/issues/6187) (code-quality, PR): Remove Icinga Studio Screenshots +* [#6181](https://github.com/icinga/icinga2/issues/6181) (Tests, PR): tests: Ensure IcingaApplication is initialized before adding config +* [#6174](https://github.com/icinga/icinga2/issues/6174) (API, PR): Fix crash without CORS setting +* [#6173](https://github.com/icinga/icinga2/issues/6173) (API, Crash): Using the API crashes Icinga2 in v2.8.1-537-g064fc80 +* [#6171](https://github.com/icinga/icinga2/issues/6171) (code-quality, PR): Update copyright of the Windows Agent to 2018 +* [#6163](https://github.com/icinga/icinga2/issues/6163) (PR): Fix reload handling by updating the PID file before process overtake +* [#6160](https://github.com/icinga/icinga2/issues/6160) (code-quality, PR): Replace std::vector:push\_back calls with initializer list +* [#6126](https://github.com/icinga/icinga2/issues/6126) (PR): Require systemd headers +* [#6113](https://github.com/icinga/icinga2/issues/6113) (Tests, PR): appveyor: Disable artifacts until we use them +* [#6107](https://github.com/icinga/icinga2/issues/6107) (code-quality, PR): Allow MYSQL\_LIB to be specified by ENV variable +* [#6105](https://github.com/icinga/icinga2/issues/6105) (Tests): Snapshot builds fail on livestatus tests +* [#6098](https://github.com/icinga/icinga2/issues/6098) (API, code-quality, PR): Clean up CORS implementation +* [#6085](https://github.com/icinga/icinga2/issues/6085) (Cluster, Crash, PR): Fix crash with anonymous clients on certificate signing request and storing sent bytes +* [#6083](https://github.com/icinga/icinga2/issues/6083) (Log, code-quality, PR): Fix wrong type logging in ConfigItem::Commit +* [#6082](https://github.com/icinga/icinga2/issues/6082) (Installation, Packages): PID file removed after reload +* [#6063](https://github.com/icinga/icinga2/issues/6063) (Compat, PR): Deprecate CheckResultReader +* [#6062](https://github.com/icinga/icinga2/issues/6062) (code-quality, PR): Remove the obsolete 'make-agent-config.py' script +* [#6061](https://github.com/icinga/icinga2/issues/6061) (code-quality, PR): Remove jenkins test scripts +* [#6060](https://github.com/icinga/icinga2/issues/6060) (code-quality, PR): Remove Icinga development docker scripts +* [#6059](https://github.com/icinga/icinga2/issues/6059) (code-quality, PR): Remove Icinga Studio +* [#6058](https://github.com/icinga/icinga2/issues/6058) (code-quality, PR): Clean up the Icinga plugins a bit +* [#6055](https://github.com/icinga/icinga2/issues/6055) (Check Execution, Windows, code-quality, PR): methods: Remove unused clrchecktask feature +* [#6054](https://github.com/icinga/icinga2/issues/6054) (Check Execution, Windows, code-quality): Remove unused clrchecktask +* [#6051](https://github.com/icinga/icinga2/issues/6051) (code-quality, PR): Set FOLDER cmake property for the icingaloader target +* [#6050](https://github.com/icinga/icinga2/issues/6050) (code-quality, PR): Replace boost::algorithm::split calls with String::Split +* [#6044](https://github.com/icinga/icinga2/issues/6044) (code-quality, PR): Implement support for frozen arrays and dictionaries +* [#6038](https://github.com/icinga/icinga2/issues/6038) (PR): Fix missing include for boost::split +* [#6037](https://github.com/icinga/icinga2/issues/6037) (PR): Fix build error on Windows +* [#6029](https://github.com/icinga/icinga2/issues/6029) (code-quality, PR): Remove duplicate semicolons +* [#6028](https://github.com/icinga/icinga2/issues/6028) (Packages): python notification not running when icinga ran as a service +* [#6026](https://github.com/icinga/icinga2/issues/6026) (Check Execution, Windows, PR): Fix flapping support for Windows +* [#6025](https://github.com/icinga/icinga2/issues/6025) (Windows): Implement Flapping on Windows +* [#6023](https://github.com/icinga/icinga2/issues/6023): Icinga should check whether the libsystemd library is available +* [#6017](https://github.com/icinga/icinga2/issues/6017) (PR): Remove build breaking include +* [#6015](https://github.com/icinga/icinga2/issues/6015) (code-quality, PR): Fix whitespaces in CMakeLists files +* [#6009](https://github.com/icinga/icinga2/issues/6009) (PR): Build fix for ancient versions of GCC +* [#6008](https://github.com/icinga/icinga2/issues/6008) (PR): Fix compatibility with CMake \< 3.1 +* [#6007](https://github.com/icinga/icinga2/issues/6007) (PR): Fix missing include +* [#6005](https://github.com/icinga/icinga2/issues/6005) (PR): Fix incorrect dependencies for mkunity targets +* [#5999](https://github.com/icinga/icinga2/issues/5999) (PR): Build fix +* [#5998](https://github.com/icinga/icinga2/issues/5998) (code-quality, PR): Build all remaining libraries as object libraries +* [#5997](https://github.com/icinga/icinga2/issues/5997) (PR): Use gcc-ar and gcc-ranlib when building with -flto +* [#5994](https://github.com/icinga/icinga2/issues/5994) (InfluxDB, PR): InfluxDBWriter: Fix macro in template +* [#5993](https://github.com/icinga/icinga2/issues/5993) (code-quality, PR): Use CMake object libraries for our libs +* [#5992](https://github.com/icinga/icinga2/issues/5992) (code-quality, PR): Remove unused includes +* [#5984](https://github.com/icinga/icinga2/issues/5984) (DB IDO, PR): Fix missing static libraries for DB IDO +* [#5983](https://github.com/icinga/icinga2/issues/5983) (code-quality, PR): Use initializer lists for arrays and dictionaries +* [#5980](https://github.com/icinga/icinga2/issues/5980) (code-quality, PR): Explicitly pass 1 or 0 for notification filters in DB IDO +* [#5974](https://github.com/icinga/icinga2/issues/5974) (PR): Fix non-unity builds with the icinga check +* [#5971](https://github.com/icinga/icinga2/issues/5971) (code-quality, PR): Remove libdemo and libhello +* [#5970](https://github.com/icinga/icinga2/issues/5970) (code-quality, PR): Allocate ConfigItemBuilder objects on the stack +* [#5969](https://github.com/icinga/icinga2/issues/5969) (code-quality, PR): Remove the WorkQueue::m\_StatsMutex instance variable +* [#5968](https://github.com/icinga/icinga2/issues/5968) (code-quality, PR): Update the RingBuffer class to use a regular mutex instead of ObjectLock +* [#5967](https://github.com/icinga/icinga2/issues/5967) (code-quality, PR): Avoid accessing attributes for validators where not necessary +* [#5965](https://github.com/icinga/icinga2/issues/5965) (code-quality, PR): Avoid unnecessary casts in the JSON encoder +* [#5961](https://github.com/icinga/icinga2/issues/5961) (PR): Fix macro warning from the icinga check +* [#5960](https://github.com/icinga/icinga2/issues/5960): Macro warning from the icinga check +* [#5957](https://github.com/icinga/icinga2/issues/5957) (code-quality, PR): Change a bunch more copyright headers for 2018 +* [#5955](https://github.com/icinga/icinga2/issues/5955) (Configuration, code-quality, PR): Avoid mutex contention in the config parser +* [#5946](https://github.com/icinga/icinga2/issues/5946) (code-quality, PR): Use clang-tidy to add some more C++11 features +* [#5945](https://github.com/icinga/icinga2/issues/5945) (code-quality, PR): Fix incorrect indentation for code generated by mkclass +* [#5944](https://github.com/icinga/icinga2/issues/5944) (code-quality, PR): Add the final keyword to classes +* [#5939](https://github.com/icinga/icinga2/issues/5939) (PR): Build fix for Debian wheezy +* [#5937](https://github.com/icinga/icinga2/issues/5937) (code-quality, PR): Remove inline methods and use explicit template instantiation to minimize the number of weak symbols +* [#5936](https://github.com/icinga/icinga2/issues/5936) (code-quality, PR): Clean up source lists in the CMakeLists.txt files +* [#5935](https://github.com/icinga/icinga2/issues/5935) (code-quality, PR): Implement support for precompiled headers +* [#5934](https://github.com/icinga/icinga2/issues/5934) (code-quality, PR): Add more include/library paths for MySQL and PostgreSQL +* [#5933](https://github.com/icinga/icinga2/issues/5933) (code-quality, PR): Change copyright headers for 2018 +* [#5932](https://github.com/icinga/icinga2/issues/5932) (code-quality, PR): Fix copyright header in cli/troubleshootcommand.hpp +* [#5931](https://github.com/icinga/icinga2/issues/5931) (code-quality, PR): Improve detection for linker flags +* [#5930](https://github.com/icinga/icinga2/issues/5930) (code-quality, PR): Replace boost::function with std::function +* [#5929](https://github.com/icinga/icinga2/issues/5929) (code-quality, PR): Get rid of boost::assign::list\_of in mkclass +* [#5927](https://github.com/icinga/icinga2/issues/5927) (code-quality, PR): Build libraries as static libraries +* [#5909](https://github.com/icinga/icinga2/issues/5909) (code-quality, PR): WIP: Improve build times +* [#5903](https://github.com/icinga/icinga2/issues/5903) (code-quality, PR): Cleanup CompatUtility class and features +* [#5897](https://github.com/icinga/icinga2/issues/5897) (code-quality, PR): Remove unnecessary inline statements +* [#5894](https://github.com/icinga/icinga2/issues/5894) (code-quality, PR): Remove string\_iless +* [#5891](https://github.com/icinga/icinga2/issues/5891) (code-quality, PR): Update .gitignore +* [#5889](https://github.com/icinga/icinga2/issues/5889) (code-quality, PR): execvpe: Fixup indention for readability +* [#5887](https://github.com/icinga/icinga2/issues/5887) (PR): Windows build fix +* [#5886](https://github.com/icinga/icinga2/issues/5886) (code-quality): Remove unnecessary 'inline' keyword +* [#5882](https://github.com/icinga/icinga2/issues/5882) (code-quality, PR): Avoid unnecessary allocations +* [#5871](https://github.com/icinga/icinga2/issues/5871) (code-quality, PR): Unit tests for the LegacyTimePeriod class +* [#5868](https://github.com/icinga/icinga2/issues/5868) (Configuration, code-quality, PR): Use std::unique\_ptr for Expression objects +* [#5865](https://github.com/icinga/icinga2/issues/5865) (code-quality, PR): Add missing initializer in Utility::NewUniqueID\(\) +* [#5862](https://github.com/icinga/icinga2/issues/5862) (code-quality, PR): Replace a few more NULLs with nullptr +* [#5858](https://github.com/icinga/icinga2/issues/5858) (Tests, code-quality, PR): Travis: Add support for Coverity +* [#5857](https://github.com/icinga/icinga2/issues/5857) (code-quality, PR): Fix compiler warnings +* [#5855](https://github.com/icinga/icinga2/issues/5855) (PR): Fix build problems with Visual Studio 2017 +* [#5848](https://github.com/icinga/icinga2/issues/5848) (code-quality, PR): Fix COPYING format +* [#5846](https://github.com/icinga/icinga2/issues/5846) (code-quality, PR): Fix compiler warnings +* [#5831](https://github.com/icinga/icinga2/issues/5831) (Check Execution, Configuration): No checks were launched on snapshot version 2.8.0.71 \(RHEL6\) +* [#5827](https://github.com/icinga/icinga2/issues/5827) (code-quality, PR): Replace StatsFunction with Function +* [#5825](https://github.com/icinga/icinga2/issues/5825) (code-quality, PR): Replace boost::assign::list\_of with initializer lists +* [#5824](https://github.com/icinga/icinga2/issues/5824) (code-quality, PR): Replace a few Boost features with equivalent C++11 features +* [#5821](https://github.com/icinga/icinga2/issues/5821) (Packages, Windows): check\_disk build error +* [#5819](https://github.com/icinga/icinga2/issues/5819) (code-quality, PR): Avoid unnecessary allocations in the FunctionCallExpression class +* [#5816](https://github.com/icinga/icinga2/issues/5816) (code-quality, PR): Re-implement WrapFunction\(\) using C++11 features +* [#5809](https://github.com/icinga/icinga2/issues/5809) (Documentation, Installation, PR): Raise required OpenSSL version to 1.0.1 +* [#5758](https://github.com/icinga/icinga2/issues/5758) (Documentation, Packages): Completely remove the spec file from the icinga2 repository +* [#5743](https://github.com/icinga/icinga2/issues/5743) (CLI, Configuration, Installation): node setup: Deprecate --master\_host and use --parent\_host instead +* [#5725](https://github.com/icinga/icinga2/issues/5725) (code-quality, PR): Use real UUIDs for Utility::NewUniqueID +* [#5388](https://github.com/icinga/icinga2/issues/5388) (Packages, PR): Handle mis-detection with clang on RHEL/CentOS 7 +* [#3246](https://github.com/icinga/icinga2/issues/3246) (Installation): Add option to windows installer to disable inclusion of conf.d directory ## 2.8.4 (2018-04-25) diff --git a/CMakeLists.txt b/CMakeLists.txt index e66efb2e8..bfc558769 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,22 +1,7 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ cmake_minimum_required(VERSION 2.8.8) -set(BOOST_MIN_VERSION "1.48.0") +set(BOOST_MIN_VERSION "1.66.0") project(icinga2) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -42,24 +27,45 @@ option (USE_SYSTEMD set(HAVE_SYSTEMD ${USE_SYSTEMD}) -file(STRINGS VERSION VERSION_LINE REGEX "^Version: ") -string(REPLACE "Version: " "" ICINGA2_VERSION ${VERSION_LINE}) - include(GNUInstallDirs) include(InstallConfig) +include(SetFullDir) set(ICINGA2_USER "icinga" CACHE STRING "Icinga 2 user") set(ICINGA2_GROUP "icinga" CACHE STRING "Icinga 2 group") set(ICINGA2_COMMAND_GROUP "icingacmd" CACHE STRING "Icinga 2 command group") -set(ICINGA2_RUNDIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/run" CACHE STRING "/run directory") set(ICINGA2_PLUGINDIR "/usr/lib/nagios/plugins" CACHE STRING "Path for the check plugins") set(ICINGA2_GIT_VERSION_INFO ON CACHE BOOL "Whether to use git describe") set(ICINGA2_UNITY_BUILD ON CACHE BOOL "Whether to perform a unity build") set(ICINGA2_LTO_BUILD OFF CACHE BOOL "Whether to use LTO") -if(NOT WIN32) - set(ICINGA2_SYSCONFIGFILE "${CMAKE_INSTALL_FULL_SYSCONFDIR}/sysconfig/icinga2" CACHE PATH "where to store configuation for the init system, defaults to /etc/sysconfig/icinga2") +set(ICINGA2_CONFIGDIR "${CMAKE_INSTALL_SYSCONFDIR}/icinga2" CACHE FILEPATH "Main config directory, e.g. /etc/icinga2") +set(ICINGA2_CACHEDIR "${CMAKE_INSTALL_LOCALSTATEDIR}/cache/icinga2" CACHE FILEPATH "Directory for cache files, e.g. /var/cache/icinga2") +set(ICINGA2_DATADIR "${CMAKE_INSTALL_LOCALSTATEDIR}/lib/icinga2" CACHE FILEPATH "Data directory for the daemon, e.g. /var/lib/icinga2") +set(ICINGA2_LOGDIR "${CMAKE_INSTALL_LOCALSTATEDIR}/log/icinga2" CACHE FILEPATH "Logging directory, e.g. /var/log/icinga2") +set(ICINGA2_SPOOLDIR "${CMAKE_INSTALL_LOCALSTATEDIR}/spool/icinga2" CACHE FILEPATH "Spooling directory, e.g. /var/spool/icinga2") +set(ICINGA2_RUNDIR "${CMAKE_INSTALL_LOCALSTATEDIR}/run" CACHE STRING "/run directory (deprecated, please use ICINGA2_INITRUNDIR)") +set(ICINGA2_INITRUNDIR "${ICINGA2_RUNDIR}/icinga2" CACHE FILEPATH "Runtime data for the init system, e.g. /run/icinga2") +set(ICINGA2_PKGDATADIR "${CMAKE_INSTALL_DATADIR}/icinga2" CACHE FILEPATH "Installed data, e.g. /usr/share/icinga2") +set(ICINGA2_INCLUDEDIR "${ICINGA2_PKGDATADIR}/include" CACHE FILEPATH "Include directory for the ITL, e.g. /usr/share/icinga2/include") + +# ensure absolute paths +set_full_dir(ICINGA2_FULL_CONFIGDIR "${ICINGA2_CONFIGDIR}") +set_full_dir(ICINGA2_FULL_CACHEDIR "${ICINGA2_CACHEDIR}") +set_full_dir(ICINGA2_FULL_DATADIR "${ICINGA2_DATADIR}") +set_full_dir(ICINGA2_FULL_LOGDIR "${ICINGA2_LOGDIR}") +set_full_dir(ICINGA2_FULL_SPOOLDIR "${ICINGA2_SPOOLDIR}") +set_full_dir(ICINGA2_FULL_RUNDIR "${ICINGA2_RUNDIR}") +set_full_dir(ICINGA2_FULL_INITRUNDIR "${ICINGA2_INITRUNDIR}") +set_full_dir(ICINGA2_FULL_PKGDATADIR "${ICINGA2_PKGDATADIR}") +set_full_dir(ICINGA2_FULL_INCLUDEDIR "${ICINGA2_INCLUDEDIR}") + +set(LOGROTATE_DIR "${CMAKE_INSTALL_SYSCONFDIR}/logrotate.d" CACHE STRING "Location of logrotate configs, e.g. /etc/logrotate.d") +set(BASHCOMPLETION_DIR "${CMAKE_INSTALL_SYSCONFDIR}/bash_completion.d" CACHE STRING "Location of bash_completion files, e.g. /etc/bash_completion.d") + +if(NOT WIN32) + set(ICINGA2_SYSCONFIGFILE "${CMAKE_INSTALL_SYSCONFDIR}/sysconfig/icinga2" CACHE PATH "where to store configuation for the init system, defaults to /etc/sysconfig/icinga2") endif() site_name(ICINGA2_BUILD_HOST_NAME) @@ -95,15 +101,38 @@ else() string(SUBSTRING ${SPEC_REVISION} 10 ${SPEC_REVISION_LENGTH} SPEC_REVISION) set(GIT_VERSION "r${SPEC_VERSION}-${SPEC_REVISION}") + set(ICINGA2_VERSION "${SPEC_VERSION}") + else() + # use GIT version as ICINGA2_VERSION + string(REGEX REPLACE "^[rv]" "" ICINGA2_VERSION "${GIT_VERSION}") endif() configure_file(icinga-version.h.cmake icinga-version.h) endif() +# NuGet on Windows requires a semantic versioning, example: 2.10.4.123 (only 4 element, only numeric) +string(REGEX REPLACE "-([0-9]+).*$" ".\\1" ICINGA2_VERSION_SAFE "${ICINGA2_VERSION}") +string(REGEX REPLACE "-[^\\.]*(.*)$" "\\1" ICINGA2_VERSION_SAFE "${ICINGA2_VERSION_SAFE}") +message(STATUS "ICINGA2_VERSION_SAFE=${ICINGA2_VERSION_SAFE}") + if(WIN32) set(Boost_USE_STATIC_LIBS ON) - add_definitions(-DBOOST_ALL_NO_LIB) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /bigobj") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") + # Disabled for linking issues for newer Boost versions, they link against Windows SDKs + #add_definitions(-DBOOST_ALL_NO_LIB) + + # Disable optimization for Boost::context + # https://www.boost.org/doc/libs/1_69_0/libs/context/doc/html/context/overview.html + # https://docs.microsoft.com/en-us/cpp/build/reference/gl-whole-program-optimization?view=vs-2017 + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /bigobj /GL- /EHs") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj /GL- /EHs") + + # detect if 32-bit target + if(CMAKE_VS_PLATFORM_NAME STREQUAL "Win32") + # SAFESEH is not supported in Boost on Windows x86 + # maybe it is when Boost is compiled with it... + # https://lists.boost.org/Archives/boost/2013/10/206720.php + # https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers?view=vs-2017 + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO") + endif() endif() if(NOT DEFINED LOGROTATE_HAS_SU) @@ -121,9 +150,18 @@ if(NOT DEFINED LOGROTATE_HAS_SU) endif() if(LOGROTATE_HAS_SU) set(LOGROTATE_USE_SU "\n\tsu ${ICINGA2_USER} ${ICINGA2_GROUP}") +else() + set(LOGROTATE_CREATE "\n\tcreate 644 ${ICINGA2_USER} ${ICINGA2_GROUP}") endif() -find_package(Boost ${BOOST_MIN_VERSION} COMPONENTS thread system program_options regex REQUIRED) +find_package(Boost ${BOOST_MIN_VERSION} COMPONENTS context coroutine date_time filesystem thread system program_options regex REQUIRED) + +# Boost.Coroutine2 (the successor of Boost.Coroutine) +# (1) doesn't even exist in old Boost versions and +# (2) isn't supported by ASIO, yet. +add_definitions(-DBOOST_COROUTINES_NO_DEPRECATION_WARNING) + +add_definitions(-DBOOST_FILESYSTEM_NO_DEPRECATED) link_directories(${Boost_LIBRARY_DIRS}) include_directories(${Boost_INCLUDE_DIRS}) @@ -134,15 +172,13 @@ include_directories(${OPENSSL_INCLUDE_DIR}) set(base_DEPS ${CMAKE_DL_LIBS} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES}) set(base_OBJS $ $ $) -find_package(YAJL) +# JSON +find_package(JSON) +include_directories(${JSON_INCLUDE}) -if(NOT YAJL_FOUND) - include_directories(${icinga2_BINARY_DIR}/third-party/yajl/include) - link_directories(${icinga2_BINARY_DIR}/third-party/yajl) - list(APPEND base_OBJS $) -else() - list(APPEND base_DEPS ${YAJL_LIBRARIES}) -endif() +# UTF8CPP +find_package(UTF8CPP) +include_directories(${UTF8CPP_INCLUDE}) find_package(Editline) set(HAVE_EDITLINE "${EDITLINE_FOUND}") @@ -183,6 +219,7 @@ if(WIN32) endif() set(CMAKE_MACOSX_RPATH 1) +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH};${CMAKE_INSTALL_FULL_LIBDIR}/icinga2") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Qunused-arguments -fcolor-diagnostics") @@ -203,7 +240,7 @@ endif() if(CMAKE_C_COMPILER_ID STREQUAL "GNU") if(CMAKE_SYSTEM_NAME MATCHES AIX) - set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -g -lpthread") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -lpthread") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -lpthread") elseif(CMAKE_SYSTEM_NAME MATCHES "kOpenBSD.*|OpenBSD.*") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -pthread") @@ -277,7 +314,7 @@ if(ICINGA2_LTO_BUILD) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -flto") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9.0) AND NOT OPENBSD) set(CMAKE_AR "gcc-ar") @@ -340,6 +377,54 @@ if(NOT MSVC) endif() endif() +# Architecture specifics +# - Log the target architecture +# - ARM needs to link against atomic +if(NOT MSVC) + # inspired by https://github.com/civetweb/civetweb/blob/master/cmake/DetermineTargetArchitecture.cmake + execute_process( + COMMAND ${CMAKE_C_COMPILER} -dumpmachine + RESULT_VARIABLE RESULT + OUTPUT_VARIABLE ARCH + ERROR_QUIET + ) + + if (RESULT) + message(STATUS "Failed to detect target architecture with compiler ${CMAKE_C_COMPILER}: ${RESULT}") + endif() + + string(REGEX MATCH "([^-]+).*" ARCH_MATCH "${ARCH}") + if (NOT CMAKE_MATCH_1 OR NOT ARCH_MATCH) + message(STATUS "Failed to match the target architecture: ${ARCH}") + endif() + + set(ARCH ${CMAKE_MATCH_1}) + + message(STATUS "Target architecture - ${ARCH}") + + # ARM settings + if("${ARCH}" STREQUAL "arm") + check_cxx_source_compiles( "include ; int main(){ std::atomic x; x.fetch_add(1); x.sub_add(1); }" CXX_ATOMIC) + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -latomic") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -latomic") + set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} -latomic") + endif() + +else() + if("${MSVC_C_ARCHITECTURE_ID}" STREQUAL "X86") + set(ARCH "i686") + elseif("${MSVC_C_ARCHITECTURE_ID}" STREQUAL "x64") + set(ARCH "x86_64") + elseif("${MSVC_C_ARCHITECTURE_ID}" STREQUAL "ARM") + set(ARCH "arm") + else() + message(FATAL_ERROR "Failed to determine the MSVC target architecture: ${MSVC_C_ARCHITECTURE_ID}") + endif() + + message(STATUS "Target architecture - ${ARCH}") +endif() + configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h ESCAPE_QUOTES) install( @@ -372,8 +457,8 @@ if(ICINGA2_WITH_TESTS) endif() set(CPACK_PACKAGE_NAME "Icinga 2") -set(CPACK_PACKAGE_VENDOR "Icinga Development Team") -set(CPACK_PACKAGE_VERSION ${ICINGA2_VERSION}) +set(CPACK_PACKAGE_VENDOR "Icinga GmbH") +set(CPACK_PACKAGE_VERSION ${ICINGA2_VERSION_SAFE}) set(CPACK_PACKAGE_INSTALL_DIRECTORY "ICINGA2") set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/icinga-app\\\\icinga.ico") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt") @@ -381,11 +466,11 @@ set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt") set(CPACK_PACKAGE_EXECUTABLES "Icinga2SetupAgent;Icinga 2 Agent Wizard") set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/icinga-app\\\\icinga.ico") set(CPACK_WIX_UPGRADE_GUID "52F2BEAA-4DF0-4C3E-ABDC-C0F61DE4DF8A") -set(CPACK_WIX_EXTENSIONS "WixUtilExtension") set(CPACK_WIX_UI_BANNER "${CMAKE_CURRENT_SOURCE_DIR}/icinga-installer/bannrbmp.bmp") set(CPACK_WIX_UI_DIALOG "${CMAKE_CURRENT_SOURCE_DIR}/icinga-installer/dlgbmp.bmp") set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_BINARY_DIR}/icinga-installer/icinga2.wixpatch.Debug") set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_BINARY_DIR}/icinga-installer/icinga2.wixpatch") +set(CPACK_WIX_EXTENSIONS "WixUtilExtension" "WixNetFxExtension") set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION "sbin") set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) @@ -393,11 +478,11 @@ include(InstallRequiredSystemLibraries) if(WIN32) if(CMAKE_VS_PLATFORM_NAME STREQUAL "x64") - set(NSCP_URL "https://github.com/mickem/nscp/releases/download/0.5.0.62/NSCP-0.5.0.62-x64.msi") - set(NSCP_SHA256 "1854de86ad4fda3391f273de0f9985b702c014bdec01b26ad28a1343177f537f") + set(NSCP_URL "https://github.com/mickem/nscp/releases/download/0.5.2.39/NSCP-0.5.2.39-x64.msi") + set(NSCP_SHA256 "dfe93c293f30586b02510d8b7884e4e177b93a5fead8b5dc6de8103532e6e159") else() - set(NSCP_URL "https://github.com/mickem/nscp/releases/download/0.5.0.62/NSCP-0.5.0.62-Win32.msi") - set(NSCP_SHA256 "2186b60d588fa0811344ce709332f9c63670019c62ae92eae49698bf76205a95") + set(NSCP_URL "https://github.com/mickem/nscp/releases/download/0.5.2.39/NSCP-0.5.2.39-Win32.msi") + set(NSCP_SHA256 "ca6a67fb01c1468f2b510fd2f9eb0750887db3fb49a0302732c1421c85c6627c") endif() set(NSCP_SHA256SUM "") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1d6992b86..fc9a4bfb0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,6 +23,9 @@ bug reports and features requests or writing code to add enhancements or fix bug Please consider our [roadmap](https://github.com/Icinga/icinga2/milestones) and [open issues](https://github.com/icinga/icinga2/issues) when you start contributing to the project. +Issues labeled with [help wanted](https://github.com/Icinga/icinga2/labels/help%20wanted) or +[good first issue](https://github.com/Icinga/icinga2/labels/good%20first%20issue) will +help you get started more easily. Before starting your work on Icinga 2, you should [fork the project](https://help.github.com/articles/fork-a-repo/) to your GitHub account. This allows you to freely experiment with your changes. @@ -64,12 +67,12 @@ Please continue to learn about [branches](CONTRIBUTING.md#contributing-branches) Choosing a proper name for a branch helps us identify its purpose and possibly find an associated bug or feature. -Generally a branch name should include a topic such as `fix` or `feature` followed +Generally a branch name should include a topic such as `bugfix` or `feature` followed by a description and an issue number if applicable. Branches should have only changes relevant to a specific issue. ``` -git checkout -b fix/service-template-typo-1234 +git checkout -b bugfix/service-template-typo-1234 git checkout -b feature/config-handling-1235 ``` @@ -111,13 +114,13 @@ Don't worry, you can squash those changes into a single commit later on. ## Pull Requests Once you've commited your changes, please update your local master -branch and rebase your fix/feature branch against it before submitting a PR. +branch and rebase your bugfix/feature branch against it before submitting a PR. ``` git checkout master git pull upstream HEAD -git checkout fix/notifications +git checkout bugfix/notifications git rebase master ``` @@ -126,12 +129,12 @@ It might be necessary to force push after rebasing - use with care! New branch: ``` -git push --set-upstream origin fix/notifications +git push --set-upstream origin bugfix/notifications ``` Existing branch: ``` -git push -f origin fix/notifications +git push -f origin bugfix/notifications ``` You can now either use the [hub](https://hub.github.com) CLI tool to create a PR, or nagivate @@ -168,7 +171,7 @@ git pull upstream HEAD Then change to your working branch and start rebasing it against master: ``` -git checkout fix/notifications +git checkout bugfix/notifications git rebase master ``` @@ -192,22 +195,22 @@ git rebase --continue Once succeeded ensure to push your changed history remotely. ``` -git push -f origin fix/notifications +git push -f origin bugfix/notifications ``` If you fear to break things, do the rebase in a backup branch first and later replace your current branch. ``` -git checkout fix/notifications -git checkout -b fix/notifications-rebase +git checkout bugfix/notifications +git checkout -b bugfix/notifications-rebase git rebase master -git branch -D fix/notifications -git checkout -b fix/notifications +git branch -D bugfix/notifications +git checkout -b bugfix/notifications -git push -f origin fix/notifications +git push -f origin bugfix/notifications ``` ### Squash Commits @@ -237,17 +240,14 @@ squash b37fd5377 Doc updates Save and let rebase to its job. Then force push the changes to the remote origin. ``` -git push -f origin fix/notifications +git push -f origin bugfix/notifications ``` ## Testing -Basic unit test coverage is provided by running `make test` during package builds. -Read the [INSTALL.md](INSTALL.md) file for more information about development builds. - -Snapshot packages from the laster development branch are available inside the -[package repository](https://packages.icinga.com). +Please follow the [documentation](https://icinga.com/docs/icinga2/snapshot/doc/21-development/#test-icinga-2) +for build and test instructions. You can help test-drive the latest Icinga 2 snapshot packages inside the [Icinga 2 Vagrant boxes](https://github.com/icinga/icinga-vagrant). @@ -255,17 +255,11 @@ You can help test-drive the latest Icinga 2 snapshot packages inside the ## Source Code Patches -Icinga 2 is written in C++ and uses the Boost libraries. We are also using the C++11 standard where applicable (please -note the minimum required compiler versions in the [INSTALL.md](INSTALL.md) file. - Icinga 2 can be built on Linux/Unix nodes and Windows clients. In order to develop patches for Icinga 2, you should prepare your own local build environment and know how to work with C++. -More tips: - -* Requirements and source code installation for Linux/Unix is explained inside the [INSTALL.md](INSTALL.md) file. -* Debug requirements and GDB instructions can be found in the [documentation](https://github.com/Icinga/icinga2/blob/master/doc/20-development.md). -* If you are planning to develop and debug the Windows client, setup a Windows environment with [Visual Studio](https://www.visualstudio.com/vs/community/). An example can be found in [this blogpost](https://blog.netways.de/2015/08/24/developing-icinga-2-on-windows-10-using-visual-studio-2015/). +Please follow the [development documentation](https://icinga.com/docs/icinga2/latest/doc/21-development/) +for development environments, the style guide and more advanced insights. ## Documentation Patches @@ -466,7 +460,7 @@ By default, pull requests allow to push into the repository of the PR creator. Example for [#4956](https://github.com/Icinga/icinga2/pull/4956): -At the bottom it says "Add more commits by pushing to the fix/persistent-comments-are-not-persistent branch on TheFlyingCorpse/icinga2." +At the bottom it says "Add more commits by pushing to the bugfix/persistent-comments-are-not-persistent branch on TheFlyingCorpse/icinga2." First off, add the remote repository as additional origin and fetch its content: @@ -478,20 +472,20 @@ git fetch --all Checkout the mentioned remote branch into a local branch (Note: `theflyingcorpse` is the name of the remote): ``` -git checkout theflyingcorpse/fix/persistent-comments-are-not-persistent -b fix/persistent-comments-are-not-persistent +git checkout theflyingcorpse/bugfix/persistent-comments-are-not-persistent -b bugfix/persistent-comments-are-not-persistent ``` Rebase, amend, squash or add your own commits on top. -Once you are satisfied, push the changes to the remote `theflyingcorpse` and its branch `fix/persistent-comments-are-not-persistent`. +Once you are satisfied, push the changes to the remote `theflyingcorpse` and its branch `bugfix/persistent-comments-are-not-persistent`. The syntax here is `git push :`. ``` -git push theflyingcorpse fix/persistent-comments-are-not-persistent:fix/persistent-comments-are-not-persistent +git push theflyingcorpse bugfix/persistent-comments-are-not-persistent:bugfix/persistent-comments-are-not-persistent ``` In case you've changed the commit history (rebase, amend, squash), you'll need to force push. Be careful, this can't be reverted! ``` -git push -f theflyingcorpse fix/persistent-comments-are-not-persistent:fix/persistent-comments-are-not-persistent +git push -f theflyingcorpse bugfix/persistent-comments-are-not-persistent:bugfix/persistent-comments-are-not-persistent ``` diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index 6c3b9aa0b..000000000 --- a/INSTALL.md +++ /dev/null @@ -1,395 +0,0 @@ -# Installing Icinga 2 - -The recommended way of installing Icinga 2 is to use packages. The Icinga -project provides both release and development packages for a number -of operating systems. - -Please check the documentation in the [doc/](doc/) directory for a current list -of available packages and detailed installation instructions. - -The online documentation is available on [icinga.com/docs](https://www.icinga.com/docs/) -and will guide you step by step. - -There are a number of known caveats when installing from source such as -incorrect directory and file permissions. So even if you're planning to -not use the official packages it is advisable to build your own Debian -or RPM packages. - -**Disclaimer** - -This information is intended for developers and packagers. It might be incomplete or unclear -in some cases. Make also sure to check our [packaging scripts on GitHub](https://github.com/Icinga/icinga-packaging)! - -# Build Requirements - -The following requirements need to be fulfilled in order to build the -application using a dist tarball (including notes for distributions): - -* cmake >= 2.6 -* GNU make (make) or ninja-build -* C++ compiler which supports C++11 - - RHEL/Fedora/SUSE: gcc-c++ >= 4.7 (extra Developer Tools on RHEL5/6 see below) - - Debian/Ubuntu: build-essential - - Alpine: build-base - - you can also use clang++ -* pkg-config -* OpenSSL library and header files >= 1.0.1 - - RHEL/Fedora: openssl-devel - - SUSE: libopenssl-devel (for SLES 11: libopenssl1-devel) - - Debian/Ubuntu: libssl-dev - - Alpine: libressl-dev -* Boost library and header files >= 1.48.0 - - RHEL/Fedora: boost148-devel - - Debian/Ubuntu: libboost-all-dev - - Alpine: boost-dev -* GNU bison (bison) -* GNU flex (flex) >= 2.5.35 -* Systemd headers - - Only required when using Systemd - - Debian/Ubuntu: libsystemd-dev - - RHEL/Fedora: systemd-devel - -## Optional features - -* MySQL (disable with CMake variable `ICINGA2_WITH_MYSQL` to `OFF`) - - RHEL/Fedora: mysql-devel - - SUSE: libmysqlclient-devel - - Debian/Ubuntu: default-libmysqlclient-dev | libmysqlclient-dev - - Alpine: mariadb-dev -* PostgreSQL (disable with CMake variable `ICINGA2_WITH_PGSQL` to `OFF`) - - RHEL/Fedora: postgresql-devel - - Debian/Ubuntu: libpq-dev - - postgresql-dev on Alpine -* YAJL (Faster JSON library) - - RHEL/Fedora: yajl-devel - - Debian: libyajl-dev - - Alpine: yajl-dev -* libedit (CLI console) - - RHEL/Fedora: libedit-devel on CentOS (RHEL requires rhel-7-server-optional-rpms) - - Debian/Ubuntu/Alpine: libedit-dev -* Termcap (only required if libedit doesn't already link against termcap/ncurses) - - RHEL/Fedora: libtermcap-devel - - Debian/Ubuntu: (not necessary) - -## Special requirements - -**FreeBSD**: libexecinfo (automatically used when Icinga 2 is installed via port or package) - -**RHEL5** ships an ancient flex version. Updated packages are available for -example from the repoforge buildtools repository. - -* x86: https://mirror.hs-esslingen.de/repoforge/redhat/el5/en/i386/buildtools/ -* x86\_64: https://mirror.hs-esslingen.de/repoforge/redhat/el5/en/x86\_64/buildtools/ - -## Runtime user environment - -By default Icinga will run as user 'icinga' and group 'icinga'. Additionally the -external command pipe and livestatus features require a dedicated command group -'icingacmd'. You can choose your own user/group names and pass them to CMake -using the `ICINGA2_USER`, `ICINGA2_GROUP` and `ICINGA2_COMMAND_GROUP` variables. -``` -# groupadd icinga -# groupadd icingacmd -# useradd -c "icinga" -s /sbin/nologin -G icingacmd -g icinga icinga -``` - -On Alpine (which uses ash busybox) you can run: -``` -# addgroup -S icinga -# addgroup -S icingacmd -# adduser -S -D -H -h /var/spool/icinga2 -s /sbin/nologin -G icinga -g icinga icinga -# adduser icinga icingacmd -``` - -Add the web server user to the icingacmd group in order to grant it write -permissions to the external command pipe and livestatus socket: -``` -# usermod -a -G icingacmd www-data -``` - -Make sure to replace "www-data" with the name of the user your web server -is running as. - -# Building Icinga 2 - -Once you have installed all the necessary build requirements you can build -Icinga 2 using the following commands: -``` -$ mkdir build && cd build -$ cmake .. -$ make -$ make install -``` - -You can specify an alternative installation prefix using `-DCMAKE_INSTALL_PREFIX`: -``` -$ cmake .. -DCMAKE_INSTALL_PREFIX=/tmp/icinga2 -``` - -## CMake Variables - -In addition to `CMAKE_INSTALL_PREFIX` here are most of the supported Icinga-specific cmake variables. - -**System Environment** -- `ICINGA2_GIT_VERSION_INFO`: Whether to use Git to determine the version number; defaults to `ON` -- `ICINGA2_USER`: The user Icinga 2 should run as; defaults to `icinga` -- `ICINGA2_GROUP`: The group Icinga 2 should run as; defaults to `icinga` -- `ICINGA2_COMMAND_GROUP`: The command group Icinga 2 should use; defaults to `icingacmd` -- `ICINGA2_RUNDIR`: The location of the "run" directory; defaults to `CMAKE_INSTALL_LOCALSTATEDIR/run` -- `CMAKE_INSTALL_SYSCONFDIR`: The configuration directory; defaults to `CMAKE_INSTALL_PREFIX/etc` -- `ICINGA2_SYSCONFIGFILE`: Where to put the config file the initscript/systemd pulls it's dirs from; - defaults to `CMAKE_INSTALL_PREFIX/etc/sysconfig/icinga2` -- `CMAKE_INSTALL_LOCALSTATEDIR`: The state directory; defaults to `CMAKE_INSTALL_PREFIX/var` -- `ICINGA2_PLUGINDIR`: The path for the Monitoring Plugins project binaries; defaults to `/usr/lib/nagios/plugins` - -**Build Optimization** -- `ICINGA2_UNITY_BUILD`: Whether to perform a unity build; defaults to `ON` -- `ICINGA2_LTO_BUILD`: Whether to use link time optimization (LTO); defaults to `OFF` - -**Init System** -- `USE_SYSTEMD=ON|OFF`: Use systemd or a classic SysV initscript; defaults to `OFF` -- `INSTALL_SYSTEMD_SERVICE_AND_INITSCRIPT=ON|OFF` Force install both the systemd service definition file - and the SysV initscript in parallel, regardless of how `USE_SYSTEMD` is set. - Only use this for special packaging purposes and if you know what you are doing. - Defaults to `OFF`. - -**Features:** -- `ICINGA2_WITH_CHECKER`: Determines whether the checker module is built; defaults to `ON` -- `ICINGA2_WITH_COMPAT`: Determines whether the compat module is built; defaults to `ON` -- `ICINGA2_WITH_DEMO`: Determines whether the demo module is built; defaults to `OFF` -- `ICINGA2_WITH_HELLO`: Determines whether the hello module is built; defaults to `OFF` -- `ICINGA2_WITH_LIVESTATUS`: Determines whether the Livestatus module is built; defaults to `ON` -- `ICINGA2_WITH_NOTIFICATION`: Determines whether the notification module is built; defaults to `ON` -- `ICINGA2_WITH_PERFDATA`: Determines whether the perfdata module is built; defaults to `ON` -- `ICINGA2_WITH_TESTS`: Determines whether the unit tests are built; defaults to `ON` - -**MySQL or MariaDB:** - -The following settings can be tuned for the MySQL / MariaDB IDO feature. - -- `ICINGA2_WITH_MYSQL`: Determines whether the MySQL IDO module is built; defaults to `ON` -- `MYSQL_CLIENT_LIBS`: Client implementation used (mysqlclient / mariadbclient); defaults searches for `mysqlclient` and `mariadbclient` -- `MYSQL_INCLUDE_DIR`: Directory containing include files for the mysqlclient; default empty - - checking multiple paths like `/usr/include/mysql` - -See [FindMySQL.cmake](third-party/cmake/FindMySQL.cmake) for the implementation. - -**PostgreSQL:** - -The following settings can be tuned for the PostgreSQL IDO feature. - -- `ICINGA2_WITH_PGSQL`: Determines whether the PostgreSQL IDO module is built; defaults to `ON` -- `PostgreSQL_INCLUDE_DIR`: Top-level directory containing the PostgreSQL include directories -- `PostgreSQL_LIBRARY_DIR`: Top-level directory containing the PostgreSQL libraries - -See [FindMySQL.cmake](third-party/cmake/FindPostgreSQL.cmake) for the implementation. - -**Version detection:** - -CMake determines the Icinga 2 version number using `git describe` if the -source directory is contained in a Git repository. Otherwise the version number -is extracted from the [VERSION](VERSION) file. This behavior can be -overridden by creating a file called `icinga-version.h.force` in the source -directory. Alternatively the `-DICINGA2_GIT_VERSION_INFO=OFF` option for CMake -can be used to disable the usage of `git describe`. - -# Building packages - -> **WARNING:** Some of this information is outdated! - -## Building RPMs - -### Build Environment on RHEL, CentOS, Fedora, Amazon Linux - -Setup your build environment: -``` -yum -y install rpmdevtools -``` - -### Build Environment on SuSE/SLES - -SLES: -``` -zypper addrepo http://download.opensuse.org/repositories/devel:tools/SLE_12_SP2/devel:tools.repo -zypper refresh -zypper install rpmdevtools spectool -``` - -OpenSuSE: -``` -zypper addrepo http://download.opensuse.org/repositories/devel:tools/openSUSE_Leap_42.3/devel:tools.repo -zypper refresh -zypper install rpmdevtools spectool -``` - -### Package Builds - -Prepare the rpmbuild directory tree: -``` -cd $HOME -rpmdev-setuptree -``` - -Copy the icinga2.spec file to `rpmbuild/SPEC` or fetch the latest version: -``` -curl https://raw.githubusercontent.com/Icinga/rpm-icinga2/master/icinga2.spec -o $HOME/rpmbuild/SPECS/icinga2.spec -``` - -Copy the tarball to `rpmbuild/SOURCES` e.g. by using the `spectool` binary -provided with `rpmdevtools`: -``` -cd $HOME/rpmbuild/SOURCES -spectool -g ../SPECS/icinga2.spec - -cd $HOME/rpmbuild -``` - -Install the build dependencies. Example for CentOS 7: -``` -yum -y install libedit-devel ncurses-devel gcc-c++ libstdc++-devel openssl-devel \ -cmake flex bison boost-devel systemd mysql-devel postgresql-devel httpd \ -selinux-policy-devel checkpolicy selinux-policy selinux-policy-doc -``` - -Note: If you are using Amazon Linux, systemd is not required. - -A shorter way is available using the `yum-builddep` command on RHEL based systems: -``` -yum-builddep SPECS/icinga2.spec -``` - -Build the RPM: -``` -rpmbuild -ba SPECS/icinga2.spec -``` - -### Additional Hints - -#### SELinux policy module - -The following packages are required to build the SELinux policy module: - -* checkpolicy -* selinux-policy (selinux-policy on CentOS 6, selinux-policy-devel on CentOS 7) -* selinux-policy-doc - -#### RHEL/CentOS 5 and 6 - -The RedHat Developer Toolset is required for building Icinga 2 beforehand. -This contains a modern version of flex and a C++ compiler which supports -C++11 features. -``` -cat >/etc/yum.repos.d/devtools-2.repo <$HOME/.rpmmacros < **WARNING:** This information is outdated! - -Setup your build environment on Debian/Ubuntu, copy the 'debian' directory from -the Debian packaging Git repository (https://github.com/Icinga/pkg-icinga2-debian) -into your source tree and run the following command: -``` -$ dpkg-buildpackage -uc -us -``` - -## Build Alpine Linux packages - -A simple way to setup a build environment is installing Alpine in a chroot. -In this way, you can set up an Alpine build environment in a chroot under a -different Linux distro. -There is a script that simplifies these steps with just two commands, and -can be found [here](https://github.com/alpinelinux/alpine-chroot-install). - -Once the build environment is installed, you can setup the system to build -the packages by following [this document](https://wiki.alpinelinux.org/wiki/Creating_an_Alpine_package). - -# Build Post Install Tasks - -After building Icinga 2 yourself, your package build system should at least run the following post -install requirements: - -* enable the `checker`, `notification` and `mainlog` feature by default -* run 'icinga2 api setup' in order to enable the `api` feature and generate SSL certificates for the node - -## Run Icinga 2 - -Icinga 2 comes with a binary that takes care of loading all the relevant -components (e.g. for check execution, notifications, etc.): -``` -# icinga2 daemon -[2016-12-08 16:44:24 +0100] information/cli: Icinga application loader (version: v2.5.4-231-gb10a6b7; debug) -[2016-12-08 16:44:24 +0100] information/cli: Loading configuration file(s). -[2016-12-08 16:44:25 +0100] information/ConfigItem: Committing config item(s). -... -``` - -### Init Script - -Icinga 2 can be started as a daemon using the provided init script: -``` -# /etc/init.d/icinga2 -Usage: /etc/init.d/icinga2 {start|stop|restart|reload|checkconfig|status} -``` - -### Systemd - -If your distribution uses Systemd: -``` -# systemctl {start|stop|reload|status|enable|disable} icinga2 -``` - -In case the distribution is running Systemd >227, you'll also -need to package and install the `etc/initsystem/icinga2.service.limits.conf` -file into `/etc/systemd/system/icinga2.service.d`. - -### openrc - -Or if your distribution uses openrc (like Alpine): -``` -# rc-service icinga2 -Usage: /etc/init.d/icinga2 {start|stop|restart|reload|checkconfig|status} - ``` - -Note: the openrc's init.d is not shipped by default. -A working init.d with openrc can be found here: (https://git.alpinelinux.org/cgit/aports/plain/community/icinga2/icinga2.initd). If you have customized some path, edit the file and adjust it according with your setup. -Those few steps can be followed: -``` -# wget https://git.alpinelinux.org/cgit/aports/plain/community/icinga2/icinga2.initd -# mv icinga2.initd /etc/init.d/icinga2 -# chmod +x /etc/init.d/icinga2 -``` - -Icinga 2 reads a single configuration file which is used to specify all -configuration settings (global settings, hosts, services, etc.). The -configuration format is explained in detail in the [doc/](doc/) directory. - -By default `make install` installs example configuration files in -`/usr/local/etc/icinga2` unless you have specified a different prefix or -sysconfdir. diff --git a/NEWS b/NEWS index ef80fe476..3cc501a7a 100644 --- a/NEWS +++ b/NEWS @@ -1 +1 @@ -News for this application can be found on the project website at https://www.icinga.com +News for this application can be found on the project website at https://icinga.com diff --git a/README.md b/README.md index 595af36fe..31847a584 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,60 @@ [![Build Status](https://travis-ci.org/Icinga/icinga2.svg?branch=master)](https://travis-ci.org/Icinga/icinga2) +[![Github Tag](https://img.shields.io/github/tag/Icinga/icinga2.svg)](https://github.com/Icinga/icinga2) # Icinga 2 -![Icinga Logo](https://www.icinga.com/wp-content/uploads/2014/06/icinga_logo.png) +![Icinga Logo](https://icinga.com/wp-content/uploads/2014/06/icinga_logo.png) #### Table of Contents 1. [About][About] -2. [License][License] -3. [Installation][Installation] -4. [Documentation][Documentation] -5. [Support][Support] +2. [Installation][Installation] +3. [Documentation][Documentation] +4. [Support][Support] +5. [License][License] 6. [Contributing][Contributing] ## About -Icinga 2 is an open source monitoring system which checks the availability of your -network resources, notifies users of outages, and generates performance data for reporting. +[Icinga](https://icinga.com/products/) is a monitoring system which checks +the availability of your network resources, notifies users of outages, and generates +performance data for reporting. -Scalable and extensible, Icinga 2 can monitor large, complex environments across +Scalable and extensible, Icinga can monitor large, complex environments across multiple locations. -Icinga 2 as monitoring core works best with [Icinga Web 2](https://www.icinga.com/products/icinga-web-2/) -as web interface. +Icinga 2 is the monitoring server and requires [Icinga Web 2](https://icinga.com/products/) +on top in your Icinga Stack. The [configuration](https://icinga.com/products/configuration/) +can be easily managed with either the [Icinga Director](https://icinga.com/docs/director/latest/), +config management tools or plain text within the [Icinga DSL](https://icinga.com/docs/icinga2/latest/doc/17-language-reference/). -More information can be found at [www.icinga.com](https://www.icinga.com/products/icinga-2/) -and inside the [documentation](https://www.icinga.com/docs/icinga2/latest/). +![Icinga Dashboard](https://icinga.com/wp-content/uploads/2017/12/icingaweb2-2.5.0-dashboard.png) -## Licensing +## Installation + +* [Installation](https://icinga.com/docs/icinga2/latest/doc/02-installation/) +* [Monitoring Basics](https://icinga.com/docs/icinga2/latest/doc/03-monitoring-basics/) +* [Configuration](https://icinga.com/docs/icinga2/latest/doc/04-configuration/) +* [Distributed Monitoring](https://icinga.com/docs/icinga2/latest/doc/06-distributed-monitoring/) +* [Addons, Integrations and Features](https://icinga.com/docs/icinga2/latest/doc/13-addons/) +* [Troubleshooting](https://icinga.com/docs/icinga2/latest/doc/15-troubleshooting/) +* [Upgrading](https://icinga.com/docs/icinga2/latest/doc/16-upgrading-icinga-2/) + +Once Icinga Server and Web are running in your distributed environment, +make sure to check out the many [Icinga modules](https://icinga.com/docs/) +for even better monitoring. + +## Documentation + +The documentation is available on [icinga.com/docs](https://icinga.com/docs/icinga2/latest/). + +## Support + +Check the [project website](https://icinga.com) for status updates. Join the +[community channels](https://icinga.com/community/) for questions +or ask an Icinga partner for [professional support](https://icinga.com/support/). + +## License Icinga 2 and the Icinga 2 documentation are licensed under the terms of the GNU General Public License Version 2, you will find a copy of this license in the @@ -47,21 +74,6 @@ so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. -## Installation - -Read the [INSTALL.md](INSTALL.md) file for more information about how to install it. - -## Documentation - -The documentation is located in the [doc/](doc/) directory. The latest documentation -is also available on https://docs.icinga.com - -## Support - -Check the project website at https://www.icinga.com for status updates. Join the -[community channels](https://www.icinga.com/community) for questions -or ask an Icinga partner for [professional support](https://www.icinga.com/services/support/). - ## Contributing There are many ways to contribute to Icinga -- whether it be sending patches, @@ -70,6 +82,13 @@ contribution is appreciated! Please continue reading in the [contributing chapter](CONTRIBUTING.md). +If you are a packager, please read the [development chapter](https://icinga.com/docs/icinga2/latest/doc/21-development/) +for more details. + +### Security Issues + +For reporting security issues please visit [this page](https://icinga.com/contact/security/). + [About]: #about [License]: #license diff --git a/RELEASE.md b/RELEASE.md index 44a32cfa3..ab816d9cc 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -12,7 +12,7 @@ - [5. Package Builds](#package-builds) - [5.1. RPM Packages](#rpm-packages) - [5.2. DEB Packages](#deb-packages) -- [6. Build Server](#build-server) +- [6. Build Server](#build-infrastructure) - [7. Release Tests](#release-tests) - [8. GitHub Release](#github-release) - [9. Chocolatey](#chocolatey) @@ -26,7 +26,7 @@ Specify the release version. ``` -VERSION=2.8.3 +VERSION=2.11.0 ``` Add your signing key to your Git configuration file, if not already there. @@ -60,7 +60,7 @@ git log --use-mailmap | grep '^Author:' | cut -f2- -d' ' | sort | uniq > AUTHORS ## Version -Update the version in the version file: +Update the version: ``` sed -i "s/Version: .*/Version: $VERSION/g" VERSION @@ -68,29 +68,12 @@ sed -i "s/Version: .*/Version: $VERSION/g" VERSION ## Changelog -Update the [CHANGELOG.md](CHANGELOG.md) file. +Choose the most important issues and summarize them in multiple groups/paragraphs. Provide links to the mentioned +issues/PRs. At the start include a link to the milestone's closed issues. -Export these environment variables: - -``` -export ICINGA_GITHUB_AUTH_USERNAME='user' -export ICINGA_GITHUB_AUTH_TOKEN='token' -export ICINGA_GITHUB_PROJECT='icinga/icinga2' -``` - -Run the script which updates the [CHANGELOG.md](CHANGELOG.md) file. - -``` -./changelog.py -git diff -``` ## Git Tag -> **Major Releases**: Commit these changes to the `master` branch. -> -> **Minor Releases**: Commit changes to the `support` branch. - ``` git commit -v -a -m "Release version $VERSION" ``` @@ -112,135 +95,147 @@ git push --tags ``` git checkout master -git checkout -b support/2.9 -git push -u origin support/2.9 +git push + +git checkout -b support/2.12 +git push -u origin support/2.12 ``` -**For minor releases:** Push the support branch, cherry-pick the release commit -into master and merge the support branch: - -``` -git push -u origin support/2.8 -git checkout master -git cherry-pick support/2.8 -git merge --strategy=ours support/2.8 -git push origin master -``` ## Package Builds +``` +mkdir $HOME/dev/icinga/packaging +cd $HOME/dev/icinga/packaging +``` + ### RPM Packages ``` -git clone git@github.com:icinga/rpm-icinga2.git && cd rpm-icinga2 +git clone git@git.icinga.com:icinga/rpm-icinga2.git && cd rpm-icinga2 ``` -#### Branch Workflow - -**Major releases** are branched off `master`. +### DEB Packages ``` -git checkout master && git pull +git clone git@git.icinga.com:packaging/deb-icinga2.git && cd deb-icinga2 ``` -**Bugfix releases** are created in the `release` branch and later merged to master. +#### Raspbian Packages ``` -git checkout release && git pull +git clone git@git.icinga.com:icinga/raspbian-icinga2.git && cd raspbian-icinga2 ``` -#### Release Commit - -Set the `Version`, `Revision` and `changelog` inside the spec file. +### Windows Packages ``` -sed -i "s/Version: .*/Version: $VERSION/g" icinga2.spec +git clone git@git.icinga.com:icinga/windows-icinga2.git && cd windows-icinga2 +``` + + +### Branch Workflow + +Checkout `master` and create a new branch. + +* For releases use x.x[.x] as branch name (e.g. 2.11 or 2.11.1) +* For releases with revision use x.x.x-n (e.g. 2.11.0-2) + + +### Switch Build Type + +Edit file `.gitlab-ci.yml` and comment variable `ICINGA_BUILD_TYPE` out. + +```yaml +variables: + ... + #ICINGA_BUILD_TYPE: snapshot + ... +``` + +Commit the change. + +``` +git commit -av -m "Switch build type for $VERSION-1" + +#### RPM Release Preparations + +Set the `Version`, `revision` and `%changelog` inside the spec file: + +``` +sed -i "s/Version:.*/Version: $VERSION/g" icinga2.spec vim icinga2.spec %changelog -* Tue Apr 24 2018 Jean Flach 2.8.3-1 -- Update to 2.8.3 +* Thu Sep 19 2019 Michael Friedrich 2.11.0-1 +- Update to 2.11.0 ``` -``` -git commit -av -m "Release 2.8.3-1" -git push -``` +#### DEB and Raspbian Release Preparations -**Note for major releases**: Update release branch to latest. -`git checkout release && git pull && git merge master && git push` - -**Note for minor releases**: Cherry-pick the release commit into master. -`git checkout master && git pull && git cherry-pick release && git push` - - -### DEB Packages +Update file `debian/changelog` and add at the beginning: ``` -git clone git@github.com:icinga/deb-icinga2.git && cd deb-icinga2 -``` +icinga2 (2.11.0-1) icinga; urgency=medium -#### Branch Workflow + * Release 2.11.0 -**Major releases** are branched off `master`. - -``` -git checkout master && git pull -``` - -**Bugfix releases** are created in the `release` branch and later merged to master. - -``` -git checkout release && git pull -``` - -#### Release Commit - -Set the `Version`, `Revision` and `changelog` inside the spec file. - -``` -./dch 2.8.3-1 "Update to 2.8.3" -``` - -``` -git commit -av -m "Release 2.8.3-1" -git push -``` - -``` -git commit -av -m "Release 2.8.3-1" -``` - -**Note for major releases**: Update release branch to latest. -`git checkout release && git pull && git merge master && git push` - -**Note for minor releases**: Cherry-pick the release commit into master. -`git checkout master && git pull && git cherry-pick release && git push` - - -#### DEB with dch on macOS - -``` -docker run -v `pwd`:/mnt/packaging -ti ubuntu:xenial bash - -apt-get update -apt-get install git dev-tools vim -cd /mnt/packaging - -git config --global user.name "Michael Friedrich" -git config --global user.email "michael.friedrich@icinga.com" - -./dch 2.8.3-1 "Update to 2.8.3" + -- Michael Friedrich Thu, 19 Sep 2019 10:50:31 +0200 ``` -## Build Server +### Release Commit + +Commit the changes and push the branch. + +``` +git commit -av -m "Release $VERSION-1" +git push origin 2.11 +``` + +GitLab will now build snapshot packages based on the tag `v2.11.0` of Icinga 2. + +### Package Tests + +In order to test the created packages you can download a job's artifacts: + +Visit [git.icinga.com](https://git.icinga.com/packaging/rpm-icinga2) +and navigate to the respective pipeline under `CI / CD -> Pipelines`. + +There click on the job you want to download packages from. + +The job's output appears. On the right-hand sidebar you can browse its artifacts. + +Once there, navigate to `build/RPMS/noarch` where you'll find the packages. + +### Release Packages + +To build release packages and upload them to [packages.icinga.com](https://packages.icinga.com) +tag the release commit and push it. + +``` +git tag -s 2.11.0-1 +git push origin 2.11.0-1 +``` + +Now cherry pick the release commit to `master` so that the changes are transferred back to it. + +**Attention**: Only the release commit. *NOT* the one switching the build type! + + +## Build Infrastructure + +https://git.icinga.com/packaging/rpm-icinga2/pipelines +https://git.icinga.com/packaging/deb-icinga2/pipelines +https://git.icinga.com/packaging/windows-icinga2/pipelines +https://git.icinga.com/packaging/raspbian-icinga2/pipelines * Verify package build changes for this version. * Test the snapshot packages for all distributions beforehand. -* Build the newly created Git tag for Debian/RHEL/SuSE. -* Build the newly created Git tag for Windows. + +Once the release repository tags are pushed, release builds +are triggered and automatically published to packages.icinga.com ## Release Tests @@ -249,52 +244,127 @@ git config --global user.email "michael.friedrich@icinga.com" * Test the [setup wizard](https://packages.icinga.com/windows/) inside a Windows VM. * Start a new docker container and install/run icinga2. -Example for CentOS7: +### CentOS ``` docker run -ti centos:latest bash yum -y install https://packages.icinga.com/epel/icinga-rpm-release-7-latest.noarch.rpm +yum -y install epel-release yum -y install icinga2 icinga2 daemon -C ``` +### Ubuntu + +``` +docker run -ti ubuntu:bionic bash + +apt-get update +apt-get -y install apt-transport-https wget gnupg + +wget -O - https://packages.icinga.com/icinga.key | apt-key add - + +. /etc/os-release; if [ ! -z ${UBUNTU_CODENAME+x} ]; then DIST="${UBUNTU_CODENAME}"; else DIST="$(lsb_release -c| awk '{print $2}')"; fi; \ + echo "deb https://packages.icinga.com/ubuntu icinga-${DIST} main" > \ + /etc/apt/sources.list.d/${DIST}-icinga.list + echo "deb-src https://packages.icinga.com/ubuntu icinga-${DIST} main" >> \ + /etc/apt/sources.list.d/${DIST}-icinga.list + +apt-get update + +apt-get -y install icinga2 +icinga2 daemon -C +``` + + ## GitHub Release Create a new release for the newly created Git tag: https://github.com/Icinga/icinga2/releases +> Hint: Choose [tags](https://github.com/Icinga/icinga2/tags), pick one to edit and +> make this a release. You can also create a draft release. + +The release body should contain a short changelog, with links +into the roadmap, changelog and blogpost. + + ## Chocolatey Navigate to the git repository on your Windows box which already has chocolatey installed. Pull/checkout the release. -Create the nupkg package: +Create the nupkg package (or use the one generated on https://packages.icinga.com/windows): ``` cpack ``` -Install the created icinga2 package locally: +Fetch the API key from https://chocolatey.org/account and use the `choco push` +command line. ``` -choco install icinga2 -version 2.8.3 -fdv "%cd%" -source "'%cd%;https://chocolatey.org/api/v2/'" +choco apikey --key xxx --source https://push.chocolatey.org/ + +choco push Icinga2-v2.11.0.nupkg --source https://push.chocolatey.org/ ``` -Upload the package to [chocolatey](https://chocolatey.org/packages/upload). ## Post Release ### Online Documentation -Ask @bobapple to update the documentation at docs.icinga.com. +> Only required for major releases. + +Navigate to `puppet-customer/icinga.git` and do the following steps: + +#### Testing + +``` +git checkout testing && git pull +vim files/var/www/docs/config/icinga2-latest.yml + +git commit -av -m "icinga-web: Update docs for Icinga 2" + +git push +``` + +SSH into the webserver and do a manual Puppet dry run with the testing environment. + +``` +puppet agent -t --environment testing --noop +``` + +Once succeeded, continue with production deployment. + +#### Production + +``` +git checkout master && git pull +git merge testing +git push +``` + +SSH into the webserver and do a manual Puppet run from the production environment (default). + +``` +puppet agent -t +``` + +#### Manual Generation + +SSH into the webserver or ask @bobapple. + +``` +cd /usr/local/icinga-docs-tools && ./build-docs.rb -c /var/www/docs/config/icinga2-latest.yml +``` ### Announcement -* Create a new blog post on www.icinga.com/blog -* Social media: [Twitter](https://twitter.com/icinga), [Facebook](https://www.facebook.com/icinga), [G+](https://plus.google.com/+icinga), [Xing](https://www.xing.com/communities/groups/icinga-da4b-1060043), [LinkedIn](https://www.linkedin.com/groups/Icinga-1921830/about) -* Update IRC channel topic +* Create a new blog post on [icinga.com/blog](https://icinga.com/blog) including a featured image +* Create a release topic on [community.icinga.com](https://community.icinga.com) +* Release email to net-tech & team ### Project Management * Add new minor version on [GitHub](https://github.com/Icinga/icinga2/milestones). -* Close the released version on [GitHub](https://github.com/Icinga/icinga2/milestones). diff --git a/VERSION b/VERSION index 0abdb3cf0..1ec9a7841 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -Version: 2.8.4 +Version: 2.11.0 Revision: 1 diff --git a/agent/CMakeLists.txt b/agent/CMakeLists.txt index 98885c62b..59c1e26c4 100644 --- a/agent/CMakeLists.txt +++ b/agent/CMakeLists.txt @@ -1,26 +1,10 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ if(MSVC) include_external_msproject( icinga2setupagent ${CMAKE_CURRENT_SOURCE_DIR}/windows-setup-agent/Icinga2SetupAgent.csproj TYPE FAE04EC0-301F-11D3-BF4B-00C04F79EFBC - PLATFORM Win32 ) install( diff --git a/agent/windows-setup-agent/App.config b/agent/windows-setup-agent/App.config index 49c7a61c6..5669c3558 100644 --- a/agent/windows-setup-agent/App.config +++ b/agent/windows-setup-agent/App.config @@ -1,7 +1,6 @@  - - - - + + + \ No newline at end of file diff --git a/agent/windows-setup-agent/Icinga2SetupAgent.csproj b/agent/windows-setup-agent/Icinga2SetupAgent.csproj index 0a5146d95..17fe54ff5 100644 --- a/agent/windows-setup-agent/Icinga2SetupAgent.csproj +++ b/agent/windows-setup-agent/Icinga2SetupAgent.csproj @@ -3,13 +3,13 @@ Debug - x86 + x64 {A86F1159-66E8-4BDB-BF28-A2BDAF76517C} WinExe Properties Icinga Icinga2SetupAgent - v2.0 + v4.6 512 publish\ @@ -37,6 +37,7 @@ DEBUG;TRACE prompt 4 + false x86 @@ -46,6 +47,7 @@ TRACE prompt 4 + false x86 @@ -55,6 +57,7 @@ TRACE prompt 4 + false x86 @@ -64,6 +67,48 @@ TRACE prompt 4 + false + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + x86 + pdbonly + true + bin\RelWithDebInfo\ + TRACE + prompt + 4 + false + + + x86 + pdbonly + true + bin\MinSizeRel\ + TRACE + prompt + 4 + false x64 @@ -74,6 +119,7 @@ DEBUG;TRACE prompt 4 + false x64 @@ -83,6 +129,7 @@ TRACE prompt 4 + false x64 @@ -92,6 +139,7 @@ TRACE prompt 4 + false x64 @@ -101,6 +149,7 @@ TRACE prompt 4 + false icinga.ico @@ -203,7 +252,7 @@ - icinga2 Icinga 2 - ${SPEC_VERSION} - The Icinga Project - Icinga Development Team + ${ICINGA2_VERSION_SAFE} + Icinga GmbH + Icinga GmbH icinga2 - Monitoring Agent for Windows Icinga 2 is an open source monitoring platform which notifies users about host and service outages. - https://www.icinga.com/ + https://icinga.com/ icinga2 agent monitoring admin - https://www.icinga.com/resources/faq/ + https://icinga.com/resources/faq/ https://github.com/Icinga/icinga2/blob/master/ChangeLog https://docs.icinga.com/icinga2/ https://github.com/Icinga/icinga2/issues https://github.com/Icinga/icinga2 https://github.com/Icinga/icinga2 false - https://www.icinga.com/wp-content/uploads/2015/05/icinga_icon_128x128.png + https://icinga.com/wp-content/uploads/2015/05/icinga_icon_128x128.png diff --git a/cmake/FindJSON.cmake b/cmake/FindJSON.cmake new file mode 100644 index 000000000..b7d5d79d2 --- /dev/null +++ b/cmake/FindJSON.cmake @@ -0,0 +1,9 @@ +FIND_PATH (JSON_INCLUDE json.hpp HINTS "${PROJECT_SOURCE_DIR}/third-party/nlohmann_json") + +if (JSON_INCLUDE) + set(JSON_BuildTests OFF CACHE INTERNAL "") + + message(STATUS "Found JSON: ${JSON_INCLUDE}" ) +else () + message(FATAL_ERROR "Unable to include json.hpp") +endif () diff --git a/cmake/FindUTF8CPP.cmake b/cmake/FindUTF8CPP.cmake new file mode 100644 index 000000000..b00035357 --- /dev/null +++ b/cmake/FindUTF8CPP.cmake @@ -0,0 +1,7 @@ +FIND_PATH (UTF8CPP_INCLUDE utf8.h HINTS "${PROJECT_SOURCE_DIR}/third-party/utf8cpp/source") + +if (UTF8CPP_INCLUDE) + message(STATUS "Found UTF8CPP: ${UTF8CPP_INCLUDE}" ) +else () + message(FATAL_ERROR "Unable to include utf8.h") +endif () diff --git a/cmake/FindYAJL.cmake b/cmake/FindYAJL.cmake deleted file mode 100644 index 186e31953..000000000 --- a/cmake/FindYAJL.cmake +++ /dev/null @@ -1,28 +0,0 @@ -# - Try to find libyajl -# Once done this will define -# YAJL_FOUND - System has YAJL -# YAJL_INCLUDE_DIRS - The YAJL include directories -# YAJL_LIBRARIES - The libraries needed to use YAJL -# YAJL_DEFINITIONS - Compiler switches required for using YAJL - -find_package(PkgConfig) -pkg_check_modules(PC_YAJL QUIET yajl) -set(YAJL_DEFINITIONS ${PC_YAJL_CFLAGS_OTHER}) - -find_path(YAJL_INCLUDE_DIR yajl/yajl_version.h - HINTS ${PC_YAJL_INCLUDEDIR} ${PC_YAJL_INCLUDE_DIRS} - PATH_SUFFIXES libyajl) - -find_library(YAJL_LIBRARY NAMES yajl libyajl - HINTS ${PC_YAJL_LIBDIR} ${PC_YAJL_LIBRARY_DIRS}) - -set(YAJL_LIBRARIES ${YAJL_LIBRARY} ) -set(YAJL_INCLUDE_DIRS ${YAJL_INCLUDE_DIR}) - -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set YAJL_FOUND to TRUE -# if all listed variables are TRUE -find_package_handle_standard_args(yajl DEFAULT_MSG - YAJL_LIBRARY YAJL_INCLUDE_DIR) - -mark_as_advanced(YAJL_INCLUDE_DIR YAJL_LIBRARY) diff --git a/cmake/InstallConfig.cmake b/cmake/InstallConfig.cmake index f01357c79..70eae91e6 100644 --- a/cmake/InstallConfig.cmake +++ b/cmake/InstallConfig.cmake @@ -1,52 +1,47 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com) +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ # -# 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. +# Install $src into directory $dest - usually only used for config files # -# 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. +# * similar to install() a non absolute path is prefixed with CMAKE_INSTALL_PREFIX on runtime +# * in case of CPack path with be prefixed with share/skel/ +# * DESTDIR is prefixed as well # -# 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. +# also see https://cmake.org/cmake/help/latest/command/install.html function(install_if_not_exists src dest) - set(real_dest "${dest}") if(NOT IS_ABSOLUTE "${src}") set(src "${CMAKE_CURRENT_SOURCE_DIR}/${src}") endif() + get_filename_component(src_name "${src}" NAME) - get_filename_component(basename_dest "${src}" NAME) - string(REPLACE "/" "\\\\" nsis_src "${src}") - string(REPLACE "/" "\\\\" nsis_dest_dir "${real_dest}") - string(REPLACE "/" "\\\\" nsis_dest "${real_dest}/${basename_dest}") + install(CODE " - if(\"\$ENV{DESTDIR}\" STREQUAL \"\") - set(target_dir \${CMAKE_INSTALL_PREFIX}) + set(dest \"${dest}\") + + if (\"\${CMAKE_INSTALL_PREFIX}\" MATCHES .*/_CPack_Packages/.*) + set(dest \"share/skel/\${dest}\") + set(force_overwrite TRUE) else() - set(target_dir \$ENV{DESTDIR}) + set(force_overwrite FALSE) endif() - if(\${CMAKE_INSTALL_PREFIX} MATCHES .*/_CPack_Packages/.* OR NOT EXISTS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\${skel_prefix}${dest}/${src_name}\") - message(STATUS \"Installing: \$ENV{DESTDIR}${dest}/${src_name}\") - if(\${CMAKE_INSTALL_PREFIX} MATCHES .*/_CPack_Packages/.*) - set(skel_prefix \"share/skel/\") - else() - set(skel_prefix \"\") - endif() - execute_process(COMMAND \${CMAKE_COMMAND} -E copy \"${src}\" - \"\${target_dir}/\${skel_prefix}${dest}/${src_name}\" + + if(NOT IS_ABSOLUTE \"\${dest}\") + set(dest \"\${CMAKE_INSTALL_PREFIX}/\${dest}\") + endif() + + set(full_dest \"\$ENV{DESTDIR}\${dest}/${src_name}\") + + if(force_overwrite OR NOT EXISTS \"\${full_dest}\") + message(STATUS \"Installing: ${src} into \${full_dest}\") + + execute_process(COMMAND \${CMAKE_COMMAND} -E copy \"${src}\" \"\${full_dest}\" RESULT_VARIABLE copy_result ERROR_VARIABLE error_output) if(copy_result) message(FATAL_ERROR \${error_output}) endif() else() - message(STATUS \"Skipping : \${target_dir}/${dest}/${src_name}\") + message(STATUS \"Skipping : \${full_dest}\") endif() ") endfunction(install_if_not_exists) diff --git a/cmake/SetFullDir.cmake b/cmake/SetFullDir.cmake new file mode 100644 index 000000000..8dce669af --- /dev/null +++ b/cmake/SetFullDir.cmake @@ -0,0 +1,11 @@ +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ +# +# Ensures a directory is absolute by prefixing CMAKE_INSTALL_PREFIX if it is not +# similar to CMAKE_INSTALL_FULL_... https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html +function(set_full_dir var path) + if(NOT IS_ABSOLUTE "${path}") + message(STATUS "Prefixing in ${var} \"${path}\" with ${CMAKE_INSTALL_PREFIX}") + set(path "${CMAKE_INSTALL_PREFIX}/${path}") + endif() + set(${var} "${path}" PARENT_SCOPE) +endfunction(set_full_dir) diff --git a/config.h.cmake b/config.h.cmake index c461e029f..16fa190f1 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -13,17 +13,24 @@ #cmakedefine ICINGA2_UNITY_BUILD -#define ICINGA_PREFIX "${CMAKE_INSTALL_PREFIX}" -#define ICINGA_SYSCONFDIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}" -#define ICINGA_RUNDIR "${ICINGA2_RUNDIR}" -#define ICINGA_LOCALSTATEDIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}" -#define ICINGA_PKGDATADIR "${CMAKE_INSTALL_FULL_DATADIR}/icinga2" -#define ICINGA_INCLUDECONFDIR "${CMAKE_INSTALL_FULL_DATADIR}/icinga2/include" +#define ICINGA_CONFIGDIR "${ICINGA2_FULL_CONFIGDIR}" +#define ICINGA_DATADIR "${ICINGA2_FULL_DATADIR}" +#define ICINGA_LOGDIR "${ICINGA2_FULL_LOGDIR}" +#define ICINGA_CACHEDIR "${ICINGA2_FULL_CACHEDIR}" +#define ICINGA_SPOOLDIR "${ICINGA2_FULL_SPOOLDIR}" +#define ICINGA_INITRUNDIR "${ICINGA2_FULL_INITRUNDIR}" +#define ICINGA_INCLUDECONFDIR "${ICINGA2_FULL_INCLUDEDIR}" #define ICINGA_USER "${ICINGA2_USER}" #define ICINGA_GROUP "${ICINGA2_GROUP}" -#define ICINGA_SYSCONFIGFILE "${ICINGA2_SYSCONFIGFILE}" #define ICINGA_BUILD_HOST_NAME "${ICINGA2_BUILD_HOST_NAME}" #define ICINGA_BUILD_COMPILER_NAME "${ICINGA2_BUILD_COMPILER_NAME}" #define ICINGA_BUILD_COMPILER_VERSION "${ICINGA2_BUILD_COMPILER_VERSION}" +// Deprecated options? +#define ICINGA_PKGDATADIR "${ICINGA2_FULL_PKGDATADIR}" +#define ICINGA_PREFIX "${CMAKE_INSTALL_PREFIX}" +#define ICINGA_SYSCONFDIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}" +#define ICINGA_RUNDIR "${ICINGA2_FULL_RUNDIR}" +#define ICINGA_LOCALSTATEDIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}" + #endif /* CONFIG_H */ diff --git a/contrib/GPLHeader b/contrib/GPLHeader index 6bc8a95ba..9b2cfd1a2 100644 --- a/contrib/GPLHeader +++ b/contrib/GPLHeader @@ -1,19 +1 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ - +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ diff --git a/contrib/discover-api.py b/contrib/discover-api.py index 9c3dd6a20..48be827d7 100755 --- a/contrib/discover-api.py +++ b/contrib/discover-api.py @@ -1,20 +1,5 @@ #!/usr/bin/env python -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ import sys import subprocess diff --git a/contrib/discover.py b/contrib/discover.py index a49b62c95..8a7d0fe3e 100755 --- a/contrib/discover.py +++ b/contrib/discover.py @@ -1,20 +1,5 @@ #!/usr/bin/env python -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ import sys import subprocess diff --git a/doc/01-about.md b/doc/01-about.md index 7669ec81e..582e226d8 100644 --- a/doc/01-about.md +++ b/doc/01-about.md @@ -2,24 +2,45 @@ ## What is Icinga 2? -Icinga 2 is an open source monitoring system which checks the availability of -your network resources, notifies users of outages and generates performance -data for reporting. +[Icinga](https://icinga.com/products/) is a monitoring system which checks +the availability of your network resources, notifies users of outages, and generates +performance data for reporting. -Scalable and extensible, Icinga 2 can monitor large, complex environments across +Scalable and extensible, Icinga can monitor large, complex environments across multiple locations. -## Licensing +Icinga 2 is the monitoring server and requires [Icinga Web 2](https://icinga.com/products/) +on top in your Icinga Stack. The [configuration](https://icinga.com/products/configuration/) +can be easily managed with either the [Icinga Director](https://icinga.com/docs/director/latest/), +config management tools or plain text within the [Icinga DSL](04-configuration.md#configuration). -Icinga 2 and the Icinga 2 documentation are licensed under the terms of the GNU -General Public License Version 2. You will find a copy of this license in the -LICENSE file included in the source package. -## Support +![Icinga 2 Distributed Master and Satellites with Agents](images/distributed-monitoring/icinga2_distributed_monitoring_scenarios_master_satellites_agents.png) -Check the project website at https://www.icinga.com for status updates. Join the -[community channels](https://www.icinga.com/community/get-involved/) for questions -or ask an Icinga partner for [professional support](https://www.icinga.com/services/support/). +## Start with Icinga + +* [Installation](02-installation.md#installation) +* [Monitoring Basics](03-monitoring-basics.md#monitoring-basics) +* [Configuration](04-configuration.md#configuration) +* [Distributed Monitoring](06-distributed-monitoring.md#distributed-monitoring) +* [Addons, Integrations and Features](13-addons.md#addons) +* [Troubleshooting](15-troubleshooting.md#troubleshooting) +* [Upgrading](16-upgrading-icinga-2.md#upgrading-icinga-2) + +Once Icinga Server and Web are running in your distributed environment, +make sure to check out the many [Icinga modules](https://icinga.com/docs/) +for even better monitoring. + +## What's New + +You can follow the development and release milestones on [GitHub](https://github.com/icinga/icinga2/issues). +Please follow our release announcements on [icinga.com](https://icinga.com/blog/) too. + +## Support + +Check the project website at [icinga.com](https://icinga.com) for status updates. Join the +[community channels](https://icinga.com/community/) for questions +or get in touch for [professional support](https://icinga.com/subscription/). ## Contribute @@ -29,15 +50,21 @@ contribution is appreciated! Please continue reading in the [Contributing chapter](https://github.com/Icinga/icinga2/blob/master/CONTRIBUTING.md). +### Security Issues + +For reporting security issues please visit [this page](https://icinga.com/contact/security/). + ### Icinga 2 Development The Git repository is located on [GitHub](https://github.com/Icinga/icinga2). Icinga 2 is written in C++ and can be built on Linux/Unix and Windows. -Read more about development builds in the [INSTALL.md](https://github.com/Icinga/icinga2/blob/master/INSTALL.md) -file. +Read more about development builds in the [development chapter](21-development.md#development). -## What's New -The Icinga 2 Changelog is located [here](https://github.com/Icinga/icinga2/blob/master/CHANGELOG.md). -Please follow our release announcements on [icinga.com](https://www.icinga.com/blog/) too. +## License + +Icinga 2 and the Icinga 2 documentation are licensed under the terms of the GNU +General Public License Version 2. You will find a copy of this license in the +LICENSE file included in the source package. + diff --git a/doc/02-getting-started.md b/doc/02-installation.md similarity index 85% rename from doc/02-getting-started.md rename to doc/02-installation.md index 244a797de..b247ca972 100644 --- a/doc/02-getting-started.md +++ b/doc/02-installation.md @@ -1,7 +1,7 @@ -# Getting Started +# Installation -This tutorial is a step-by-step introduction to installing [Icinga 2](02-getting-started.md#setting-up-icinga2) -and [Icinga Web 2](02-getting-started.md#setting-up-icingaweb2). +This tutorial is a step-by-step introduction to installing [Icinga 2](02-installation.md#setting-up-icinga2) +and [Icinga Web 2](02-installation.md#setting-up-icingaweb2). It assumes that you are familiar with the operating system you're using to install Icinga 2. In case you are upgrading an existing setup, please ensure to @@ -13,13 +13,21 @@ First off you have to install Icinga 2. The preferred way of doing this is to use the official package repositories depending on which operating system and distribution you are running. +Official repositories ([support matrix](https://icinga.com/subscription/support-details/)): + Distribution | Repository ------------------------|--------------------------- Debian | [Icinga Repository](https://packages.icinga.com/debian/) Ubuntu | [Icinga Repository](https://packages.icinga.com/ubuntu/) + Raspbian | [Icinga Repository](https://packages.icinga.com/raspbian/). Note that **Raspbian `icinga-buster` is required.** RHEL/CentOS | [Icinga Repository](https://packages.icinga.com/epel/) openSUSE | [Icinga Repository](https://packages.icinga.com/openSUSE/) SLES | [Icinga Repository](https://packages.icinga.com/SUSE/) + +Community repositories: + + Distribution | Repository + ------------------------|--------------------------- Gentoo | [Upstream](https://packages.gentoo.org/package/net-analyzer/icinga2) FreeBSD | [Upstream](https://www.freshports.org/net-mgmt/icinga2) OpenBSD | [Upstream](http://ports.su/net/icinga/core2,-main) @@ -29,84 +37,106 @@ and distribution you are running. Packages for distributions other than the ones listed above may also be available. Please contact your distribution packagers. +> **Note** +> +> Windows is only supported for agent installations. Please refer +> to the [distributed monitoring chapter](06-distributed-monitoring.md#distributed-monitoring-setup-client-windows). + ### Package Repositories You need to add the Icinga repository to your package management configuration. The following commands must be executed with `root` permissions unless noted otherwise. +#### Debian/Ubuntu/Raspbian Repositories + Debian: ``` -apt-get -y install apt-transport-https +apt-get update +apt-get -y install apt-transport-https wget gnupg wget -O - https://packages.icinga.com/icinga.key | apt-key add - -echo 'deb https://packages.icinga.com/debian icinga-stretch main' >/etc/apt/sources.list.d/icinga.list + +DIST=$(awk -F"[)(]+" '/VERSION=/ {print $2}' /etc/os-release); \ + echo "deb https://packages.icinga.com/debian icinga-${DIST} main" > \ + /etc/apt/sources.list.d/${DIST}-icinga.list + echo "deb-src https://packages.icinga.com/debian icinga-${DIST} main" >> \ + /etc/apt/sources.list.d/${DIST}-icinga.list + apt-get update ``` Ubuntu: ``` -apt-get -y install apt-transport-https +apt-get update +apt-get -y install apt-transport-https wget gnupg wget -O - https://packages.icinga.com/icinga.key | apt-key add - -echo 'deb https://packages.icinga.com/ubuntu icinga-xenial main' >/etc/apt/sources.list.d/icinga.list + +. /etc/os-release; if [ ! -z ${UBUNTU_CODENAME+x} ]; then DIST="${UBUNTU_CODENAME}"; else DIST="$(lsb_release -c| awk '{print $2}')"; fi; \ + echo "deb https://packages.icinga.com/ubuntu icinga-${DIST} main" > \ + /etc/apt/sources.list.d/${DIST}-icinga.list + echo "deb-src https://packages.icinga.com/ubuntu icinga-${DIST} main" >> \ + /etc/apt/sources.list.d/${DIST}-icinga.list + apt-get update ``` +Raspbian Buster: + +``` +apt-get update +apt-get -y install apt-transport-https wget gnupg + +wget -O - https://packages.icinga.com/icinga.key | apt-key add - + +DIST=$(awk -F"[)(]+" '/VERSION=/ {print $2}' /etc/os-release); \ + echo "deb https://packages.icinga.com/raspbian icinga-${DIST} main" > \ + /etc/apt/sources.list.d/icinga.list + echo "deb-src https://packages.icinga.com/raspbian icinga-${DIST} main" >> \ + /etc/apt/sources.list.d/icinga.list + +apt-get update +``` + +##### Debian Backports Repository + +> **Note**: +> +> This repository is required for Debian Stretch since v2.11. + +Debian Stretch: + +``` +DIST=$(awk -F"[)(]+" '/VERSION=/ {print $2}' /etc/os-release); \ + echo "deb https://deb.debian.org/debian ${DIST}-backports main" > \ + /etc/apt/sources.list.d/${DIST}-backports.list + +apt-get update +``` + +#### RHEL/CentOS/Fedora Repositories + RHEL/CentOS 7: ``` yum install https://packages.icinga.com/epel/icinga-rpm-release-7-latest.noarch.rpm ``` -RHEL/CentOS 6: +RHEL/CentOS 6 x64: ``` yum install https://packages.icinga.com/epel/icinga-rpm-release-6-latest.noarch.rpm ``` -Fedora 27: +Fedora 29: ``` -dnf install https://packages.icinga.com/fedora/icinga-rpm-release-27-latest.noarch.rpm +dnf install https://packages.icinga.com/fedora/icinga-rpm-release-29-latest.noarch.rpm ``` -Fedora 26: - -``` -dnf install https://packages.icinga.com/fedora/icinga-rpm-release-26-latest.noarch.rpm -``` - -SLES 12: - -``` -zypper ar https://packages.icinga.com/SUSE/ICINGA-release.repo -zypper ref -``` - -SLES 11: - -``` -zypper ar https://packages.icinga.com/SUSE/ICINGA-release-11.repo -zypper ref -``` - -openSUSE: - -``` -zypper ar https://packages.icinga.com/openSUSE/ICINGA-release.repo -zypper ref -``` - -Alpine Linux: - -``` -echo "http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories -apk update -``` - -#### RHEL/CentOS EPEL Repository +##### RHEL/CentOS EPEL Repository The packages for RHEL/CentOS depend on other packages which are distributed as part of the [EPEL repository](https://fedoraproject.org/wiki/EPEL). @@ -126,17 +156,38 @@ subscription-manager repos --enable rhel-7-server-optional-rpms subscription-manager repos --enable rhel-6-server-optional-rpms ``` -#### SLES Security Repository +#### SLES/OpenSUSE Repositories -The packages for SLES 11 depend on the `openssl1` package which is distributed -as part of the [SLES 11 Security Module](https://www.suse.com/communities/conversations/introducing-the-suse-linux-enterprise-11-security-module/). +The release repository also provides the required Boost 1.66+ packages +since v2.11. -#### SLES 12 SDK +SLES 15/12: -Icinga 2 requires the `libboost_chrono1_54_0` package from the `SLES 12 SDK` repository. Refer to the SUSE Enterprise -Linux documentation for further information. +``` +rpm --import https://packages.icinga.com/icinga.key -#### Alpine Linux Notes +zypper ar https://packages.icinga.com/SUSE/ICINGA-release.repo +zypper ref +``` + +openSUSE: + +``` +rpm --import https://packages.icinga.com/icinga.key + +zypper ar https://packages.icinga.com/openSUSE/ICINGA-release.repo +zypper ref +``` + +#### Alpine Linux Repositories + +Alpine Linux: + +``` +echo "http://dl-cdn.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories +echo "http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories +apk update +``` The example provided assumes that you are running Alpine edge, which is the -dev branch and is a rolling release. If you are using a stable version please "pin" the edge repository on the latest Icinga 2 package version. @@ -155,14 +206,6 @@ Debian/Ubuntu: apt-get install icinga2 ``` -RHEL/CentOS 6: - -``` -yum install icinga2 -chkconfig icinga2 on -service icinga2 start -``` - RHEL/CentOS 7 and Fedora: ``` @@ -171,6 +214,14 @@ systemctl enable icinga2 systemctl start icinga2 ``` +RHEL/CentOS 6: + +``` +yum install icinga2 +chkconfig icinga2 on +service icinga2 start +``` + SLES/openSUSE: ``` @@ -215,8 +266,8 @@ By default Icinga 2 uses the following files and directories: Path | Description ----------------------------------------------|------------------------------------ /etc/icinga2 | Contains Icinga 2 configuration files. - /usr/lib/systemd/system/icinga2.service | The Icinga 2 Systemd service file on systems using Systemd. - /etc/systemd/system/icinga2.service.d/limits.conf | On distributions with Systemd >227, additional service limits are required. + /usr/lib/systemd/system/icinga2.service | The Icinga 2 systemd service file on systems using systemd. + /etc/systemd/system/icinga2.service.d/limits.conf | On distributions with systemd >227, additional service limits are required. /etc/init.d/icinga2 | The Icinga 2 init script on systems using SysVinit or OpenRC. /usr/sbin/icinga2 | Shell wrapper for the Icinga 2 binary. /usr/lib\*/icinga2 | Libraries and the Icinga 2 binary (use `find /usr -type f -name icinga2` to locate the binary path). @@ -255,7 +306,7 @@ Without plugins Icinga 2 does not know how to check external services. The an extensive set of plugins which can be used with Icinga 2 to check whether services are working properly. -These plugins are required to make the [example configuration](04-configuring-icinga-2.md#configuring-icinga2-overview) +These plugins are required to make the [example configuration](04-configuration.md#configuring-icinga2-overview) work out-of-the-box. For your convenience here is a list of package names for some of the more @@ -268,7 +319,7 @@ SLES/OpenSUSE | monitoring-plugins | [server:monitoring](https://build. Debian/Ubuntu | monitoring-plugins | - | /usr/lib/nagios/plugins FreeBSD | monitoring-plugins | - | /usr/local/libexec/nagios Alpine Linux | monitoring-plugins | - | /usr/lib/monitoring-plugins -OS X | nagios-plugins | [MacPorts](https://www.macports.org), [Homebrew](https://brew.sh) | /opt/local/libexec or /usr/local/sbin +macOS | monitoring-plugins | [MacPorts](https://www.macports.org), [Homebrew](https://brew.sh) | /opt/local/libexec or /usr/local/sbin The recommended way of installing these standard plugins is to use your distribution's package manager. @@ -286,9 +337,7 @@ yum install nagios-plugins-all ``` The packages for RHEL/CentOS depend on other packages which are distributed -as part of the [EPEL repository](https://fedoraproject.org/wiki/EPEL). Please -make sure to enable this repository by following -[these instructions](https://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F). +as part of the [EPEL repository](02-installation.md#package-repositories-rhel-epel). Fedora: @@ -322,7 +371,7 @@ Note: For Alpine you don't need to explicitly add the `monitoring-plugins` packa `icinga2` and is pulled automatically. Depending on which directory your plugins are installed into you may need to -update the global `PluginDir` constant in your [Icinga 2 configuration](04-configuring-icinga-2.md#constants-conf). +update the global `PluginDir` constant in your [Icinga 2 configuration](04-configuration.md#constants-conf). This constant is used by the check command definitions contained in the Icinga Template Library to determine where to find the plugin binaries. @@ -335,10 +384,10 @@ to determine where to find the plugin binaries. ### Systemd Service -Some distributions (e.g. Fedora, openSUSE and RHEL/CentOS 7) use Systemd. The -Icinga 2 packages automatically install the necessary Systemd unit files. +Some distributions (e.g. Fedora, openSUSE and RHEL/CentOS 7) use systemd. The +Icinga 2 packages automatically install the necessary systemd unit files. -The Icinga 2 Systemd service can be (re-)started, reloaded, stopped and also +The Icinga 2 systemd service can be (re-)started, reloaded, stopped and also queried for its current status. ``` @@ -387,7 +436,7 @@ If you're stuck with configuration errors, you can manually invoke the > **Tip** > -> If you are running into fork errors with Systemd enabled distributions, +> If you are running into fork errors with systemd enabled distributions, > please check the [troubleshooting chapter](15-troubleshooting.md#check-fork-errors). ### Init Script @@ -500,7 +549,7 @@ Test it: # vim /etc/icinga2/conf.d/templates.conf ``` -![Vim with syntax highlighting](images/getting-started/vim-syntax.png "Vim with Icinga 2 syntax highlighting") +![Vim with syntax highlighting](images/installation/vim-syntax.png "Vim with Icinga 2 syntax highlighting") ### Configuration Syntax Highlighting using Nano @@ -544,7 +593,7 @@ Test it: $ nano /etc/icinga2/conf.d/templates.conf ``` -![Nano with syntax highlighting](images/getting-started/nano-syntax.png "Nano with Icinga 2 syntax highlighting") +![Nano with syntax highlighting](images/installation/nano-syntax.png "Nano with Icinga 2 syntax highlighting") ## Setting up Icinga Web 2 @@ -554,8 +603,8 @@ This chapter explains how to set up Icinga Web 2. The DB IDO (Database Icinga Data Output) feature for Icinga 2 take care of exporting all configuration and status information into a database. -Please choose whether to install [MySQL](02-getting-started.md#configuring-db-ido-mysql) or -[PostgreSQL](02-getting-started.md#configuring-db-ido-postgresql). +Please choose whether to install [MySQL](02-installation.md#configuring-db-ido-mysql) or +[PostgreSQL](02-installation.md#configuring-db-ido-postgresql). ### Configuring DB IDO MySQL @@ -578,16 +627,6 @@ systemctl start mariadb mysql_secure_installation ``` -RHEL/CentOS 6: - -``` -yum install mysql-server mysql -chkconfig mysqld on -service mysqld start - -mysql_secure_installation -``` - SUSE: ``` @@ -665,7 +704,7 @@ GRANT SELECT, INSERT, UPDATE, DELETE, DROP, CREATE VIEW, INDEX, EXECUTE ON icing quit ``` -![setting up the database on CentOS 7](images/getting-started/mariadb-centos7.png "Setting up the database on CentOS 7") +![setting up the database on CentOS 7](images/installation/mariadb-centos7.png "Setting up the database on CentOS 7") After creating the database you can import the Icinga 2 IDO schema using the following command. Enter the root password into the prompt when asked. @@ -705,7 +744,7 @@ Alpine Linux: rc-service icinga2 restart ``` -Continue with the [webserver setup](02-getting-started.md#icinga2-user-interface-webserver). +Continue with the [webserver setup](02-installation.md#icinga2-user-interface-webserver). ### Configuring DB IDO PostgreSQL @@ -717,15 +756,6 @@ Debian/Ubuntu: apt-get install postgresql ``` -RHEL/CentOS 6: - -``` -yum install postgresql-server postgresql -chkconfig postgresql on -service postgresql initdb -service postgresql start -``` - RHEL/CentOS 7: ``` @@ -846,7 +876,7 @@ export PGPASSWORD=icinga psql -U icinga -d icinga < /usr/share/icinga2-ido-pgsql/schema/pgsql.sql ``` -![importing the Icinga 2 IDO schema](images/getting-started/postgr-import-ido.png "Importing the Icinga 2 IDO schema on Debian Jessie") +![importing the Icinga 2 IDO schema](images/installation/postgr-import-ido.png "Importing the Icinga 2 IDO schema on Debian Jessie") #### Enabling the IDO PostgreSQL module @@ -880,7 +910,7 @@ Alpine Linux: rc-service icinga2 restart ``` -Continue with the [webserver setup](02-getting-started.md#icinga2-user-interface-webserver). +Continue with the [webserver setup](02-installation.md#icinga2-user-interface-webserver). ### Webserver @@ -902,19 +932,11 @@ systemctl enable httpd systemctl start httpd ``` -RHEL/CentOS 6: - -``` -yum install httpd -chkconfig httpd on -service httpd start -``` - SUSE: ``` zypper install apache2 -chkconfig on +chkconfig apache2 on service apache2 start ``` @@ -1003,10 +1025,10 @@ rc-service icinga2 restart ### Installing Icinga Web 2 -Please consult the [installation documentation](https://www.icinga.com/docs/icingaweb2/latest/doc/02-Installation/) +Please consult the [installation documentation](https://icinga.com/docs/icingaweb2/latest/doc/02-Installation/) for further instructions on how to install Icinga Web 2. -The Icinga 2 API can be defined as [command transport](https://www.icinga.com/docs/icingaweb2/latest/modules/monitoring/doc/05-Command-Transports/) +The Icinga 2 API can be defined as [command transport](https://icinga.com/docs/icingaweb2/latest/modules/monitoring/doc/05-Command-Transports/) in Icinga Web 2 >= 2.4. ## Addons diff --git a/doc/03-monitoring-basics.md b/doc/03-monitoring-basics.md index 8e8e59839..569d0f85d 100644 --- a/doc/03-monitoring-basics.md +++ b/doc/03-monitoring-basics.md @@ -208,10 +208,10 @@ You can also import existing non-template objects. ### Multiple Templates -The following example uses [custom attributes](03-monitoring-basics.md#custom-attributes) which +The following example uses [custom variables](03-monitoring-basics.md#custom-variables) which are provided in each template. The `web-server` template is used as the base template for any host providing web services. In addition to that it -specifies the custom attribute `webserver_type`, e.g. `apache`. Since this +specifies the custom variable `webserver_type`, e.g. `apache`. Since this template is also the base template, we import the `generic-host` template here. This provides the `check_command` attribute by default and we don't need to set it anywhere later on. @@ -226,7 +226,7 @@ template Host "web-server" { ``` The `wp-server` host template specifies a Wordpress instance and sets -the `application_type` custom attribute. Please note the `+=` [operator](17-language-reference.md#dictionary-operators) +the `application_type` custom variable. Please note the `+=` [operator](17-language-reference.md#dictionary-operators) which adds [dictionary](17-language-reference.md#dictionary) items, but does not override any previous `vars` attribute. @@ -265,10 +265,22 @@ object Host "wp1.example.com" { } ``` -## Custom Attributes + + -In addition to built-in attributes you can define your own attributes -inside the `vars` attribute: +## Custom Variables + +In addition to built-in object attributes you can define your own custom +attributes inside the `vars` attribute. + +> **Tip** +> +> This is called `custom variables` throughout the documentation, backends and web interfaces. +> +> Older documentation versions referred to this as `custom attribute`. + +The following example specifies the key `ssh_port` as custom +variable and assigns an integer value. ``` object Host "localhost" { @@ -295,17 +307,17 @@ or vars["ssh_port"] = 2222 ``` -### Custom Attribute Values +### Custom Variable Values -Valid values for custom attributes include: +Valid values for custom variables include: * [Strings](17-language-reference.md#string-literals), [numbers](17-language-reference.md#numeric-literals) and [booleans](17-language-reference.md#boolean-literals) * [Arrays](17-language-reference.md#array) and [dictionaries](17-language-reference.md#dictionary) -* [Functions](03-monitoring-basics.md#custom-attributes-functions) +* [Functions](03-monitoring-basics.md#custom-variables-functions) You can also define nested values such as dictionaries in dictionaries. -This example defines the custom attribute `disks` as dictionary. +This example defines the custom variable `disks` as dictionary. The first key is set to `disk /` is itself set to a dictionary with one key-value pair. @@ -338,7 +350,7 @@ Another example which is shown in the example configuration: } ``` -This defines the `notification` custom attribute as dictionary +This defines the `notification` custom variable as dictionary with the key `mail`. Its value is a dictionary with the key `groups` which itself has an array as value. Note: This array is the exact same as the `user_groups` attribute for [notification apply rules](#03-monitoring-basics.md#using-apply-notifications) @@ -354,11 +366,13 @@ expects. } ``` + + -### Functions as Custom Attributes +### Functions as Custom Variables -Icinga 2 lets you specify [functions](17-language-reference.md#functions) for custom attributes. -The special case here is that whenever Icinga 2 needs the value for such a custom attribute it runs +Icinga 2 lets you specify [functions](17-language-reference.md#functions) for custom variables. +The special case here is that whenever Icinga 2 needs the value for such a custom variable it runs the function and uses whatever value the function returns: ``` @@ -430,22 +444,25 @@ Accessing object attributes at runtime inside these functions is described in th ## Runtime Macros -Macros can be used to access other objects' attributes at runtime. For example they -are used in command definitions to figure out which IP address a check should be -run against: +Macros can be used to access other objects' attributes and [custom variables](03-monitoring-basics.md#custom-variables) +at runtime. For example they are used in command definitions to figure out +which IP address a check should be run against: ``` object CheckCommand "my-ping" { - command = [ PluginDir + "/check_ping", "-H", "$ping_address$" ] + command = [ PluginDir + "/check_ping" ] arguments = { + "-H" = "$ping_address$" "-w" = "$ping_wrta$,$ping_wpl$%" "-c" = "$ping_crta$,$ping_cpl$%" "-p" = "$ping_packets$" } + // Resolve from a host attribute, or custom variable. vars.ping_address = "$address$" + // Default values vars.ping_wrta = 100 vars.ping_wpl = 5 @@ -464,7 +481,7 @@ object Host "router" { In this example we are using the `$address$` macro to refer to the host's `address` attribute. -We can also directly refer to custom attributes, e.g. by using `$ping_wrta$`. Icinga +We can also directly refer to custom variables, e.g. by using `$ping_wrta$`. Icinga automatically tries to find the closest match for the attribute you specified. The exact rules for this are explained in the next section. @@ -483,12 +500,12 @@ up macros and their respective values: 2. Service object 3. Host object 4. Command object -5. Global custom attributes in the `Vars` constant +5. Global custom variables in the `Vars` constant -This execution order allows you to define default values for custom attributes +This execution order allows you to define default values for custom variables in your command objects. -Here's how you can override the custom attribute `ping_packets` from the previous +Here's how you can override the custom variable `ping_packets` from the previous example: ``` @@ -500,7 +517,7 @@ object Service "ping" { } ``` -If a custom attribute isn't defined anywhere, an empty value is used and a warning is +If a custom variable isn't defined anywhere, an empty value is used and a warning is written to the Icinga 2 log. You can also directly refer to a specific attribute -- thereby ignoring these evaluation @@ -510,14 +527,14 @@ rules -- by specifying the full attribute name: $service.vars.ping_wrta$ ``` -This retrieves the value of the `ping_wrta` custom attribute for the service. This -returns an empty value if the service does not have such a custom attribute no matter +This retrieves the value of the `ping_wrta` custom variable for the service. This +returns an empty value if the service does not have such a custom variable no matter whether another object such as the host has this attribute. ### Host Runtime Macros -The following host custom attributes are available in all commands that are executed for +The following host custom variables are available in all commands that are executed for hosts or services: Name | Description @@ -583,7 +600,7 @@ attributes can be accessed too. ### Command Runtime Macros -The following custom attributes are available in all commands: +The following custom variables are available in all commands: Name | Description -----------------------|-------------- @@ -591,7 +608,7 @@ The following custom attributes are available in all commands: ### User Runtime Macros -The following custom attributes are available in all commands that are executed for +The following custom variables are available in all commands that are executed for users: Name | Description @@ -661,7 +678,7 @@ attribute and reference an existing host attribute. ``` object Service "ping4" { check_command = "ping4" - host_name = "icinga2-client1.localdomain" + host_name = "icinga2-agent1.localdomain" } ``` @@ -688,7 +705,7 @@ More explanations on assign where expressions can be found [here](03-monitoring- Before you start with apply rules keep the following in mind: * Define the best match. - * A set of unique [custom attributes](03-monitoring-basics.md#custom-attributes) for these hosts/services? + * A set of unique [custom variables](03-monitoring-basics.md#custom-variables) for these hosts/services? * Or [group](03-monitoring-basics.md#groups) memberships, e.g. a host being a member of a hostgroup which should have a service set? * A generic pattern [match](18-library-reference.md#global-functions-match) on the host/service name? * [Multiple expressions combined](03-monitoring-basics.md#using-apply-expressions) with `&&` or `||` [operators](17-language-reference.md#expression-operators) @@ -710,12 +727,12 @@ objects in that scope (host and/or service objects). vars.application_type = host.vars.application_type ``` -[Custom attributes](03-monitoring-basics.md#custom-attributes) can also store +[Custom variables](03-monitoring-basics.md#custom-variables) can also store nested dictionaries and arrays. That way you can use them for not only matching for their existence or values in apply expressions, but also assign ("inherit") their values into the generated objected from apply rules. -Remember the examples shown for [custom attribute values](03-monitoring-basics.md#custom-attributes-values): +Remember the examples shown for [custom variable values](03-monitoring-basics.md#custom-variables-values): ``` vars.notification["mail"] = { @@ -725,7 +742,7 @@ Remember the examples shown for [custom attribute values](03-monitoring-basics.m You can do two things here: -* Check for the existence of the `notification` custom attribute and its nested dictionary key `mail`. +* Check for the existence of the `notification` custom variable and its nested dictionary key `mail`. If this is boolean true, the notification object will be generated. * Assign the value of the `groups` key to the `user_groups` attribute. @@ -742,9 +759,9 @@ apply Notification "mail-icingaadmin" to Host { A more advanced example is to use [apply rules with for loops on arrays or dictionaries](03-monitoring-basics.md#using-apply-for) provided by -[custom atttributes](03-monitoring-basics.md#custom-attributes) or groups. +[custom atttributes](03-monitoring-basics.md#custom-variables) or groups. -Remember the examples shown for [custom attribute values](03-monitoring-basics.md#custom-attributes-values): +Remember the examples shown for [custom variable values](03-monitoring-basics.md#custom-variables-values): ``` vars.disks["disk /"] = { @@ -801,7 +818,7 @@ Assign a service to a specific host in a host group [array](18-library-reference assign where "hostgroup-dev" in host.groups ``` -Assign an object when a custom attribute is [equal](17-language-reference.md#expression-operators) to a value: +Assign an object when a custom variable is [equal](17-language-reference.md#expression-operators) to a value: ``` assign where host.vars.application_type == "database" @@ -827,8 +844,8 @@ Match the host name by using a [regular expression](18-library-reference.md#glob assign where regex("^webserver-[\\d+]", host.name) ``` -[Match](18-library-reference.md#global-functions-match) all `*mysql*` patterns in the host name and (`&&`) custom attribute `prod_mysql_db` -matches the `db-*` pattern. All hosts with the custom attribute `test_server` set to `true` +[Match](18-library-reference.md#global-functions-match) all `*mysql*` patterns in the host name and (`&&`) custom variable `prod_mysql_db` +matches the `db-*` pattern. All hosts with the custom variable `test_server` set to `true` should be ignored, or any host name ending with `*internal` pattern. ``` @@ -843,11 +860,11 @@ object HostGroup "mysql-server" { Similar example for advanced notification apply rule filters: If the service attribute `notes` [matches](18-library-reference.md#global-functions-match) the `has gold support 24x7` string `AND` one of the -two condition passes, either the `customer` host custom attribute is set to `customer-xy` -`OR` the host custom attribute `always_notify` is set to `true`. +two condition passes, either the `customer` host custom variable is set to `customer-xy` +`OR` the host custom variable `always_notify` is set to `true`. The notification is ignored for services whose host name ends with `*internal` -`OR` the `priority` custom attribute is [less than](17-language-reference.md#expression-operators) `2`. +`OR` the `priority` custom variable is [less than](17-language-reference.md#expression-operators) `2`. ``` template Notification "cust-xy-notification" { @@ -867,11 +884,11 @@ More advanced examples are covered [here](08-advanced-topics.md#use-functions-as ### Apply Services to Hosts -The sample configuration already includes a detailed example in [hosts.conf](04-configuring-icinga-2.md#hosts-conf) -and [services.conf](04-configuring-icinga-2.md#services-conf) for this use case. +The sample configuration already includes a detailed example in [hosts.conf](04-configuration.md#hosts-conf) +and [services.conf](04-configuration.md#services-conf) for this use case. The example for `ssh` applies a service object to all hosts with the `address` -attribute being defined and the custom attribute `os` set to the string `Linux` in `vars`. +attribute being defined and the custom variable `os` set to the string `Linux` in `vars`. ``` apply Service "ssh" { @@ -902,17 +919,17 @@ apply Notification "mail-noc" to Service { ``` In this example the `mail-noc` notification will be created as object for all services having the -`notification.mail` custom attribute defined. The notification command is set to `mail-service-notification` +`notification.mail` custom variable defined. The notification command is set to `mail-service-notification` and all members of the user group `noc` will get notified. It is also possible to generally apply a notification template and dynamically overwrite values from -the template by checking for custom attributes. This can be achieved by using [conditional statements](17-language-reference.md#conditional-statements): +the template by checking for custom variables. This can be achieved by using [conditional statements](17-language-reference.md#conditional-statements): ``` apply Notification "host-mail-noc" to Host { import "mail-host-notification" - // replace interval inherited from `mail-host-notification` template with new notfication interval set by a host custom attribute + // replace interval inherited from `mail-host-notification` template with new notfication interval set by a host custom variable if (host.vars.notification_interval) { interval = host.vars.notification_interval } @@ -922,7 +939,7 @@ apply Notification "host-mail-noc" to Host { period = host.vars.notification_period } - // Send SMS instead of email if the host's custom attribute `notification_type` is set to `sms` + // Send SMS instead of email if the host's custom variable `notification_type` is set to `sms` if (host.vars.notification_type == "sms") { command = "sms-host-notification" } else { @@ -939,7 +956,7 @@ In the example above the notification template `mail-host-notification` contains all relevant notification settings. The apply rule is applied on all host objects where the `host.address` is defined. -If the host object as a specific custom attributed set, its value is inherited +If the host object has a specific custom variable set, its value is inherited into the local notification object scope, e.g. `host.vars.notification_interval`, `host.vars.notification_period` and `host.vars.notification_type`. This overwrites attributes already specified in the imported `mail-host-notification` @@ -964,7 +981,7 @@ Detailed examples can be found in the [dependencies](03-monitoring-basics.md#dep ### Apply Recurring Downtimes to Hosts and Services -The sample configuration includes an example in [downtimes.conf](04-configuring-icinga-2.md#downtimes-conf). +The sample configuration includes an example in [downtimes.conf](04-configuration.md#downtimes-conf). Detailed examples can be found in the [recurring downtimes](08-advanced-topics.md#recurring-downtimes) chapter. @@ -975,8 +992,8 @@ Next to the standard way of using [apply rules](03-monitoring-basics.md#using-ap there is the requirement of applying objects based on a set (array or dictionary) using [apply for](17-language-reference.md#apply-for) expressions. -The sample configuration already includes a detailed example in [hosts.conf](04-configuring-icinga-2.md#hosts-conf) -and [services.conf](04-configuring-icinga-2.md#services-conf) for this use case. +The sample configuration already includes a detailed example in [hosts.conf](04-configuration.md#hosts-conf) +and [services.conf](04-configuration.md#services-conf) for this use case. Take the following example: A host provides the snmp oids for different service check types. This could look like the following example: @@ -984,7 +1001,7 @@ types. This could look like the following example: ``` object Host "router-v6" { check_command = "hostalive" - address6 = "::1" + address6 = "2001:db8:1234::42" vars.oids["if01"] = "1.1.1.1.1" vars.oids["temp"] = "1.1.1.1.2" @@ -993,7 +1010,7 @@ object Host "router-v6" { ``` The idea is to create service objects for `if01` and `temp` but not `bgp`. -The oid value should also be used as service custom attribute `snmp_oid`. +The oid value should also be used as service custom variable `snmp_oid`. This is the command argument required by the [snmp](10-icinga-template-library.md#plugin-check-command-snmp) check command. The service's `display_name` should be set to the identifier inside the dictionary, @@ -1009,7 +1026,7 @@ apply Service for (identifier => oid in host.vars.oids) { } ``` -Icinga 2 evaluates the `apply for` rule for all objects with the custom attribute +Icinga 2 evaluates the `apply for` rule for all objects with the custom variable `oids` set. It iterates over all dictionary items inside the `for` loop and evaluates the `assign/ignore where` expressions. You can access the loop variable @@ -1026,13 +1043,15 @@ unwanted services. A different approach would be to match the `oid` value with a > **Note** > > You don't need an `assign where` expression which checks for the existence of the -> `oids` custom attribute. +> `oids` custom variable. This method saves you from creating multiple apply rules. It also moves the attribute specification logic from the service to the host. + + -#### Apply For and Custom Attribute Override +#### Apply For and Custom Variable Override Imagine a different more advanced example: You are monitoring your network device (host) with many interfaces (services). The following requirements/problems apply: @@ -1046,17 +1065,17 @@ dynamically generated. > **Tip** > -> Define the SNMP community as global constant in your [constants.conf](04-configuring-icinga-2.md#constants-conf) file. +> Define the SNMP community as global constant in your [constants.conf](04-configuration.md#constants-conf) file. ``` const IftrafficSnmpCommunity = "public" ``` -Define the `interfaces` [custom attribute](03-monitoring-basics.md#custom-attributes) +Define the `interfaces` [custom variable](03-monitoring-basics.md#custom-variables) on the `cisco-catalyst-6509-34` host object and add three example interfaces as dictionary keys. Specify additional attributes inside the nested dictionary -as learned with [custom attribute values](03-monitoring-basics.md#custom-attributes-values): +as learned with [custom variable values](03-monitoring-basics.md#custom-variables-values): ``` object Host "cisco-catalyst-6509-34" { @@ -1068,7 +1087,7 @@ object Host "cisco-catalyst-6509-34" { * and key name in service apply for later on */ vars.interfaces["GigabitEthernet0/2"] = { - /* define all custom attributes with the + /* define all custom variables with the * same name required for command parameters/arguments * in service apply (look into your CheckCommand definition) */ @@ -1141,17 +1160,17 @@ interface_config = { ``` Access the dictionary keys with the [indexer](17-language-reference.md#indexer) syntax -and assign them to custom attributes used as command parameters for the `iftraffic` +and assign them to custom variables used as command parameters for the `iftraffic` check command. ``` - /* map the custom attributes as command arguments */ + /* map the custom variables as command arguments */ vars.iftraffic_units = interface_config.iftraffic_units vars.iftraffic_community = interface_config.iftraffic_community ``` If you just want to inherit all attributes specified inside the `interface_config` -dictionary, add it to the generated service custom attributes like this: +dictionary, add it to the generated service custom variables like this: ``` /* the above can be achieved in a shorter fashion if the names inside host.vars.interfaces @@ -1161,7 +1180,7 @@ dictionary, add it to the generated service custom attributes like this: vars += interface_config ``` -If the user did not specify default values for required service custom attributes, +If the user did not specify default values for required service custom variables, add them here. This also helps to avoid unwanted configuration validation errors or runtime failures. Please read more about conditional statements [here](17-language-reference.md#conditional-statements). @@ -1211,7 +1230,7 @@ more object attributes which can be e.g. seen in external interfaces. > after successful [configuration validation](11-cli-commands.md#config-validation). Verify that the apply-for-rule successfully created the service objects with the -inherited custom attributes: +inherited custom variables: ``` # icinga2 daemon -C @@ -1292,7 +1311,7 @@ object Host "opennebula-host" { } ``` -`hosting` is a custom attribute with the Dictionary value type. +`hosting` is a custom variable with the Dictionary value type. This is mandatory to iterate with the `key => value` notation in the below apply for rule. @@ -1553,26 +1572,34 @@ send notifications to all group members. > Only users who have been notified of a problem before (`Warning`, `Critical`, `Unknown` states for services, `Down` for hosts) will receive `Recovery` notifications. +Icinga 2 v2.10 allows you to configure `Acknowledgement` and/or `Recovery` +without a `Problem` notification. These notifications will be sent without +any problem notifications beforehand, and can be used for e.g. ticket systems. + +``` + types = [ Acknowledgement, Recovery ] +``` + ### Notifications: Users from Host/Service A common pattern is to store the users and user groups on the host or service objects instead of the notification object itself. -The sample configuration provided in [hosts.conf](04-configuring-icinga-2.md#hosts-conf) and [notifications.conf](notifications-conf) +The sample configuration provided in [hosts.conf](04-configuration.md#hosts-conf) and [notifications.conf](notifications-conf) already provides an example for this question. > **Tip** > > Please make sure to read the [apply](03-monitoring-basics.md#using-apply) and -> [custom attribute values](03-monitoring-basics.md#custom-attributes-values) chapter to +> [custom variable values](03-monitoring-basics.md#custom-variables-values) chapter to > fully understand these examples. -Specify the user and groups as nested custom attribute on the host object: +Specify the user and groups as nested custom variable on the host object: ``` -object Host "icinga2-client1.localdomain" { +object Host "icinga2-agent1.localdomain" { [...] vars.notification["mail"] = { @@ -1589,7 +1616,7 @@ As you can see, there is the option to use two different notification apply rules here: One for `mail` and one for `sms`. This example assigns the `users` and `groups` nested keys from the `notification` -custom attribute to the actual notification object attributes. +custom variable to the actual notification object attributes. Since errors are hard to debug if host objects don't specify the required configuration attributes, you can add a safety condition which logs which @@ -1661,7 +1688,7 @@ notification users and groups are inherited from the service and if not set, from the host object. A default user is set too. ``` -apply Notification "mail-host-notification" to Service { +apply Notification "mail-service-notification" to Service { [...] if (service.vars.notification.mail.users) { @@ -1675,11 +1702,11 @@ apply Notification "mail-host-notification" to Service { if (service.vars.notification.mail.groups) { user_groups = service.vars.notification.mail.groups - } else (host.vars.notification.mail.groups) { + } else if (host.vars.notification.mail.groups) { user_groups = host.vars.notification.mail.groups } - assign where host.vars.notification.mail && typeof(host.vars.notification.mail) == Dictionary + assign where ( host.vars.notification.mail && typeof(host.vars.notification.mail) == Dictionary ) || ( service.vars.notification.mail && typeof(service.vars.notification.mail) == Dictionary ) } ``` @@ -1866,7 +1893,7 @@ using the `check_command` attribute. #### Integrate the Plugin with a CheckCommand Definition Unless you have done so already, download your check plugin and put it -into the [PluginDir](04-configuring-icinga-2.md#constants-conf) directory. The following example uses the +into the [PluginDir](04-configuration.md#constants-conf) directory. The following example uses the `check_mysql` plugin contained in the Monitoring Plugins package. The plugin path and all command arguments are made a list of @@ -1896,7 +1923,7 @@ Please continue reading in the [plugins section](05-service-monitoring.md#servic #### Passing Check Command Parameters from Host or Service -Check command parameters are defined as custom attributes which can be accessed as runtime macros +Check command parameters are defined as custom variables which can be accessed as runtime macros by the executed check command. The check command parameters for ITL provided plugin check command definitions are documented @@ -1905,11 +1932,11 @@ The check command parameters for ITL provided plugin check command definitions a In order to practice passing command parameters you should [integrate your own plugin](03-monitoring-basics.md#command-plugin-integration). -The following example will use `check_mysql` provided by the [Monitoring Plugins installation](02-getting-started.md#setting-up-check-plugins). +The following example will use `check_mysql` provided by the [Monitoring Plugins installation](02-installation.md#setting-up-check-plugins). -Define the default check command custom attributes, for example `mysql_user` and `mysql_password` +Define the default check command custom variables, for example `mysql_user` and `mysql_password` (freely definable naming schema) and optional their default threshold values. You can -then use these custom attributes as runtime macros for [command arguments](03-monitoring-basics.md#command-arguments) +then use these custom variables as runtime macros for [command arguments](03-monitoring-basics.md#command-arguments) on the command line. > **Tip** @@ -1918,8 +1945,8 @@ on the command line. > readability. `mysql_user` helps understanding the context better than just > `user` as argument. -The default custom attributes can be overridden by the custom attributes -defined in the host or service using the check command `my-mysql`. The custom attributes +The default custom variables can be overridden by the custom variables +defined in the host or service using the check command `my-mysql`. The custom variables can also be inherited from a parent template using additive inheritance (`+=`). ``` @@ -1965,7 +1992,7 @@ The check command definition also sets `mysql_host` to the `$address$` default v this command parameter if for example your MySQL host is not running on the same server's ip address. Make sure pass all required command parameters, such as `mysql_user`, `mysql_password` and `mysql_database`. -`MysqlUsername` and `MysqlPassword` are specified as [global constants](04-configuring-icinga-2.md#constants-conf) +`MysqlUsername` and `MysqlPassword` are specified as [global constants](04-configuration.md#constants-conf) in this example. ``` @@ -1988,10 +2015,10 @@ apply Service "mysql-icinga-db-health" { ``` -Take a different example: The example host configuration in [hosts.conf](04-configuring-icinga-2.md#hosts-conf) +Take a different example: The example host configuration in [hosts.conf](04-configuration.md#hosts-conf) also applies an `ssh` service check. Your host's ssh port is not the default `22`, but set to `2022`. -You can pass the command parameter as custom attribute `ssh_port` directly inside the service apply rule -inside [services.conf](04-configuring-icinga-2.md#services-conf): +You can pass the command parameter as custom variable `ssh_port` directly inside the service apply rule +inside [services.conf](04-configuration.md#services-conf): ``` apply Service "ssh" { @@ -2008,7 +2035,7 @@ If you prefer this being configured at the host instead of the service, modify t object instead. The runtime macro resolving order is described [here](03-monitoring-basics.md#macro-evaluation-order). ``` -object Host "icinga2-client1.localdomain { +object Host "icinga2-agent1.localdomain { ... vars.ssh_port = 2022 } @@ -2018,10 +2045,10 @@ object Host "icinga2-client1.localdomain { The host `localhost` with the generated services from the `basic-partitions` dictionary (see [apply for](03-monitoring-basics.md#using-apply-for) for details) checks a basic set of disk partitions -with modified custom attributes (warning thresholds at `10%`, critical thresholds at `5%` +with modified custom variables (warning thresholds at `10%`, critical thresholds at `5%` free disk space). -The custom attribute `disk_partition` can either hold a single string or an array of +The custom variable `disk_partition` can either hold a single string or an array of string values for passing multiple partitions to the `check_disk` check plugin. ``` @@ -2047,121 +2074,360 @@ apply Service for (disk => config in host.vars.local_disks) { ``` -More details on using arrays in custom attributes can be found in -[this chapter](03-monitoring-basics.md#custom-attributes). +More details on using arrays in custom variables can be found in +[this chapter](03-monitoring-basics.md#custom-variables). #### Command Arguments -By defining a check command line using the `command` attribute Icinga 2 -will resolve all macros in the static string or array. Sometimes it is -required to extend the arguments list based on a met condition evaluated -at command execution. Or making arguments optional -- only set if the -macro value can be resolved by Icinga 2. +Next to the short `command` array specified in the command object, +it is advised to define plugin/script parameters in the `arguments` +dictionary attribute. + +The value of the `--parameter` key itself is a dictionary with additional +keys. They allow to create generic command objects and are also for documentation +purposes, e.g. with the `description` field copying the plugin's help text in there. +The Icinga Director uses this field to show the argument's purpose when selecting it. ``` -object CheckCommand "http" { - command = [ PluginDir + "/check_http" ] + arguments = { + "--parameter" = { + description = "..." + value = "..." + } + } +``` + +Each argument is optional by default and is omitted if +the value is not set. + +Learn more about integrating plugins with CheckCommand +objects in [this chapter](05-service-monitoring.md#service-monitoring-plugin-checkcommand). + +There are additional possibilities for creating a command only once, +with different parameters and arguments, shown below. + +##### Command Arguments: Value + +In order to find out about the command argument, call the plugin's help +or consult the README. + +``` +./check_systemd.py --help + +... + + -u UNIT, --unit UNIT Name of the systemd unit that is beeing tested. +``` + +Whenever the long parameter name is available, prefer this over the short one. + +``` + arguments = { + "--unit" = { + + } + } +``` + +Define a unique `prefix` for the command's specific arguments. Best practice is to follow this schema: + +``` +_ +``` + +Therefore use `systemd_` as prefix, and use the long plugin parameter name `unit` inside the [runtime macro](03-monitoring-basics.md#runtime-macros) +syntax. + +``` + arguments = { + "--unit" = { + value = "$systemd_unit$" + } + } +``` + +In order to specify a default value, specify +a [custom variable](03-monitoring-basics.md#custom-variables) inside +the CheckCommand object. + +``` + vars.systemd_unit = "icinga2" +``` + +This value can be overridden from the host/service +object as command parameters. + + +##### Command Arguments: Description + +Best practice, also inside the [ITL](10-icinga-template-library.md#icinga-template-library), is to always +copy the command parameter help output into the `description` +field of your check command. + +Learn more about integrating plugins with CheckCommand +objects in [this chapter](05-service-monitoring.md#service-monitoring-plugin-checkcommand). + +With the [example above](03-monitoring-basics.md#command-arguments-value), +inspect the parameter's help text. + +``` +./check_systemd.py --help + +... + + -u UNIT, --unit UNIT Name of the systemd unit that is beeing tested. +``` + +Copy this into the command arguments `description` entry. + +``` + arguments = { + "--unit" = { + value = "$systemd_unit$" + description = "Name of the systemd unit that is beeing tested." + } + } +``` + +##### Command Arguments: Required + +Specifies whether this command argument is required, or not. By +default all arguments are optional. + +> **Tip** +> +> Good plugins provide optional parameters in square brackets, e.g. `[-w SECONDS]`. + +The `required` field can be toggled with a [boolean](17-language-reference.md#boolean-literals) value. + +``` + arguments = { + "--host" = { + value = "..." + description = "..." + required = true + } + } +``` + +Whenever the check is executed and the argument is missing, Icinga +logs an error. This allows to better debug configuration errors +instead of sometimes unreadable plugin errors when parameters are +missing. + +##### Command Arguments: Skip Key + +The `arguments` attribute requires a key, empty values are not allowed. +To overcome this for parameters which don't need the name in front of +the value, use the `skip_key` [boolean](17-language-reference.md#boolean-literals) toggle. + +``` + command = [ PrefixDir + "/bin/icingacli", "businessprocess", "process", "check" ] arguments = { - "-H" = "$http_vhost$" - "-I" = "$http_address$" - "-u" = "$http_uri$" - "-p" = "$http_port$" - "-S" = { - set_if = "$http_ssl$" + "--process" = { + value = "$icingacli_businessprocess_process$" + description = "Business process to monitor" + skip_key = true + required = true + order = -1 } + } +``` + +The service specifies the [custom variable](03-monitoring-basics.md#custom-variables) `icingacli_businessprocess_process`. + +``` + vars.icingacli_businessprocess_process = "bp-shop-web" +``` + +This results in this command line without the `--process` parameter: + +``` +'/bin/icingacli' 'businessprocess' 'process' 'check' 'bp-shop-web' +``` + +You can use this method to put everything into the `arguments` attribute +in a defined order and without keys. This avoids entries in the `command` +attributes too. + + +##### Command Arguments: Set If + +This can be used for the following scenarios: + +**Parameters without value, e.g. `--sni`.** + +``` + command = [ PluginDir + "/check_http"] + + arguments = { "--sni" = { set_if = "$http_sni$" } - "-a" = { - value = "$http_auth_pair$" - description = "Username:password on sites with basic authentication" - } - "--no-body" = { - set_if = "$http_ignore_body$" - } - "-r" = "$http_expect_body_regex$" - "-w" = "$http_warn_time$" - "-c" = "$http_critical_time$" - "-e" = "$http_expect$" } - - vars.http_address = "$address$" - vars.http_ssl = false - vars.http_sni = false -} ``` -The example shows the `check_http` check command defining the most common -arguments. Each of them is optional by default and is omitted if -the value is not set. For example, if the service calling the check command -does not have `vars.http_port` set, it won't get added to the command -line. +Whenever a host/service object sets the `http_sni` [custom variable](03-monitoring-basics.md#custom-variables) +to `true`, the parameter is added to the command line. -If the `vars.http_ssl` custom attribute is set in the service, host or command -object definition, Icinga 2 will add the `-S` argument based on the `set_if` -numeric value to the command line. String values are not supported. +``` +'/usr/lib64/nagios/plugins/check_http' '--sni' +``` -If the macro value cannot be resolved, Icinga 2 will not add the defined argument -to the final command argument array. Empty strings for macro values won't omit -the argument. - -That way you can use the `check_http` command definition for both, with and -without SSL enabled checks saving you duplicated command definitions. - -Details on all available options can be found in the -[CheckCommand object definition](09-object-types.md#objecttype-checkcommand). - -##### Command Arguments: set_if - -The `set_if` attribute in command arguments can be used to only add -this parameter if the runtime macro value is boolean `true`. - -Best practice is to define and pass only [boolean](17-language-reference.md#boolean-literals) values here. [Numeric](17-language-reference.md#numeric-literals) values are allowed too. -Examples: +**Parameters with value, but additionally controlled with an extra custom variable boolean flag.** + +The following example is taken from the [postgres]() CheckCommand. The host +parameter should use a `value` but only whenever the `postgres_unixsocket` +[custom variable](03-monitoring-basics.md#custom-variables) is set to false. + +Note: `set_if` is using a runtime lambda function because the value +is evaluated at runtime. This is explained in [this chapter](08-advanced-topics.md#use-functions-object-config). ``` -vars.test_b = true -vars.test_n = 3.0 + command = [ PluginContribDir + "/check_postgres.pl" ] -arguments = { - "-x" = { - set_if = "$test_b$" + arguments = { + "-H" = { + value = "$postgres_host$" + set_if = {{ macro("$postgres_unixsocket$") == false }} + description = "hostname(s) to connect to; defaults to none (Unix socket)" } - "-y" = { - set_if = "$test_n$" +``` + +An executed check for this host and services ... + +``` +object Host "postgresql-cluster" { + // ... + + vars.postgres_host = "192.168.56.200" + vars.postgres_unixsocket = false +} +``` + +... use the following command line: + +``` +'/usr/lib64/nagios/plugins/check_postgres.pl' '-H' '192.168.56.200' +``` + +Host/service objects which set `postgres_unixsocket` to `false` don't add the `-H` parameter +and its value to the command line. + +References: [abbreviated lambda syntax](17-language-reference.md#nullary-lambdas), [macro](18-library-reference.md#scoped-functions-macro). + +##### Command Arguments: Order + +Plugin may require parameters in a special order. One after the other, +or e.g. one parameter always in the first position. + +``` + arguments = { + "--first" = { + value = "..." + description = "..." + order = -5 + } + "--second" = { + value = "..." + description = "..." + order = -4 + } + "--last" = { + value = "..." + description = "..." + order = 99 + } + } +``` + +Keep in mind that positional arguments need to be tested thoroughly. + +##### Command Arguments: Repeat Key + +Parameters can use [Array](17-language-reference.md#array) as value type. Whenever Icinga encounters +an array, it repeats the parameter key and each value element by default. + +``` + command = [ NscpPath + "\\nscp.exe", "client" ] + + arguments = { + "-a" = { + value = "$nscp_arguments$" + description = "..." + repeat_key = true + } + } +``` + +On a host/service object, specify the `nscp_arguments` [custom variable](03-monitoring-basics.md#custom-variables) +as an array. + +``` + vars.nscp_arguments = [ "exclude=sppsvc", "exclude=ShellHWDetection" ] +``` + +This translates into the following command line: + +``` +nscp.exe 'client' '-a' 'exclude=sppsvc' '-a' 'exclude=ShellHWDetection' +``` + +If the plugin requires you to pass the list without repeating the key, +set `repeat_key = false` in the argument definition. + +``` + command = [ NscpPath + "\\nscp.exe", "client" ] + + arguments = { + "-a" = { + value = "$nscp_arguments$" + description = "..." + repeat_key = false + } + } +``` + +This translates into the following command line: + +``` +nscp.exe 'client' '-a' 'exclude=sppsvc' 'exclude=ShellHWDetection' +``` + + +##### Command Arguments: Key + +The `arguments` attribute requires unique keys. Sometimes, you'll +need to override this in the resulting command line with same key +names. Therefore you can specifically override the arguments key. + +``` +arguments = { + "--key1" = { + value = "..." + key = "-specialkey" + } + "--key2" = { + value = "..." + key = "-specialkey" } } ``` -If you accidentally used a [String](17-language-reference.md#string-literals) value, this could lead into -an undefined behaviour. - -If you still want to work with String values and other variants, you can also -use runtime evaluated functions for `set_if`. +This results in the following command line: ``` -vars.test_s = "1.1.2.1" -arguments = { - "-z" = { - set_if = {{ - var str = macro("$test_s$") - - return regex("^\d.\d.\d.\d$", str) - }} - } + '-specialkey' '...' '-specialkey' '...' ``` -References: [abbreviated lambda syntax](17-language-reference.md#nullary-lambdas), [macro](18-library-reference.md#scoped-functions-macro), [regex](18-library-reference.md#global-functions-regex). - - #### Environment Variables The `env` command object attribute specifies a list of environment variables with values calculated -from custom attributes which should be exported as environment variables prior to executing the command. +from custom variables which should be exported as environment variables prior to executing the command. This is useful for example for hiding sensitive information on the command line output when passing credentials to database checks: @@ -2195,7 +2461,7 @@ the database credentials in the user's environment. > **Note** > > If the CheckCommand also supports setting the parameter in the command line, -> ensure to use a different name for the custom attribute. Otherwise Icinga 2 +> ensure to use a different name for the custom variable. Otherwise Icinga 2 > adds the command line parameter. If a specific CheckCommand object provided with the [Icinga Template Library](10-icinga-template-library.md#icinga-template-library) @@ -2213,13 +2479,13 @@ object CheckCommand "mysql_health_env" { } ``` -Specify the custom attributes `mysql_health_env_username` and `mysql_health_env_password` +Specify the custom variables `mysql_health_env_username` and `mysql_health_env_password` in the service object then. > **Note** > > Keep in mind that the values are still visible with the [debug console](11-cli-commands.md#cli-command-console) -> and the inspect mode in the [Icinga Director](https://www.icinga.com/docs/director/latest/). +> and the inspect mode in the [Icinga Director](https://icinga.com/docs/director/latest/). You can also set global environment variables in the application's sysconfig configuration file, e.g. `HOME` or specific library paths @@ -2265,54 +2531,13 @@ defaults can always be overwritten locally. > > This example requires the `mail` binary installed on the Icinga 2 > master. - -#### Notification Commands in 2.7 - -Icinga 2 v2.7.0 introduced new notification scripts which support both -environment variables and command line parameters. - -Therefore the `NotificationCommand` objects inside the [commands.conf](04-configuring-icinga-2.md#commands-conf) -and `Notification` apply rules inside the [notifications.conf](04-configuring-icinga-2.md#notifications-conf) -configuration files have been updated. Your configuration needs to be -updated next to the notification scripts themselves. - -> **Note** > -> Several parameters have been changed. Please review the notification -> script parameters and configuration objects before updating your production -> environment. - -The safest way is to incorporate the configuration updates from -v2.7.0 inside the [commands.conf](04-configuring-icinga-2.md#commands-conf) and [notifications.conf](04-configuring-icinga-2.md#notifications-conf) -configuration files. - -A quick-fix is shown below: - -``` -@@ -5,7 +5,8 @@ object NotificationCommand "mail-host-notification" { - - env = { - NOTIFICATIONTYPE = "$notification.type$" -- HOSTALIAS = "$host.display_name$" -+ HOSTNAME = "$host.name$" -+ HOSTDISPLAYNAME = "$host.display_name$" - HOSTADDRESS = "$address$" - HOSTSTATE = "$host.state$" - LONGDATETIME = "$icinga.long_date_time$" -@@ -22,8 +23,9 @@ object NotificationCommand "mail-service-notification" { - - env = { - NOTIFICATIONTYPE = "$notification.type$" -- SERVICEDESC = "$service.name$" -- HOSTALIAS = "$host.display_name$" -+ SERVICENAME = "$service.name$" -+ HOSTNAME = "$host.name$" -+ HOSTDISPLAYNAME = "$host.display_name$" - HOSTADDRESS = "$address$" - SERVICESTATE = "$service.state$" - LONGDATETIME = "$icinga.long_date_time$" -``` - +> Depending on the distribution, you need a local mail transfer +> agent (MTA) such as Postfix, Exim or Sendmail in order +> to send emails. +> +> These tools virtually provide the `mail` binary executed +> by the notification scripts below. #### mail-host-notification @@ -2383,7 +2608,7 @@ account but all parents are inherited. The `parent_host_name` and `parent_service_name` attributes are mandatory for service dependencies, `parent_host_name` is required for host dependencies. [Apply rules](03-monitoring-basics.md#using-apply) will allow you to -[determine these attributes](03-monitoring-basics.md#dependencies-apply-custom-attributes) in a more +[determine these attributes](03-monitoring-basics.md#dependencies-apply-custom-variables) in a more dynamic fashion if required. ``` @@ -2416,14 +2641,19 @@ states = [ OK, Critical, Unknown ] > If the parent service object changes into the `Warning` state, this > dependency will fail and render all child objects (hosts or services) unreachable. -You can determine the child's reachability by querying the `is_reachable` attribute -in for example [DB IDO](24-appendix.md#schema-db-ido-extensions). +You can determine the child's reachability by querying the `last_reachable` attribute +via the [REST API](12-icinga2-api.md#icinga2-api). + +> **Note** +> +> Reachability calculation depends on fresh and processed check results. If dependencies +> disable checks for child objects, this won't work reliably. ### Implicit Dependencies for Services on Host Icinga 2 automatically adds an implicit dependency for services on their host. That way service notifications are suppressed when a host is `DOWN` or `UNREACHABLE`. This dependency -does not overwrite other dependencies and implicitely sets `disable_notifications = true` and +does not overwrite other dependencies and implicitly sets `disable_notifications = true` and `states = [ Up ]` for all service objects. Service checks are still executed. If you want to prevent them from happening, you can @@ -2485,7 +2715,10 @@ apply Dependency "internet" to Service { } ``` -### Apply Dependencies based on Custom Attributes + + + +### Apply Dependencies based on Custom Variables You can use [apply rules](03-monitoring-basics.md#using-apply) to set parent or child attributes, e.g. `parent_host_name` to other objects' @@ -2493,7 +2726,7 @@ attributes. A common example are virtual machines hosted on a master. The object name of that master is auto-generated from your CMDB or VMWare inventory -into the host's custom attributes (or a generic template for your +into the host's custom variables (or a generic template for your cloud). Define your master host object: @@ -2515,7 +2748,7 @@ template Host "generic-vm" { ``` Add a template for all hosts on your example.com cloud setting -custom attribute `vm_parent` to `master.example.com`: +custom variable `vm_parent` to `master.example.com`: ``` template Host "generic-vm-example.com" { @@ -2538,7 +2771,7 @@ object Host "www.example2.com" { Apply the host dependency to all child hosts importing the `generic-vm` template and set the `parent_host_name` -to the previously defined custom attribute `host.vars.vm_parent`. +to the previously defined custom variable `host.vars.vm_parent`. ``` apply Dependency "vm-host-to-parent-master" to Host { @@ -2570,55 +2803,40 @@ will detect their reachability immediately when executing checks. ### Dependencies for Agent Checks -Another classic example are agent based checks. You would define a health check +Another good example are agent based checks. You would define a health check for the agent daemon responding to your requests, and make all other services querying that daemon depend on that health check. -The following configuration defines two nrpe based service checks `nrpe-load` -and `nrpe-disk` applied to the host `nrpe-server` [matched](18-library-reference.md#global-functions-match) -by its name. The health check is defined as `nrpe-health` service. +``` +apply Service "agent-health" { + check_command = "cluster-zone" + + display_name = "cluster-health-" + host.name + + /* This follows the convention that the agent zone name is the FQDN which is the same as the host object name. */ + vars.cluster_zone = host.name + + assign where host.vars.agent_endpoint +} +``` + +Now, make all other agent based checks dependent on the OK state of the `agent-health` +service. ``` -apply Service "nrpe-health" { - import "generic-service" - check_command = "nrpe" - assign where match("nrpe-*", host.name) -} +apply Dependency "agent-health-check" to Service { + parent_service_name = "agent-health" -apply Service "nrpe-load" { - import "generic-service" - check_command = "nrpe" - vars.nrpe_command = "check_load" - assign where match("nrpe-*", host.name) -} - -apply Service "nrpe-disk" { - import "generic-service" - check_command = "nrpe" - vars.nrpe_command = "check_disk" - assign where match("nrpe-*", host.name) -} - -object Host "nrpe-server" { - import "generic-host" - address = "192.168.1.5" -} - -apply Dependency "disable-nrpe-checks" to Service { - parent_service_name = "nrpe-health" - - states = [ OK ] - disable_checks = true + states = [ OK ] // Fail if the parent service state switches to NOT-OK disable_notifications = true - assign where service.check_command == "nrpe" - ignore where service.name == "nrpe-health" + + assign where host.vars.agent_endpoint // Automatically assigns all agent endpoint checks as child services on the matched host + ignore where service.name == "agent-health" // Avoid a self reference from child to parent } + ``` -The `disable-nrpe-checks` dependency is applied to all services -on the `nrpe-service` host using the `nrpe` check_command attribute -but not the `nrpe-health` service itself. - +This is described in detail in [this chapter](06-distributed-monitoring.md#distributed-monitoring-health-checks). ### Event Commands @@ -2769,7 +2987,7 @@ The script only is executed if the service state is `CRITICAL`. Warning and Unkn are ignored as they indicate not an immediate failure. ``` -[root@icinga2-client1.localdomain /]# vim /usr/lib64/nagios/plugins/restart_service +[root@icinga2-agent1.localdomain /]# vim /usr/lib64/nagios/plugins/restart_service #!/bin/bash @@ -2800,7 +3018,7 @@ else fi fi -[root@icinga2-client1.localdomain /]# chmod +x /usr/lib64/nagios/plugins/restart_service +[root@icinga2-agent1.localdomain /]# chmod +x /usr/lib64/nagios/plugins/restart_service ``` Add a service on the master node which is executed via command endpoint on the client. @@ -2808,15 +3026,15 @@ Set the `event_command` attribute to `restart_service`, the name of the previous EventCommand object. ``` -[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/icinga2-client1.localdomain.conf +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/icinga2-agent1.localdomain.conf object Service "Process httpd" { check_command = "procs" event_command = "restart_service" max_check_attempts = 4 - host_name = "icinga2-client1.localdomain" - command_endpoint = "icinga2-client1.localdomain" + host_name = "icinga2-agent1.localdomain" + command_endpoint = "icinga2-agent1.localdomain" vars.procs_command = "httpd" vars.procs_warning = "1:10" @@ -2824,17 +3042,17 @@ object Service "Process httpd" { } ``` -In order to test this configuration just stop the `httpd` on the remote host `icinga2-client1.localdomain`. +In order to test this configuration just stop the `httpd` on the remote host `icinga2-agent1.localdomain`. ``` -[root@icinga2-client1.localdomain /]# systemctl stop httpd +[root@icinga2-agent1.localdomain /]# systemctl stop httpd ``` You can enable the [debug log](15-troubleshooting.md#troubleshooting-enable-debug-output) and search for the executed command line. ``` -[root@icinga2-client1.localdomain /]# tail -f /var/log/icinga2/debug.log | grep restart_service +[root@icinga2-agent1.localdomain /]# tail -f /var/log/icinga2/debug.log | grep restart_service ``` #### Use Event Commands to Restart Service Daemon via Command Endpoint on Windows @@ -2910,21 +3128,21 @@ Set the `event_command` attribute to `restart_service-windows`, the name of the EventCommand object. ``` -[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/icinga2-client2.localdomain.conf +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/icinga2-agent2.localdomain.conf object Service "Service httpd" { check_command = "service-windows" event_command = "restart_service-windows" max_check_attempts = 4 - host_name = "icinga2-client2.localdomain" - command_endpoint = "icinga2-client2.localdomain" + host_name = "icinga2-agent2.localdomain" + command_endpoint = "icinga2-agent2.localdomain" vars.service_win_service = "httpd" } ``` -In order to test this configuration just stop the `httpd` on the remote host `icinga2-client1.localdomain`. +In order to test this configuration just stop the `httpd` on the remote host `icinga2-agent1.localdomain`. ``` C:> net stop httpd @@ -2985,7 +3203,7 @@ object EventCommand "event_by_ssh" { ``` The actual event command only passes the `event_by_ssh_command` attribute. -The `event_by_ssh_service` custom attribute takes care of passing the correct +The `event_by_ssh_service` custom variable takes care of passing the correct daemon name, while `test $service.state_id$ -gt 0` makes sure that the daemon is only restarted when the service is not in an `OK` state. @@ -3018,7 +3236,7 @@ apply Service "http" { } ``` -Specify the `httpd_name` custom attribute on the host to assign the +Specify the `httpd_name` custom variable on the host to assign the service and set the event handler service. ``` @@ -3030,15 +3248,15 @@ object Host "remote-http-host" { } ``` -In order to test this configuration just stop the `httpd` on the remote host `icinga2-client1.localdomain`. +In order to test this configuration just stop the `httpd` on the remote host `icinga2-agent1.localdomain`. ``` -[root@icinga2-client1.localdomain /]# systemctl stop httpd +[root@icinga2-agent1.localdomain /]# systemctl stop httpd ``` You can enable the [debug log](15-troubleshooting.md#troubleshooting-enable-debug-output) and search for the executed command line. ``` -[root@icinga2-client1.localdomain /]# tail -f /var/log/icinga2/debug.log | grep by_ssh +[root@icinga2-agent1.localdomain /]# tail -f /var/log/icinga2/debug.log | grep by_ssh ``` diff --git a/doc/04-configuring-icinga-2.md b/doc/04-configuration.md similarity index 57% rename from doc/04-configuring-icinga-2.md rename to doc/04-configuration.md index 61e920126..be787e77b 100644 --- a/doc/04-configuring-icinga-2.md +++ b/doc/04-configuration.md @@ -1,8 +1,18 @@ -# Configuring Icinga 2: First Steps +# Configuration -This chapter provides an introduction into best practices for your Icinga 2 configuration. -The configuration files which are automatically created when installing the Icinga 2 packages -are a good way to start with Icinga 2. +The Icinga [configuration](https://icinga.com/products/configuration/) +can be easily managed with either the [Icinga Director](https://icinga.com/docs/director/latest/), +config management tools or plain text within the [Icinga DSL](04-configuration.md#configuration). + +Before looking into web based configuration or any sort of automation, +we recommend to start with the configuration files and fully understand +the possibilities of the Icinga DSL (Domain Specific Language). + +The package installation provides example configuration which already +monitors the local Icinga server. You can view the monitoring details +in Icinga Web. + +![Icinga Web Local Server](images/configuration/icinga_web_local_server.png) The [Language Reference](17-language-reference.md#language-reference) chapter explains details on value types (string, number, dictionaries, etc.) and the general configuration syntax. @@ -15,8 +25,9 @@ decide for a possible strategy. There are many ways of creating Icinga 2 configuration objects: +* The [Icinga Director](https://icinga.com/docs/director/latest/) as web based and/or automation configuration interface + * [Monitoring Automation with Icinga - The Director](https://icinga.com/2019/04/23/monitoring-automation-with-icinga-the-director/) * Manually with your preferred editor, for example vi(m), nano, notepad, etc. -* A configuration tool for Icinga 2 e.g. the [Icinga Director](https://github.com/Icinga/icingaweb2-module-director) * Generated by a [configuration management tool](13-addons.md#configuration-tools) such as Puppet, Chef, Ansible, etc. * A custom exporter script from your CMDB or inventory tool * etc. @@ -36,7 +47,7 @@ host and service basis. Then you should look for the object specific configuration setting `host_name` etc. accordingly. You decide on the "best" layout for configuration files and directories. Ensure that -the [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) configuration file includes them. +the [icinga2.conf](04-configuration.md#icinga2-conf) configuration file includes them. Consider these ideas: @@ -48,12 +59,12 @@ Consider these ideas: In either way of choosing the right strategy you should additionally check the following: -* Are there any specific attributes describing the host/service you could set as `vars` custom attributes? +* Are there any specific attributes describing the host/service you could set as `vars` custom variables? You can later use them for applying assign/ignore rules, or export them into external interfaces. * Put hosts into hostgroups, services into servicegroups and use these attributes for your apply rules. * Use templates to store generic attributes for your objects and apply rules making your configuration more readable. Details can be found in the [using templates](03-monitoring-basics.md#object-inheritance-using-templates) chapter. -* Apply rules may overlap. Keep a central place (for example, [services.conf](04-configuring-icinga-2.md#services-conf) or [notifications.conf](04-configuring-icinga-2.md#notifications-conf)) storing +* Apply rules may overlap. Keep a central place (for example, [services.conf](04-configuration.md#services-conf) or [notifications.conf](04-configuration.md#notifications-conf)) storing the configuration instead of defining apply rules deep in your configuration tree. * Every plugin used as check, notification or event command requires a `Command` definition. Further details can be looked up in the [check commands](03-monitoring-basics.md#check-commands) chapter. @@ -66,7 +77,7 @@ There is a detailed chapter on [distributed monitoring scenarios](06-distributed Please ensure to have read the [introduction](06-distributed-monitoring.md#distributed-monitoring) at first glance. If you happen to have further questions, do not hesitate to join the -[community support channels](https://www.icinga.com/community/get-involved/) +[community forum](https://community.icinga.com) and ask community members for their experience and best practices. ## Your Configuration @@ -77,14 +88,16 @@ If you prefer to organize your own local object tree, you can also remove Create a new configuration directory, e.g. `objects.d` and include it in your icinga2.conf file. - [root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/objects.d +``` +[root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/objects.d - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/icinga2.conf +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/icinga2.conf - /* Local object configuration on our master instance. */ - include_recursive "objects.d" +/* Local object configuration on our master instance. */ +include_recursive "objects.d" +``` -This approach is used by the [Icinga 2 Puppet module](https://github.com/Icinga/puppet-icinga2). +This approach is used by the [Icinga 2 Puppet module](https://icinga.com/products/integrations/puppet/). If you plan to setup a distributed setup with HA clusters and clients, please refer to [this chapter](#06-distributed-monitoring.md#distributed-monitoring-top-down) for examples with `zones.d` as configuration directory. @@ -97,76 +110,84 @@ An example configuration file is installed for you in `/etc/icinga2/icinga2.conf Here's a brief description of the example configuration: - /** - * Icinga 2 configuration file - * -- this is where you define settings for the Icinga application including - * which hosts/services to check. - * - * For an overview of all available configuration options please refer - * to the documentation that is distributed as part of Icinga 2. - */ +``` +/** +* Icinga 2 configuration file +* -- this is where you define settings for the Icinga application including +* which hosts/services to check. +* +* For an overview of all available configuration options please refer +* to the documentation that is distributed as part of Icinga 2. +*/ +``` Icinga 2 supports [C/C++-style comments](17-language-reference.md#comments). - /** - * The constants.conf defines global constants. - */ - include "constants.conf" +/** +* The constants.conf defines global constants. +*/ +include "constants.conf" The `include` directive can be used to include other files. - /** - * The zones.conf defines zones for a cluster setup. - * Not required for single instance setups. - */ - include "zones.conf" +``` +/** +* The zones.conf defines zones for a cluster setup. +* Not required for single instance setups. +*/ +include "zones.conf" +``` The [Icinga Template Library](10-icinga-template-library.md#icinga-template-library) provides a set of common templates and [CheckCommand](03-monitoring-basics.md#check-commands) definitions. - /** - * The Icinga Template Library (ITL) provides a number of useful templates - * and command definitions. - * Common monitoring plugin command definitions are included separately. - */ - include - include - include - include +``` +/** +* The Icinga Template Library (ITL) provides a number of useful templates +* and command definitions. +* Common monitoring plugin command definitions are included separately. +*/ +include +include +include +include - /** - * This includes the Icinga 2 Windows plugins. These command definitions - * are required on a master node when a client is used as command endpoint. - */ - include +/** +* This includes the Icinga 2 Windows plugins. These command definitions +* are required on a master node when a client is used as command endpoint. +*/ +include - /** - * This includes the NSClient++ check commands. These command definitions - * are required on a master node when a client is used as command endpoint. - */ - include +/** +* This includes the NSClient++ check commands. These command definitions +* are required on a master node when a client is used as command endpoint. +*/ +include - /** - * The features-available directory contains a number of configuration - * files for features which can be enabled and disabled using the - * icinga2 feature enable / icinga2 feature disable CLI commands. - * These commands work by creating and removing symbolic links in - * the features-enabled directory. - */ - include "features-enabled/*.conf" +/** +* The features-available directory contains a number of configuration +* files for features which can be enabled and disabled using the +* icinga2 feature enable / icinga2 feature disable CLI commands. +* These commands work by creating and removing symbolic links in +* the features-enabled directory. +*/ +include "features-enabled/*.conf" +``` This `include` directive takes care of including the configuration files for all the features which have been enabled with `icinga2 feature enable`. See [Enabling/Disabling Features](11-cli-commands.md#enable-features) for more details. - /** - * Although in theory you could define all your objects in this file - * the preferred way is to create separate directories and files in the conf.d - * directory. Each of these files must have the file extension ".conf". - */ - include_recursive "conf.d" +``` +/** +* Although in theory you could define all your objects in this file +* the preferred way is to create separate directories and files in the conf.d +* directory. Each of these files must have the file extension ".conf". +*/ +include_recursive "conf.d" +``` -You can put your own configuration files in the [conf.d](04-configuring-icinga-2.md#conf-d) directory. This +You can put your own configuration files in the [conf.d](04-configuration.md#conf-d) directory. This directive makes sure that all of your own configuration files are included. ### constants.conf @@ -175,7 +196,7 @@ The `constants.conf` configuration file can be used to define global constants. By default, you need to make sure to set these constants: -* The `PluginDir` constant must be set to the path where the [Monitoring Project plugins](02-getting-started.md#setting-up-check-plugins) are installed. +* The `PluginDir` constant must be set to the path where the [Monitoring Project plugins](02-installation.md#setting-up-check-plugins) are installed. This constant is used by a number of [built-in check command definitions](10-icinga-template-library.md#icinga-template-library). * The `NodeName` constant defines your local node name. Should be set to FQDN which is the default @@ -184,27 +205,30 @@ cluster setup. Example: - /* The directory which contains the plugins from the Monitoring Plugins project. */ - const PluginDir = "/usr/lib64/nagios/plugins" +``` +/* The directory which contains the plugins from the Monitoring Plugins project. */ +const PluginDir = "/usr/lib64/nagios/plugins" - /* The directory which contains the Manubulon plugins. - * Check the documentation, chapter "SNMP Manubulon Plugin Check Commands", for details. - */ - const ManubulonPluginDir = "/usr/lib64/nagios/plugins" +/* The directory which contains the Manubulon plugins. +* Check the documentation, chapter "SNMP Manubulon Plugin Check Commands", for details. +*/ +const ManubulonPluginDir = "/usr/lib64/nagios/plugins" - /* Our local instance name. By default this is the server's hostname as returned by `hostname --fqdn`. - * This should be the common name from the API certificate. - */ - //const NodeName = "localhost" +/* Our local instance name. By default this is the server's hostname as returned by `hostname --fqdn`. +* This should be the common name from the API certificate. +*/ +//const NodeName = "localhost" - /* Our local zone name. */ - const ZoneName = NodeName +/* Our local zone name. */ +const ZoneName = NodeName - /* Secret key for remote node tickets */ - const TicketSalt = "" +/* Secret key for remote node tickets */ +const TicketSalt = "" +``` The `ZoneName` and `TicketSalt` constants are required for remote client -and distributed setups only. +and distributed setups. The `node setup/wizard` CLI tools take care of +populating these values. ### zones.conf @@ -212,7 +236,7 @@ This file can be used to specify the required [Zone](09-object-types.md#objectty and [Endpoint](09-object-types.md#objecttype-endpoint) configuration object for [distributed monitoring](06-distributed-monitoring.md#distributed-monitoring). -By default the `NodeName` and `ZoneName` [constants](04-configuring-icinga-2.md#constants-conf) will be used. +By default the `NodeName` and `ZoneName` [constants](04-configuration.md#constants-conf) will be used. It also contains several [global zones](06-distributed-monitoring.md#distributed-monitoring-global-zone-config-sync) for distributed monitoring environments. @@ -225,38 +249,38 @@ for your `Zone` and `Endpoint` object names. This directory contains **example configuration** which should help you get started with monitoring the local host and its services. It is included in the -[icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) configuration file by default. +[icinga2.conf](04-configuration.md#icinga2-conf) configuration file by default. It can be used as reference example for your own configuration strategy. Just keep in mind to include the main directories in the -[icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) file. +[icinga2.conf](04-configuration.md#icinga2-conf) file. > **Note** > -> You can remove the include directive in [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) +> You can remove the include directive in [icinga2.conf](04-configuration.md#icinga2-conf) > if you prefer your own way of deploying Icinga 2 configuration. Further details on configuration best practice and how to build your -own strategy is described in [this chapter](04-configuring-icinga-2.md#configuration-best-practice). +own strategy is described in [this chapter](04-configuration.md#configuration-best-practice). Available configuration files which are installed by default: -* [hosts.conf](04-configuring-icinga-2.md#hosts-conf) -* [services.conf](04-configuring-icinga-2.md#services-conf) -* [users.conf](04-configuring-icinga-2.md#users-conf) -* [notifications.conf](04-configuring-icinga-2.md#notifications-conf) -* [commands.conf](04-configuring-icinga-2.md#commands-conf) -* [groups.conf](04-configuring-icinga-2.md#groups-conf) -* [templates.conf](04-configuring-icinga-2.md#templates-conf) -* [downtimes.conf](04-configuring-icinga-2.md#downtimes-conf) -* [timeperiods.conf](04-configuring-icinga-2.md#timeperiods-conf) -* [api-users.conf](04-configuring-icinga-2.md#api-users-conf) -* [app.conf](04-configuring-icinga-2.md#app-conf) +* [hosts.conf](04-configuration.md#hosts-conf) +* [services.conf](04-configuration.md#services-conf) +* [users.conf](04-configuration.md#users-conf) +* [notifications.conf](04-configuration.md#notifications-conf) +* [commands.conf](04-configuration.md#commands-conf) +* [groups.conf](04-configuration.md#groups-conf) +* [templates.conf](04-configuration.md#templates-conf) +* [downtimes.conf](04-configuration.md#downtimes-conf) +* [timeperiods.conf](04-configuration.md#timeperiods-conf) +* [api-users.conf](04-configuration.md#api-users-conf) +* [app.conf](04-configuration.md#app-conf) #### hosts.conf The `hosts.conf` file contains an example host based on your -`NodeName` setting in [constants.conf](04-configuring-icinga-2.md#constants-conf). You +`NodeName` setting in [constants.conf](04-configuration.md#constants-conf). You can use global constants for your object names instead of string values. @@ -264,82 +288,84 @@ The `import` keyword is used to import the `generic-host` template which takes care of setting up the host check command to `hostalive`. If you require a different check command, you can override it in the object definition. -The `vars` attribute can be used to define custom attributes which are available +The `vars` attribute can be used to define custom variables which are available for check and notification commands. Most of the [Plugin Check Commands](10-icinga-template-library.md#icinga-template-library) in the Icinga Template Library require an `address` attribute. -The custom attribute `os` is evaluated by the `linux-servers` group in -[groups.conf](04-configuring-icinga-2.md#groups-conf) making the local host a member. +The custom variable `os` is evaluated by the `linux-servers` group in +[groups.conf](04-configuration.md#groups-conf) making the local host a member. The example host will show you how to: * define http vhost attributes for the `http` service apply rule defined -in [services.conf](04-configuring-icinga-2.md#services-conf). +in [services.conf](04-configuration.md#services-conf). * define disks (all, specific `/`) and their attributes for the `disk` -service apply rule defined in [services.conf](04-configuring-icinga-2.md#services-conf). +service apply rule defined in [services.conf](04-configuration.md#services-conf). * define notification types (`mail`) and set the groups attribute. This -will be used by notification apply rules in [notifications.conf](04-configuring-icinga-2.md#notifications-conf). +will be used by notification apply rules in [notifications.conf](04-configuration.md#notifications-conf). -If you've installed [Icinga Web 2](02-getting-started.md#setting-up-icingaweb2), you can +If you've installed [Icinga Web 2](02-installation.md#setting-up-icingaweb2), you can uncomment the http vhost attributes and reload Icinga 2. The apply -rules in [services.conf](04-configuring-icinga-2.md#services-conf) will automatically +rules in [services.conf](04-configuration.md#services-conf) will automatically generate a new service checking the `/icingaweb2` URI using the `http` check. - /* - * Host definitions with object attributes - * used for apply rules for Service, Notification, - * Dependency and ScheduledDowntime objects. - * - * Tip: Use `icinga2 object list --type Host` to - * list all host objects after running - * configuration validation (`icinga2 daemon -C`). - */ +``` +/* +* Host definitions with object attributes +* used for apply rules for Service, Notification, +* Dependency and ScheduledDowntime objects. +* +* Tip: Use `icinga2 object list --type Host` to +* list all host objects after running +* configuration validation (`icinga2 daemon -C`). +*/ - /* - * This is an example host based on your - * local host's FQDN. Specify the NodeName - * constant in `constants.conf` or use your - * own description, e.g. "db-host-1". - */ +/* + * This is an example host based on your + * local host's FQDN. Specify the NodeName + * constant in `constants.conf` or use your + * own description, e.g. "db-host-1". + */ - object Host NodeName { - /* Import the default host template defined in `templates.conf`. */ - import "generic-host" +object Host NodeName { + /* Import the default host template defined in `templates.conf`. */ + import "generic-host" - /* Specify the address attributes for checks e.g. `ssh` or `http`. */ - address = "127.0.0.1" - address6 = "::1" + /* Specify the address attributes for checks e.g. `ssh` or `http`. */ + address = "127.0.0.1" + address6 = "::1" - /* Set custom attribute `os` for hostgroup assignment in `groups.conf`. */ - vars.os = "Linux" + /* Set custom variable `os` for hostgroup assignment in `groups.conf`. */ + vars.os = "Linux" - /* Define http vhost attributes for service apply rules in `services.conf`. */ - vars.http_vhosts["http"] = { - http_uri = "/" - } - /* Uncomment if you've sucessfully installed Icinga Web 2. */ - //vars.http_vhosts["Icinga Web 2"] = { - // http_uri = "/icingaweb2" - //} + /* Define http vhost attributes for service apply rules in `services.conf`. */ + vars.http_vhosts["http"] = { + http_uri = "/" + } + /* Uncomment if you've sucessfully installed Icinga Web 2. */ + //vars.http_vhosts["Icinga Web 2"] = { + // http_uri = "/icingaweb2" + //} - /* Define disks and attributes for service apply rules in `services.conf`. */ - vars.disks["disk"] = { - /* No parameters. */ - } - vars.disks["disk /"] = { - disk_partitions = "/" - } + /* Define disks and attributes for service apply rules in `services.conf`. */ + vars.disks["disk"] = { + /* No parameters. */ + } + vars.disks["disk /"] = { + disk_partitions = "/" + } - /* Define notification mail attributes for notification apply rules in `notifications.conf`. */ - vars.notification["mail"] = { - /* The UserGroup `icingaadmins` is defined in `users.conf`. */ - groups = [ "icingaadmins" ] - } - } + /* Define notification mail attributes for notification apply rules in `notifications.conf`. */ + vars.notification["mail"] = { + /* The UserGroup `icingaadmins` is defined in `users.conf`. */ + groups = [ "icingaadmins" ] + } +} +``` This is only the host object definition. Now we'll need to make sure that this -host and your additional hosts are getting [services](04-configuring-icinga-2.md#services-conf) applied. +host and your additional hosts are getting [services](04-configuration.md#services-conf) applied. > **Tip** > @@ -363,8 +389,8 @@ Service(s) | Applied on host(s) `load`, `procs`, `swap`, `users`, `icinga` | The `NodeName` host only. `ping4`, `ping6` | All hosts with `address` resp. `address6` attribute. `ssh` | All hosts with `address` and `vars.os` set to `Linux` -`http`, optional: `Icinga Web 2` | All hosts with custom attribute `http_vhosts` defined as dictionary. -`disk`, `disk /` | All hosts with custom attribute `disks` defined as dictionary. +`http`, optional: `Icinga Web 2` | All hosts with custom variable `http_vhosts` defined as dictionary. +`disk`, `disk /` | All hosts with custom variable `disks` defined as dictionary. The Debian packages also include an additional `apt` service check applied to the local host. @@ -376,24 +402,26 @@ which we enabled earlier by including the `itl` and `plugins` configuration file Example `load` service apply rule: - apply Service "load" { - import "generic-service" +``` +apply Service "load" { +import "generic-service" - check_command = "load" +check_command = "load" - /* Used by the ScheduledDowntime apply rule in `downtimes.conf`. */ - vars.backup_downtime = "02:00-03:00" +/* Used by the ScheduledDowntime apply rule in `downtimes.conf`. */ +vars.backup_downtime = "02:00-03:00" - assign where host.name == NodeName - } +assign where host.name == NodeName +} +``` The `apply` keyword can be used to create new objects which are associated with another group of objects. You can `import` existing templates, define (custom) attributes. -The custom attribute `backup_downtime` is defined to a specific timerange string. +The custom variable `backup_downtime` is defined to a specific timerange string. This variable value will be used for applying a `ScheduledDowntime` object to -these services in [downtimes.conf](04-configuring-icinga-2.md#downtimes-conf). +these services in [downtimes.conf](04-configuration.md#downtimes-conf). In this example the `assign where` condition is a boolean expression which is evaluated for all objects of type `Host` and a new service with name "load" @@ -403,16 +431,18 @@ may be used in `assign where` conditions. Multiple `assign where` condition can be combined with `AND` using the `&&` operator as shown in the `ssh` example: - apply Service "ssh" { - import "generic-service" +``` +apply Service "ssh" { + import "generic-service" - check_command = "ssh" + check_command = "ssh" - assign where host.address && host.vars.os == "Linux" - } + assign where host.address && host.vars.os == "Linux" +} +``` In this example, the service `ssh` is applied to all hosts having the `address` -attribute defined `AND` having the custom attribute `os` set to the string +attribute defined `AND` having the custom variable `os` set to the string `Linux`. You can modify this condition to match multiple expressions by combining `AND` and `OR` using `&&` and `||` [operators](17-language-reference.md#expression-operators), for example @@ -424,21 +454,22 @@ rules. While one `apply` rule for `ssh` will only create a service for matching hosts, you can go one step further: Generate apply rules based on array items or dictionary key-value pairs. -The idea is simple: Your host in [hosts.conf](04-configuring-icinga-2.md#hosts-conf) defines the -`disks` dictionary as custom attribute in `vars`. +The idea is simple: Your host in [hosts.conf](04-configuration.md#hosts-conf) defines the +`disks` dictionary as custom variable in `vars`. -Remember the example from [hosts.conf](04-configuring-icinga-2.md#hosts-conf): - - ... - /* Define disks and attributes for service apply rules in `services.conf`. */ - vars.disks["disk"] = { - /* No parameters. */ - } - vars.disks["disk /"] = { - disk_partition = "/" - } - ... +Remember the example from [hosts.conf](04-configuration.md#hosts-conf): +``` +... + /* Define disks and attributes for service apply rules in `services.conf`. */ + vars.disks["disk"] = { + /* No parameters. */ + } + vars.disks["disk /"] = { + disk_partition = "/" + } +... +``` This dictionary contains multiple service names we want to monitor. `disk` should just check all available disks, while `disk /` will pass an additional @@ -466,23 +497,25 @@ generated service Configuration example: - apply Service for (disk => config in host.vars.disks) { - import "generic-service" +``` +apply Service for (disk => config in host.vars.disks) { + import "generic-service" - check_command = "disk" + check_command = "disk" - vars += config - } + vars += config +} +``` A similar example is used for the `http` services. That way you can make your host the information provider for all apply rules. Define them once, and only manage your hosts. -Look into [notifications.conf](04-configuring-icinga-2.md#notifications-conf) how this technique is used +Look into [notifications.conf](04-configuration.md#notifications-conf) how this technique is used for applying notifications to hosts and services using their type and user attributes. -Don't forget to install the [check plugins](02-getting-started.md#setting-up-check-plugins) required by +Don't forget to install the [check plugins](02-installation.md#setting-up-check-plugins) required by the hosts and services and their check commands. Further details on the monitoring configuration can be found in the @@ -491,22 +524,23 @@ Further details on the monitoring configuration can be found in the #### users.conf Defines the `icingaadmin` User and the `icingaadmins` UserGroup. The latter is used in -[hosts.conf](04-configuring-icinga-2.md#hosts-conf) for defining a custom host attribute later used in -[notifications.conf](04-configuring-icinga-2.md#notifications-conf) for notification apply rules. +[hosts.conf](04-configuration.md#hosts-conf) for defining a custom host attribute later used in +[notifications.conf](04-configuration.md#notifications-conf) for notification apply rules. - object User "icingaadmin" { - import "generic-user" +``` +object User "icingaadmin" { + import "generic-user" - display_name = "Icinga 2 Admin" - groups = [ "icingaadmins" ] + display_name = "Icinga 2 Admin" + groups = [ "icingaadmins" ] - email = "icinga@localhost" - } - - object UserGroup "icingaadmins" { - display_name = "Icinga 2 Admin Group" - } + email = "icinga@localhost" +} +object UserGroup "icingaadmins" { + display_name = "Icinga 2 Admin Group" +} +``` #### notifications.conf @@ -519,31 +553,33 @@ nested dictionary attribute `notification.mail` is set. Please note that the `to` keyword is important in [notification apply rules](03-monitoring-basics.md#using-apply-notifications) defining whether these notifications are applies to hosts or services. -The `import` keyword imports the specific mail templates defined in [templates.conf](04-configuring-icinga-2.md#templates-conf). +The `import` keyword imports the specific mail templates defined in [templates.conf](04-configuration.md#templates-conf). The `interval` attribute is not explicitly set -- it [defaults to 30 minutes](09-object-types.md#objecttype-notification). By setting the `user_groups` to the value provided by the -respective [host.vars.notification.mail](04-configuring-icinga-2.md#hosts-conf) attribute we'll -implicitely use the `icingaadmins` UserGroup defined in [users.conf](04-configuring-icinga-2.md#users-conf). +respective [host.vars.notification.mail](04-configuration.md#hosts-conf) attribute we'll +implicitely use the `icingaadmins` UserGroup defined in [users.conf](04-configuration.md#users-conf). - apply Notification "mail-icingaadmin" to Host { - import "mail-host-notification" +``` +apply Notification "mail-icingaadmin" to Host { + import "mail-host-notification" - user_groups = host.vars.notification.mail.groups - users = host.vars.notification.mail.users + user_groups = host.vars.notification.mail.groups + users = host.vars.notification.mail.users - assign where host.vars.notification.mail - } + assign where host.vars.notification.mail +} - apply Notification "mail-icingaadmin" to Service { - import "mail-service-notification" +apply Notification "mail-icingaadmin" to Service { + import "mail-service-notification" - user_groups = host.vars.notification.mail.groups - users = host.vars.notification.mail.users + user_groups = host.vars.notification.mail.groups + users = host.vars.notification.mail.users - assign where host.vars.notification.mail - } + assign where host.vars.notification.mail +} +``` More details on defining notifications and their additional attributes such as filters can be read in [this chapter](03-monitoring-basics.md#alert-notifications). @@ -551,7 +587,7 @@ filters can be read in [this chapter](03-monitoring-basics.md#alert-notification #### commands.conf This is the place where your own command configuration can be defined. By default -only the notification commands used by the notification templates defined in [templates.conf](04-configuring-icinga-2.md#templates-conf). +only the notification commands used by the notification templates defined in [templates.conf](04-configuration.md#templates-conf). You can freely customize these notification commands, and adapt them for your needs. Read more on that topic [here](03-monitoring-basics.md#notification-commands). @@ -559,122 +595,129 @@ Read more on that topic [here](03-monitoring-basics.md#notification-commands). #### groups.conf The example host defined in [hosts.conf](hosts-conf) already has the -custom attribute `os` set to `Linux` and is therefore automatically +custom variable `os` set to `Linux` and is therefore automatically a member of the host group `linux-servers`. This is done by using the [group assign](17-language-reference.md#group-assign) expressions similar to previously seen [apply rules](03-monitoring-basics.md#using-apply). - object HostGroup "linux-servers" { - display_name = "Linux Servers" +``` +object HostGroup "linux-servers" { + display_name = "Linux Servers" - assign where host.vars.os == "Linux" - } + assign where host.vars.os == "Linux" +} - object HostGroup "windows-servers" { - display_name = "Windows Servers" +object HostGroup "windows-servers" { + display_name = "Windows Servers" - assign where host.vars.os == "Windows" - } + assign where host.vars.os == "Windows" +} +``` Service groups can be grouped together by similar pattern matches. The [match function](18-library-reference.md#global-functions-match) expects a wildcard match string and the attribute string to match with. - object ServiceGroup "ping" { - display_name = "Ping Checks" +``` +object ServiceGroup "ping" { + display_name = "Ping Checks" - assign where match("ping*", service.name) - } + assign where match("ping*", service.name) +} - object ServiceGroup "http" { - display_name = "HTTP Checks" +object ServiceGroup "http" { + display_name = "HTTP Checks" - assign where match("http*", service.check_command) - } + assign where match("http*", service.check_command) +} - object ServiceGroup "disk" { - display_name = "Disk Checks" - - assign where match("disk*", service.check_command) - } +object ServiceGroup "disk" { + display_name = "Disk Checks" + assign where match("disk*", service.check_command) +} +``` #### templates.conf Most of the example configuration objects use generic global templates by default: - template Host "generic-host" { - max_check_attempts = 5 - check_interval = 1m - retry_interval = 30s +``` +template Host "generic-host" { + max_check_attempts = 5 + check_interval = 1m + retry_interval = 30s - check_command = "hostalive" - } + check_command = "hostalive" +} - template Service "generic-service" { - max_check_attempts = 3 - check_interval = 1m - retry_interval = 30s - } +template Service "generic-service" { + max_check_attempts = 3 + check_interval = 1m + retry_interval = 30s +} +``` The `hostalive` check command is part of the [Plugin Check Commands](10-icinga-template-library.md#icinga-template-library). +``` +template Notification "mail-host-notification" { + command = "mail-host-notification" - template Notification "mail-host-notification" { - command = "mail-host-notification" + states = [ Up, Down ] + types = [ Problem, Acknowledgement, Recovery, Custom, + FlappingStart, FlappingEnd, + DowntimeStart, DowntimeEnd, DowntimeRemoved ] - states = [ Up, Down ] - types = [ Problem, Acknowledgement, Recovery, Custom, - FlappingStart, FlappingEnd, - DowntimeStart, DowntimeEnd, DowntimeRemoved ] + period = "24x7" +} - period = "24x7" - } +template Notification "mail-service-notification" { + command = "mail-service-notification" - template Notification "mail-service-notification" { - command = "mail-service-notification" + states = [ OK, Warning, Critical, Unknown ] + types = [ Problem, Acknowledgement, Recovery, Custom, + FlappingStart, FlappingEnd, + DowntimeStart, DowntimeEnd, DowntimeRemoved ] - states = [ OK, Warning, Critical, Unknown ] - types = [ Problem, Acknowledgement, Recovery, Custom, - FlappingStart, FlappingEnd, - DowntimeStart, DowntimeEnd, DowntimeRemoved ] - - period = "24x7" - } + period = "24x7" +} +``` More details on `Notification` object attributes can be found [here](09-object-types.md#objecttype-notification). #### downtimes.conf -The `load` service apply rule defined in [services.conf](04-configuring-icinga-2.md#services-conf) defines -the `backup_downtime` custom attribute. +The `load` service apply rule defined in [services.conf](04-configuration.md#services-conf) defines +the `backup_downtime` custom variable. The ScheduledDowntime apply rule uses this attribute to define the default value for the time ranges required for recurring downtime slots. Learn more about downtimes in [this chapter](08-advanced-topics.md#downtimes). - apply ScheduledDowntime "backup-downtime" to Service { - author = "icingaadmin" - comment = "Scheduled downtime for backup" +``` +apply ScheduledDowntime "backup-downtime" to Service { + author = "icingaadmin" + comment = "Scheduled downtime for backup" - ranges = { - monday = service.vars.backup_downtime - tuesday = service.vars.backup_downtime - wednesday = service.vars.backup_downtime - thursday = service.vars.backup_downtime - friday = service.vars.backup_downtime - saturday = service.vars.backup_downtime - sunday = service.vars.backup_downtime - } - - assign where service.vars.backup_downtime != "" - } + ranges = { + monday = service.vars.backup_downtime + tuesday = service.vars.backup_downtime + wednesday = service.vars.backup_downtime + thursday = service.vars.backup_downtime + friday = service.vars.backup_downtime + saturday = service.vars.backup_downtime + sunday = service.vars.backup_downtime + } + assign where service.vars.backup_downtime != "" +} +``` #### timeperiods.conf diff --git a/doc/05-service-monitoring.md b/doc/05-service-monitoring.md index 855b75b53..3158d162f 100644 --- a/doc/05-service-monitoring.md +++ b/doc/05-service-monitoring.md @@ -4,44 +4,142 @@ The power of Icinga 2 lies in its modularity. There are thousands of community plugins available next to the standard plugins provided by the [Monitoring Plugins project](https://www.monitoring-plugins.org). +Start your research on [Icinga Exchange](https://exchange.icinga.com) +and look which services are already [covered](05-service-monitoring.md#service-monitoring-overview). + +The [requirements chapter](05-service-monitoring.md#service-monitoring-requirements) guides you +through the plugin setup, tests and their integration with an [existing](05-service-monitoring.md#service-monitoring-plugin-checkcommand) +or [new](05-service-monitoring.md#service-monitoring-plugin-checkcommand-new) CheckCommand object +and host/service objects inside the [Director](05-service-monitoring.md#service-monitoring-plugin-checkcommand-integration-director) +or [Icinga config files](05-service-monitoring.md#service-monitoring-plugin-checkcommand-integration-config-files). +It also adds hints on [modifying](05-service-monitoring.md#service-monitoring-plugin-checkcommand-modify) existing commands. + +Plugins follow the [Plugin API specification](05-service-monitoring.md#service-monitoring-plugin-api) +which is enriched with examples and also code examples to get you started with +[your own plugin](05-service-monitoring.md#service-monitoring-plugin-new). + + + ## Requirements ### Plugins -All existing Nagios or Icinga 1.x plugins work with Icinga 2. Community +All existing Icinga or Nagios plugins work with Icinga 2. Community plugins can be found for example on [Icinga Exchange](https://exchange.icinga.com). -The recommended way of setting up these plugins is to copy them to a common directory -and create a new global constant, e.g. `CustomPluginDir` in your [constants.conf](04-configuring-icinga-2.md#constants-conf) -configuration file: +The recommended way of setting up these plugins is to copy them +into the `PluginDir` directory. - # cp check_snmp_int.pl /opt/monitoring/plugins - # chmod +x /opt/plugins/check_snmp_int.pl +If you have plugins with many dependencies, consider creating a +custom RPM/DEB package which handles the required libraries and binaries. - # cat /etc/icinga2/constants.conf - /** - * This file defines global constants which can be used in - * the other configuration files. At a minimum the - * PluginDir constant should be defined. - */ +Configuration management tools such as Puppet, Ansible, Chef or Saltstack +also help with automatically installing the plugins on different +operating systems. They can also help with installing the required +dependencies, e.g. Python libraries, Perl modules, etc. - const PluginDir = "/usr/lib/nagios/plugins" - const CustomPluginDir = "/opt/monitoring/plugins" +### Plugin Setup + +Good plugins provide installations and configuration instructions +in their docs and/or README on GitHub. + +Sometimes dependencies are not listed, or your distribution differs from the one +described. Try running the plugin after setup and [ensure it works](05-service-monitoring.md#service-monitoring-plugins-it-works). + +#### Ensure it works Prior to using the check plugin with Icinga 2 you should ensure that it is working properly by trying to run it on the console using whichever user Icinga 2 is running as: - # su - icinga -s /bin/bash - $ /opt/monitoring/plugins/check_snmp_int.pl --help +RHEL/CentOS/Fedora + +``` +sudo -u icinga /usr/lib64/nagios/plugins/check_mysql_health --help +``` + +Debian/Ubuntu + +``` +sudo -u nagios /usr/lib/nagios/plugins/check_mysql_health --help +``` Additional libraries may be required for some plugins. Please consult the plugin documentation and/or the included README file for installation instructions. Sometimes plugins contain hard-coded paths to other components. Instead of changing -the plugin it might be easier to create a symbolic link to make sure it doesn't get overwritten during the next update. +the plugin it might be easier to create a symbolic link to make sure it doesn't get +overwritten during the next update. Sometimes there are plugins which do not exactly fit your requirements. In that case you can modify an existing plugin or just write your own. +#### Plugin Dependency Errors + +Plugins can be scripts (Shell, Python, Perl, Ruby, PHP, etc.) +or compiled binaries (C, C++, Go). + +These scripts/binaries may require additional libraries +which must be installed on every system they are executed. + +> **Tip** +> +> Don't test the plugins on your master instance, instead +> do that on the satellites and clients which execute the +> checks. + +There are errors, now what? Typical errors are missing libraries, +binaries or packages. + +##### Python Example + +Example for a Python plugin which uses the `tinkerforge` module +to query a network service: + +``` +ImportError: No module named tinkerforge.ip_connection +``` + +Its [documentation](https://github.com/NETWAYS/check_tinkerforge#installation) +points to installing the `tinkerforge` Python module. + +##### Perl Example + +Example for a Perl plugin which uses SNMP: + +``` +Can't locate Net/SNMP.pm in @INC (you may need to install the Net::SNMP module) +``` + +Prior to installing the Perl module via CPAN, look for a distribution +specific package, e.g. `libnet-snmp-perl` on Debian/Ubuntu or `perl-Net-SNMP` +on RHEL/CentOS. + + +#### Optional: Custom Path + +If you are not using the default `PluginDir` directory, you +can create a custom plugin directory and constant +and reference this in the created CheckCommand objects. + +Create a common directory e.g. `/opt/monitoring/plugins` +and install the plugin there. + +``` +mkdir -p /opt/monitoring/plugins +cp check_snmp_int.pl /opt/monitoring/plugins +chmod +x /opt/monitoring/plugins/check_snmp_int.pl +``` + +Next create a new global constant, e.g. `CustomPluginDir` +in your [constants.conf](04-configuration.md#constants-conf) +configuration file: + +``` +vim /etc/icinga2/constants.conf + +const PluginDir = "/usr/lib/nagios/plugins" +const CustomPluginDir = "/opt/monitoring/plugins" +``` + ### CheckCommand Definition Each plugin requires a [CheckCommand](09-object-types.md#objecttype-checkcommand) object in your @@ -50,54 +148,617 @@ configuration which can be used in the [Service](09-object-types.md#objecttype-s Please check if the Icinga 2 package already provides an [existing CheckCommand definition](10-icinga-template-library.md#icinga-template-library). -If that's the case, throroughly check the required parameters and integrate the check command -into your host and service objects. + +If that's the case, thoroughly check the required parameters and integrate the check command +into your host and service objects. Best practice is to run the plugin on the CLI +with the required parameters first. + +Example for database size checks with [check_mysql_health](10-icinga-template-library.md#plugin-contrib-command-mysql_health). + +``` +/usr/lib64/nagios/plugins/check_mysql_health --hostname '127.0.0.1' --username root --password icingar0xx --mode sql --name 'select sum(data_length + index_length) / 1024 / 1024 from information_schema.tables where table_schema = '\''icinga'\'';' '--name2' 'db_size' --units 'MB' --warning 4096 --critical 8192 +``` + +The parameter names inside the ITL commands follow the +`_` schema. + +#### Icinga Director Integration + +Navigate into `Commands > External Commands` and search for `mysql_health`. +Select `mysql_health` and navigate into the `Fields` tab. + +In order to access the parameters, the Director requires you to first +define the needed custom data fields: + +* `mysql_health_hostname` +* `mysql_health_username` and `mysql_health_password` +* `mysql_health_mode` +* `mysql_health_name`, `mysql_health_name2` and `mysql_health_units` +* `mysql_health_warning` and `mysql_health_critical` + +Create a new host template and object where you'll generic +settings like `mysql_health_hostname` (if it differs from the host's +`address` attribute) and `mysql_health_username` and `mysql_health_password`. + +Create a new service template for `mysql-health` and set the `mysql_health` +as check command. You can also define a default for `mysql_health_mode`. + +Next, create a service apply rule or a new service set which gets assigned +to matching host objects. + + +#### Icinga Config File Integration + +Create or modify a host object which stores +the generic database defaults and prepares details +for a service apply for rule. + +``` +object Host "icinga2-master1.localdomain" { + check_command = "hostalive" + address = "..." + + // Database listens locally, not external + vars.mysql_health_hostname = "127.0.0.1" + + // Basic database size checks for Icinga DBs + vars.databases["icinga"] = { + mysql_health_warning = 4096 //MB + mysql_health_critical = 8192 //MB + } + vars.databases["icingaweb2"] = { + mysql_health_warning = 4096 //MB + mysql_health_critical = 8192 //MB + } +} +``` + +The host object prepares the database details and thresholds already +for advanced [apply for](03-monitoring-basics.md#using-apply-for) rules. It also uses +conditions to fetch host specified values, or set default values. + +``` +apply Service "db-size-" for (db_name => config in host.vars.databases) { + check_interval = 1m + retry_interval = 30s + + check_command = "mysql_health" + + if (config.mysql_health_username) { + vars.mysql_healt_username = config.mysql_health_username + } else { + vars.mysql_health_username = "root" + } + if (config.mysql_health_password) { + vars.mysql_healt_password = config.mysql_health_password + } else { + vars.mysql_health_password = "icingar0xx" + } + + vars.mysql_health_mode = "sql" + vars.mysql_health_name = "select sum(data_length + index_length) / 1024 / 1024 from information_schema.tables where table_schema = '" + db_name + "';" + vars.mysql_health_name2 = "db_size" + vars.mysql_health_units = "MB" + + if (config.mysql_health_warning) { + vars.mysql_health_warning = config.mysql_health_warning + } + if (config.mysql_health_critical) { + vars.mysql_health_critical = config.mysql_health_critical + } + + vars += config +} +``` + +#### New CheckCommand + +This chapter describes how to add a new CheckCommand object for a plugin. Please make sure to follow these conventions when adding a new command object definition: * Use [command arguments](03-monitoring-basics.md#command-arguments) whenever possible. The `command` attribute must be an array in `[ ... ]` for shell escaping. -* Define a unique `prefix` for the command's specific arguments. That way you can safely -set them on host/service level and you'll always know which command they control. +* Define a unique `prefix` for the command's specific arguments. Best practice is to follow this schema: + +``` +_ +``` + +That way you can safely set them on host/service level and you'll always know which command they control. * Use command argument default values, e.g. for thresholds. * Use [advanced conditions](09-object-types.md#objecttype-checkcommand) like `set_if` definitions. -This is an example for a custom `my-snmp-int` check command: +Before starting with the CheckCommand definition, please check +the existing objects available inside the ITL. They follow best +practices and are maintained by developers and our community. - object CheckCommand "my-snmp-int" { - command = [ CustomPluginDir + "/check_snmp_int.pl" ] +This example picks a new plugin called [check_systemd](https://exchange.icinga.com/joseffriedrich/check_systemd) +uploaded to Icinga Exchange in June 2019. - arguments = { - "-H" = "$snmp_address$" - "-C" = "$snmp_community$" - "-p" = "$snmp_port$" - "-2" = { - set_if = "$snmp_v2$" - } - "-n" = "$snmp_interface$" - "-f" = { - set_if = "$snmp_perf$" - } - "-w" = "$snmp_warn$" - "-c" = "$snmp_crit$" - } +First, [install](05-service-monitoring.md#service-monitoring-plugins-setup) the plugin and ensure +that [it works](05-service-monitoring.md#service-monitoring-plugins-it-works). Then run it with the +`--help` parameter to see the actual parameters (docs might be outdated). - vars.snmp_v2 = true - vars.snmp_perf = true - vars.snmp_warn = "300,400" - vars.snmp_crit = "0,600" +``` +./check_systemd.py --help + +usage: check_systemd.py [-h] [-c SECONDS] [-e UNIT | -u UNIT] [-v] [-V] + [-w SECONDS] + +... + +optional arguments: + -h, --help show this help message and exit + -c SECONDS, --critical SECONDS + Startup time in seconds to result in critical status. + -e UNIT, --exclude UNIT + Exclude a systemd unit from the checks. This option + can be applied multiple times. For example: -e mnt- + data.mount -e task.service. + -u UNIT, --unit UNIT Name of the systemd unit that is beeing tested. + -v, --verbose Increase output verbosity (use up to 3 times). + -V, --version show program's version number and exit + -w SECONDS, --warning SECONDS + Startup time in seconds to result in warning status. +``` + +The argument description is important, based on this you need to create the +command arguments. + +> **Tip** +> +> When you are using the Director, you can prepare the commands as files +> e.g. inside the `global-templates` zone. Then run the kickstart wizard +> again to import the commands as external reference. +> +> If you prefer to use the Director GUI/CLI, please apply the steps +> in the `Add Command` form. + +Start with the basic plugin call without any parameters. + +``` +object CheckCommand "systemd" { // Plugin name without 'check_' prefix + command = [ PluginContribDir + "/check_systemd.py" ] // Use the 'PluginContribDir' constant, see the contributed ITL commands +} +``` + +Run a config validation to see if that works, `icinga2 daemon -C` + +Next, analyse the plugin parameters. Plugins with a good help output show +optional parameters in square brackes. This is the case for all parameters +for this plugin. If there are required parameters, use the `required` key +inside the argument. + +The `arguments` attribute is a dictionary which takes the parameters as keys. + +``` + arguments = { + "--unit" = { ... } + } +``` + +If there a long parameter names available, prefer them. This increases +readability in both the configuration as well as the executed command line. + +The argument value itself is a sub dictionary which has additional keys: + +* `value` which references the runtime macro string +* `description` where you copy the plugin parameter help text into +* `required`, `set_if`, etc. for advanced parameters, check the [CheckCommand object](09-object-types.md#objecttype-checkcommand) chapter. + +The runtime macro syntax is required to allow value extraction when +the command is executed. + +> **Tip** +> +> Inside the Director, store the new command first in order to +> unveil the `Arguments` tab. + +Best practice is to use the command name as prefix, in this specific +case e.g. `systemd_unit`. + +``` + arguments = { + "--unit" = { + value = "$systemd_unit$" // The service parameter would then be defined as 'vars.systemd_unit = "icinga2"' + description = "Name of the systemd unit that is beeing tested." } + "--warning" = { + value = "$systemd_warning$" + description = "Startup time in seconds to result in warning status." + } + "--critical" = { + value = "$systemd_critical$" + description = "Startup time in seconds to result in critical status." + } + } +``` + +This may take a while -- validate the configuration in between up until +the CheckCommand definition is done. + +Then test and integrate it into your monitoring configuration. + +Remember: Do it once and right, and never touch the CheckCommand again. +Optional arguments allow different use cases and scenarios. -For further information on your monitoring configuration read the -[Monitoring Basics](03-monitoring-basics.md#monitoring-basics) chapter. +Once you have created your really good CheckCommand, please consider +sharing it with our community by creating a new PR on [GitHub](https://github.com/Icinga/icinga2/blob/master/CONTRIBUTING.md). +_Please also update the documentation for the ITL._ + + +> **Tip** +> +> Inside the Director, you can render the configuration in the Deployment +> section. Extract the static configuration object and use that as a source +> for sending it upstream. + + + +#### Modify Existing CheckCommand + +Sometimes an existing CheckCommand inside the ITL is missing a parameter. +Or you don't need a default parameter value being set. + +Instead of copying the entire configuration object, you can import +an object into another new object. + +``` +object CheckCommand "http-custom" { + import "http" // Import existing http object + + arguments += { // Use additive assignment to add missing parameters + "--key" = { + value = "$http_..." // Keep the parameter name the same as with http + } + } + + // Override default parameters + vars.http_address = "..." +} +``` + +This CheckCommand can then be referenced in your host/service object +definitions. -If you have created your own `CheckCommand` definition, please kindly -[send it upstream](https://www.icinga.com/community/get-involved/). ### Plugin API -Currently Icinga 2 supports the native plugin API specification from the Monitoring Plugins project. It is defined in the [Monitoring Plugins Development Guidelines](https://www.monitoring-plugins.org/doc/guidelines.html). +Icinga 2 supports the native plugin API specification from the Monitoring Plugins project. +It is defined in the [Monitoring Plugins](https://www.monitoring-plugins.org) guidelines. + +The Icinga documentation revamps the specification into our +own guideline enriched with examples and best practices. + +#### Output + +The output should be as short and as detailed as possible. The +most common cases include: + +- Viewing a problem list in Icinga Web and dashboards +- Getting paged about a problem +- Receiving the alert on the CLI or forwarding it to external (ticket) systems + +Examples: + +``` +: + +OK: MySQL connection time is fine (0.0002s) +WARNING: MySQL connection time is slow (0.5s > 0.1s threshold) +CRITICAL: MySQL connection time is causing degraded performance (3s > 0.5s threshold) +``` + +Icinga supports reading multi-line output where Icinga Web +only shows the first line in the listings and everything in the detail view. + +Example for an end2end check with many smaller test cases integrated: + +``` +OK: Online banking works. +Testcase 1: Site reached. +Testcase 2: Attempted login, JS loads. +Testcase 3: Login succeeded. +Testcase 4: View current state works. +Testcase 5: Transactions fine. +``` + +If the extended output shouldn't be visible in your monitoring, but only for testing, +it is recommended to implement the `--verbose` plugin parameter to allow +developers and users to debug further. Check [here](05-service-monitoring.md#service-monitoring-plugin-api-verbose) +for more implementation tips. + +> **Tip** +> +> More debug output also helps when implementing your plugin. +> +> Best practice is to have the plugin parameter and handling implemented first, +> then add it anywhere you want to see more, e.g. from initial database connections +> to actual query results. + + +#### Status + +Value | Status | Description +------|-----------|------------------------------- +0 | OK | The check went fine and everything is considered working. +1 | Warning | The check is above the given warning threshold, or anything else is suspicious requiring attention before it breaks. +2 | Critical | The check exceeded the critical threshold, or something really is broken and will harm the production environment. +3 | Unknown | Invalid parameters, low level resource errors (IO device busy, no fork resources, TCP sockets, etc.) preventing the actual check. Higher level errors such as DNS resolving, TCP connection timeouts should be treated as `Critical` instead. Whenever the plugin reaches its timeout (best practice) it should also terminate with `Unknown`. + +Keep in mind that these are service states. Icinga automatically maps +the [host state](03-monitoring-basics.md#check-result-state-mapping) from the returned plugin states. + +#### Thresholds + +A plugin calculates specific values and may decide about the exit state on its own. +This is done with thresholds - warning and critical values which are compared with +the actual value. Upon this logic, the exit state is determined. + +Imagine the following value and defined thresholds: + +``` +ptc_value = 57.8 + +warning = 50 +critical = 60 +``` + +Whenever `ptc_value` is higher than warning or critical, it should return +the appropriate [state](05-service-monitoring.md#service-monitoring-plugin-api-status). + +The threshold evaluation order also is important: + +* Critical thresholds are evaluated first and superseed everything else. +* Warning thresholds are evaluated second +* If no threshold is matched, return the OK state + +Avoid using hardcoded threshold values in your plugins, always +add them to the argument parser. + +Example for Python: + +``` +import argparse +import signal +import sys + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + + parser.add_argument("-w", "--warning", help="Warning threshold. Single value or range, e.g. '20:50'.") + parser.add_argument("-c", "--critical", help="Critical threshold. Single vluae or range, e.g. '25:45'.") + + args = parser.parse_args() +``` + +Users might call plugins only with the critical threshold parameter, +leaving out the warning parameter. Keep this in mind when evaluating +the thresholds, always check if the parameters have been defined before. + +``` + if args.critical: + if ptc_value > args.critical: + print("CRITICAL - ...") + sys.exit(2) # Critical + + if args.warning: + if ptc_value > args.warning: + print("WARNING - ...") + sys.exit(1) # Warning + + print("OK - ...") + sys.exit(0) # OK +``` + +The above is a simplified example for printing the [output](05-service-monitoring.md#service-monitoring-plugin-api-output) +and using the [state](05-service-monitoring.md#service-monitoring-plugin-api-status) +as exit code. + +Before diving into the implementation, learn more about required +[performance data metrics](05-service-monitoring.md#service-monitoring-plugin-api-performance-data-metrics) +and more best practices below. + +##### Threshold Ranges + +Threshold ranges can be used to specify an alert window, e.g. whenever a calculated +value is between a lower and higher critical threshold. + +The schema for threshold ranges looks as follows. The `@` character in square brackets +is optional. + +``` +[@]start:end +``` + +There are a few requirements for ranges: + +* `start <= end`. Add a check in your code and let the user know about problematic values. + +``` +10:20 # OK + +30:10 # Error +``` + +* `start:` can be omitted if its value is 0. This is the default handling for single threshold values too. + +``` +10 # Every value > 10 and < 0, outside of 0..10 +``` + +* If `end` is omitted, assume end is infinity. + +``` +10: # < 10, outside of 10..∞ +``` + +* In order to specify negative infinity, use the `~` character. + +``` +~:10 # > 10, outside of -∞..10 +``` + +* Raise alert if value is outside of the defined range. + +``` +10:20 # < 10 or > 20, outside of 10..20 +``` + +* Start with `@` to raise an alert if the value is **inside** the defined range, inclusive start/end values. + +``` +@10:20 # >= 10 and <= 20, inside of 10..20 +``` + +Best practice is to either implement single threshold values, or fully support ranges. +This requires parsing the input parameter values, therefore look for existing libraries +already providing this functionality. + +[check_tinkerforge](https://github.com/NETWAYS/check_tinkerforge/blob/master/check_tinkerforge.py) +implements a simple parser to avoid dependencies. + + +#### Performance Data Metrics + +Performance data metrics must be appended to the plugin output with a preceding `|` character. +The schema is as follows: + +``` + | 'label'=value[UOM];[warn];[crit];[min];[max] +``` + +The label should be encapsulated with single quotes. Avoid spaces or special characters such +as `%` in there, this could lead to problems with metric receivers such as Graphite. + +Labels must not include `'` and `=` characters. Keep the label length as short and unique as possible. + +Example: + +``` +'load1'=4.7 +``` + +Values must respect the C/POSIX locale and not implement e.g. German locale for floating point numbers with `,`. +Icinga sets `LC_NUMERIC=C` to enforce this locale on plugin execution. + +##### Unit of Measurement (UOM) + +Unit | Description +---------|--------------------------------- +None | Integer or floating point number for any type (processes, users, etc.). +`s` | Seconds, can be `s`, `ms`, `us`. +`%` | Percentage. +`B` | Bytes, can be `KB`, `MB`, `GB`, `TB`. Lowercase is also possible. +`c` | A continuous counter (e.g. interface traffic counters). + +Icinga metric writers normalize these values to the lowest common base, e.g. seconds and bytes. +Bad plugins change the UOM for different sizing, e.g. returning the disk usage in MB and later GB +for the same performance data label. This is to ensure that graphs always look the same. + +``` +'rta'=12.445000ms 'pl'=0% +``` + +##### Thresholds and Min/Max + +Next to the performance data value, warn, crit, min, max can optionally be provided. They must be separated +with the semi-colon `;` character. They share the same UOM with the performance data value. + +``` +$ check_ping -4 -H icinga.com -c '200,15%' -w '100,5%' + +PING OK - Packet loss = 0%, RTA = 12.44 ms|rta=12.445000ms;100.000000;200.000000;0.000000 pl=0%;5;15;0 +``` + +##### Multiple Performance Data Values + +Multiple performance data values must be joined with a space character. The below example +is from the [check_load](10-icinga-template-library.md#plugin-check-command-load) plugin. + +``` +load1=4.680;1.000;2.000;0; load5=0.000;5.000;10.000;0; load15=0.000;10.000;20.000;0; +``` + +#### Timeout + +Icinga has a safety mechanism where it kills processes running for too +long. The timeout can be specified in [CheckCommand objects](09-object-types.md#objecttype-checkcommand) +or on the host/service object. + +Best practice is to control the timeout in the plugin itself +and provide a clear message followed by the Unknown state. + +Example in Python taken from [check_tinkerforge](https://github.com/NETWAYS/check_tinkerforge/blob/master/check_tinkerforge.py): + +``` +import argparse +import signal +import sys + +def handle_sigalrm(signum, frame, timeout=None): + output('Plugin timed out after %d seconds' % timeout, 3) + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + # ... add more arguments + parser.add_argument("-t", "--timeout", help="Timeout in seconds (default 10s)", type=int, default=10) + args = parser.parse_args() + + signal.signal(signal.SIGALRM, partial(handle_sigalrm, timeout=args.timeout)) + signal.alarm(args.timeout) + + # ... perform the check and generate output/status +``` + +#### Versions + +Plugins should provide a version via `-V` or `--version` parameter +which is bumped on releases. This allows to identify problems with +too old or new versions on the community support channels. + +Example in Python taken from [check_tinkerforge](https://github.com/NETWAYS/check_tinkerforge/blob/master/check_tinkerforge.py): + +``` +import argparse +import signal +import sys + +__version__ = '0.9.1' + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + + parser.add_argument('-V', '--version', action='version', version='%(prog)s v' + sys.modules[__name__].__version__) +``` + +#### Verbose + +Plugins should provide a verbose mode with `-v` or `--verbose` in order +to show more detailed log messages. This helps to debug and analyse the +flow and execution steps inside the plugin. + +Ensure to add the parameter prior to implementing the check logic into +the plugin. + +Example in Python taken from [check_tinkerforge](https://github.com/NETWAYS/check_tinkerforge/blob/master/check_tinkerforge.py): + +``` +import argparse +import signal +import sys + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + + parser.add_argument('-v', '--verbose', action='store_true') + + if args.verbose: + print("Verbose debug output") +``` + ### Create a new Plugin @@ -113,42 +774,55 @@ its output/exit code and return your specified output/exit code. On the other hand plugins for specific services and hardware might not yet exist. -Common best practices when creating a new plugin are for example: +> **Tip** +> +> Watch this presentation from Icinga Camp Berlin to learn more +> about [How to write checks that don't suck](https://www.youtube.com/watch?v=Ey_APqSCoFQ). + +Common best practices: * Choose the programming language wisely * Scripting languages (Bash, Python, Perl, Ruby, PHP, etc.) are easier to write and setup but their check execution might take longer (invoking the script interpreter as overhead, etc.). * Plugins written in C/C++, Go, etc. improve check execution time but may generate an overhead with installation and packaging. -* Use a modern VCS such as Git for developing the plugin (e.g. share your plugin on GitHub). +* Use a modern VCS such as Git for developing the plugin, e.g. share your plugin on GitHub and let it sync to [Icinga Exchange](https://exchange.icinga.com). +* **Look into existing plugins endorsed by community members.** + +Implementation hints: + * Add parameters with key-value pairs to your plugin. They should allow long names (e.g. `--host localhost`) and also short parameters (e.g. `-H localhost`) - * `-h|--help` should print the version and all details about parameters and runtime invocation. -* Add a verbose/debug output functionality for detailed on-demand logging. + * `-h|--help` should print the version and all details about parameters and runtime invocation. Note: Python's ArgParse class provides this OOTB. + * `--version` should print the plugin [version](05-service-monitoring.md#service-monitoring-plugin-api-versions). +* Add a [verbose/debug output](05-service-monitoring.md#service-monitoring-plugin-api-verbose) functionality for detailed on-demand logging. * Respect the exit codes required by the [Plugin API](05-service-monitoring.md#service-monitoring-plugin-api). -* Always add performance data to your plugin output +* Always add [performance data](05-service-monitoring.md#service-monitoring-plugin-api-performance-data-metrics) to your plugin output. +* Allow to specify [warning/critical thresholds](05-service-monitoring.md#service-monitoring-plugin-api-thresholds) as parameters. Example skeleton: - # 1. include optional libraries - # 2. global variables - # 3. helper functions and/or classes - # 4. define timeout condition +``` +# 1. include optional libraries +# 2. global variables +# 3. helper functions and/or classes +# 4. define timeout condition - if () then - print "UNKNOWN - Timeout (...) reached | 'time'=30.0 - endif +if () then + print "UNKNOWN - Timeout (...) reached | 'time'=30.0 +endif - # 5. main method +# 5. main method - + - if () then - print "CRITICAL - ... | 'time'=0.1 'myperfdatavalue'=5.0 - exit(2) - else if () then - print "WARNING - ... | 'time'=0.1 'myperfdatavalue'=3.0 - exit(1) - else - print "OK - ... | 'time'=0.2 'myperfdatavalue'=1.0 - endif +if () then + print "CRITICAL - ... | 'time'=0.1 'myperfdatavalue'=5.0 + exit(2) +else if () then + print "WARNING - ... | 'time'=0.1 'myperfdatavalue'=3.0 + exit(1) +else + print "OK - ... | 'time'=0.2 'myperfdatavalue'=1.0 +endif +``` There are various plugin libraries available which will help with plugin execution and output formatting too, for example @@ -162,12 +836,17 @@ with plugin execution and output formatting too, for example Once you've finished your plugin please upload/sync it to [Icinga Exchange](https://exchange.icinga.com/new). Thanks in advance! + ## Service Monitoring Overview The following examples should help you to start implementing your own ideas. There is a variety of plugins available. This collection is not complete -- if you have any updates, please send a documentation patch upstream. +Please visit our [community forum](https://community.icinga.com) which +may provide an answer to your use case already. If not, do not hesitate +to create a new topic. + ### General Monitoring If the remote service is available (via a network protocol and port), @@ -226,11 +905,13 @@ Instead, choose a plugin and configure its parameters and thresholds. The follow * [ftp](10-icinga-template-library.md#plugin-check-command-ftp) * [webinject](10-icinga-template-library.md#plugin-contrib-command-webinject) * [squid](10-icinga-template-library.md#plugin-contrib-command-squid) -* [apache_status](10-icinga-template-library.md#plugin-contrib-command-apache_status) +* [apache-status](10-icinga-template-library.md#plugin-contrib-command-apache-status) * [nginx_status](10-icinga-template-library.md#plugin-contrib-command-nginx_status) * [kdc](10-icinga-template-library.md#plugin-contrib-command-kdc) * [rbl](10-icinga-template-library.md#plugin-contrib-command-rbl) +* [Icinga Certificate Monitoring](https://icinga.com/products/icinga-certificate-monitoring/) + ### Java Monitoring * [jmx4perl](10-icinga-template-library.md#plugin-contrib-command-jmx4perl) @@ -255,11 +936,14 @@ Instead, choose a plugin and configure its parameters and thresholds. The follow ### VMware Monitoring +* [Icinga Module for vSphere](https://icinga.com/products/icinga-module-for-vsphere/) * [esxi_hardware](10-icinga-template-library.md#plugin-contrib-command-esxi-hardware) * [VMware](10-icinga-template-library.md#plugin-contrib-vmware) **Tip**: If you are encountering timeouts using the VMware Perl SDK, check [this blog entry](https://www.claudiokuenzler.com/blog/650/slow-vmware-perl-sdk-soap-request-error-libwww-version). +Ubuntu 16.04 LTS can have troubles with random entropy in Perl asked [here](https://monitoring-portal.org/t/check-vmware-api-slow-when-run-multiple-times/2868). +In that case, [haveged](http://issihosts.com/haveged/) may help. ### SAP Monitoring diff --git a/doc/06-distributed-monitoring.md b/doc/06-distributed-monitoring.md index fe3e04c51..17396000a 100644 --- a/doc/06-distributed-monitoring.md +++ b/doc/06-distributed-monitoring.md @@ -1,42 +1,51 @@ -# Distributed Monitoring with Master, Satellites, and Clients +# Distributed Monitoring with Master, Satellites and Agents This chapter will guide you through the setup of a distributed monitoring environment, including high-availability clustering and setup details -for the Icinga 2 client. +for Icinga masters, satellites and agents. -## Roles: Master, Satellites, and Clients +## Roles: Master, Satellites and Agents Icinga 2 nodes can be given names for easier understanding: * A `master` node which is on top of the hierarchy. * A `satellite` node which is a child of a `satellite` or `master` node. -* A `client` node which works as an `agent` connected to `master` and/or `satellite` nodes. +* An `agent` node which is connected to `master` and/or `satellite` nodes. -![Icinga 2 Distributed Roles](images/distributed-monitoring/icinga2_distributed_roles.png) +![Icinga 2 Distributed Roles](images/distributed-monitoring/icinga2_distributed_monitoring_roles.png) Rephrasing this picture into more details: * A `master` node has no parent node. - * A `master`node is where you usually install Icinga Web 2. - * A `master` node can combine executed checks from child nodes into backends and notifications. + * A `master`node is where you usually install Icinga Web 2. + * A `master` node can combine executed checks from child nodes into backends and notifications. * A `satellite` node has a parent and a child node. - * A `satellite` node may execute checks on its own or delegate check execution to child nodes. - * A `satellite` node can receive configuration for hosts/services, etc. from the parent node. - * A `satellite` node continues to run even if the master node is temporarily unavailable. -* A `client` node only has a parent node. - * A `client` node will either run its own configured checks or receive command execution events from the parent node. + * A `satellite` node may execute checks on its own or delegate check execution to child nodes. + * A `satellite` node can receive configuration for hosts/services, etc. from the parent node. + * A `satellite` node continues to run even if the master node is temporarily unavailable. +* An `agent` node only has a parent node. + * An `agent` node will either run its own configured checks or receive command execution events from the parent node. + +A client can be a secondary master, a satellite or an agent. It +typically requests something from the primary master or parent node. The following sections will refer to these roles and explain the differences and the possibilities this kind of setup offers. +> **Note** +> +> Previous versions of this documentation used the term `Icinga client`. +> This has been refined into `Icinga agent` and is visible in the docs, +> backends and web interfaces. + **Tip**: If you just want to install a single master node that monitors several hosts -(i.e. Icinga 2 clients), continue reading -- we'll start with +(i.e. Icinga agents), continue reading -- we'll start with simple examples. In case you are planning a huge cluster setup with multiple levels and -lots of clients, read on -- we'll deal with these cases later on. +lots of satellites and agents, read on -- we'll deal with these cases later on. The installation on each system is the same: You need to install the -[Icinga 2 package](02-getting-started.md#setting-up-icinga2) and the required [plugins](02-getting-started.md#setting-up-check-plugins). +[Icinga 2 package](02-installation.md#setting-up-icinga2) and the required [plugins](02-installation.md#setting-up-check-plugins). The required configuration steps are mostly happening on the command line. You can also [automate the setup](06-distributed-monitoring.md#distributed-monitoring-automation). @@ -48,23 +57,25 @@ The first thing you need learn about a distributed setup is the hierarchy of the The Icinga 2 hierarchy consists of so-called [zone](09-object-types.md#objecttype-zone) objects. Zones depend on a parent-child relationship in order to trust each other. -![Icinga 2 Distributed Zones](images/distributed-monitoring/icinga2_distributed_zones.png) +![Icinga 2 Distributed Zones](images/distributed-monitoring/icinga2_distributed_monitoring_zones.png) Have a look at this example for the `satellite` zones which have the `master` zone as a parent zone: - object Zone "master" { - //... - } +``` +object Zone "master" { + //... +} - object Zone "satellite region 1" { - parent = "master" - //... - } +object Zone "satellite region 1" { + parent = "master" + //... +} - object Zone "satellite region 2" { - parent = "master" - //... - } +object Zone "satellite region 2" { + parent = "master" + //... +} +``` There are certain limitations for child zones, e.g. their members are not allowed to send configuration commands to the parent zone members. Vice versa, the @@ -72,43 +83,51 @@ trust hierarchy allows for example the `master` zone to send configuration files to the `satellite` zone. Read more about this in the [security section](06-distributed-monitoring.md#distributed-monitoring-security). -`client` nodes also have their own unique zone. By convention you -can use the FQDN for the zone name. +`agent` nodes also have their own unique zone. By convention you +must use the FQDN for the zone name. ## Endpoints Nodes which are a member of a zone are so-called [Endpoint](09-object-types.md#objecttype-endpoint) objects. -![Icinga 2 Distributed Endpoints](images/distributed-monitoring/icinga2_distributed_endpoints.png) +![Icinga 2 Distributed Endpoints](images/distributed-monitoring/icinga2_distributed_monitoring_endpoints.png) Here is an example configuration for two endpoints in different zones: - object Endpoint "icinga2-master1.localdomain" { - host = "192.168.56.101" - } +``` +object Endpoint "icinga2-master1.localdomain" { + host = "192.168.56.101" +} - object Endpoint "icinga2-satellite1.localdomain" { - host = "192.168.56.105" - } +object Endpoint "icinga2-satellite1.localdomain" { + host = "192.168.56.105" +} - object Zone "master" { - endpoints = [ "icinga2-master1.localdomain" ] - } +object Zone "master" { + endpoints = [ "icinga2-master1.localdomain" ] +} - object Zone "satellite" { - endpoints = [ "icinga2-satellite1.localdomain" ] - parent = "master" - } +object Zone "satellite" { + endpoints = [ "icinga2-satellite1.localdomain" ] + parent = "master" +} +``` All endpoints in the same zone work as high-availability setup. For example, if you have two nodes in the `master` zone, they will load-balance the check execution. Endpoint objects are important for specifying the connection -information, e.g. if the master should actively try to connect to a client. +information, e.g. if the master should actively try to connect to an agent. The zone membership is defined inside the `Zone` object definition using the `endpoints` attribute with an array of `Endpoint` names. +> **Note** +> +> There is a known [problem](https://github.com/Icinga/icinga2/issues/3533) +> with >2 endpoints in a zone and a message routing loop. +> The config validation will log a warning to let you know about this too. + If you want to check the availability (e.g. ping checks) of the node you still need a [Host](09-object-types.md#objecttype-host) object. @@ -116,8 +135,8 @@ you still need a [Host](09-object-types.md#objecttype-host) object. In case you are using the CLI commands later, you don't have to write this configuration from scratch in a text editor. -The [ApiListener](09-object-types.md#objecttype-apilistener) -object is used to load the SSL certificates and specify restrictions, e.g. +The [ApiListener](09-object-types.md#objecttype-apilistener) object is +used to load the TLS certificates and specify restrictions, e.g. for accepting configuration commands. It is also used for the [Icinga 2 REST API](12-icinga2-api.md#icinga2-api) which shares @@ -129,7 +148,9 @@ and `accept_config` can be configured here. In order to use the `api` feature you need to enable it and restart Icinga 2. - icinga2 feature enable api +``` +icinga2 feature enable api +``` ## Conventions @@ -152,8 +173,10 @@ While there are certain mechanisms to ensure a secure communication between all nodes (firewalls, policies, software hardening, etc.), Icinga 2 also provides additional security: -* SSL certificates are mandatory for communication between nodes. The CLI commands -help you create those certificates. +* TLS v1.2+ is required. +* TLS cipher lists are hardened [by default](09-object-types.md#objecttype-apilistener). +* TLS certificates are mandatory for communication between nodes. The CLI command wizards +help you create these certificates. * Child zones only receive updates (check results, commands, etc.) for their configured objects. * Child zones are not allowed to push configuration updates to parent zones. * Zones cannot interfere with other zones and influence each other. Each checkable host or service object is assigned to **one zone** only. @@ -165,32 +188,58 @@ The connection is secured by TLS. The message protocol uses an internal API, and as such message types and names may change internally and are not documented. Zones build the trust relationship in a distributed environment. If you do not specify -a zone for a client and specify the parent zone, its zone members e.g. the master instance -won't trust the client. +a zone for an agent/satellite and specify the parent zone, its zone members e.g. the master instance +won't trust the agent/satellite. Building this trust is key in your distributed environment. That way the parent node knows that it is able to send messages to the child zone, e.g. configuration objects, configuration in global zones, commands to be executed in this zone/for this endpoint. It also receives check results from the child zone for checkable objects (host/service). -Vice versa, the client trusts the master and accepts configuration and commands if enabled -in the api feature. If the client would send configuration to the parent zone, the parent nodes -will deny it. The parent zone is the configuration entity, and does not trust clients in this matter. -A client could attempt to modify a different client for example, or inject a check command +Vice versa, the agent/satellite trusts the master and accepts configuration and commands if enabled +in the api feature. If the agent/satellite would send configuration to the parent zone, the parent nodes +will deny it. The parent zone is the configuration entity, and does not trust agents/satellites in this matter. +An agent/satellite could attempt to modify a different agent/satellite for example, or inject a check command with malicious code. -While it may sound complicated for client setups, it removes the problem with different roles -and configurations for a master and a client. Both of them work the same way, are configured +While it may sound complicated for agent/satellite setups, it removes the problem with different roles +and configurations for a master and child nodes. Both of them work the same way, are configured in the same way (Zone, Endpoint, ApiListener), and you can troubleshoot and debug them in just one go. +## Versions and Upgrade + +It generally is advised to use the newest releases with the same version on all instances. +Prior to upgrading, make sure to plan a maintenance window. + +The Icinga project aims to allow the following compatibility: + +``` +master (2.11) >= satellite (2.10) >= agent (2.9) +``` + +Older agent versions may work, but there's no guarantee. Always keep in mind that +older versions are out of support and can contain bugs. + +In terms of an upgrade, ensure that the master is upgraded first, then +involved satellites, and last the Icinga agents. If you are on v2.10 +currently, first upgrade the master instance(s) to 2.11, and then proceed +with the satellites. Things are getting easier with any sort of automation +tool (Puppet, Ansible, etc.). + +Releases and new features may require you to upgrade master/satellite instances at once, +this is highlighted in the [upgrading docs](16-upgrading-icinga-2.md#upgrading-icinga-2) if needed. +One example is the CA Proxy and on-demand signing feature +available since v2.8 where all involved instances need this version +to function properly. + ## Master Setup This section explains how to install a central single master node using the `node wizard` command. If you prefer to do an automated installation, please refer to the [automated setup](06-distributed-monitoring.md#distributed-monitoring-automation) section. -Install the [Icinga 2 package](02-getting-started.md#setting-up-icinga2) and setup -the required [plugins](02-getting-started.md#setting-up-check-plugins) if you haven't done +Install the [Icinga 2 package](02-installation.md#setting-up-icinga2) and setup +the required [plugins](02-installation.md#setting-up-check-plugins) if you haven't done so already. **Note**: Windows is not supported for a master node setup. @@ -212,9 +261,9 @@ The setup wizard will ensure that the following steps are taken: * Enable the `api` feature. * Generate a new certificate authority (CA) in `/var/lib/icinga2/ca` if it doesn't exist. * Create a certificate for this node signed by the CA key. -* Update the [zones.conf](04-configuring-icinga-2.md#zones-conf) file with the new zone hierarchy. -* Update the [ApiListener](06-distributed-monitoring.md#distributed-monitoring-apilistener) and [constants](04-configuring-icinga-2.md#constants-conf) configuration. -* Update the [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) to disable the `conf.d` inclusion, and add the `api-users.conf` file inclusion. +* Update the [zones.conf](04-configuration.md#zones-conf) file with the new zone hierarchy. +* Update the [ApiListener](06-distributed-monitoring.md#distributed-monitoring-apilistener) and [constants](04-configuration.md#constants-conf) configuration. +* Update the [icinga2.conf](04-configuration.md#icinga2-conf) to disable the `conf.d` inclusion, and add the `api-users.conf` file inclusion. Here is an example of a master setup for the `icinga2-master1.localdomain` node on CentOS 7: @@ -225,7 +274,7 @@ Welcome to the Icinga 2 Setup Wizard! We will guide you through all required configuration details. -Please specify if this is a satellite/client setup ('n' installs a master setup) [Y/n]: n +Please specify if this is a satellite/agent setup ('n' installs a master setup) [Y/n]: n Starting the Master setup routine... @@ -255,9 +304,9 @@ Now restart your Icinga 2 daemon to finish the installation! ``` You can verify that the CA public and private keys are stored in the `/var/lib/icinga2/ca` directory. -Keep this path secure and include it in your [backups](02-getting-started.md#install-backup). +Keep this path secure and include it in your [backups](02-installation.md#install-backup). -In case you lose the CA private key you have to generate a new CA for signing new client +In case you lose the CA private key you have to generate a new CA for signing new agent/satellite certificate requests. You then have to also re-create new signed certificates for all existing nodes. @@ -276,7 +325,7 @@ and should be the same on all master instances. You can avoid signing and deploying certificates [manually](06-distributed-monitoring.md#distributed-monitoring-advanced-hints-certificates-manual) by using built-in methods for auto-signing certificate signing requests (CSR): -* [CSR Auto-Signing](06-distributed-monitoring.md#distributed-monitoring-setup-csr-auto-signing) which uses a client ticket generated on the master as trust identifier. +* [CSR Auto-Signing](06-distributed-monitoring.md#distributed-monitoring-setup-csr-auto-signing) which uses a client (an agent or a satellite) ticket generated on the master as trust identifier. * [On-Demand CSR Signing](06-distributed-monitoring.md#distributed-monitoring-setup-on-demand-csr-signing) which allows to sign pending certificate requests on the master. Both methods are described in detail below. @@ -287,20 +336,21 @@ Both methods are described in detail below. ### CSR Auto-Signing -A client which sends a certificate signing request (CSR) must authenticate itself -in a trusted way. The master generates a client ticket which is included in this request. +A client can be a secondary master, a satellite or an agent. It sends a certificate signing request (CSR) +and must authenticate itself in a trusted way. The master generates a client ticket which is included in this request. That way the master can verify that the request matches the previously trusted ticket and sign the request. > **Note** > -> Icinga 2 v2.8 adds the possibility to forward signing requests on a satellite -> to the master node. This helps with the setup of [three level clusters](#06-distributed-monitoring.md#distributed-monitoring-scenarios-master-satellite-client) +> Icinga 2 v2.8 added the possibility to forward signing requests on a satellite +> to the master node. This is called `CA Proxy` in blog posts and design drafts. +> This functionality helps with the setup of [three level clusters](06-distributed-monitoring.md#distributed-monitoring-scenarios-master-satellite-agents) > and more. Advantages: -* Nodes can be installed by different users who have received the client ticket. +* Nodes (secondary master, satellites, agents) can be installed by different users who have received the client ticket. * No manual interaction necessary on the master node. * Automation tools like Puppet, Ansible, etc. can retrieve the pre-generated ticket in their client catalog and run the node setup directly. @@ -310,42 +360,58 @@ Disadvantages: * Tickets need to be generated on the master and copied to client setup wizards. * No central signing management. +#### CSR Auto-Signing: Preparation -Setup wizards for satellite/client nodes will ask you for this specific client ticket. +Prior to using this mode, ensure that the following steps are taken on +the signing master: + +* The [master setup](06-distributed-monitoring.md#distributed-monitoring-setup-master) was run successfully. This includes: + * Generated a CA key pair + * Generated a private ticket salt stored in the `TicketSalt` constant, set as `ticket_salt` attribute inside the [api](09-object-types.md#objecttype-apilistener) feature. +* Restart of the master instance. + +#### CSR Auto-Signing: On the master + +Setup wizards for agent/satellite nodes will ask you for this specific client ticket. There are two possible ways to retrieve the ticket: * [CLI command](11-cli-commands.md#cli-command-pki) executed on the master node. * [REST API](12-icinga2-api.md#icinga2-api) request against the master node. + Required information: Parameter | Description --------------------|-------------------- - Common name (CN) | **Required.** The common name for the satellite/client. By convention this should be the FQDN. + Common name (CN) | **Required.** The common name for the agent/satellite. By convention this should be the FQDN. -The following example shows how to generate a ticket on the master node `icinga2-master1.localdomain` for the client `icinga2-client1.localdomain`: +The following example shows how to generate a ticket on the master node `icinga2-master1.localdomain` for the agent `icinga2-agent1.localdomain`: - [root@icinga2-master1.localdomain /]# icinga2 pki ticket --cn icinga2-client1.localdomain +``` +[root@icinga2-master1.localdomain /]# icinga2 pki ticket --cn icinga2-agent1.localdomain +``` Querying the [Icinga 2 API](12-icinga2-api.md#icinga2-api) on the master requires an [ApiUser](12-icinga2-api.md#icinga2-api-authentication) object with at least the `actions/generate-ticket` permission. - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/conf.d/api-users.conf +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/conf.d/api-users.conf - object ApiUser "client-pki-ticket" { - password = "bea11beb7b810ea9ce6ea" //change this - permissions = [ "actions/generate-ticket" ] - } +object ApiUser "client-pki-ticket" { + password = "bea11beb7b810ea9ce6ea" //change this + permissions = [ "actions/generate-ticket" ] +} - [root@icinga2-master1.localdomain /]# systemctl restart icinga2 +[root@icinga2-master1.localdomain /]# systemctl restart icinga2 Retrieve the ticket on the master node `icinga2-master1.localdomain` with `curl`, for example: - [root@icinga2-master1.localdomain /]# curl -k -s -u client-pki-ticket:bea11beb7b810ea9ce6ea -H 'Accept: application/json' \ - -X POST 'https://localhost:5665/v1/actions/generate-ticket' -d '{ "cn": "icinga2-client1.localdomain" }' + [root@icinga2-master1.localdomain /]# curl -k -s -u client-pki-ticket:bea11beb7b810ea9ce6ea -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/generate-ticket' -d '{ "cn": "icinga2-agent1.localdomain" }' +``` -Store that ticket number for the satellite/client setup below. +Store that ticket number for the [agent/satellite setup](06-distributed-monitoring.md#distributed-monitoring-setup-agent-satellite) below. > **Note** > @@ -354,12 +420,14 @@ Store that ticket number for the satellite/client setup below. > to the authorized Puppet agent node which will invoke the > [automated setup steps](06-distributed-monitoring.md#distributed-monitoring-automation-cli-node-setup). + ### On-Demand CSR Signing -Icinga 2 v2.8 adds the possibility to sign certificates from clients without -requiring a client ticket for auto-signing. +The client can be a secondary master, satellite or agent. +It sends a certificate signing request to specified parent node without any +ticket. The admin on the primary master is responsible for reviewing and signing the requests +with the private CA key. -Instead, the client sends a certificate signing request to specified parent node. This could either be directly the master, or a satellite which forwards the request to the signing master. @@ -373,16 +441,34 @@ Disadvantages: * Asynchronous step for automated deployments. * Needs client verification on the master. +#### On-Demand CSR Signing: Preparation -You can list certificate requests by using the `ca list` CLI command. This also shows -which requests already have been signed. +Prior to using this mode, ensure that the following steps are taken on +the signing master: + +* The [master setup](06-distributed-monitoring.md#distributed-monitoring-setup-master) was run successfully. This includes: + * Generated a CA key pair +* Restart of the master instance. + +#### On-Demand CSR Signing: On the master + +You can list pending certificate signing requests with the `ca list` CLI command. ``` [root@icinga2-master1.localdomain /]# icinga2 ca list Fingerprint | Timestamp | Signed | Subject -----------------------------------------------------------------|---------------------|--------|-------- -403da5b228df384f07f980f45ba50202529cded7c8182abf96740660caa09727 | 2017/09/06 17:02:40 | * | CN = icinga2-client1.localdomain -71700c28445109416dd7102038962ac3fd421fbb349a6e7303b6033ec1772850 | 2017/09/06 17:20:02 | | CN = icinga2-client2.localdomain +71700c28445109416dd7102038962ac3fd421fbb349a6e7303b6033ec1772850 | 2017/09/06 17:20:02 | | CN = icinga2-agent2.localdomain +``` + +In order to show all requests, use the `--all` parameter. + +``` +[root@icinga2-master1.localdomain /]# icinga2 ca list --all +Fingerprint | Timestamp | Signed | Subject +-----------------------------------------------------------------|---------------------|--------|-------- +403da5b228df384f07f980f45ba50202529cded7c8182abf96740660caa09727 | 2017/09/06 17:02:40 | * | CN = icinga2-agent1.localdomain +71700c28445109416dd7102038962ac3fd421fbb349a6e7303b6033ec1772850 | 2017/09/06 17:20:02 | | CN = icinga2-agent2.localdomain ``` **Tip**: Add `--json` to the CLI command to retrieve the details in JSON format. @@ -392,57 +478,78 @@ and pass its fingerprint as argument. ``` [root@icinga2-master1.localdomain /]# icinga2 ca sign 71700c28445109416dd7102038962ac3fd421fbb349a6e7303b6033ec1772850 -information/cli: Signed certificate for 'CN = icinga2-client2.localdomain'. +information/cli: Signed certificate for 'CN = icinga2-agent2.localdomain'. ``` -## Client/Satellite Setup +> **Note** +> +> `ca list` cannot be used as historical inventory. Certificate +> signing requests older than 1 week are automatically deleted. -This section describes the setup of a satellite and/or client connected to an +You can also remove an undesired CSR using the `ca remove` command using the +syntax as the `ca sign` command. + +``` +[root@pym ~]# icinga2 ca remove 5c31ca0e2269c10363a97e40e3f2b2cd56493f9194d5b1852541b835970da46e +information/cli: Certificate 5c31ca0e2269c10363a97e40e3f2b2cd56493f9194d5b1852541b835970da46e removed. +``` +If you want to restore a certificate you have removed, you can use `ca restore`. + + + + +## Agent/Satellite Setup + +This section describes the setup of an agent or satellite connected to an existing master node setup. If you haven't done so already, please [run the master setup](06-distributed-monitoring.md#distributed-monitoring-setup-master). Icinga 2 on the master node must be running and accepting connections on port `5665`. + + -### Client/Satellite Setup on Linux +### Agent/Satellite Setup on Linux -Please ensure that you've run all the steps mentioned in the [client/satellite section](06-distributed-monitoring.md#distributed-monitoring-setup-satellite-client). +Please ensure that you've run all the steps mentioned in the [agent/satellite section](06-distributed-monitoring.md#distributed-monitoring-setup-agent-satellite). -Install the [Icinga 2 package](02-getting-started.md#setting-up-icinga2) and setup -the required [plugins](02-getting-started.md#setting-up-check-plugins) if you haven't done +Install the [Icinga 2 package](02-installation.md#setting-up-icinga2) and setup +the required [plugins](02-installation.md#setting-up-check-plugins) if you haven't done so already. The next step is to run the `node wizard` CLI command. -In this example we're generating a ticket on the master node `icinga2-master1.localdomain` for the client `icinga2-client1.localdomain`: +In this example we're generating a ticket on the master node `icinga2-master1.localdomain` for the agent `icinga2-agent1.localdomain`: - [root@icinga2-master1.localdomain /]# icinga2 pki ticket --cn icinga2-client1.localdomain - 4f75d2ecd253575fe9180938ebff7cbca262f96e +``` +[root@icinga2-master1.localdomain /]# icinga2 pki ticket --cn icinga2-agent1.localdomain +4f75d2ecd253575fe9180938ebff7cbca262f96e +``` Note: You don't need this step if you have chosen to use [On-Demand CSR Signing](06-distributed-monitoring.md#distributed-monitoring-setup-on-demand-csr-signing). -Start the wizard on the client `icinga2-client1.localdomain`: +Start the wizard on the agent `icinga2-agent1.localdomain`: ``` -[root@icinga2-client1.localdomain /]# icinga2 node wizard +[root@icinga2-agent1.localdomain /]# icinga2 node wizard Welcome to the Icinga 2 Setup Wizard! We will guide you through all required configuration details. ``` -Press `Enter` or add `y` to start a satellite or client setup. +Press `Enter` or add `y` to start a satellite or agent setup. ``` -Please specify if this is a satellite/client setup ('n' installs a master setup) [Y/n]: +Please specify if this is an agent/satellite setup ('n' installs a master setup) [Y/n]: ``` Press `Enter` to use the proposed name in brackets, or add a specific common name (CN). By convention this should be the FQDN. ``` -Starting the Client/Satellite setup routine... +Starting the Agent/Satellite setup routine... -Please specify the common name (CN) [icinga2-client1.localdomain]: icinga2-client1.localdomain +Please specify the common name (CN) [icinga2-agent1.localdomain]: icinga2-agent1.localdomain ``` Specify the direct parent for this node. This could be your primary master `icinga2-master1.localdomain` @@ -505,7 +612,7 @@ Proceed with adding the optional client ticket for [CSR auto-signing](06-distrib ``` Please specify the request ticket generated on your Icinga 2 master (optional). - (Hint: # icinga2 pki ticket --cn 'icinga2-client1.localdomain'): + (Hint: # icinga2 pki ticket --cn 'icinga2-agent1.localdomain'): 4f75d2ecd253575fe9180938ebff7cbca262f96e ``` @@ -541,10 +648,10 @@ in the generated zone configuration file. Set the local zone name to something else, if you are installing a satellite or secondary master instance. ``` -Local zone name [icinga2-client1.localdomain]: +Local zone name [icinga2-agent1.localdomain]: ``` -Set the parent zone name to something else than `master` if this client connects to a satellite instance instead of the master. +Set the parent zone name to something else than `master` if this agents connects to a satellite instance instead of the master. ``` Parent zone name [master]: @@ -561,7 +668,7 @@ Do you want to specify additional global zones? [y/N]: N ``` Last but not least the wizard asks you whether you want to disable the inclusion of the local configuration -directory in `conf.d`, or not. Defaults to disabled, as clients either are checked via command endpoint, or +directory in `conf.d`, or not. Defaults to disabled, as agents either are checked via command endpoint, or they receive configuration synced from the parent zone. ``` @@ -589,7 +696,7 @@ Now restart your Icinga 2 daemon to finish the installation! Restart Icinga 2 as requested. ``` -[root@icinga2-client1.localdomain /]# systemctl restart icinga2 +[root@icinga2-agent1.localdomain /]# systemctl restart icinga2 ``` Here is an overview of all parameters in detail: @@ -599,8 +706,8 @@ Here is an overview of all parameters in detail: Common name (CN) | **Required.** By convention this should be the host's FQDN. Defaults to the FQDN. Master common name | **Required.** Use the common name you've specified for your master node before. Establish connection to the parent node | **Optional.** Whether the node should attempt to connect to the parent node or not. Defaults to `y`. - Master/Satellite endpoint host | **Required if the the client needs to connect to the master/satellite.** The parent endpoint's IP address or FQDN. This information is included in the `Endpoint` object configuration in the `zones.conf` file. - Master/Satellite endpoint port | **Optional if the the client needs to connect to the master/satellite.** The parent endpoints's listening port. This information is included in the `Endpoint` object configuration. + Master/Satellite endpoint host | **Required if the the agent needs to connect to the master/satellite.** The parent endpoint's IP address or FQDN. This information is included in the `Endpoint` object configuration in the `zones.conf` file. + Master/Satellite endpoint port | **Optional if the the agent needs to connect to the master/satellite.** The parent endpoints's listening port. This information is included in the `Endpoint` object configuration. Add more master/satellite endpoints | **Optional.** If you have multiple master/satellite nodes configured, add them here. Parent Certificate information | **Required.** Verify that the connecting host really is the requested master node. Request ticket | **Optional.** Add the [ticket](06-distributed-monitoring.md#distributed-monitoring-setup-csr-auto-signing) generated on the master. @@ -608,8 +715,8 @@ Here is an overview of all parameters in detail: API bind port | **Optional.** Allows to specify the port the ApiListener is bound to. For advanced usage only (requires changing the default port 5665 everywhere). Accept config | **Optional.** Whether this node accepts configuration sync from the master node (required for [config sync mode](06-distributed-monitoring.md#distributed-monitoring-top-down-config-sync)). For [security reasons](06-distributed-monitoring.md#distributed-monitoring-security) this defaults to `n`. Accept commands | **Optional.** Whether this node accepts command execution messages from the master node (required for [command endpoint mode](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint)). For [security reasons](06-distributed-monitoring.md#distributed-monitoring-security) this defaults to `n`. - Local zone name | **Optional.** Allows to specify the name for the local zone. This comes in handy when this instance is a satellite, not a client. Defaults to the FQDN. - Parent zone name | **Optional.** Allows to specify the name for the parent zone. This is important if the client has a satellite instance as parent, not the master. Defaults to `master`. + Local zone name | **Optional.** Allows to specify the name for the local zone. This comes in handy when this instance is a satellite, not an agent. Defaults to the FQDN. + Parent zone name | **Optional.** Allows to specify the name for the parent zone. This is important if the agent has a satellite instance as parent, not the master. Defaults to `master`. Global zones | **Optional.** Allows to specify more global zones in addition to `global-templates` and `director-global`. Defaults to `n`. Disable conf.d | **Optional.** Allows to disable the inclusion of the `conf.d` directory which holds local example configuration. Clients should retrieve their configuration from the parent node, or act as command endpoint execution bridge. Defaults to `y`. @@ -619,7 +726,7 @@ The setup wizard will ensure that the following steps are taken: * Create a certificate signing request (CSR) for the local node. * Request a signed certificate i(optional with the provided ticket number) on the master node. * Allow to verify the parent node's certificate. -* Store the signed client certificate and ca.crt in `/var/lib/icinga2/certs`. +* Store the signed agent/satellite certificate and ca.crt in `/var/lib/icinga2/certs`. * Update the `zones.conf` file with the new zone hierarchy. * Update `/etc/icinga2/features-enabled/api.conf` (`accept_config`, `accept_commands`) and `constants.conf`. * Update `/etc/icinga2/icinga2.conf` and comment out `include_recursive "conf.d"`. @@ -628,45 +735,44 @@ You can verify that the certificate files are stored in the `/var/lib/icinga2/ce > **Note** > -> The certificate location changed in v2.8 to `/var/lib/icinga2/certs`. Please read the [upgrading chapter](16-upgrading-icinga-2.md#upgrading-to-2-8-certificate-paths) -> for more details. - -> **Note** -> -> If the client is not directly connected to the certificate signing master, -> signing requests and responses might need some minutes to fully update the client certificates. +> If the agent is not directly connected to the certificate signing master, +> signing requests and responses might need some minutes to fully update the agent certificates. > > If you have chosen to use [On-Demand CSR Signing](06-distributed-monitoring.md#distributed-monitoring-setup-on-demand-csr-signing) > certificates need to be signed on the master first. Ticket-less setups require at least Icinga 2 v2.8+ on all involved instances. -Now that you've successfully installed a Linux/Unix satellite/client instance, please proceed to +Now that you've successfully installed a Linux/Unix agent/satellite instance, please proceed to the [configuration modes](06-distributed-monitoring.md#distributed-monitoring-configuration-modes). + + -### Client Setup on Windows +### Agent Setup on Windows -Download the MSI-Installer package from [https://packages.icinga.com/windows/](https://packages.icinga.com/windows/). +The supported Windows agent versions are listed [here](https://icinga.com/subscription/support-details/). Requirements: -* Windows Vista/Server 2008 or higher -* Versions older than Windows 10/Server 2016 require the [Universal C Runtime for Windows](https://support.microsoft.com/en-us/help/2999226/update-for-universal-c-runtime-in-windows) -* [Microsoft .NET Framework 2.0](https://www.microsoft.com/de-de/download/details.aspx?id=1639) for the setup wizard +* [Microsoft .NET Framework 4.6](https://www.microsoft.com/en-US/download/details.aspx?id=53344) or higher. This is the default on Windows Server 2016 or later. +* [Universal C Runtime for Windows](https://support.microsoft.com/en-us/help/2999226/update-for-universal-c-runtime-in-windows) for Windows Server 2012 and older. -The installer package includes the [NSClient++](https://www.nsclient.org/) package -so that Icinga 2 can use its built-in plugins. You can find more details in -[this chapter](06-distributed-monitoring.md#distributed-monitoring-windows-nscp). -The Windows package also installs native [monitoring plugin binaries](06-distributed-monitoring.md#distributed-monitoring-windows-plugins) +#### Agent Setup on Windows: Installer + +Download the MSI-Installer package from [https://packages.icinga.com/windows/](https://packages.icinga.com/windows/). +The preferred flavor is `x86_64` for modern Windows systems. + +The Windows package provides native [monitoring plugin binaries](06-distributed-monitoring.md#distributed-monitoring-windows-plugins) to get you started more easily. +The installer package also includes the [NSClient++](https://www.nsclient.org/) package +to allow using its built-in plugins. You can find more details in +[this chapter](06-distributed-monitoring.md#distributed-monitoring-windows-nscp). > **Note** > -> Please note that Icinga 2 was designed to run as light-weight client on Windows. +> Please note that Icinga 2 was designed to run as light-weight agent on Windows. > There is no support for satellite instances. -#### Windows Client Setup Start - Run the MSI-Installer package and follow the instructions shown in the screenshots. ![Icinga 2 Windows Setup](images/distributed-monitoring/icinga2_windows_setup_installer_01.png) @@ -675,12 +781,14 @@ Run the MSI-Installer package and follow the instructions shown in the screensho ![Icinga 2 Windows Setup](images/distributed-monitoring/icinga2_windows_setup_installer_04.png) ![Icinga 2 Windows Setup](images/distributed-monitoring/icinga2_windows_setup_installer_05.png) -The graphical installer offers to run the Icinga 2 setup wizard after the installation. Select -the check box to proceed. +The graphical installer offers to run the [Icinga Agent setup wizard](06-distributed-monitoring.md#distributed-monitoring-setup-agent-windows-configuration-wizard) +after the installation. Select the check box to proceed. > **Tip** > -> You can also run the Icinga 2 setup wizard from the Start menu later. +> You can also run the Icinga agent setup wizard from the Start menu later. + +#### Agent Setup on Windows: Configuration Wizard On a fresh installation the setup wizard guides you through the initial configuration. It also provides a mechanism to send a certificate request to the [CSR signing master](distributed-monitoring-setup-sign-certificates-master). @@ -700,7 +808,7 @@ Add the following details: Parameter | Description -------------------------------|------------------------------- - Instance name | **Required.** The master/satellite endpoint name where this client is a direct child of. + Instance name | **Required.** The master/satellite endpoint name where this agent is a direct child of. Master/Satellite endpoint host | **Required.** The master or satellite's IP address or FQDN. This information is included in the `Endpoint` object configuration in the `zones.conf` file. Master/Satellite endpoint port | **Optional.** The master or satellite's listening port. This information is included in the `Endpoint` object configuration. @@ -712,12 +820,13 @@ When needed you can add an additional global zone (the zones `global-templates` Optionally enable the following settings: - Parameter | Description - ----------------------------------|---------------------------------- - Accept config | **Optional.** Whether this node accepts configuration sync from the master node (required for [config sync mode](06-distributed-monitoring.md#distributed-monitoring-top-down-config-sync)). For [security reasons](06-distributed-monitoring.md#distributed-monitoring-security) this is disabled by default. - Accept commands | **Optional.** Whether this node accepts command execution messages from the master node (required for [command endpoint mode](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint)). For [security reasons](06-distributed-monitoring.md#distributed-monitoring-security) this is disabled by default. - Run Icinga 2 service as this user | **Optional.** Specify a different Windows user. This defaults to `NT AUTHORITY\Network Service` and is required for more privileged service checks. - Install NSClient++ | **Optional.** The Windows installer bundles the NSClient++ installer for additional [plugin checks](06-distributed-monitoring.md#distributed-monitoring-windows-nscp). + Parameter | Description + --------------------------------------------------------|---------------------------------- + Accept commands from master/satellite instance(s) | **Optional.** Whether this node accepts command execution messages from the master node (required for [command endpoint mode](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint)). For [security reasons](06-distributed-monitoring.md#distributed-monitoring-security) this is disabled by default. + Accept config updates from master/satellite instance(s) | **Optional.** Whether this node accepts configuration sync from the master node (required for [config sync mode](06-distributed-monitoring.md#distributed-monitoring-top-down-config-sync)). For [security reasons](06-distributed-monitoring.md#distributed-monitoring-security) this is disabled by default. + Run Icinga 2 service as this user | **Optional.** Specify a different Windows user. This defaults to `NT AUTHORITY\Network Service` and is required for more privileged service checks. + Install/Update bundled NSClient++ | **Optional.** The Windows installer bundles the NSClient++ installer for additional [plugin checks](06-distributed-monitoring.md#distributed-monitoring-windows-nscp). + Disable including local 'conf.d' directory | **Optional.** Allows to disable the `include_recursive "conf.d"` directive except for the `api-users.conf` file in the `icinga2.conf` file. Defaults to `true`. ![Icinga 2 Windows Setup](images/distributed-monitoring/icinga2_windows_setup_wizard_03.png) @@ -726,7 +835,7 @@ Verify the certificate from the master/satellite instance where this node should ![Icinga 2 Windows Setup](images/distributed-monitoring/icinga2_windows_setup_wizard_04.png) -#### Bundled NSClient++ Setup +#### Bundled NSClient++ Setup If you have chosen to install/update the NSClient++ package, the Icinga 2 setup wizard asks you to do so. @@ -764,7 +873,7 @@ The NSClient++ REST API can be used to query metrics. [check_nscp_api](06-distri uses this transport method. -#### Finish Windows Client Setup +#### Finish Windows Agent Setup Finish the Windows setup wizard. @@ -774,11 +883,6 @@ If you did not provide a setup ticket, you need to sign the certificate request The setup wizards tells you to do so. The Icinga 2 service is running at this point already and will automatically receive and update a signed client certificate. -> **Note** -> -> Ticket-less setups require at least Icinga 2 v2.8+ on all involved instances. - - ![Icinga 2 Windows Setup](images/distributed-monitoring/icinga2_windows_setup_wizard_06_finish_no_ticket.png) Icinga 2 is automatically started as a Windows service. @@ -790,35 +894,22 @@ Click `Examine Config` in the setup wizard to open a new Explorer window. ![Icinga 2 Windows Setup](images/distributed-monitoring/icinga2_windows_setup_wizard_examine_config.png) -The configuration files can be modified with your favorite editor e.g. Notepad. +The configuration files can be modified with your favorite editor e.g. Notepad++ or vim in Powershell (via chocolatey). -In order to use the [top down](06-distributed-monitoring.md#distributed-monitoring-top-down) client +In order to use the [top down](06-distributed-monitoring.md#distributed-monitoring-top-down) agent configuration prepare the following steps. -Add a [global zone](06-distributed-monitoring.md#distributed-monitoring-global-zone-config-sync) -for syncing check commands later. Navigate to `C:\ProgramData\icinga2\etc\icinga2` and open -the `zones.conf` file in your preferred editor. Add the following lines if not existing already: - -``` -object Zone "global-templates" { - global = true -} -``` - -> **Note:** -> -> Packages >= 2.8 provide this configuration by default. - -You don't need any local configuration on the client except for +You don't need any local configuration on the agent except for CheckCommand definitions which can be synced using the global zone above. Therefore disable the inclusion of the `conf.d` directory in the `icinga2.conf` file. + Navigate to `C:\ProgramData\icinga2\etc\icinga2` and open the `icinga2.conf` file in your preferred editor. Remove or comment (`//`) the following line: ``` -// Commented out, not required on a client with top down mode +// Commented out, not required on an agent with top down mode //include_recursive "conf.d" ``` @@ -827,29 +918,30 @@ the following line: > Packages >= 2.9 provide an option in the setup wizard to disable this. > Defaults to disabled. -Validate the configuration on Windows open an administrator terminal +Validate the configuration on Windows open an administrative Powershell and run the following command: ``` -C:\WINDOWS\system32>cd "C:\Program Files\ICINGA2\sbin" -C:\Program Files\ICINGA2\sbin>icinga2.exe daemon -C +C:\> cd C:\Program Files\ICINGA2\sbin + +C:\Program Files\ICINGA2\sbin> .\icinga2.exe daemon -C ``` **Note**: You have to run this command in a shell with `administrator` privileges. -Now you need to restart the Icinga 2 service. Run `services.msc` from the start menu -and restart the `icinga2` service. Alternatively, you can use the `net {start,stop}` CLI commands. +Now you need to restart the Icinga 2 service. Run `services.msc` from the start menu and restart the `icinga2` service. +Alternatively open an administrative Powershell and run the following commands: -![Icinga 2 Windows Service Start/Stop](images/distributed-monitoring/icinga2_windows_cmd_admin_net_start_stop.png) +``` +C:\> Restart-Service icinga2 -Now that you've successfully installed a Windows client, please proceed to +C:\> Get-Service icinga2 +``` + + +Now that you've successfully installed a Windows agent, please proceed to the [detailed configuration modes](06-distributed-monitoring.md#distributed-monitoring-configuration-modes). -> **Note** -> -> The certificate location changed in v2.8 to `%ProgramData%\var\lib\icinga2\certs`. -> Please read the [upgrading chapter](16-upgrading-icinga-2.md#upgrading-to-2-8-certificate-paths) -> for more details. ## Configuration Modes @@ -857,19 +949,19 @@ There are different ways to ensure that the Icinga 2 cluster nodes execute checks, send notifications, etc. The preferred method is to configure monitoring objects on the master -and distribute the configuration to satellites and clients. +and distribute the configuration to satellites and agents. -The following chapters will explain this in detail with hands-on manual configuration +The following chapters explain this in detail with hands-on manual configuration examples. You should test and implement this once to fully understand how it works. Once you are familiar with Icinga 2 and distributed monitoring, you can start with additional integrations to manage and deploy your configuration: -* [Icinga Director](https://github.com/icinga/icingaweb2-module-director) provides a web interface to manage configuration and also allows to sync imported resources (CMDB, PuppetDB, etc.) -* [Ansible Roles](https://github.com/Icinga/icinga2-ansible) -* [Puppet Module](https://github.com/Icinga/puppet-icinga2) -* [Chef Cookbook](https://github.com/Icinga/chef-icinga2) +* [Icinga Director](https://icinga.com/docs/director/latest/) provides a web interface to manage configuration and also allows to sync imported resources (CMDB, PuppetDB, etc.) +* [Ansible Roles](https://icinga.com/products/integrations/) +* [Puppet Module](https://icinga.com/products/integrations/puppet/) +* [Chef Cookbook](https://icinga.com/products/integrations/chef/) More details can be found [here](13-addons.md#configuration-tools). @@ -880,24 +972,24 @@ There are two different behaviors with check execution: * Send a command execution event remotely: The scheduler still runs on the parent node. * Sync the host/service objects directly to the child node: Checks are executed locally. -Again, technically it does not matter whether this is a `client` or a `satellite` +Again, technically it does not matter whether this is an `agent` or a `satellite` which is receiving configuration or command execution events. ### Top Down Command Endpoint -This mode will force the Icinga 2 node to execute commands remotely on a specified endpoint. -The host/service object configuration is located on the master/satellite and the client only -needs the CheckCommand object definitions being used there. +This mode forces the Icinga 2 node to execute commands remotely on a specified endpoint. +The host/service object configuration is located on the master/satellite and the agent only +needs the CheckCommand object definitions available. Every endpoint has its own remote check queue. The amount of checks executed simultaneously -can be limited on the endpoint with the `MaxConcurrentChecks` constant defined in [constants.conf](04-configuring-icinga-2.md#constants-conf). Icinga 2 may discard check requests, +can be limited on the endpoint with the `MaxConcurrentChecks` constant defined in [constants.conf](04-configuration.md#constants-conf). Icinga 2 may discard check requests, if the remote check queue is full. -![Icinga 2 Distributed Top Down Command Endpoint](images/distributed-monitoring/icinga2_distributed_top_down_command_endpoint.png) +![Icinga 2 Distributed Top Down Command Endpoint](images/distributed-monitoring/icinga2_distributed_monitoring_agent_checks_command_endpoint.png) Advantages: -* No local checks need to be defined on the child node (client). +* No local checks need to be defined on the child node (agent). * Light-weight remote check execution (asynchronous events). * No [replay log](06-distributed-monitoring.md#distributed-monitoring-advanced-hints-command-endpoint-log-duration) is necessary for the child node. * Pin checks to specific endpoints (if the child zone consists of 2 endpoints). @@ -913,65 +1005,55 @@ commands, you need to configure the `Zone` and `Endpoint` hierarchy on all nodes. * `icinga2-master1.localdomain` is the configuration master in this scenario. -* `icinga2-client1.localdomain` acts as client which receives command execution messages via command endpoint from the master. In addition, it receives the global check command configuration from the master. +* `icinga2-agent1.localdomain` acts as agent which receives command execution messages via command endpoint from the master. In addition, it receives the global check command configuration from the master. Include the endpoint and zone configuration on **both** nodes in the file `/etc/icinga2/zones.conf`. The endpoint configuration could look like this, for example: - [root@icinga2-client1.localdomain /]# vim /etc/icinga2/zones.conf - - object Endpoint "icinga2-master1.localdomain" { - host = "192.168.56.101" - } - - object Endpoint "icinga2-client1.localdomain" { - host = "192.168.56.111" - } - -Next, you need to define two zones. There is no naming convention, best practice is to either use `master`, `satellite`/`client-fqdn` or to choose region names for example `Europe`, `USA` and `Asia`, though. - -**Note**: Each client requires its own zone and endpoint configuration. Best practice -is to use the client's FQDN for all object names. - -The `master` zone is a parent of the `icinga2-client1.localdomain` zone: - - [root@icinga2-client1.localdomain /]# vim /etc/icinga2/zones.conf - - object Zone "master" { - endpoints = [ "icinga2-master1.localdomain" ] //array with endpoint names - } - - object Zone "icinga2-client1.localdomain" { - endpoints = [ "icinga2-client1.localdomain" ] - - parent = "master" //establish zone hierarchy - } - -In addition, add a [global zone](06-distributed-monitoring.md#distributed-monitoring-global-zone-config-sync) -for syncing check commands later: - ``` -[root@icinga2-client1.localdomain /]# vim /etc/icinga2/zones.conf +[root@icinga2-agent1.localdomain /]# vim /etc/icinga2/zones.conf -object Zone "global-templates" { - global = true +object Endpoint "icinga2-master1.localdomain" { + host = "192.168.56.101" +} + +object Endpoint "icinga2-agent1.localdomain" { + host = "192.168.56.111" + log_duration = 0 // Disable the replay log for command endpoint agents } ``` -> **Note:** -> -> Packages >= 2.8 provide this configuration by default. +Next, you need to define two zones. There is no naming convention, best practice is to either use `master`, `satellite`/`agent-fqdn` or to choose region names for example `Europe`, `USA` and `Asia`, though. -You don't need any local configuration on the client except for +**Note**: Each agent requires its own zone and endpoint configuration. Best practice +is to use the agent's FQDN for all object names. + +The `master` zone is a parent of the `icinga2-agent1.localdomain` zone: + +``` +[root@icinga2-agent1.localdomain /]# vim /etc/icinga2/zones.conf + +object Zone "master" { + endpoints = [ "icinga2-master1.localdomain" ] //array with endpoint names +} + +object Zone "icinga2-agent1.localdomain" { + endpoints = [ "icinga2-agent1.localdomain" ] + + parent = "master" //establish zone hierarchy +} +``` + +You don't need any local configuration on the agent except for CheckCommand definitions which can be synced using the global zone above. Therefore disable the inclusion of the `conf.d` directory in `/etc/icinga2/icinga2.conf`. ``` -[root@icinga2-client1.localdomain /]# vim /etc/icinga2/icinga2.conf +[root@icinga2-agent1.localdomain /]# vim /etc/icinga2/icinga2.conf -// Commented out, not required on a client as command endpoint +// Commented out, not required on an agent as command endpoint //include_recursive "conf.d" ``` @@ -980,93 +1062,102 @@ in `/etc/icinga2/icinga2.conf`. > Packages >= 2.9 provide an option in the setup wizard to disable this. > Defaults to disabled. -Edit the `api` feature on the client `icinga2-client1.localdomain` in -the `/etc/icinga2/features-enabled/api.conf` file and make sure to set -`accept_commands` and `accept_config` to `true`: - - [root@icinga2-client1.localdomain /]# vim /etc/icinga2/features-enabled/api.conf - - object ApiListener "api" { - //... - accept_commands = true - accept_config = true - } - Now it is time to validate the configuration and to restart the Icinga 2 daemon on both nodes. Example on CentOS 7: - [root@icinga2-client1.localdomain /]# icinga2 daemon -C - [root@icinga2-client1.localdomain /]# systemctl restart icinga2 +``` +[root@icinga2-agent1.localdomain /]# icinga2 daemon -C +[root@icinga2-agent1.localdomain /]# systemctl restart icinga2 - [root@icinga2-master1.localdomain /]# icinga2 daemon -C - [root@icinga2-master1.localdomain /]# systemctl restart icinga2 +[root@icinga2-master1.localdomain /]# icinga2 daemon -C +[root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` -Once the clients have successfully connected, you are ready for the next step: **execute -a remote check on the client using the command endpoint**. +Once the agents have successfully connected, you are ready for the next step: **execute +a remote check on the agent using the command endpoint**. Include the host and service object configuration in the `master` zone -- this will help adding a secondary master for high-availability later. - [root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/master +``` +[root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/master +``` Add the host and service objects you want to monitor. There is no limitation for files and directories -- best practice is to sort things by type. -By convention a master/satellite/client host object should use the same name as the endpoint object. -You can also add multiple hosts which execute checks against remote services/clients. +By convention a master/satellite/agent host object should use the same name as the endpoint object. +You can also add multiple hosts which execute checks against remote services/agents. - [root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf +The following example adds the `agent_endpoint` custom variable to the +host and stores its name (FQDN). _Versions older than 2.11 +used the `client_endpoint` custom variable._ - object Host "icinga2-client1.localdomain" { - check_command = "hostalive" //check is executed on the master - address = "192.168.56.111" +This custom variable serves two purposes: 1) Service apply rules can match against it. +2) Apply rules can retrieve its value and assign it to the `command_endpoint` attribute. - vars.client_endpoint = name //follows the convention that host name == endpoint name - } +``` +[root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf -Given that you are monitoring a Linux client, we'll add a remote [disk](10-icinga-template-library.md#plugin-check-command-disk) +object Host "icinga2-agent1.localdomain" { + check_command = "hostalive" //check is executed on the master + address = "192.168.56.111" + + vars.agent_endpoint = name //follows the convention that host name == endpoint name +} +``` + +Given that you are monitoring a Linux agent, add a remote [disk](10-icinga-template-library.md#plugin-check-command-disk) check. - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf - apply Service "disk" { - check_command = "disk" +apply Service "disk" { + check_command = "disk" - //specify where the check is executed - command_endpoint = host.vars.client_endpoint + // Specify the remote agent as command execution endpoint, fetch the host custom variable + command_endpoint = host.vars.agent_endpoint - assign where host.vars.client_endpoint - } + // Only assign where a host is marked as agent endpoint + assign where host.vars.agent_endpoint +} +``` If you have your own custom `CheckCommand` definition, add it to the global zone: - [root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/global-templates - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/global-templates/commands.conf +``` +[root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/global-templates +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/global-templates/commands.conf - object CheckCommand "my-cmd" { - //... - } +object CheckCommand "my-cmd" { + //... +} +``` Save the changes and validate the configuration on the master node: - [root@icinga2-master1.localdomain /]# icinga2 daemon -C - +``` +[root@icinga2-master1.localdomain /]# icinga2 daemon -C +``` Restart the Icinga 2 daemon (example for CentOS 7): - [root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` +[root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` The following steps will happen: * Icinga 2 validates the configuration on `icinga2-master1.localdomain` and restarts. * The `icinga2-master1.localdomain` node schedules and executes the checks. -* The `icinga2-client1.localdomain` node receives the execute command event with additional command parameters. -* The `icinga2-client1.localdomain` node maps the command parameters to the local check command, executes the check locally, and sends back the check result message. +* The `icinga2-agent1.localdomain` node receives the execute command event with additional command parameters. +* The `icinga2-agent1.localdomain` node maps the command parameters to the local check command, executes the check locally, and sends back the check result message. -As you can see, no interaction from your side is required on the client itself, and it's not necessary to reload the Icinga 2 service on the client. +As you can see, no interaction from your side is required on the agent itself, and it's not necessary to reload the Icinga 2 service on the agent. You have learned the basics about command endpoint checks. Proceed with the [scenarios](06-distributed-monitoring.md#distributed-monitoring-scenarios) @@ -1080,7 +1171,7 @@ It comes in handy if you want to configure everything on the master node and sync the satellite checks (disk, memory, etc.). The satellites run their own local scheduler and will send the check result messages back to the master. -![Icinga 2 Distributed Top Down Config Sync](images/distributed-monitoring/icinga2_distributed_top_down_config_sync.png) +![Icinga 2 Distributed Top Down Config Sync](images/distributed-monitoring/icinga2_distributed_monitoring_satellite_config_sync.png) Advantages: @@ -1096,126 +1187,151 @@ Disadvantages: * Additional zone and endpoint configuration needed. * Replay log is replicated on reconnect after connection loss. This might increase the data transfer and create an overload on the connection. +> **Note** +> +> This mode only supports **configuration text files** for Icinga. Do not abuse +> this for syncing binaries, this is not supported and may harm your production +> environment. The config sync uses checksums to detect changes, binaries may +> trigger reload loops. +> +> This is a fair warning. If you want to deploy plugin binaries, create +> packages for dependency management and use infrastructure lifecycle tools +> such as Foreman, Puppet, Ansible, etc. + To make sure that all involved nodes accept configuration and/or commands, you need to configure the `Zone` and `Endpoint` hierarchy on all nodes. * `icinga2-master1.localdomain` is the configuration master in this scenario. -* `icinga2-client2.localdomain` acts as client which receives configuration from the master. Checks are scheduled locally. +* `icinga2-satellite1.localdomain` acts as satellite which receives configuration from the master. Checks are scheduled locally. Include the endpoint and zone configuration on **both** nodes in the file `/etc/icinga2/zones.conf`. The endpoint configuration could look like this: - [root@icinga2-client2.localdomain /]# vim /etc/icinga2/zones.conf +``` +[root@icinga2-satellite1.localdomain /]# vim /etc/icinga2/zones.conf - object Endpoint "icinga2-master1.localdomain" { - host = "192.168.56.101" - } +object Endpoint "icinga2-master1.localdomain" { + host = "192.168.56.101" +} - object Endpoint "icinga2-client2.localdomain" { - host = "192.168.56.112" - } +object Endpoint "icinga2-satellite1.localdomain" { + host = "192.168.56.105" +} +``` -Next, you need to define two zones. There is no naming convention, best practice is to either use `master`, `satellite`/`client-fqdn` or to choose region names for example `Europe`, `USA` and `Asia`, though. +Next, you need to define two zones. There is no naming convention, best practice is to either use `master`, `satellite`/`agent-fqdn` or to choose region names for example `Europe`, `USA` and `Asia`, though. -**Note**: Each client requires its own zone and endpoint configuration. Best practice -is to use the client's FQDN for all object names. +The `master` zone is a parent of the `satellite` zone: -The `master` zone is a parent of the `icinga2-client2.localdomain` zone: +``` +[root@icinga2-agent2.localdomain /]# vim /etc/icinga2/zones.conf - [root@icinga2-client2.localdomain /]# vim /etc/icinga2/zones.conf +object Zone "master" { + endpoints = [ "icinga2-master1.localdomain" ] //array with endpoint names +} - object Zone "master" { - endpoints = [ "icinga2-master1.localdomain" ] //array with endpoint names - } +object Zone "satellite" { + endpoints = [ "icinga2-satellite1.localdomain" ] - object Zone "icinga2-client2.localdomain" { - endpoints = [ "icinga2-client2.localdomain" ] + parent = "master" //establish zone hierarchy +} +``` - parent = "master" //establish zone hierarchy - } - -Edit the `api` feature on the client `icinga2-client2.localdomain` in +Edit the `api` feature on the satellite `icinga2-satellite1.localdomain` in the `/etc/icinga2/features-enabled/api.conf` file and set `accept_config` to `true`. - [root@icinga2-client2.localdomain /]# vim /etc/icinga2/features-enabled/api.conf +``` +[root@icinga2-satellite1.localdomain /]# vim /etc/icinga2/features-enabled/api.conf - object ApiListener "api" { - //... - accept_config = true - } +object ApiListener "api" { + //... + accept_config = true +} +``` Now it is time to validate the configuration and to restart the Icinga 2 daemon on both nodes. Example on CentOS 7: - [root@icinga2-client2.localdomain /]# icinga2 daemon -C - [root@icinga2-client2.localdomain /]# systemctl restart icinga2 - - [root@icinga2-master1.localdomain /]# icinga2 daemon -C - [root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` +[root@icinga2-satellite1.localdomain /]# icinga2 daemon -C +[root@icinga2-satellite1.localdomain /]# systemctl restart icinga2 +[root@icinga2-master1.localdomain /]# icinga2 daemon -C +[root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` **Tip**: Best practice is to use a [global zone](06-distributed-monitoring.md#distributed-monitoring-global-zone-config-sync) for common configuration items (check commands, templates, groups, etc.). -Once the clients have connected successfully, it's time for the next step: **execute -a local check on the client using the configuration sync**. +Once the satellite(s) have connected successfully, it's time for the next step: **execute +a local check on the satellite using the configuration sync**. Navigate to `/etc/icinga2/zones.d` on your master node `icinga2-master1.localdomain` and create a new directory with the same -name as your satellite/client zone name: +name as your satellite/agent zone name: - [root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/icinga2-client2.localdomain +``` +[root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/satellite +``` Add the host and service objects you want to monitor. There is no limitation for files and directories -- best practice is to sort things by type. -By convention a master/satellite/client host object should use the same name as the endpoint object. -You can also add multiple hosts which execute checks against remote services/clients. +By convention a master/satellite/agent host object should use the same name as the endpoint object. +You can also add multiple hosts which execute checks against remote services/agents via [command endpoint](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint) +checks. - [root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/icinga2-client2.localdomain - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/icinga2-client2.localdomain]# vim hosts.conf +``` +[root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/satellite +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim hosts.conf - object Host "icinga2-client2.localdomain" { - check_command = "hostalive" - address = "192.168.56.112" - zone = "master" //optional trick: sync the required host object to the client, but enforce the "master" zone to execute the check - } +object Host "icinga2-satellite1.localdomain" { + check_command = "hostalive" + address = "192.168.56.112" + zone = "master" //optional trick: sync the required host object to the satellite, but enforce the "master" zone to execute the check +} +``` -Given that you are monitoring a Linux client we'll just add a local [disk](10-icinga-template-library.md#plugin-check-command-disk) +Given that you are monitoring a Linux satellite add a local [disk](10-icinga-template-library.md#plugin-check-command-disk) check. - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/icinga2-client2.localdomain]# vim services.conf +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim services.conf - object Service "disk" { - host_name = "icinga2-client2.localdomain" +object Service "disk" { + host_name = "icinga2-satellite1.localdomain" - check_command = "disk" - } + check_command = "disk" +} +``` Save the changes and validate the configuration on the master node: - [root@icinga2-master1.localdomain /]# icinga2 daemon -C +``` +[root@icinga2-master1.localdomain /]# icinga2 daemon -C +``` Restart the Icinga 2 daemon (example for CentOS 7): - [root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` +[root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` The following steps will happen: * Icinga 2 validates the configuration on `icinga2-master1.localdomain`. * Icinga 2 copies the configuration into its zone config store in `/var/lib/icinga2/api/zones`. * The `icinga2-master1.localdomain` node sends a config update event to all endpoints in the same or direct child zones. -* The `icinga2-client2.localdomain` node accepts config and populates the local zone config store with the received config files. -* The `icinga2-client2.localdomain` node validates the configuration and automatically restarts. +* The `icinga2-satellite1.localdomain` node accepts config and populates the local zone config store with the received config files. +* The `icinga2-satellite1.localdomain` node validates the configuration and automatically restarts. -Again, there is no interaction required on the client -itself. +Again, there is no interaction required on the satellite itself. You can also use the config sync inside a high-availability zone to ensure that all config objects are synced among zone members. @@ -1232,185 +1348,258 @@ section where you can find detailed information on extending the setup. If you are eager to start fresh instead you might take a look into the -[Icinga Director](https://github.com/icinga/icingaweb2-module-director). +[Icinga Director](https://icinga.com/docs/director/latest/). ## Scenarios The following examples should give you an idea on how to build your own distributed monitoring environment. We've seen them all in production -environments and received feedback from our [community](https://www.icinga.com/community/get-involved/) -and [partner support](https://www.icinga.com/services/support/) channels: +environments and received feedback from our [community](https://community.icinga.com/) +and [partner support](https://icinga.com/support/) channels: -* Single master with clients. -* HA master with clients as command endpoint. -* Three level cluster with config HA masters, satellites receiving config sync, and clients checked using command endpoint. +* [Single master with agents](06-distributed-monitoring.md#distributed-monitoring-master-agents). +* [HA master with agents as command endpoint](06-distributed-monitoring.md#distributed-monitoring-scenarios-ha-master-agents) +* [Three level cluster](06-distributed-monitoring.md#distributed-monitoring-scenarios-master-satellite-agents) with config HA masters, satellites receiving config sync, and agents checked using command endpoint. -### Master with Clients +You can also extend the cluster tree depth to four levels e.g. with 2 satellite levels. +Just keep in mind that multiple levels become harder to debug in case of errors. -![Icinga 2 Distributed Master with Clients](images/distributed-monitoring/icinga2_distributed_scenarios_master_clients.png) +You can also start with a single master setup, and later add a secondary +master endpoint. This requires an extra step with the [initial sync](06-distributed-monitoring.md#distributed-monitoring-advanced-hints-initial-sync) +for cloning the runtime state. This is described in detail [here](06-distributed-monitoring.md#distributed-monitoring-scenarios-ha-master-agents). + + + + +### Master with Agents + +In this scenario, a single master node runs the check scheduler, notifications +and IDO database backend and uses the [command endpoint mode](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint) +to execute checks on the remote agents. + +![Icinga 2 Distributed Master with Agents](images/distributed-monitoring/icinga2_distributed_monitoring_scenarios_master_with_agents.png) * `icinga2-master1.localdomain` is the primary master node. -* `icinga2-client1.localdomain` and `icinga2-client2.localdomain` are two child nodes as clients. +* `icinga2-agent1.localdomain` and `icinga2-agent2.localdomain` are two child nodes as agents. Setup requirements: * Set up `icinga2-master1.localdomain` as [master](06-distributed-monitoring.md#distributed-monitoring-setup-master). -* Set up `icinga2-client1.localdomain` and `icinga2-client2.localdomain` as [client](06-distributed-monitoring.md#distributed-monitoring-setup-satellite-client). +* Set up `icinga2-agent1.localdomain` and `icinga2-agent2.localdomain` as [agent](06-distributed-monitoring.md#distributed-monitoring-setup-agent-satellite). Edit the `zones.conf` configuration file on the master: - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf - object Endpoint "icinga2-master1.localdomain" { - } +object Endpoint "icinga2-master1.localdomain" { + // That's us +} - object Endpoint "icinga2-client1.localdomain" { - host = "192.168.56.111" //the master actively tries to connect to the client - } +object Endpoint "icinga2-agent1.localdomain" { + host = "192.168.56.111" // The master actively tries to connect to the agent + log_duration = 0 // Disable the replay log for command endpoint agents +} - object Endpoint "icinga2-client2.localdomain" { - host = "192.168.56.112" //the master actively tries to connect to the client - } +object Endpoint "icinga2-agent2.localdomain" { + host = "192.168.56.112" // The master actively tries to connect to the agent + log_duration = 0 // Disable the replay log for command endpoint agents +} - object Zone "master" { - endpoints = [ "icinga2-master1.localdomain" ] - } +object Zone "master" { + endpoints = [ "icinga2-master1.localdomain" ] +} - object Zone "icinga2-client1.localdomain" { - endpoints = [ "icinga2-client1.localdomain" ] +object Zone "icinga2-agent1.localdomain" { + endpoints = [ "icinga2-agent1.localdomain" ] - parent = "master" - } + parent = "master" +} - object Zone "icinga2-client2.localdomain" { - endpoints = [ "icinga2-client2.localdomain" ] +object Zone "icinga2-agent2.localdomain" { + endpoints = [ "icinga2-agent2.localdomain" ] - parent = "master" - } + parent = "master" +} - /* sync global commands */ - object Zone "global-templates" { - global = true - } +/* sync global commands */ +object Zone "global-templates" { + global = true +} +object Zone "director-global" { + global = true +} +``` -The two client nodes do not necessarily need to know about each other. The only important thing +The two agent nodes do not need to know about each other. The only important thing is that they know about the parent zone and their endpoint members (and optionally the global zone). If you specify the `host` attribute in the `icinga2-master1.localdomain` endpoint object, -the client will actively try to connect to the master node. Since we've specified the client -endpoint's attribute on the master node already, we don't want the clients to connect to the +the agent will actively try to connect to the master node. Since you've specified the agent +endpoint's attribute on the master node already, you don't want the agents to connect to the master. **Choose one [connection direction](06-distributed-monitoring.md#distributed-monitoring-advanced-hints-connection-direction).** - [root@icinga2-client1.localdomain /]# vim /etc/icinga2/zones.conf +``` +[root@icinga2-agent1.localdomain /]# vim /etc/icinga2/zones.conf - object Endpoint "icinga2-master1.localdomain" { - //do not actively connect to the master by leaving out the 'host' attribute - } +object Endpoint "icinga2-master1.localdomain" { + // Do not actively connect to the master by leaving out the 'host' attribute +} - object Endpoint "icinga2-client1.localdomain" { - } +object Endpoint "icinga2-agent1.localdomain" { + // That's us +} - object Zone "master" { - endpoints = [ "icinga2-master1.localdomain" ] - } +object Zone "master" { + endpoints = [ "icinga2-master1.localdomain" ] +} - object Zone "icinga2-client1.localdomain" { - endpoints = [ "icinga2-client1.localdomain" ] +object Zone "icinga2-agent1.localdomain" { + endpoints = [ "icinga2-agent1.localdomain" ] - parent = "master" - } + parent = "master" +} - /* sync global commands */ - object Zone "global-templates" { - global = true - } +/* sync global commands */ +object Zone "global-templates" { + global = true +} +object Zone "director-global" { + global = true +} +``` +``` +[root@icinga2-agent2.localdomain /]# vim /etc/icinga2/zones.conf - [root@icinga2-client2.localdomain /]# vim /etc/icinga2/zones.conf +object Endpoint "icinga2-master1.localdomain" { + // Do not actively connect to the master by leaving out the 'host' attribute +} - object Endpoint "icinga2-master1.localdomain" { - //do not actively connect to the master by leaving out the 'host' attribute - } +object Endpoint "icinga2-agent2.localdomain" { + // That's us +} - object Endpoint "icinga2-client2.localdomain" { - } +object Zone "master" { + endpoints = [ "icinga2-master1.localdomain" ] +} - object Zone "master" { - endpoints = [ "icinga2-master1.localdomain" ] - } +object Zone "icinga2-agent2.localdomain" { + endpoints = [ "icinga2-agent2.localdomain" ] - object Zone "icinga2-client2.localdomain" { - endpoints = [ "icinga2-client2.localdomain" ] + parent = "master" +} - parent = "master" - } +/* sync global commands */ +object Zone "global-templates" { + global = true +} +object Zone "director-global" { + global = true +} +``` - /* sync global commands */ - object Zone "global-templates" { - global = true - } - -Now it is time to define the two client hosts and apply service checks using +Now it is time to define the two agent hosts and apply service checks using the command endpoint execution method on them. Note: You can also use the config sync mode here. Create a new configuration directory on the master node: - [root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/master +``` +[root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/master +``` -Add the two client nodes as host objects: +Add the two agent nodes as host objects: - [root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf +``` +[root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf - object Host "icinga2-client1.localdomain" { - check_command = "hostalive" - address = "192.168.56.111" - vars.client_endpoint = name //follows the convention that host name == endpoint name - } +object Host "icinga2-agent1.localdomain" { + check_command = "hostalive" + address = "192.168.56.111" - object Host "icinga2-client2.localdomain" { - check_command = "hostalive" - address = "192.168.56.112" - vars.client_endpoint = name //follows the convention that host name == endpoint name - } + vars.agent_endpoint = name //follows the convention that host name == endpoint name +} + +object Host "icinga2-agent2.localdomain" { + check_command = "hostalive" + address = "192.168.56.112" + + vars.agent_endpoint = name //follows the convention that host name == endpoint name +} +``` Add services using command endpoint checks: - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf - apply Service "ping4" { - check_command = "ping4" - //check is executed on the master node - assign where host.address - } +apply Service "ping4" { + check_command = "ping4" - apply Service "disk" { - check_command = "disk" + //check is executed on the master node + assign where host.address +} - //specify where the check is executed - command_endpoint = host.vars.client_endpoint +apply Service "disk" { + check_command = "disk" - assign where host.vars.client_endpoint - } + // Execute the check on the remote command endpoint + command_endpoint = host.vars.agent_endpoint + + // Assign the service onto an agent + assign where host.vars.agent_endpoint +} +``` Validate the configuration and restart Icinga 2 on the master node `icinga2-master1.localdomain`. - [root@icinga2-master1.localdomain /]# icinga2 daemon -C - [root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` +[root@icinga2-master1.localdomain /]# icinga2 daemon -C +[root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` -Open Icinga Web 2 and check the two newly created client hosts with two new services +Open Icinga Web 2 and check the two newly created agent hosts with two new services -- one executed locally (`ping4`) and one using command endpoint (`disk`). -### High-Availability Master with Clients +> **Note** +> +> You don't necessarily need to add the agent endpoint/zone configuration objects +> into the master's zones.conf file. Instead, you can put them into `/etc/icinga2/zones.d/master` +> either in `hosts.conf` shown above, or in a new file called `agents.conf`. -![Icinga 2 Distributed High Availability Master with Clients](images/distributed-monitoring/icinga2_distributed_scenarios_ha_master_clients.png) +> **Tip**: +> +> It's a good idea to add [health checks](06-distributed-monitoring.md#distributed-monitoring-health-checks) +to make sure that your cluster notifies you in case of failure. -This scenario is similar to the one in the [previous section](06-distributed-monitoring.md#distributed-monitoring-master-clients). The only difference is that we will now set up two master nodes in a high-availability setup. +In terms of health checks, consider adding the following for this scenario: + +- Master node(s) check the connection to the agents +- Optional: Add dependencies for the agent host to prevent unwanted notifications when agents are unreachable + +Proceed in [this chapter](06-distributed-monitoring.md#distributed-monitoring-health-checks-master-agents). + + + + +### High-Availability Master with Agents + +This scenario is similar to the one in the [previous section](06-distributed-monitoring.md#distributed-monitoring-master-agents). The only difference is that we will now set up two master nodes in a high-availability setup. These nodes must be configured as zone and endpoints objects. +![Icinga 2 Distributed High Availability Master with Agents](images/distributed-monitoring/icinga2_distributed_monitoring_scenario_ha_masters_with_agents.png) + The setup uses the capabilities of the Icinga 2 cluster. All zone members -replicate cluster events amongst each other. In addition to that, several Icinga 2 -features can enable HA functionality. +replicate cluster events between each other. In addition to that, several Icinga 2 +features can enable [HA functionality](06-distributed-monitoring.md#distributed-monitoring-high-availability-features). + +Best practice is to run the database backend on a dedicated server/cluster and +only expose a virtual IP address to Icinga and the IDO feature. By default, only one +endpoint will actively write to the backend then. Typical setups for MySQL clusters +involve Master-Master-Replication (Master-Slave-Replication in both directions) or Galera, +more tips can be found on our [community forums](https://community.icinga.com/). +The IDO object must have the same `instance_name` on all master nodes. **Note**: All nodes in the same zone require that you enable the same features for high-availability (HA). @@ -1418,16 +1607,16 @@ Overview: * `icinga2-master1.localdomain` is the config master master node. * `icinga2-master2.localdomain` is the secondary master master node without config in `zones.d`. -* `icinga2-client1.localdomain` and `icinga2-client2.localdomain` are two child nodes as clients. +* `icinga2-agent1.localdomain` and `icinga2-agent2.localdomain` are two child nodes as agents. Setup requirements: * Set up `icinga2-master1.localdomain` as [master](06-distributed-monitoring.md#distributed-monitoring-setup-master). -* Set up `icinga2-master2.localdomain` as [client](06-distributed-monitoring.md#distributed-monitoring-setup-satellite-client) (we will modify the generated configuration). -* Set up `icinga2-client1.localdomain` and `icinga2-client2.localdomain` as [clients](06-distributed-monitoring.md#distributed-monitoring-setup-satellite-client) (when asked for adding multiple masters, set to `y` and add the secondary master `icinga2-master2.localdomain`). +* Set up `icinga2-master2.localdomain` as [satellite](06-distributed-monitoring.md#distributed-monitoring-setup-agent-satellite) (**we will modify the generated configuration**). +* Set up `icinga2-agent1.localdomain` and `icinga2-agent2.localdomain` as [agents](06-distributed-monitoring.md#distributed-monitoring-setup-agent-satellite) (when asked for adding multiple masters, set to `y` and add the secondary master `icinga2-master2.localdomain`). In case you don't want to use the CLI commands, you can also manually create and sync the -required SSL certificates. We will modify and discuss all the details of the automatically generated configuration here. +required TLS certificates. We will modify and discuss all the details of the automatically generated configuration here. Since there are now two nodes in the same zone, we must consider the [high-availability features](06-distributed-monitoring.md#distributed-monitoring-high-availability-features). @@ -1441,445 +1630,791 @@ you can disable the HA feature and write to a local database on each node. Both methods require that you configure Icinga Web 2 accordingly (monitoring backend, IDO database, used transports, etc.). -The zone hierarchy could look like this. It involves putting the two master nodes -`icinga2-master1.localdomain` and `icinga2-master2.localdomain` into the `master` zone. +> **Note** +> +> You can also start with a single master shown [here](06-distributed-monitoring.md#distributed-monitoring-master-agents) and later add +> the second master. This requires an extra step with the [initial sync](06-distributed-monitoring.md#distributed-monitoring-advanced-hints-initial-sync) +> for cloning the runtime state after done. Once done, proceed here. - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf +In this scenario, we are not adding the agent configuration immediately +to the `zones.conf` file but will establish the hierarchy later. - object Endpoint "icinga2-master1.localdomain" { - host = "192.168.56.101" - } +The first master looks like this: - object Endpoint "icinga2-master2.localdomain" { - host = "192.168.56.102" - } +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf - object Endpoint "icinga2-client1.localdomain" { - host = "192.168.56.111" //the master actively tries to connect to the client - } +object Endpoint "icinga2-master1.localdomain" { + // That's us +} - object Endpoint "icinga2-client2.localdomain" { - host = "192.168.56.112" //the master actively tries to connect to the client - } +object Endpoint "icinga2-master2.localdomain" { + host = "192.168.56.102" // Actively connect to the secondary master +} - object Zone "master" { - endpoints = [ "icinga2-master1.localdomain", "icinga2-master2.localdomain" ] - } +object Zone "master" { + endpoints = [ "icinga2-master1.localdomain", "icinga2-master2.localdomain" ] +} - object Zone "icinga2-client1.localdomain" { - endpoints = [ "icinga2-client1.localdomain" ] +/* sync global commands */ +object Zone "global-templates" { + global = true +} +object Zone "director-global" { + global = true +} +``` - parent = "master" - } +The secondary master waits for connection attempts from the first master, +and therefore does not try to connect to it again. - object Zone "icinga2-client2.localdomain" { - endpoints = [ "icinga2-client2.localdomain" ] +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf - parent = "master" - } +object Endpoint "icinga2-master1.localdomain" { + // That's us +} - /* sync global commands */ - object Zone "global-templates" { - global = true - } +object Endpoint "icinga2-master2.localdomain" { + // The first master already connects to us +} -The two client nodes do not necessarily need to know about each other. The only important thing +object Zone "master" { + endpoints = [ "icinga2-master1.localdomain", "icinga2-master2.localdomain" ] +} + +/* sync global commands */ +object Zone "global-templates" { + global = true +} +object Zone "director-global" { + global = true +} +``` + +Restart both masters and ensure the initial connection and TLS handshake works. + +The two agent nodes do not need to know about each other. The only important thing is that they know about the parent zone and their endpoint members (and optionally about the global zone). If you specify the `host` attribute in the `icinga2-master1.localdomain` and `icinga2-master2.localdomain` -endpoint objects, the client will actively try to connect to the master node. Since we've specified the client -endpoint's attribute on the master node already, we don't want the clients to connect to the +endpoint objects, the agent will actively try to connect to the master node. Since we've specified the agent +endpoint's attribute on the master node already, we don't want the agent to connect to the master nodes. **Choose one [connection direction](06-distributed-monitoring.md#distributed-monitoring-advanced-hints-connection-direction).** - [root@icinga2-client1.localdomain /]# vim /etc/icinga2/zones.conf +``` +[root@icinga2-agent1.localdomain /]# vim /etc/icinga2/zones.conf - object Endpoint "icinga2-master1.localdomain" { - //do not actively connect to the master by leaving out the 'host' attribute - } +object Endpoint "icinga2-master1.localdomain" { + // Do not actively connect to the master by leaving out the 'host' attribute +} - object Endpoint "icinga2-master2.localdomain" { - //do not actively connect to the master by leaving out the 'host' attribute - } +object Endpoint "icinga2-master2.localdomain" { + // Do not actively connect to the master by leaving out the 'host' attribute +} - object Endpoint "icinga2-client1.localdomain" { - } +object Endpoint "icinga2-agent1.localdomain" { + // That's us +} - object Zone "master" { - endpoints = [ "icinga2-master1.localdomain", "icinga2-master2.localdomain" ] - } +object Zone "master" { + endpoints = [ "icinga2-master1.localdomain", "icinga2-master2.localdomain" ] +} - object Zone "icinga2-client1.localdomain" { - endpoints = [ "icinga2-client1.localdomain" ] +object Zone "icinga2-agent1.localdomain" { + endpoints = [ "icinga2-agent1.localdomain" ] - parent = "master" - } + parent = "master" +} - /* sync global commands */ - object Zone "global-templates" { - global = true - } +/* sync global commands */ +object Zone "global-templates" { + global = true +} +object Zone "director-global" { + global = true +} - [root@icinga2-client2.localdomain /]# vim /etc/icinga2/zones.conf +``` - object Endpoint "icinga2-master1.localdomain" { - //do not actively connect to the master by leaving out the 'host' attribute - } +``` +[root@icinga2-agent2.localdomain /]# vim /etc/icinga2/zones.conf - object Endpoint "icinga2-master2.localdomain" { - //do not actively connect to the master by leaving out the 'host' attribute - } +object Endpoint "icinga2-master1.localdomain" { + // Do not actively connect to the master by leaving out the 'host' attribute +} - object Endpoint "icinga2-client2.localdomain" { - } +object Endpoint "icinga2-master2.localdomain" { + // Do not actively connect to the master by leaving out the 'host' attribute +} - object Zone "master" { - endpoints = [ "icinga2-master1.localdomain", "icinga2-master2.localdomain" ] - } +object Endpoint "icinga2-agent2.localdomain" { + //That's us +} - object Zone "icinga2-client2.localdomain" { - endpoints = [ "icinga2-client2.localdomain" ] +object Zone "master" { + endpoints = [ "icinga2-master1.localdomain", "icinga2-master2.localdomain" ] +} - parent = "master" - } +object Zone "icinga2-agent2.localdomain" { + endpoints = [ "icinga2-agent2.localdomain" ] - /* sync global commands */ - object Zone "global-templates" { - global = true - } + parent = "master" +} -Now it is time to define the two client hosts and apply service checks using -the command endpoint execution method. Note: You can also use the -config sync mode here. +/* sync global commands */ +object Zone "global-templates" { + global = true +} +object Zone "director-global" { + global = true +} +``` + +Now it is time to define the two agent hosts and apply service checks using +the command endpoint execution method. Create a new configuration directory on the master node `icinga2-master1.localdomain`. **Note**: The secondary master node `icinga2-master2.localdomain` receives the configuration using the [config sync mode](06-distributed-monitoring.md#distributed-monitoring-top-down-config-sync). - [root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/master +``` +[root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/master +``` -Add the two client nodes as host objects: +Add the two agent nodes with their zone/endpoint and host object configuration. - [root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf +> **Note** +> +> In order to keep things in sync between the two HA masters, +> keep the `zones.conf` file as small as possible. +> +> You can create the agent zone and endpoint objects inside the +> master zone and have them synced to the secondary master. +> The cluster config sync enforces a reload allowing the secondary +> master to connect to the agents as well. - object Host "icinga2-client1.localdomain" { - check_command = "hostalive" - address = "192.168.56.111" - vars.client_endpoint = name //follows the convention that host name == endpoint name - } +Edit the `zones.conf` file and ensure that the agent zone/endpoint objects +are **not** specified in there. - object Host "icinga2-client2.localdomain" { - check_command = "hostalive" - address = "192.168.56.112" - vars.client_endpoint = name //follows the convention that host name == endpoint name - } +Then navigate into `/etc/icinga2/zones.d/master` and create a new file `agents.conf`. + +``` +[root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim agents.conf + +//----------------------------------------------- +// Endpoints + +object Endpoint "icinga2-agent1.localdomain" { + host = "192.168.56.111" // The master actively tries to connect to the agent + log_duration = 0 // Disable the replay log for command endpoint agents +} + +object Endpoint "icinga2-agent2.localdomain" { + host = "192.168.56.112" // The master actively tries to connect to the agent + log_duration = 0 // Disable the replay log for command endpoint agents +} + +//----------------------------------------------- +// Zones + +object Zone "icinga2-agent1.localdomain" { + endpoints = [ "icinga2-agent1.localdomain" ] + + parent = "master" +} + +object Zone "icinga2-agent2.localdomain" { + endpoints = [ "icinga2-agent2.localdomain" ] + + parent = "master" +} +``` + +Whenever you need to add an agent again, edit the mentioned files. + +Next, create the corresponding host objects for the agents. Use the same names +for host and endpoint objects. + +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf + +object Host "icinga2-agent1.localdomain" { + check_command = "hostalive" + address = "192.168.56.111" + vars.agent_endpoint = name //follows the convention that host name == endpoint name +} + +object Host "icinga2-agent2.localdomain" { + check_command = "hostalive" + address = "192.168.56.112" + vars.agent_endpoint = name //follows the convention that host name == endpoint name +} +``` Add services using command endpoint checks: - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf - apply Service "ping4" { - check_command = "ping4" - //check is executed on the master node - assign where host.address - } +apply Service "ping4" { + check_command = "ping4" - apply Service "disk" { - check_command = "disk" + // Check is executed on the master node + assign where host.address +} - //specify where the check is executed - command_endpoint = host.vars.client_endpoint +apply Service "disk" { + check_command = "disk" - assign where host.vars.client_endpoint - } + // Check is executed on the remote command endpoint + command_endpoint = host.vars.agent_endpoint + + assign where host.vars.agent_endpoint +} +``` Validate the configuration and restart Icinga 2 on the master node `icinga2-master1.localdomain`. - [root@icinga2-master1.localdomain /]# icinga2 daemon -C - [root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` +[root@icinga2-master1.localdomain /]# icinga2 daemon -C +[root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` -Open Icinga Web 2 and check the two newly created client hosts with two new services +Open Icinga Web 2 and check the two newly created agent hosts with two new services -- one executed locally (`ping4`) and one using command endpoint (`disk`). -**Tip**: It's a good idea to add [health checks](06-distributed-monitoring.md#distributed-monitoring-health-checks) +> **Tip**: +> +> It's a good idea to add [health checks](06-distributed-monitoring.md#distributed-monitoring-health-checks) to make sure that your cluster notifies you in case of failure. +In terms of health checks, consider adding the following for this scenario: -### Three Levels with Master, Satellites, and Clients +- Master node(s) check the connection to the agents +- Optional: Add dependencies for the agent host to prevent unwanted notifications when agents are unreachable -![Icinga 2 Distributed Master and Satellites with Clients](images/distributed-monitoring/icinga2_distributed_scenarios_master_satellite_client.png) +Proceed in [this chapter](06-distributed-monitoring.md#distributed-monitoring-health-checks-master-agents). + + + + +### Three Levels with Masters, Satellites and Agents This scenario combines everything you've learned so far: High-availability masters, -satellites receiving their configuration from the master zone, and clients checked via command +satellites receiving their configuration from the master zone, and agents checked via command endpoint from the satellite zones. -**Tip**: It can get complicated, so grab a pen and paper and bring your thoughts to life. -Play around with a test setup before using it in a production environment! +![Icinga 2 Distributed Master and Satellites with Agents](images/distributed-monitoring/icinga2_distributed_monitoring_scenarios_master_satellites_agents.png) + +> **Tip**: +> +> It can get complicated, so grab a pen and paper and bring your thoughts to life. +> Play around with a test setup before using it in a production environment! + +Best practice is to run the database backend on a dedicated server/cluster and +only expose a virtual IP address to Icinga and the IDO feature. By default, only one +endpoint will actively write to the backend then. Typical setups for MySQL clusters +involve Master-Master-Replication (Master-Slave-Replication in both directions) or Galera, +more tips can be found on our [community forums](https://community.icinga.com/). Overview: * `icinga2-master1.localdomain` is the configuration master master node. * `icinga2-master2.localdomain` is the secondary master master node without configuration in `zones.d`. -* `icinga2-satellite1.localdomain` and `icinga2-satellite2.localdomain` are satellite nodes in a `master` child zone. -* `icinga2-client1.localdomain` and `icinga2-client2.localdomain` are two child nodes as clients. +* `icinga2-satellite1.localdomain` and `icinga2-satellite2.localdomain` are satellite nodes in a `master` child zone. They forward CSR signing requests to the master zone. +* `icinga2-agent1.localdomain` and `icinga2-agent2.localdomain` are two child nodes as agents. Setup requirements: * Set up `icinga2-master1.localdomain` as [master](06-distributed-monitoring.md#distributed-monitoring-setup-master). -* Set up `icinga2-master2.localdomain`, `icinga2-satellite1.localdomain` and `icinga2-satellite2.localdomain` as [clients](06-distributed-monitoring.md#distributed-monitoring-setup-satellite-client) (we will modify the generated configuration). -* Set up `icinga2-client1.localdomain` and `icinga2-client2.localdomain` as [clients](06-distributed-monitoring.md#distributed-monitoring-setup-satellite-client). +* Set up `icinga2-master2.localdomain`, `icinga2-satellite1.localdomain` and `icinga2-satellite2.localdomain` as [agents](06-distributed-monitoring.md#distributed-monitoring-setup-agent-satellite) (we will modify the generated configuration). +* Set up `icinga2-agent1.localdomain` and `icinga2-agent2.localdomain` as [agents](06-distributed-monitoring.md#distributed-monitoring-setup-agent-satellite). -When being asked for the master endpoint providing CSR auto-signing capabilities, -please add the master node which holds the CA and has the `ApiListener` feature configured and enabled. -The parent endpoint must still remain the satellite endpoint name. +When being asked for the parent endpoint providing CSR auto-signing capabilities, +please add one of the satellite nodes. **Note**: This requires Icinga 2 v2.8+ +and the `CA Proxy` on all master, satellite and agent nodes. -Example for `icinga2-client1.localdomain`: +Example for `icinga2-agent1.localdomain`: - Please specify the master endpoint(s) this node should connect to: +``` +Please specify the parent endpoint(s) (master or satellite) where this node should connect to: +``` -Master is the first satellite `icinga2-satellite1.localdomain`: +Parent endpoint is the first satellite `icinga2-satellite1.localdomain`: - Master Common Name (CN from your master setup): icinga2-satellite1.localdomain - Do you want to establish a connection to the master from this node? [Y/n]: y - Please fill out the master connection information: - Master endpoint host (Your master's IP address or FQDN): 192.168.56.105 - Master endpoint port [5665]: +``` +Master/Satellite Common Name (CN from your master/satellite node): icinga2-satellite1.localdomain +Do you want to establish a connection to the parent node from this node? [Y/n]: y -Add the second satellite `icinga2-satellite2.localdomain` as master: +Please specify the master/satellite connection information: +Master/Satellite endpoint host (IP address or FQDN): 192.168.56.105 +Master/Satellite endpoint port [5665]: 5665 +``` - Add more master endpoints? [y/N]: y - Master Common Name (CN from your master setup): icinga2-satellite2.localdomain - Do you want to establish a connection to the master from this node? [Y/n]: y - Please fill out the master connection information: - Master endpoint host (Your master's IP address or FQDN): 192.168.56.106 - Master endpoint port [5665]: - Add more master endpoints? [y/N]: n +Add the second satellite `icinga2-satellite2.localdomain` as parent: -Specify the master node `icinga2-master2.localdomain` with the CA private key and ticket salt: +``` +Add more master/satellite endpoints? [y/N]: y - Please specify the master connection for CSR auto-signing (defaults to master endpoint host): - Host [192.168.56.106]: icinga2-master1.localdomain - Port [5665]: +Master/Satellite Common Name (CN from your master/satellite node): icinga2-satellite2.localdomain +Do you want to establish a connection to the parent node from this node? [Y/n]: y -In case you cannot connect to the master node from your clients, you'll manually need -to [generate the SSL certificates](06-distributed-monitoring.md#distributed-monitoring-advanced-hints-certificates-manual) -and modify the configuration accordingly. +Please specify the master/satellite connection information: +Master/Satellite endpoint host (IP address or FQDN): 192.168.56.106 +Master/Satellite endpoint port [5665]: 5665 -We'll discuss the details of the required configuration below. +Add more master/satellite endpoints? [y/N]: n +``` + +The specified parent nodes will forward the CSR signing request to the master instances. + +Proceed with adding the optional client ticket for [CSR auto-signing](06-distributed-monitoring.md#distributed-monitoring-setup-csr-auto-signing): + +``` +Please specify the request ticket generated on your Icinga 2 master (optional). + (Hint: # icinga2 pki ticket --cn 'icinga2-agent1.localdomain'): +4f75d2ecd253575fe9180938ebff7cbca262f96e +``` + +In case you've chosen to use [On-Demand CSR Signing](06-distributed-monitoring.md#distributed-monitoring-setup-on-demand-csr-signing) +you can leave the ticket question blank. + +Instead, Icinga 2 tells you to approve the request later on the master node. + +``` +No ticket was specified. Please approve the certificate signing request manually +on the master (see 'icinga2 ca list' and 'icinga2 ca sign --help' for details). +``` + +You can optionally specify a different bind host and/or port. + +``` +Please specify the API bind host/port (optional): +Bind Host []: +Bind Port []: +``` + +The next step asks you to accept configuration (required for [config sync mode](06-distributed-monitoring.md#distributed-monitoring-top-down-config-sync)) +and commands (required for [command endpoint mode](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint)). + +``` +Accept config from parent node? [y/N]: y +Accept commands from parent node? [y/N]: y +``` + +Next you can optionally specify the local and parent zone names. This will be reflected +in the generated zone configuration file. + +``` +Local zone name [icinga2-agent1.localdomain]: icinga2-agent1.localdomain +``` + +Set the parent zone name to `satellite` for this agent. + +``` +Parent zone name [master]: satellite +``` + +You can add more global zones in addition to `global-templates` and `director-global` if necessary. +Press `Enter` or choose `n`, if you don't want to add any additional. + +``` +Reconfiguring Icinga... + +Default global zones: global-templates director-global +Do you want to specify additional global zones? [y/N]: N +``` + +Last but not least the wizard asks you whether you want to disable the inclusion of the local configuration +directory in `conf.d`, or not. Defaults to disabled, since agents are checked via command endpoint and the example +configuration would collide with this mode. + +``` +Do you want to disable the inclusion of the conf.d directory [Y/n]: Y +Disabling the inclusion of the conf.d directory... +``` + + +**We'll discuss the details of the required configuration below. Most of this +configuration can be rendered by the setup wizards.** The zone hierarchy can look like this. We'll define only the directly connected zones here. -You can safely deploy this configuration onto all master and satellite zone -members. You should keep in mind to control the endpoint [connection direction](06-distributed-monitoring.md#distributed-monitoring-advanced-hints-connection-direction) -using the `host` attribute. +The master instances should actively connect to the satellite instances, therefore +the configuration on `icinga2-master1.localdomain` and `icinga2-master2.localdomain` +must include the `host` attribute for the satellite endpoints: - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf - object Endpoint "icinga2-master1.localdomain" { - host = "192.168.56.101" - } +object Endpoint "icinga2-master1.localdomain" { + // That's us +} - object Endpoint "icinga2-master2.localdomain" { - host = "192.168.56.102" - } +object Endpoint "icinga2-master2.localdomain" { + host = "192.168.56.102" // Actively connect to the second master. +} - object Endpoint "icinga2-satellite1.localdomain" { - host = "192.168.56.105" - } +object Endpoint "icinga2-satellite1.localdomain" { + host = "192.168.56.105" // Actively connect to the satellites. +} - object Endpoint "icinga2-satellite2.localdomain" { - host = "192.168.56.106" - } +object Endpoint "icinga2-satellite2.localdomain" { + host = "192.168.56.106" // Actively connect to the satellites. +} - object Zone "master" { - endpoints = [ "icinga2-master1.localdomain", "icinga2-master2.localdomain" ] - } +object Zone "master" { + endpoints = [ "icinga2-master1.localdomain", "icinga2-master2.localdomain" ] +} +``` - object Zone "satellite" { - endpoints = [ "icinga2-satellite1.localdomain", "icinga2-satellite2.localdomain" ] +The endpoint configuration on the secondary master looks similar, +but changes the connection attributes - the first master already +tries to connect, there is no need for a secondary attempt. - parent = "master" - } +``` +[root@icinga2-master2.localdomain /]# vim /etc/icinga2/zones.conf - /* sync global commands */ - object Zone "global-templates" { - global = true - } +object Endpoint "icinga2-master1.localdomain" { + // First master already connects to us +} -Repeat the configuration step for `icinga2-master2.localdomain`, `icinga2-satellite1.localdomain` -and `icinga2-satellite2.localdomain`. +object Endpoint "icinga2-master2.localdomain" { + // That's us +} + +object Endpoint "icinga2-satellite1.localdomain" { + host = "192.168.56.105" // Actively connect to the satellites. +} + +object Endpoint "icinga2-satellite2.localdomain" { + host = "192.168.56.106" // Actively connect to the satellites. +} +``` + +The zone configuration on both masters looks the same. Add this +to the corresponding `zones.conf` entries for the endpoints. + +``` +object Zone "satellite" { + endpoints = [ "icinga2-satellite1.localdomain", "icinga2-satellite2.localdomain" ] + + parent = "master" +} + +/* sync global commands */ +object Zone "global-templates" { + global = true +} + +object Zone "director-global" { + global = true +} + +``` + +In contrast to that, the satellite instances `icinga2-satellite1.localdomain` +and `icinga2-satellite2.localdomain` should not actively connect to the master +instances. + +``` +[root@icinga2-satellite1.localdomain /]# vim /etc/icinga2/zones.conf + +object Endpoint "icinga2-master1.localdomain" { + // This endpoint will connect to us +} + +object Endpoint "icinga2-master2.localdomain" { + // This endpoint will connect to us +} + +object Endpoint "icinga2-satellite1.localdomain" { + // That's us +} + +object Endpoint "icinga2-satellite2.localdomain" { + host = "192.168.56.106" // Actively connect to the secondary satellite +} +``` + +Again, only one side is required to establish the connection inside the HA zone. +Since satellite1 already connects to satellite2, leave out the `host` attribute +for `icinga2-satellite1.localdomain` on satellite2. + +``` +[root@icinga2-satellite2.localdomain /]# vim /etc/icinga2/zones.conf + +object Endpoint "icinga2-master1.localdomain" { + // This endpoint will connect to us +} + +object Endpoint "icinga2-master2.localdomain" { + // This endpoint will connect to us +} + +object Endpoint "icinga2-satellite1.localdomain" { + // First satellite already connects to us +} + +object Endpoint "icinga2-satellite2.localdomain" { + // That's us +} +``` + +The zone configuration on both satellites looks the same. Add this +to the corresponding `zones.conf` entries for the endpoints. + +``` +object Zone "master" { + endpoints = [ "icinga2-master1.localdomain", "icinga2-master2.localdomain" ] +} + +object Zone "satellite" { + endpoints = [ "icinga2-satellite1.localdomain", "icinga2-satellite2.localdomain" ] + + parent = "master" +} + +/* sync global commands */ +object Zone "global-templates" { + global = true +} + +object Zone "director-global" { + global = true +} +``` + +Keep in mind to control the endpoint [connection direction](06-distributed-monitoring.md#distributed-monitoring-advanced-hints-connection-direction) +using the `host` attribute, also for other endpoints in the same zone. Since we want to use [top down command endpoint](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint) checks, -we must configure the client endpoint and zone objects. -In order to minimize the effort, we'll sync the client zone and endpoint configuration to the -satellites where the connection information is needed as well. +we must configure the agent endpoint and zone objects. - [root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/{master,satellite,global-templates} - [root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/satellite +In order to minimize the effort, we'll sync the agent zone and endpoint configuration to the +satellites where the connection information is needed as well. Note: This only works with satellite +and agents, since there already is a trust relationship between the master and the satellite zone. +The cluster config sync to the satellite invokes an automated reload causing the agent connection attempts. - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim icinga2-client1.localdomain.conf +`icinga2-master1.localdomain` is the configuration master where everything is stored: - object Endpoint "icinga2-client1.localdomain" { - host = "192.168.56.111" //the satellite actively tries to connect to the client - } +``` +[root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/{master,satellite,global-templates} +[root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/satellite - object Zone "icinga2-client1.localdomain" { - endpoints = [ "icinga2-client1.localdomain" ] +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim icinga2-agent1.localdomain.conf - parent = "satellite" - } +object Endpoint "icinga2-agent1.localdomain" { + host = "192.168.56.111" // The satellite actively tries to connect to the agent + log_duration = 0 // Disable the replay log for command endpoint agents +} - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim icinga2-client2.localdomain.conf +object Zone "icinga2-agent1.localdomain" { + endpoints = [ "icinga2-agent1.localdomain" ] - object Endpoint "icinga2-client2.localdomain" { - host = "192.168.56.112" //the satellite actively tries to connect to the client - } + parent = "satellite" +} - object Zone "icinga2-client2.localdomain" { - endpoints = [ "icinga2-client2.localdomain" ] +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim icinga2-agent2.localdomain.conf - parent = "satellite" - } +object Endpoint "icinga2-agent2.localdomain" { + host = "192.168.56.112" // The satellite actively tries to connect to the agent + log_duration = 0 // Disable the replay log for command endpoint agents +} -The two client nodes do not necessarily need to know about each other, either. The only important thing +object Zone "icinga2-agent2.localdomain" { + endpoints = [ "icinga2-agent2.localdomain" ] + + parent = "satellite" +} +``` + +The two agent nodes do not need to know about each other. The only important thing is that they know about the parent zone (the satellite) and their endpoint members (and optionally the global zone). -If you specify the `host` attribute in the `icinga2-satellite1.localdomain` and `icinga2-satellite2.localdomain` -endpoint objects, the client node will actively try to connect to the satellite node. Since we've specified the client -endpoint's attribute on the satellite node already, we don't want the client node to connect to the -satellite nodes. **Choose one [connection direction](06-distributed-monitoring.md#distributed-monitoring-advanced-hints-connection-direction).** +> **Tipp** +> +> In the example above we've specified the `host` attribute in the agent endpoint configuration. In this mode, +> the satellites actively connect to the agents. This costs some resources on the satellite -- if you prefer to +> offload the connection attempts to the agent, or your DMZ requires this, you can also change the **[connection direction](06-distributed-monitoring.md#distributed-monitoring-advanced-hints-connection-direction).** +> +> 1) Don't set the `host` attribute for the agent endpoints put into `zones.d/satellite`. +> 2) Modify each agent's zones.conf file and add the `host` attribute to all parent satellites. You can automate this with using the `node wizard/setup` CLI commands. -Example for `icinga2-client1.localdomain`: +The agents are waiting for the satellites to connect, therefore they don't specify +the `host` attribute in the endpoint objects locally. - [root@icinga2-client1.localdomain /]# vim /etc/icinga2/zones.conf +Example for `icinga2-agent1.localdomain`: - object Endpoint "icinga2-satellite1.localdomain" { - //do not actively connect to the satellite by leaving out the 'host' attribute - } +``` +[root@icinga2-agent1.localdomain /]# vim /etc/icinga2/zones.conf - object Endpoint "icinga2-satellite2.localdomain" { - //do not actively connect to the satellite by leaving out the 'host' attribute - } +object Endpoint "icinga2-satellite1.localdomain" { + // Do not actively connect to the satellite by leaving out the 'host' attribute +} - object Endpoint "icinga2-client1.localdomain" { - //that's us - } +object Endpoint "icinga2-satellite2.localdomain" { + // Do not actively connect to the satellite by leaving out the 'host' attribute +} - object Zone "satellite" { - endpoints = [ "icinga2-satellite1.localdomain", "icinga2-satellite2.localdomain" ] - } +object Endpoint "icinga2-agent1.localdomain" { + // That's us +} - object Zone "icinga2-client1.localdomain" { - endpoints = [ "icinga2-client1.localdomain" ] +object Zone "satellite" { + endpoints = [ "icinga2-satellite1.localdomain", "icinga2-satellite2.localdomain" ] +} - parent = "satellite" - } +object Zone "icinga2-agent1.localdomain" { + endpoints = [ "icinga2-agent1.localdomain" ] - /* sync global commands */ - object Zone "global-templates" { - global = true - } + parent = "satellite" +} -Example for `icinga2-client2.localdomain`: +/* sync global commands */ +object Zone "global-templates" { + global = true +} - [root@icinga2-client2.localdomain /]# vim /etc/icinga2/zones.conf +object Zone "director-global" { + global = true +} +``` - object Endpoint "icinga2-satellite1.localdomain" { - //do not actively connect to the satellite by leaving out the 'host' attribute - } +Example for `icinga2-agent2.localdomain`: - object Endpoint "icinga2-satellite2.localdomain" { - //do not actively connect to the satellite by leaving out the 'host' attribute - } +``` +[root@icinga2-agent2.localdomain /]# vim /etc/icinga2/zones.conf - object Endpoint "icinga2-client2.localdomain" { - //that's us - } +object Endpoint "icinga2-satellite1.localdomain" { + // Do not actively connect to the satellite by leaving out the 'host' attribute +} - object Zone "satellite" { - endpoints = [ "icinga2-satellite1.localdomain", "icinga2-satellite2.localdomain" ] - } +object Endpoint "icinga2-satellite2.localdomain" { + // Do not actively connect to the satellite by leaving out the 'host' attribute +} - object Zone "icinga2-client2.localdomain" { - endpoints = [ "icinga2-client2.localdomain" ] +object Endpoint "icinga2-agent2.localdomain" { + // That's us +} - parent = "satellite" - } +object Zone "satellite" { + endpoints = [ "icinga2-satellite1.localdomain", "icinga2-satellite2.localdomain" ] +} - /* sync global commands */ - object Zone "global-templates" { - global = true - } +object Zone "icinga2-agent2.localdomain" { + endpoints = [ "icinga2-agent2.localdomain" ] -Now it is time to define the two client hosts on the master, sync them to the satellites + parent = "satellite" +} + +/* sync global commands */ +object Zone "global-templates" { + global = true +} + +object Zone "director-global" { + global = true +} +``` + +Now it is time to define the two agents hosts on the master, sync them to the satellites and apply service checks using the command endpoint execution method to them. -Add the two client nodes as host objects to the `satellite` zone. +Add the two agent nodes as host objects to the `satellite` zone. We've already created the directories in `/etc/icinga2/zones.d` including the files for the -zone and endpoint configuration for the clients. +zone and endpoint configuration for the agents. - [root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/satellite +``` +[root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/satellite +``` -Add the host object configuration for the `icinga2-client1.localdomain` client. You should +Add the host object configuration for the `icinga2-agent1.localdomain` agent. You should have created the configuration file in the previous steps and it should contain the endpoint and zone object configuration already. - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim icinga2-client1.localdomain.conf +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim icinga2-agent1.localdomain.conf - object Host "icinga2-client1.localdomain" { - check_command = "hostalive" - address = "192.168.56.111" - vars.client_endpoint = name //follows the convention that host name == endpoint name - } +object Host "icinga2-agent1.localdomain" { + check_command = "hostalive" + address = "192.168.56.111" -Add the host object configuration for the `icinga2-client2.localdomain` client configuration file: + vars.agent_endpoint = name // Follows the convention that host name == endpoint name +} +``` - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim icinga2-client2.localdomain.conf +Add the host object configuration for the `icinga2-agent2.localdomain` agent configuration file: - object Host "icinga2-client2.localdomain" { - check_command = "hostalive" - address = "192.168.56.112" - vars.client_endpoint = name //follows the convention that host name == endpoint name - } +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim icinga2-agent2.localdomain.conf + +object Host "icinga2-agent2.localdomain" { + check_command = "hostalive" + address = "192.168.56.112" + + vars.agent_endpoint = name // Follows the convention that host name == endpoint name +} +``` Add a service object which is executed on the satellite nodes (e.g. `ping4`). Pin the apply rule to the `satellite` zone only. - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim services.conf +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim services.conf - apply Service "ping4" { - check_command = "ping4" - //check is executed on the satellite node - assign where host.zone == "satellite" && host.address - } +apply Service "ping4" { + check_command = "ping4" + + // Check is executed on the satellite node + assign where host.zone == "satellite" && host.address +} +``` Add services using command endpoint checks. Pin the apply rules to the `satellite` zone only. - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim services.conf +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim services.conf - apply Service "disk" { - check_command = "disk" +apply Service "disk" { + check_command = "disk" - //specify where the check is executed - command_endpoint = host.vars.client_endpoint + // Execute the check on the remote command endpoint + command_endpoint = host.vars.agent_endpoint - assign where host.zone == "satellite" && host.vars.client_endpoint - } + assign where host.zone == "satellite" && host.vars.agent_endpoint +} +``` Validate the configuration and restart Icinga 2 on the master node `icinga2-master1.localdomain`. - [root@icinga2-master1.localdomain /]# icinga2 daemon -C - [root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` +[root@icinga2-master1.localdomain /]# icinga2 daemon -C +[root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` -Open Icinga Web 2 and check the two newly created client hosts with two new services +Open Icinga Web 2 and check the two newly created agent hosts with two new services -- one executed locally (`ping4`) and one using command endpoint (`disk`). -**Tip**: It's a good idea to add [health checks](06-distributed-monitoring.md#distributed-monitoring-health-checks) +> **Tip**: +> +> It's a good idea to add [health checks](06-distributed-monitoring.md#distributed-monitoring-health-checks) to make sure that your cluster notifies you in case of failure. +In terms of health checks, consider adding the following for this scenario: + +- Master nodes check whether the satellite zone is connected +- Satellite nodes check the connection to the agents +- Optional: Add dependencies for the agent host to prevent unwanted notifications when agents are unreachable + +Proceed in [this chapter](06-distributed-monitoring.md#distributed-monitoring-health-checks-master-satellite-agent). + + ## Best Practice We've put together a collection of configuration examples from community feedback. -If you like to share your tips and tricks with us, please join the [community channels](https://www.icinga.com/community/get-involved/)! +If you like to share your tips and tricks with us, please join the [community channels](https://icinga.com/community/)! ### Global Zone for Config Sync @@ -1893,50 +2428,63 @@ to all nodes depending on them. Common examples are: * Group objects. * TimePeriod objects. -Plugin scripts and binaries cannot be synced, this is for Icinga 2 +Plugin scripts and binaries must not be synced, this is for Icinga 2 configuration files only. Use your preferred package repository and/or configuration management tool (Puppet, Ansible, Chef, etc.) -for that. +for keeping packages and scripts uptodate. **Note**: Checkable objects (hosts and services) cannot be put into a global -zone. The configuration validation will terminate with an error. +zone. The configuration validation will terminate with an error. Apply rules +work as they are evaluated locally on each endpoint. The zone object configuration must be deployed on all nodes which should receive the global configuration files: - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf - object Zone "global-templates" { - global = true - } +object Zone "global-commands" { + global = true +} +``` -Note: Packages >= 2.8 provide this configuration by default. +The default global zones generated by the setup wizards are called `global-templates` and `director-global`. + +While you can should `global-templates` for your global configuration, `director-global` is reserved for use +by [Icinga Director](https://icinga.com/docs/director/latest/). Please don't +place any configuration in it manually. Similar to the zone configuration sync you'll need to create a new directory in `/etc/icinga2/zones.d`: - [root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/global-templates +``` +[root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/global-commands +``` Next, add a new check command, for example: - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/global-templates/commands.conf +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/global-commands/web.conf - object CheckCommand "my-cmd" { - //... - } +object CheckCommand "webinject" { + //... +} +``` -Restart the client(s) which should receive the global zone before +Restart the endpoints(s) which should receive the global zone before before restarting the parent master/satellite nodes. Then validate the configuration on the master node and restart Icinga 2. **Tip**: You can copy the example configuration files located in `/etc/icinga2/conf.d` -into your global zone. +into the default global zone `global-templates`. Example: - [root@icinga2-master1.localdomain /]# cd /etc/icinga2/conf.d - [root@icinga2-master1.localdomain /etc/icinga2/conf.d]# cp {commands,groups,notifications,services,templates,timeperiods,users}.conf /etc/icinga2/zones.d/global-templates +``` +[root@icinga2-master1.localdomain /]# cd /etc/icinga2/conf.d +[root@icinga2-master1.localdomain /etc/icinga2/conf.d]# cp {commands,groups,notifications,services,templates,timeperiods,users}.conf /etc/icinga2/zones.d/global-templates +``` ### Health Checks @@ -1947,72 +2495,158 @@ checks. In order to minimize the problems caused by this, you should configure additional health checks. -The `cluster` check, for example, will check if all endpoints in the current zone and the directly -connected zones are working properly: - - [root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/master - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/icinga2-master1.localdomain.conf - - object Host "icinga2-master1.localdomain" { - check_command = "hostalive" - address = "192.168.56.101" - } - - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/cluster.conf - - object Service "cluster" { - check_command = "cluster" - check_interval = 5s - retry_interval = 1s - - host_name = "icinga2-master1.localdomain" - } +#### cluster-zone with Masters and Agents The `cluster-zone` check will test whether the configured target zone is currently -connected or not. This example adds a health check for the [ha master with clients scenario](06-distributed-monitoring.md#distributed-monitoring-scenarios-ha-master-clients). +connected or not. This example adds a health check for the [ha master with agents scenario](06-distributed-monitoring.md#distributed-monitoring-scenarios-ha-master-agents). - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/services.conf +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/services.conf - apply Service "cluster-health" { - check_command = "cluster-zone" +apply Service "agent-health" { + check_command = "cluster-zone" - display_name = "cluster-health-" + host.name + display_name = "cluster-health-" + host.name - /* This follows the convention that the client zone name is the FQDN which is the same as the host object name. */ - vars.cluster_zone = host.name + /* This follows the convention that the agent zone name is the FQDN which is the same as the host object name. */ + vars.cluster_zone = host.name - assign where host.vars.client_endpoint - } + assign where host.vars.agent_endpoint +} +``` -In case you cannot assign the `cluster_zone` attribute, add specific -checks to your cluster: +In order to prevent unwanted notifications, add a service dependency which gets applied to +all services using the command endpoint mode. - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/cluster.conf +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/dependencies.conf - object Service "cluster-zone-satellite" { - check_command = "cluster-zone" - check_interval = 5s - retry_interval = 1s - vars.cluster_zone = "satellite" +apply Dependency "agent-health-check" to Service { + parent_service_name = "agent-health" - host_name = "icinga2-master1.localdomain" - } + states = [ OK ] // Fail if the parent service state switches to NOT-OK + disable_notifications = true + assign where host.vars.agent_endpoint // Automatically assigns all agent endpoint checks as child services on the matched host + ignore where service.name == "agent-health" // Avoid a self reference from child to parent +} +``` -If you are using top down checks with command endpoint configuration, you can -add a dependency which prevents notifications for all other failing services: +#### cluster-zone with Masters, Satellites and Agents - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/dependencies.conf +This example adds health checks for the [master, satellites and agents scenario](06-distributed-monitoring.md#distributed-monitoring-scenarios-master-satellite-agents). - apply Dependency "health-check" to Service { - parent_service_name = "child-health" +Whenever the connection between the master and satellite zone breaks, +you may encounter late check results in Icinga Web. In order to view +this failure and also send notifications, add the following configuration: - states = [ OK ] - disable_notifications = true +First, add the two masters as host objects to the master zone, if not already +existing. - assign where host.vars.client_endpoint - ignore where service.name == "child-health" - } +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/hosts.conf + +object Host "icinga2-master1.localdomain" { + check_command = "hostalive" + + address = "192.168.56.101" +} + +object Host "icinga2-master2.localdomain" { + check_command = "hostalive" + + address = "192.168.56.102" +} +``` + +Add service health checks against the satellite zone. + +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/health.conf + +apply Service "satellite-zone-health" { + check_command = "cluster-zone" + check_interval = 30s + retry_interval = 10s + + vars.cluster_zone = "satellite" + + assign where match("icinga2-master*.localdomain", host.name) +} +``` + +**Don't forget to create notification apply rules for these services.** + +Next are health checks for agents connected to the satellite zone. +Navigate into the satellite directory in `zones.d`: + +``` +[root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/satellite +``` + +You should already have configured agent host objects following [the master, satellite, agents scenario](06-distributed-monitoring.md#distributed-monitoring-scenarios-master-satellite-agents). +Add a new configuration file where all the health checks are defined. + +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim health.conf + +apply Service "agent-health" { + check_command = "cluster-zone" + + display_name = "agent-health-" + host.name + + // This follows the convention that the agent zone name is the FQDN which is the same as the host object name. + vars.cluster_zone = host.name + + // Create this health check for agent hosts in the satellite zone + assign where host.zone == "satellite" && host.vars.agent_endpoint +} +``` + +In order to prevent unwanted notifications, add a service dependency which gets applied to +all services using the command endpoint mode. + +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/satellite]# vim health.conf + +apply Dependency "agent-health-check" to Service { + parent_service_name = "agent-health" + + states = [ OK ] // Fail if the parent service state switches to NOT-OK + disable_notifications = true + + assign where host.zone == "satellite" && host.vars.agent_endpoint // Automatically assigns all agent endpoint checks as child services on the matched host + ignore where service.name == "agent-health" // Avoid a self reference from child to parent +} +``` + +This is all done on the configuration master, and requires the scenario to be fully up and running. + +#### Cluster Check + +The `cluster` check will check if all endpoints in the current zone and the directly +connected zones are working properly. The disadvantage of using this check is that +you cannot monitor 3 or more cluster levels with it. + +``` +[root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/master +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/icinga2-master1.localdomain.conf + +object Host "icinga2-master1.localdomain" { + check_command = "hostalive" + address = "192.168.56.101" +} + +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/cluster.conf + +object Service "cluster" { + check_command = "cluster" + check_interval = 5s + retry_interval = 1s + + host_name = "icinga2-master1.localdomain" +} +``` ### Pin Checks in a Zone @@ -2020,23 +2654,25 @@ In case you want to pin specific checks to their endpoints in a given zone you'l the `command_endpoint` attribute. This is reasonable if you want to execute a local disk check in the `master` Zone on a specific endpoint then. - [root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/master - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/icinga2-master1.localdomain.conf +``` +[root@icinga2-master1.localdomain /]# mkdir -p /etc/icinga2/zones.d/master +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/icinga2-master1.localdomain.conf - object Host "icinga2-master1.localdomain" { - check_command = "hostalive" - address = "192.168.56.101" - } +object Host "icinga2-master1.localdomain" { + check_command = "hostalive" + address = "192.168.56.101" +} - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/services.conf +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.d/master/services.conf - apply Service "disk" { - check_command = "disk" +apply Service "disk" { + check_command = "disk" - command_endpoint = host.name //requires a host object matching the endpoint object name e.g. icinga2-master1.localdomain + command_endpoint = host.name //requires a host object matching the endpoint object name e.g. icinga2-master1.localdomain - assign where host.zone == "master" && match("icinga2-master*", host.name) - } + assign where host.zone == "master" && match("icinga2-master*", host.name) +} +``` The `host.zone` attribute check inside the expression ensures that the service object is only created for host objects inside the `master` @@ -2050,85 +2686,91 @@ function ensures to only create services for the master nodes. By default ICMP requests are disabled in the Windows firewall. You can change that by [adding a new rule](https://support.microsoft.com/en-us/kb/947709). - C:\WINDOWS\system32>netsh advfirewall firewall add rule name="ICMP Allow incoming V4 echo request" protocol=icmpv4:8,any dir=in action=allow +``` +C:\> netsh advfirewall firewall add rule name="ICMP Allow incoming V4 echo request" protocol=icmpv4:8,any dir=in action=allow +``` #### Icinga 2 -If your master/satellite nodes should actively connect to the Windows client +If your master/satellite nodes should actively connect to the Windows agent you'll also need to ensure that port `5665` is enabled. - C:\WINDOWS\system32>netsh advfirewall firewall add rule name="Open port 5665 (Icinga 2)" dir=in action=allow protocol=TCP localport=5665 +``` +C:\> netsh advfirewall firewall add rule name="Open port 5665 (Icinga 2)" dir=in action=allow protocol=TCP localport=5665 +``` #### NSClient++ API If the [check_nscp_api](06-distributed-monitoring.md#distributed-monitoring-windows-nscp-check-api) plugin is used to query NSClient++, you need to ensure that its port is enabled. - C:\WINDOWS\system32>netsh advfirewall firewall add rule name="Open port 8443 (NSClient++ API)" dir=in action=allow protocol=TCP localport=8443 +``` +C:\> netsh advfirewall firewall add rule name="Open port 8443 (NSClient++ API)" dir=in action=allow protocol=TCP localport=8443 +``` For security reasons, it is advised to enable the NSClient++ HTTP API for local -connection from the Icinga 2 client only. Remote connections to the HTTP API +connection from the Icinga agent only. Remote connections to the HTTP API are not recommended with using the legacy HTTP API. -### Windows Client and Plugins +### Windows Agent and Plugins The Icinga 2 package on Windows already provides several plugins. Detailed [documentation](10-icinga-template-library.md#windows-plugins) is available for all check command definitions. -Add the following `include` statement on all your nodes (master, satellite, client): - - vim /etc/icinga2/icinga2.conf - - include - -Based on the [master with clients](06-distributed-monitoring.md#distributed-monitoring-master-clients) +Based on the [master with agents](06-distributed-monitoring.md#distributed-monitoring-master-agents) scenario we'll now add a local disk check. -First, add the client node as host object: +First, add the agent node as host object: - [root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf +``` +[root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf - object Host "icinga2-client2.localdomain" { - check_command = "hostalive" - address = "192.168.56.112" - vars.client_endpoint = name //follows the convention that host name == endpoint name - vars.os_type = "windows" - } +object Host "icinga2-agent2.localdomain" { + check_command = "hostalive" + address = "192.168.56.112" + vars.agent_endpoint = name //follows the convention that host name == endpoint name + vars.os_type = "windows" +} +``` Next, add the disk check using command endpoint checks (details in the [disk-windows](10-icinga-template-library.md#windows-plugins-disk-windows) documentation): - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf - apply Service "disk C:" { - check_command = "disk-windows" +apply Service "disk C:" { + check_command = "disk-windows" - vars.disk_win_path = "C:" + vars.disk_win_path = "C:" - //specify where the check is executed - command_endpoint = host.vars.client_endpoint + //specify where the check is executed + command_endpoint = host.vars.agent_endpoint - assign where host.vars.os_type == "windows" && host.vars.client_endpoint - } + assign where host.vars.os_type == "windows" && host.vars.agent_endpoint +} +``` Validate the configuration and restart Icinga 2. - [root@icinga2-master1.localdomain /]# icinga2 daemon -C - [root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` +[root@icinga2-master1.localdomain /]# icinga2 daemon -C +[root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` Open Icinga Web 2 and check your newly added Windows disk check :) -![Icinga 2 Client Windows](images/distributed-monitoring/icinga2_distributed_windows_client_disk_icingaweb2.png) +![Icinga Windows Agent](images/distributed-monitoring/icinga2_distributed_windows_client_disk_icingaweb2.png) If you want to add your own plugins please check [this chapter](05-service-monitoring.md#service-monitoring-requirements) for the requirements. -### Windows Client and NSClient++ +### Windows Agent and NSClient++ There are two methods available for querying NSClient++: -* Query the [HTTP API](06-distributed-monitoring.md#distributed-monitoring-windows-nscp-check-api) locally from an Icinga 2 client (requires a running NSClient++ service) +* Query the [HTTP API](06-distributed-monitoring.md#distributed-monitoring-windows-nscp-check-api) locally from an Icinga agent (requires a running NSClient++ service) * Run a [local CLI check](06-distributed-monitoring.md#distributed-monitoring-windows-nscp-check-local) (does not require NSClient++ as a service) Both methods have their advantages and disadvantages. One thing to @@ -2137,164 +2779,183 @@ CPU utilization, please use the HTTP API instead of the CLI sample call. #### NSCLient++ with check_nscp_api -The [Windows setup](06-distributed-monitoring.md#distributed-monitoring-setup-client-windows) already allows +The [Windows setup](06-distributed-monitoring.md#distributed-monitoring-setup-agent-windows) already allows you to install the NSClient++ package. In addition to the Windows plugins you can use the [nscp_api command](10-icinga-template-library.md#nscp-check-api) provided by the Icinga Template Library (ITL). The initial setup for the NSClient++ API and the required arguments is the described in the ITL chapter for the [nscp_api](10-icinga-template-library.md#nscp-check-api) CheckCommand. -Based on the [master with clients](06-distributed-monitoring.md#distributed-monitoring-master-clients) +Based on the [master with agents](06-distributed-monitoring.md#distributed-monitoring-master-agents) scenario we'll now add a local nscp check which queries the NSClient++ API to check the free disk space. -Define a host object called `icinga2-client2.localdomain` on the master. Add the `nscp_api_password` -custom attribute and specify the drives to check. +Define a host object called `icinga2-agent2.localdomain` on the master. Add the `nscp_api_password` +custom variable and specify the drives to check. - [root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf +``` +[root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf - object Host "icinga2-client1.localdomain" { - check_command = "hostalive" - address = "192.168.56.111" - vars.client_endpoint = name //follows the convention that host name == endpoint name - vars.os_type = "Windows" - vars.nscp_api_password = "icinga" - vars.drives = [ "C:", "D:" ] - } +object Host "icinga2-agent1.localdomain" { + check_command = "hostalive" + address = "192.168.56.111" + + vars.agent_endpoint = name //follows the convention that host name == endpoint name + vars.os_type = "Windows" + vars.nscp_api_password = "icinga" + vars.drives = [ "C:", "D:" ] +} +``` The service checks are generated using an [apply for](03-monitoring-basics.md#using-apply-for) rule based on `host.vars.drives`: - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf - apply Service "nscp-api-" for (drive in host.vars.drives) { - import "generic-service" +apply Service "nscp-api-" for (drive in host.vars.drives) { + import "generic-service" - check_command = "nscp_api" - command_endpoint = host.vars.client_endpoint + check_command = "nscp_api" + command_endpoint = host.vars.agent_endpoint - //display_name = "nscp-drive-" + drive + //display_name = "nscp-drive-" + drive - vars.nscp_api_host = "localhost" - vars.nscp_api_query = "check_drivesize" - vars.nscp_api_password = host.vars.nscp_api_password - vars.nscp_api_arguments = [ "drive=" + drive ] + vars.nscp_api_host = "localhost" + vars.nscp_api_query = "check_drivesize" + vars.nscp_api_password = host.vars.nscp_api_password + vars.nscp_api_arguments = [ "drive=" + drive ] - ignore where host.vars.os_type != "Windows" - } + ignore where host.vars.os_type != "Windows" +} +``` Validate the configuration and restart Icinga 2. - [root@icinga2-master1.localdomain /]# icinga2 daemon -C - [root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` +[root@icinga2-master1.localdomain /]# icinga2 daemon -C +[root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` Two new services ("nscp-drive-D:" and "nscp-drive-C:") will be visible in Icinga Web 2. -![Icinga 2 Distributed Monitoring Windows Client with NSClient++ nscp-api](images/distributed-monitoring/icinga2_distributed_windows_nscp_api_drivesize_icingaweb2.png) +![Icinga 2 Distributed Monitoring Windows Agent with NSClient++ nscp-api](images/distributed-monitoring/icinga2_distributed_windows_nscp_api_drivesize_icingaweb2.png) Note: You can also omit the `command_endpoint` configuration to execute the command on the master. This also requires a different value for `nscp_api_host` which defaults to `host.address`. - //command_endpoint = host.vars.client_endpoint +``` + //command_endpoint = host.vars.agent_endpoint - //vars.nscp_api_host = "localhost" + //vars.nscp_api_host = "localhost" +``` You can verify the check execution by looking at the `Check Source` attribute in Icinga Web 2 or the REST API. If you want to monitor specific Windows services, you could use the following example: - [root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf +``` +[root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf - object Host "icinga2-client1.localdomain" { - check_command = "hostalive" - address = "192.168.56.111" - vars.client_endpoint = name //follows the convention that host name == endpoint name - vars.os_type = "Windows" - vars.nscp_api_password = "icinga" - vars.services = [ "Windows Update", "wscsvc" ] - } +object Host "icinga2-agent1.localdomain" { + check_command = "hostalive" + address = "192.168.56.111" - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf + vars.agent_endpoint = name //follows the convention that host name == endpoint name + vars.os_type = "Windows" + vars.nscp_api_password = "icinga" + vars.services = [ "Windows Update", "wscsvc" ] +} - apply Service "nscp-api-" for (svc in host.vars.services) { - import "generic-service" +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf - check_command = "nscp_api" - command_endpoint = host.vars.client_endpoint +apply Service "nscp-api-" for (svc in host.vars.services) { + import "generic-service" - //display_name = "nscp-service-" + svc + check_command = "nscp_api" + command_endpoint = host.vars.agent_endpoint - vars.nscp_api_host = "localhost" - vars.nscp_api_query = "check_service" - vars.nscp_api_password = host.vars.nscp_api_password - vars.nscp_api_arguments = [ "service=" + svc ] + //display_name = "nscp-service-" + svc - ignore where host.vars.os_type != "Windows" - } + vars.nscp_api_host = "localhost" + vars.nscp_api_query = "check_service" + vars.nscp_api_password = host.vars.nscp_api_password + vars.nscp_api_arguments = [ "service=" + svc ] + + ignore where host.vars.os_type != "Windows" +} +``` #### NSCLient++ with nscp-local -The [Windows setup](06-distributed-monitoring.md#distributed-monitoring-setup-client-windows) already allows -you to install the NSClient++ package. In addition to the Windows plugins you can +The [Windows setup](06-distributed-monitoring.md#distributed-monitoring-setup-agent-windows) allows +you to install the bundled NSClient++ package. In addition to the Windows plugins you can use the [nscp-local commands](10-icinga-template-library.md#nscp-plugin-check-commands) provided by the Icinga Template Library (ITL). -![Icinga 2 Distributed Monitoring Windows Client with NSClient++](images/distributed-monitoring/icinga2_distributed_windows_nscp.png) +Add the following `include` statement on all your nodes (master, satellite, agent): -Add the following `include` statement on all your nodes (master, satellite, client): +``` +vim /etc/icinga2/icinga2.conf - vim /etc/icinga2/icinga2.conf - - include +include +``` The CheckCommand definitions will automatically determine the installed path to the `nscp.exe` binary. -Based on the [master with clients](06-distributed-monitoring.md#distributed-monitoring-master-clients) +Based on the [master with agents](06-distributed-monitoring.md#distributed-monitoring-master-agents) scenario we'll now add a local nscp check querying a given performance counter. -First, add the client node as host object: +First, add the agent node as host object: - [root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf +``` +[root@icinga2-master1.localdomain /]# cd /etc/icinga2/zones.d/master +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim hosts.conf - object Host "icinga2-client1.localdomain" { - check_command = "hostalive" - address = "192.168.56.111" - vars.client_endpoint = name //follows the convention that host name == endpoint name - vars.os_type = "windows" - } +object Host "icinga2-agent1.localdomain" { + check_command = "hostalive" + address = "192.168.56.111" + + vars.agent_endpoint = name //follows the convention that host name == endpoint name + vars.os_type = "windows" +} +``` Next, add a performance counter check using command endpoint checks (details in the [nscp-local-counter](10-icinga-template-library.md#nscp-check-local-counter) documentation): - [root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf +``` +[root@icinga2-master1.localdomain /etc/icinga2/zones.d/master]# vim services.conf - apply Service "nscp-local-counter-cpu" { - check_command = "nscp-local-counter" - command_endpoint = host.vars.client_endpoint +apply Service "nscp-local-counter-cpu" { + check_command = "nscp-local-counter" + command_endpoint = host.vars.agent_endpoint - vars.nscp_counter_name = "\\Processor(_total)\\% Processor Time" - vars.nscp_counter_perfsyntax = "Total Processor Time" - vars.nscp_counter_warning = 1 - vars.nscp_counter_critical = 5 + vars.nscp_counter_name = "\\Processor(_total)\\% Processor Time" + vars.nscp_counter_perfsyntax = "Total Processor Time" + vars.nscp_counter_warning = 1 + vars.nscp_counter_critical = 5 - vars.nscp_counter_showall = true + vars.nscp_counter_showall = true - assign where host.vars.os_type == "windows" && host.vars.client_endpoint - } + assign where host.vars.os_type == "windows" && host.vars.agent_endpoint +} +``` Validate the configuration and restart Icinga 2. - [root@icinga2-master1.localdomain /]# icinga2 daemon -C - [root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` +[root@icinga2-master1.localdomain /]# icinga2 daemon -C +[root@icinga2-master1.localdomain /]# systemctl restart icinga2 +``` Open Icinga Web 2 and check your newly added Windows NSClient++ check :) -![Icinga 2 Distributed Monitoring Windows Client with NSClient++ nscp-local](images/distributed-monitoring/icinga2_distributed_windows_nscp_counter_icingaweb2.png) +![Icinga 2 Distributed Monitoring Windows Agent with NSClient++ nscp-local](images/distributed-monitoring/icinga2_distributed_windows_nscp_counter_icingaweb2.png) > **Tip** > @@ -2309,10 +2970,11 @@ with automating setups (setup, certificates, configuration). ### Certificate Auto-Renewal -Icinga 2 v2.8+ adds the possibility that nodes request certificate updates +Icinga 2 v2.8+ added the possibility that nodes request certificate updates on their own. If their expiration date is soon enough, they automatically renew their already signed certificate by sending a signing request to the -parent node. +parent node. You'll also see a message in the logs if certificate renewal +isn't necessary. ### High-Availability for Icinga 2 Features @@ -2323,6 +2985,12 @@ By default, the following features provide advanced HA functionality: * [Checks](06-distributed-monitoring.md#distributed-monitoring-high-availability-checks) (load balanced, automated failover). * [Notifications](06-distributed-monitoring.md#distributed-monitoring-high-availability-notifications) (load balanced, automated failover). * [DB IDO](06-distributed-monitoring.md#distributed-monitoring-high-availability-db-ido) (Run-Once, automated failover). +* [Elasticsearch](09-object-types.md#objecttype-elasticsearchwriter) +* [Gelf](09-object-types.md#objecttype-gelfwriter) +* [Graphite](09-object-types.md#objecttype-graphitewriter) +* [InfluxDB](09-object-types.md#objecttype-influxdbwriter) +* [OpenTsdb](09-object-types.md#objecttype-opentsdbwriter) +* [Perfdata](09-object-types.md#objecttype-perfdatawriter) (for PNP) #### High-Availability with Checks @@ -2331,7 +2999,9 @@ have the `checker` feature enabled. Example: - # icinga2 feature enable checker +``` +# icinga2 feature enable checker +``` All nodes in the same zone load-balance the check execution. If one instance shuts down, the other nodes will automatically take over the remaining checks. @@ -2343,7 +3013,9 @@ have the `notification` feature enabled. Example: - # icinga2 feature enable notification +``` +# icinga2 feature enable notification +``` Notifications are load-balanced amongst all nodes in a zone. By default this functionality is enabled. @@ -2358,7 +3030,9 @@ have the DB IDO feature enabled. Example DB IDO MySQL: - # icinga2 feature enable ido-mysql +``` +# icinga2 feature enable ido-mysql +``` By default the DB IDO feature only runs on one node. All other nodes in the same zone disable the active IDO database connection at runtime. The node with the active DB IDO connection is @@ -2379,11 +3053,13 @@ The DB IDO feature will try to determine which cluster endpoint is currently wri to the database and bail out if another endpoint is active. You can manually verify that by running the following query command: - icinga=> SELECT status_update_time, endpoint_name FROM icinga_programstatus; - status_update_time | endpoint_name - ------------------------+--------------- - 2016-08-15 15:52:26+02 | icinga2-master1.localdomain - (1 Zeile) +``` +icinga=> SELECT status_update_time, endpoint_name FROM icinga_programstatus; + status_update_time | endpoint_name +------------------------+--------------- + 2016-08-15 15:52:26+02 | icinga2-master1.localdomain +(1 Zeile) +``` This is useful when the cluster connection between endpoints breaks, and prevents data duplication in split-brain-scenarios. The failover timeout can be set for the @@ -2391,38 +3067,43 @@ data duplication in split-brain-scenarios. The failover timeout can be set for t ### Endpoint Connection Direction -Nodes will attempt to connect to another node when its local [Endpoint](09-object-types.md#objecttype-endpoint) object +Endpoints attempt to connect to another endpoint when its local [Endpoint](09-object-types.md#objecttype-endpoint) object configuration specifies a valid `host` attribute (FQDN or IP address). Example for the master node `icinga2-master1.localdomain` actively connecting -to the client node `icinga2-client1.localdomain`: +to the agent node `icinga2-agent1.localdomain`: - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf - //... +//... - object Endpoint "icinga2-client1.localdomain" { - host = "192.168.56.111" //the master actively tries to connect to the client - log_duration = 0 - } +object Endpoint "icinga2-agent1.localdomain" { + host = "192.168.56.111" // The master actively tries to connect to the agent + log_duration = 0 // Disable the replay log for command endpoint agents +} +``` -Example for the client node `icinga2-client1.localdomain` not actively +Example for the agent node `icinga2-agent1.localdomain` not actively connecting to the master node `icinga2-master1.localdomain`: - [root@icinga2-client1.localdomain /]# vim /etc/icinga2/zones.conf +``` +[root@icinga2-agent1.localdomain /]# vim /etc/icinga2/zones.conf - //... +//... - object Endpoint "icinga2-master1.localdomain" { - //do not actively connect to the master by leaving out the 'host' attribute - log_duration = 0 - } +object Endpoint "icinga2-master1.localdomain" { + // Do not actively connect to the master by leaving out the 'host' attribute + log_duration = 0 // Disable the replay log for command endpoint agents +} +``` -It is not necessary that both the master and the client node establish +It is not necessary that both the master and the agent node establish two connections to each other. Icinga 2 will only use one connection -and close the second connection if established. +and close the second connection if established. This generates useless +CPU cycles and leads to blocking resources when the connection times out. -**Tip**: Choose either to let master/satellite nodes connect to client nodes +**Tip**: Choose either to let master/satellite nodes connect to agent nodes or vice versa. @@ -2433,60 +3114,76 @@ keep the same history (check results, notifications, etc.) when nodes are tempor disconnected and then reconnect. This functionality is not needed when a master/satellite node is sending check -execution events to a client which is purely configured for [command endpoint](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint) -checks only. +execution events to an agent which is configured as [command endpoint](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint) +for check execution. The [Endpoint](09-object-types.md#objecttype-endpoint) object attribute `log_duration` can be lower or set to 0 to fully disable any log replay updates when the -client is not connected. +agent is not connected. Configuration on the master node `icinga2-master1.localdomain`: - [root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf +``` +[root@icinga2-master1.localdomain /]# vim /etc/icinga2/zones.conf - //... +//... - object Endpoint "icinga2-client1.localdomain" { - host = "192.168.56.111" //the master actively tries to connect to the client - log_duration = 0 - } +object Endpoint "icinga2-agent1.localdomain" { + host = "192.168.56.111" // The master actively tries to connect to the agent + log_duration = 0 +} - object Endpoint "icinga2-client2.localdomain" { - host = "192.168.56.112" //the master actively tries to connect to the client - log_duration = 0 - } +object Endpoint "icinga2-agent2.localdomain" { + host = "192.168.56.112" // The master actively tries to connect to the agent + log_duration = 0 +} +``` -Configuration on the client `icinga2-client1.localdomain`: +Configuration on the agent `icinga2-agent1.localdomain`: - [root@icinga2-client1.localdomain /]# vim /etc/icinga2/zones.conf +``` +[root@icinga2-agent1.localdomain /]# vim /etc/icinga2/zones.conf - //... +//... - object Endpoint "icinga2-master1.localdomain" { - //do not actively connect to the master by leaving out the 'host' attribute - log_duration = 0 - } +object Endpoint "icinga2-master1.localdomain" { + // Do not actively connect to the master by leaving out the 'host' attribute + log_duration = 0 +} - object Endpoint "icinga2-master2.localdomain" { - //do not actively connect to the master by leaving out the 'host' attribute - log_duration = 0 - } +object Endpoint "icinga2-master2.localdomain" { + // Do not actively connect to the master by leaving out the 'host' attribute + log_duration = 0 +} +``` -### CSR auto-signing with HA and multiple Level Cluster +### Initial Sync for new Endpoints in a Zone -If you are using two masters in a High-Availability setup it can be necessary -to allow both to sign requested certificates. Ensure to safely sync the following -details in private: +> **Note** +> +> This is required if you decide to change an already running single endpoint production +> environment into a HA-enabled cluster zone with two endpoints. +> The [initial setup](06-distributed-monitoring.md#distributed-monitoring-scenarios-ha-master-clients) +> with 2 HA masters doesn't require this step. -* `TicketSalt` constant in `constants.conf`. -* `var/lib/icinga2/ca` directory. +In order to make sure that all of your zone endpoints have the same state you need +to pick the authoritative running one and copy the following content: -This also helps if you are using a [three level cluster](06-distributed-monitoring.md#distributed-monitoring-scenarios-master-satellite-client) -and your client nodes are not able to reach the CSR auto-signing master node(s). -Make sure that the directory permissions for `/var/lib/icinga2/ca` are secure -(not world readable). +* State file from `/var/lib/icinga2/icinga2.state` +* Internal config package for runtime created objects (downtimes, comments, hosts, etc.) at `/var/lib/icinga2/api/packages/_api` -**Do not expose these private keys to anywhere else. This is a matter of security.** +If you need already deployed config packages from the Director, or synced cluster zones, +you can also sync the entire `/var/lib/icinga2/api/packages` directory. This directory should also be +included in your [backup strategy](02-installation.md#install-backup). + +Do **not** sync `/var/lib/icinga2/api/zones*` manually - this is an internal directory +and handled by the Icinga cluster config sync itself. + +> **Note** +> +> Ensure that all endpoints are shut down during this procedure. Once you have +> synced the cached files, proceed with configuring the remaining endpoints +> to let them know about the new master/satellite node (zones.conf). ### Manual Certificate Creation @@ -2497,7 +3194,9 @@ Choose the host which should store the certificate authority (one of the master The first step is the creation of the certificate authority (CA) by running the following command as root user: - [root@icinga2-master1.localdomain /root]# icinga2 pki new-ca +``` +[root@icinga2-master1.localdomain /root]# icinga2 pki new-ca +``` #### Create CSR and Certificate @@ -2517,11 +3216,6 @@ Sign the CSR with the previously created CA: Repeat the steps for all instances in your setup. -> **Note** -> -> The certificate location changed in v2.8 to `/var/lib/icinga2/certs`. Please read the [upgrading chapter](16-upgrading-icinga-2.md#upgrading-to-2-8-certificate-paths) -> for more details. - #### Copy Certificates Copy the host's certificate files and the public CA certificate to `/var/lib/icinga2/certs`: @@ -2576,15 +3270,17 @@ These are collected best practices from various community channels. If you prefer an alternate method, we still recommend leaving all the Icinga 2 features intact (e.g. `icinga2 feature enable api`). You should also use well known and documented default configuration file locations (e.g. `zones.conf`). -This will tremendously help when someone is trying to help in the [community channels](https://www.icinga.com/community/get-involved/). +This will tremendously help when someone is trying to help in the [community channels](https://icinga.com/community/). ### Silent Windows Setup -If you want to install the client silently/unattended, use the `/qn` modifier. The +If you want to install the agent silently/unattended, use the `/qn` modifier. The installation should not trigger a restart, but if you want to be completely sure, you can use the `/norestart` modifier. - C:> msiexec /i C:\Icinga2-v2.5.0-x86.msi /qn /norestart +``` +C:> msiexec /i C:\Icinga2-v2.5.0-x86.msi /qn /norestart +``` Once the setup is completed you can use the `node setup` cli command too. @@ -2618,7 +3314,9 @@ Example: In case you want to bind the `ApiListener` object to a specific host/port you can specify it like this: - --listen 192.68.56.101,5665 +``` +--listen 192.68.56.101,5665 +``` In case you don't need anything in `conf.d`, use the following command line: @@ -2626,19 +3324,18 @@ In case you don't need anything in `conf.d`, use the following command line: [root@icinga2-master1.localdomain /]# icinga2 node setup --master --disable-confd ``` + + -#### Node Setup with Satellites/Clients - -> **Note** -> -> The certificate location changed in v2.8 to `/var/lib/icinga2/certs`. Please read the [upgrading chapter](16-upgrading-icinga-2.md#upgrading-to-2-8-certificate-paths) -> for more details. +#### Node Setup with Agents/Satellites Make sure that the `/var/lib/icinga2/certs` directory exists and is owned by the `icinga` user (or the user Icinga 2 is running as). - [root@icinga2-client1.localdomain /]# mkdir -p /var/lib/icinga2/certs - [root@icinga2-client1.localdomain /]# chown -R icinga:icinga /var/lib/icinga2/certs +``` +[root@icinga2-agent1.localdomain /]# mkdir -p /var/lib/icinga2/certs +[root@icinga2-agent1.localdomain /]# chown -R icinga:icinga /var/lib/icinga2/certs +``` First you'll need to generate a new local self-signed certificate. Pass the following details to the `pki new-cert` CLI command: @@ -2650,9 +3347,11 @@ Pass the following details to the `pki new-cert` CLI command: Example: - [root@icinga2-client1.localdomain /]# icinga2 pki new-cert --cn icinga2-client1.localdomain \ - --key /var/lib/icinga2/certs/icinga2-client1.localdomain.key \ - --cert /var/lib/icinga2/certs/icinga2-client1.localdomain.crt +``` +[root@icinga2-agent1.localdomain /]# icinga2 pki new-cert --cn icinga2-agent1.localdomain \ +--key /var/lib/icinga2/certs/icinga2-agent1.localdomain.key \ +--cert /var/lib/icinga2/certs/icinga2-agent1.localdomain.crt +``` Request the master certificate from the master host (`icinga2-master1.localdomain`) and store it as `trusted-master.crt`. Review it and continue. @@ -2667,12 +3366,14 @@ Pass the following details to the `pki save-cert` CLI command: Example: - [root@icinga2-client1.localdomain /]# icinga2 pki save-cert --key /var/lib/icinga2/certs/icinga2-client1.localdomain.key \ - --cert /var/lib/icinga2/certs/icinga2-client1.localdomain.crt \ - --trustedcert /var/lib/icinga2/certs/trusted-parent.crt \ - --host icinga2-master1.localdomain +``` +[root@icinga2-agent1.localdomain /]# icinga2 pki save-cert --key /var/lib/icinga2/certs/icinga2-agent1.localdomain.key \ +--cert /var/lib/icinga2/certs/icinga2-agent1.localdomain.crt \ +--trustedcert /var/lib/icinga2/certs/trusted-parent.crt \ +--host icinga2-master1.localdomain +``` -Continue with the additional node setup step. Specify a local endpoint and zone name (`icinga2-client1.localdomain`) +Continue with the additional node setup step. Specify a local endpoint and zone name (`icinga2-agent1.localdomain`) and set the master host (`icinga2-master1.localdomain`) as parent zone configuration. Specify the path to the previously stored trusted master certificate. @@ -2685,7 +3386,7 @@ Pass the following details to the `node setup` CLI command: Trusted master certificate | **Required.** Add the previously fetched trusted master certificate (this step means that you've verified its origin). Parent host | **Optional.** FQDN or IP address of the parent host. This is where the command connects for CSR signing. If not specified, you need to manually copy the parent's public CA certificate file into `/var/lib/icinga2/certs/ca.crt` in order to start Icinga 2. Parent endpoint | **Required.** Specify the parent's endpoint name. - Client zone name | **Required.** Specify the client's zone name. + Local zone name | **Required.** Specify the agent/satellite zone name. Parent zone name | **Optional.** Specify the parent's zone name. Accept config | **Optional.** Whether this node accepts configuration sync from the master node (required for [config sync mode](06-distributed-monitoring.md#distributed-monitoring-top-down-config-sync)). Accept commands | **Optional.** Whether this node accepts command execution messages from the master node (required for [command endpoint mode](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint)). @@ -2694,15 +3395,15 @@ Pass the following details to the `node setup` CLI command: > **Note** > -> The `master_host` parameter is deprecated and will be removed in 2.10.0. Please use `--parent_host` instead. +> The `master_host` parameter is deprecated and will be removed. Please use `--parent_host` instead. -Example for Icinga 2 v2.9: +Example: ``` -[root@icinga2-client1.localdomain /]# icinga2 node setup --ticket ead2d570e18c78abf285d6b85524970a0f69c22d \ ---cn icinga2-client1.localdomain \ +[root@icinga2-agent1.localdomain /]# icinga2 node setup --ticket ead2d570e18c78abf285d6b85524970a0f69c22d \ +--cn icinga2-agent1.localdomain \ --endpoint icinga2-master1.localdomain \ ---zone icinga2-client1.localdomain \ +--zone icinga2-agent1.localdomain \ --parent_zone master \ --parent_host icinga2-master1.localdomain \ --trustedcert /var/lib/icinga2/certs/trusted-parent.crt \ @@ -2710,26 +3411,32 @@ Example for Icinga 2 v2.9: --disable-confd ``` -In case the client should connect to the master node, you'll +In case the agent/satellite should connect to the master node, you'll need to modify the `--endpoint` parameter using the format `cn,host,port`: - --endpoint icinga2-master1.localdomain,192.168.56.101,5665 +``` +--endpoint icinga2-master1.localdomain,192.168.56.101,5665 +``` Specify the parent zone using the `--parent_zone` parameter. This is useful -if the client connects to a satellite, not the master instance. +if the agent connects to a satellite, not the master instance. - --parent_zone satellite +``` +--parent_zone satellite +``` -In case the client should know the additional global zone `linux-templates`, you'll +In case the agent should know the additional global zone `linux-templates`, you'll need to set the `--global-zones` parameter. - --global_zones linux-templates +``` +--global_zones linux-templates +``` The `--parent-host` parameter is optional since v2.9 and allows you to perform a connection-less setup. You cannot restart Icinga 2 yet, the CLI command asked to to manually copy the parent's public CA certificate file in `/var/lib/icinga2/certs/ca.crt`. Once Icinga 2 is started, it sends a ticket signing request to the parent node. If you have provided a ticket, the master node -signs the request and sends it back to the client which performs a certificate update in-memory. +signs the request and sends it back to the agent/satellite which performs a certificate update in-memory. In case you did not provide a ticket, you need to manually sign the CSR on the master node which holds the CA's key pair. @@ -2737,58 +3444,95 @@ which holds the CA's key pair. **You can find additional best practices below.** -Add an additional global zone. Please note the `>>` append mode. - - [root@icinga2-client1.localdomain /]# cat <>/etc/icinga2/zones.conf - object Zone "global-templates" { - global = true - } - EOF - -Note: Packages >= 2.8 provide this configuration by default. - -If this client node is configured as [remote command endpoint execution](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint) +If this agent node is configured as [remote command endpoint execution](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint) you can safely disable the `checker` feature. The `node setup` CLI command already disabled the `notification` feature. - [root@icinga2-client1.localdomain /]# icinga2 feature disable checker +``` +[root@icinga2-agent1.localdomain /]# icinga2 feature disable checker +``` Disable "conf.d" inclusion if this is a [top down](06-distributed-monitoring.md#distributed-monitoring-top-down) -configured client. +configured agent. - [root@icinga2-client1.localdomain /]# sed -i 's/include_recursive "conf.d"/\/\/include_recursive "conf.d"/g' /etc/icinga2/icinga2.conf +``` +[root@icinga2-agent1.localdomain /]# sed -i 's/include_recursive "conf.d"/\/\/include_recursive "conf.d"/g' /etc/icinga2/icinga2.conf +``` + +**Note**: This is the default since v2.9. **Optional**: Add an ApiUser object configuration for remote troubleshooting. - [root@icinga2-client1.localdomain /]# cat </etc/icinga2/conf.d/api-users.conf - object ApiUser "root" { - password = "clientsupersecretpassword" - permissions = ["*"] - } - EOF +``` +[root@icinga2-agent1.localdomain /]# cat </etc/icinga2/conf.d/api-users.conf +object ApiUser "root" { + password = "agentsupersecretpassword" + permissions = ["*"] +} +EOF +``` In case you've previously disabled the "conf.d" directory only add the file file `conf.d/api-users.conf`: - [root@icinga2-client1.localdomain /]# echo 'include "conf.d/api-users.conf"' >> /etc/icinga2/icinga2.conf +``` +[root@icinga2-agent1.localdomain /]# echo 'include "conf.d/api-users.conf"' >> /etc/icinga2/icinga2.conf +``` Finally restart Icinga 2. - [root@icinga2-client1.localdomain /]# systemctl restart icinga2 +``` +[root@icinga2-agent1.localdomain /]# systemctl restart icinga2 +``` Your automation tool must then configure master node in the meantime. -Add the global zone `global-templates` in case it did not exist. - # cat <>/etc/icinga2/zones.conf - object Endpoint "icinga2-client1.localdomain" { - //client connects itself - } +``` +# cat <>/etc/icinga2/zones.conf +object Endpoint "icinga2-agent1.localdomain" { + // Agent connects itself +} - object Zone "icinga2-client1.localdomain" { - endpoints = [ "icinga2-client1.localdomain" ] - parent = "master" - } +object Zone "icinga2-agent1.localdomain" { + endpoints = [ "icinga2-agent1.localdomain" ] + parent = "master" +} - object Zone "global-templates" { - global = true - } - EOF +EOF +``` + +## Using Multiple Environments + +> **Note** +> +> This documentation only covers the basics. Full functionality requires a not yet released addon. + +In some cases it can be desired to run multiple Icinga instances on the same host. +Two potential scenarios include: + +* Different versions of the same monitoring configuration (e.g. production and testing) +* Disparate sets of checks for entirely unrelated monitoring environments (e.g. infrastructure and applications) + +The configuration is done with the global constants `ApiBindHost` and `ApiBindPort` +or the `bind_host` and `bind_port` attributes of the +[ApiListener](09-object-types.md#objecttype-apilistener) object. + +The environment must be set with the global constant `Environment` or as object attribute +of the [IcingaApplication](09-object-types.md#objecttype-icingaapplication) object. + +In any case the constant is default value for the attribute and the direct configuration in the objects +have more precedence. The constants have been added to allow the values being set from the CLI on startup. + +When Icinga establishes a TLS connection to another cluster instance it automatically uses the [SNI extension](https://en.wikipedia.org/wiki/Server_Name_Indication) +to signal which endpoint it is attempting to connect to. On its own this can already be used to position multiple +Icinga instances behind a load balancer. + +SNI example: `icinga2-agent1.localdomain` + +However, if the environment is configured to `production`, Icinga appends the environment name to the SNI hostname like this: + +SNI example with environment: `icinga2-agent1.localdomain:production` + +Middleware like loadbalancers or TLS proxies can read the SNI header and route the connection to the appropriate target. +I.e., it uses a single externally-visible TCP port (usually 5665) and forwards connections to one or more Icinga +instances which are bound to a local TCP port. It does so by inspecting the environment name that is sent as part of the +SNI extension. diff --git a/doc/07-agent-based-monitoring.md b/doc/07-agent-based-monitoring.md index e2597a341..4d2802b07 100644 --- a/doc/07-agent-based-monitoring.md +++ b/doc/07-agent-based-monitoring.md @@ -1,181 +1,221 @@ -# Additional Agent-based Checks +# Agent-based Checks If the remote services are not directly accessible through the network, a local agent installation exposing the results to check queries can become handy. +Prior to installing and configuration an agent service, evaluate possible +options based on these requirements: + +* Security (authentication, TLS certificates, secure connection handling, etc.) +* Connection direction + * Master/satellite can execute commands directly or + * Agent sends back passive/external check results +* Availability on specific OS types and versions + * Packages available +* Configuration and initial setup +* Updates and maintenance, compatibility + +Available agent types: + +* [Icinga Agent](07-agent-based-monitoring.md#agent-based-checks-icinga) on Linux/Unix and Windows +* [SSH](07-agent-based-monitoring.md#agent-based-checks-ssh) on Linux/Unix +* [SNMP](07-agent-based-monitoring.md#agent-based-checks-snmp) on Linux/Unix and hardware +* [SNMP Traps](07-agent-based-monitoring.md#agent-based-checks-snmp-traps) as passive check results +* [REST API](07-agent-based-monitoring.md#agent-based-checks-rest-api) for passive external check results +* [NSClient++](07-agent-based-monitoring.md#agent-based-checks-nsclient) and [WMI](07-agent-based-monitoring.md#agent-based-checks-wmi) on Windows + + +## Icinga Agent + +For the most common setups on Linux/Unix and Windows, we recommend +to setup the Icinga agent in a [distributed environment](06-distributed-monitoring.md#distributed-monitoring). + +![Icinga 2 Distributed Master with Agents](images/distributed-monitoring/icinga2_distributed_monitoring_scenarios_master_with_agents.png) + +Key benefits: + +* Directly integrated into the distributed monitoring stack of Icinga +* Works on Linux/Unix and Windows +* Secure communication with TLS +* Connection can be established from both sides. Once connected, command execution and check results are exchanged. + * Master/satellite connects to agent + * Agent connects to parent satellite/master +* Same configuration language and binaries +* Troubleshooting docs and community best practices + +Follow the setup and configuration instructions [here](06-distributed-monitoring.md#distributed-monitoring-setup-agent-satellite). + +On Windows hosts, the Icinga agent can query a local NSClient++ service +for additional checks in case there are no plugins available. The NSCP +installer is bundled with Icinga and can be installed with the setup wizard. + +![Icinga 2 Windows Setup](images/distributed-monitoring/icinga2_windows_setup_wizard_01.png) + +## SSH + +> **Tip** +> +> This is the recommended way for systems where the Icinga agent is not available +> Be it specific hardware architectures, old systems or forbidden to install an additional software. + +This method uses the SSH service on the remote host to execute +an arbitrary plugin command line. The output and exit code is +returned and used by the core. + +The `check_by_ssh` plugin takes care of this. It is available in the +[Monitoring Plugins package](02-installation.md#setting-up-check-plugins). +For your convenience, the Icinga template library provides the [by_ssh](10-icinga-template-library.md#plugin-check-command-by-ssh) +CheckCommand already. + +### SSH: Preparations + +SSH key pair for the Icinga daemon user. In case the user has no shell, temporarily enable this. +When asked for a passphrase, **do not set it** and press enter. + +``` +sudo su - icinga + +ssh-keygen -b 4096 -t rsa -C "icinga@$(hostname) user for check_by_ssh" -f $HOME/.ssh/id_rsa +``` + +On the remote agent, create the icinga user and generate a temporary password. + +``` +useradd -m icinga +passwd icinga +``` + +Copy the public key from the Icinga server to the remote agent, e.g. with `ssh-copy-id` +or manually into `/home/icinga/.ssh/authorized_keys`. +This will ask for the password once. + +``` +sudo su - icinga + +ssh-copy-id -i $HOME/.ssh/id_rsa icinga@ssh-agent1.localdomain +``` + +After the SSH key is copied, test at the connection **at least once** and +accept the host key verification. If you forget about this step, checks will +become UNKNOWN later. + +``` +ssh -i $HOME/.ssh/id_rsa icinga@ssh-agent1.localdomain +``` + +After the SSH key login works, disable the previously enabled logins. + +* Remote agent user's password with `passwd -l icinga` +* Local icinga user terminal + +Also, ensure that the permissions are correct for the `.ssh` directory +as otherwise logins will fail. + +* `.ssh` directory: 700 +* `.ssh/id_rsa.pub` public key file: 644 +* `.ssh/id_rsa` private key file: 600 + + +### SSH: Configuration + +First, create a host object which has SSH configured and enabled. +Mark this e.g. with the custom variable `agent_type` to later +use this for service apply rule matches. Best practice is to +store that in a specific template, either in the static configuration +or inside the Director. + +``` +template Host "ssh-agent" { + check_command = "hostalive" + + vars.agent_type = "ssh" + vars.os_type = "linux" +} + +object Host "ssh-agent1.localdomain" { + import "ssh-agent" + + address = "192.168.56.115" +} +``` + +Example for monitoring the remote users: + +``` +apply Service "users" { + check_command = "by_ssh" + + vars.by_ssh_command = [ "/usr/lib/nagios/plugins/check_users" ] + + // Follows the same principle as with command arguments, e.g. for ordering + vars.by_ssh_arguments = { + "-w" = { + value = "$users_wgreater$" // Can reference an existing custom variable defined on the host or service, evaluated at runtime + } + "-c" = { + value = "$users_cgreater$" + } + } + + vars.users_wgreater = 3 + vars.users_cgreater = 5 + + assign where host.vars.os_type == "linux" && host.vars.agent_type == "ssh" +} +``` + +A more advanced example with better arguments is shown in [this blogpost](https://www.netways.de/blog/2016/03/21/check_by_ssh-mit-icinga-2/). + + ## SNMP -The SNMP daemon runs on the remote system and answers SNMP queries by plugin -binaries. The [Monitoring Plugins package](02-getting-started.md#setting-up-check-plugins) ships +The SNMP daemon runs on the remote system and answers SNMP queries by plugin scripts. +The [Monitoring Plugins package](02-installation.md#setting-up-check-plugins) provides the `check_snmp` plugin binary, but there are plenty of [existing plugins](05-service-monitoring.md#service-monitoring-plugins) for specific use cases already around, for example monitoring Cisco routers. -The following example uses the [SNMP ITL](10-icinga-template-library.md#plugin-check-command-snmp) `CheckCommand` and just -overrides the `snmp_oid` custom attribute. A service is created for all hosts which -have the `snmp-community` custom attribute. +The following example uses the [SNMP ITL](10-icinga-template-library.md#plugin-check-command-snmp) +CheckCommand and sets the `snmp_oid` custom variable. A service is created for all hosts which +have the `snmp-community` custom variable. - apply Service "uptime" { - import "generic-service" +``` +template Host "snmp-agent" { + check_command = "hostalive" - check_command = "snmp" - vars.snmp_oid = "1.3.6.1.2.1.1.3.0" - vars.snmp_miblist = "DISMAN-EVENT-MIB" + vars.agent_type = "snmp" - assign where host.vars.snmp_community != "" - } + vars.snmp_community = "public-icinga" +} -Additional SNMP plugins are available using the [Manubulon SNMP Plugins](10-icinga-template-library.md#snmp-manubulon-plugin-check-commands). +object Host "snmp-agent1.localdomain" { + import "snmp-agent" +} +``` + +``` +apply Service "uptime" { + import "generic-service" + + check_command = "snmp" + vars.snmp_oid = "1.3.6.1.2.1.1.3.0" + vars.snmp_miblist = "DISMAN-EVENT-MIB" + + assign where host.vars.agent_type == "snmp" && host.vars.snmp_community != "" +} +``` If no `snmp_miblist` is specified, the plugin will default to `ALL`. As the number of available MIB files on the system increases so will the load generated by this plugin if no `MIB` is specified. As such, it is recommended to always specify at least one `MIB`. -## SSH +Additional SNMP plugins are available using the [Manubulon SNMP Plugins](10-icinga-template-library.md#snmp-manubulon-plugin-check-commands). -Calling a plugin using the SSH protocol to execute a plugin on the remote server fetching -its return code and output. The `by_ssh` command object is part of the built-in templates and -requires the `check_by_ssh` check plugin which is available in the [Monitoring Plugins package](02-getting-started.md#setting-up-check-plugins). - - object CheckCommand "by_ssh_swap" { - import "by_ssh" - - vars.by_ssh_command = "/usr/lib/nagios/plugins/check_swap -w $by_ssh_swap_warn$ -c $by_ssh_swap_crit$" - vars.by_ssh_swap_warn = "75%" - vars.by_ssh_swap_crit = "50%" - } - - object Service "swap" { - import "generic-service" - - host_name = "remote-ssh-host" - - check_command = "by_ssh_swap" - - vars.by_ssh_logname = "icinga" - } - -## NSClient++ - -[NSClient++](https://nsclient.org/) works on both Windows and Linux platforms and is well -known for its magnificent Windows support. There are alternatives like the WMI interface, -but using `NSClient++` will allow you to run local scripts similar to check plugins fetching -the required output and performance counters. - -You can use the `check_nt` plugin from the Monitoring Plugins project to query NSClient++. -Icinga 2 provides the [nscp check command](10-icinga-template-library.md#plugin-check-command-nscp) for this: - -Example: - - object Service "disk" { - import "generic-service" - - host_name = "remote-windows-host" - - check_command = "nscp" - - vars.nscp_variable = "USEDDISKSPACE" - vars.nscp_params = "c" - vars.nscp_warn = 70 - vars.nscp_crit = 80 - } - -For details on the `NSClient++` configuration please refer to the [official documentation](https://docs.nsclient.org/). - -## NSCA-NG - -[NSCA-ng](http://www.nsca-ng.org) provides a client-server pair that allows the -remote sender to push check results into the Icinga 2 `ExternalCommandListener` -feature. - -> **Note** -> -> This addon works in a similar fashion like the Icinga 1.x distributed model. If you -> are looking for a real distributed architecture with Icinga 2, scroll down. - -## NRPE - -[NRPE](https://docs.icinga.com/latest/en/nrpe.html) runs as daemon on the remote client including -the required plugins and command definitions. -Icinga 2 calls the `check_nrpe` plugin binary in order to query the configured command on the -remote client. - -> **Note** -> -> The NRPE protocol is considered insecure and has multiple flaws in its -> design. Upstream is not willing to fix these issues. -> -> In order to stay safe, please use the native [Icinga 2 client](06-distributed-monitoring.md#distributed-monitoring) -> instead. - -The NRPE daemon uses its own configuration format in nrpe.cfg while `check_nrpe` -can be embedded into the Icinga 2 `CheckCommand` configuration syntax. - -You can use the `check_nrpe` plugin from the NRPE project to query the NRPE daemon. -Icinga 2 provides the [nrpe check command](10-icinga-template-library.md#plugin-check-command-nrpe) for this: - -Example: - - object Service "users" { - import "generic-service" - - host_name = "remote-nrpe-host" - - check_command = "nrpe" - vars.nrpe_command = "check_users" - } - -nrpe.cfg: - - command[check_users]=/usr/local/icinga/libexec/check_users -w 5 -c 10 - -If you are planning to pass arguments to NRPE using the `-a` -command line parameter, make sure that your NRPE daemon has them -supported and enabled. - -> **Note** -> -> Enabling command arguments in NRPE is considered harmful -> and exposes a security risk allowing attackers to execute -> commands remotely. Details at [seclists.org](http://seclists.org/fulldisclosure/2014/Apr/240). - -The plugin check command `nrpe` provides the `nrpe_arguments` custom -attribute which expects either a single value or an array of values. - -Example: - - object Service "nrpe-disk-/" { - import "generic-service" - - host_name = "remote-nrpe-host" - - check_command = "nrpe" - vars.nrpe_command = "check_disk" - vars.nrpe_arguments = [ "20%", "10%", "/" ] - } - -Icinga 2 will execute the nrpe plugin like this: - - /usr/lib/nagios/plugins/check_nrpe -H -c 'check_disk' -a '20%' '10%' '/' - -NRPE expects all additional arguments in an ordered fashion -and interprets the first value as `$ARG1$` macro, the second -value as `$ARG2$`, and so on. - -nrpe.cfg: - - command[check_disk]=/usr/local/icinga/libexec/check_disk -w $ARG1$ -c $ARG2$ -p $ARG3$ - -Using the above example with `nrpe_arguments` the command -executed by the NRPE daemon looks similar to that: - - /usr/local/icinga/libexec/check_disk -w 20% -c 10% -p / - -You can pass arguments in a similar manner to [NSClient++](07-agent-based-monitoring.md#agent-based-checks-nsclient) -when using its NRPE supported check method. +For network monitoring, community members advise to use [nwc_health](05-service-monitoring.md#service-monitoring-network) +for example. -## Passive Check Results and SNMP Traps +## SNMP Traps and Passive Check Results SNMP Traps can be received and filtered by using [SNMPTT](http://snmptt.sourceforge.net/) and specific trap handlers passing the check results to Icinga 2. @@ -193,14 +233,16 @@ state or from a missed reset event. Add a directive in `snmptt.conf` - EVENT coldStart .1.3.6.1.6.3.1.1.5.1 "Status Events" Normal - FORMAT Device reinitialized (coldStart) - EXEC echo "[$@] PROCESS_SERVICE_CHECK_RESULT;$A;Coldstart;2;The snmp agent has reinitialized." >> /var/run/icinga2/cmd/icinga2.cmd - SDESC - A coldStart trap signifies that the SNMPv2 entity, acting - in an agent role, is reinitializing itself and that its - configuration may have been altered. - EDESC +``` +EVENT coldStart .1.3.6.1.6.3.1.1.5.1 "Status Events" Normal +FORMAT Device reinitialized (coldStart) +EXEC echo "[$@] PROCESS_SERVICE_CHECK_RESULT;$A;Coldstart;2;The snmp agent has reinitialized." >> /var/run/icinga2/cmd/icinga2.cmd +SDESC +A coldStart trap signifies that the SNMPv2 entity, acting +in an agent role, is reinitializing itself and that its +configuration may have been altered. +EDESC +``` 1. Define the `EVENT` as per your need. 2. Construct the `EXEC` statement with the service name matching your template @@ -208,107 +250,123 @@ applied to your _n_ hosts. The host address inferred by SNMPTT will be the correlating factor. You can have snmptt provide host names or ip addresses to match your Icinga convention. +> **Note** +> +> Replace the deprecated command pipe EXEC statement with a curl call +> to the REST API action [process-check-result](12-icinga2-api.md#icinga2-api-actions-process-check-result). + Add an `EventCommand` configuration object for the passive service auto reset event. - object EventCommand "coldstart-reset-event" { - command = [ SysconfDir + "/icinga2/conf.d/custom/scripts/coldstart_reset_event.sh" ] +``` +object EventCommand "coldstart-reset-event" { + command = [ ConfigDir + "/conf.d/custom/scripts/coldstart_reset_event.sh" ] - arguments = { - "-i" = "$service.state_id$" - "-n" = "$host.name$" - "-s" = "$service.name$" - } - } + arguments = { + "-i" = "$service.state_id$" + "-n" = "$host.name$" + "-s" = "$service.name$" + } +} +``` Create the `coldstart_reset_event.sh` shell script to pass the expanded variable data in. The `$service.state_id$` is important in order to prevent an endless loop of event firing after the service has been reset. - #!/bin/bash +``` +#!/bin/bash - SERVICE_STATE_ID="" - HOST_NAME="" - SERVICE_NAME="" +SERVICE_STATE_ID="" +HOST_NAME="" +SERVICE_NAME="" - show_help() - { - cat <<-EOF - Usage: ${0##*/} [-h] -n HOST_NAME -s SERVICE_NAME - Writes a coldstart reset event to the Icinga command pipe. +show_help() +{ +cat <<-EOF + Usage: ${0##*/} [-h] -n HOST_NAME -s SERVICE_NAME + Writes a coldstart reset event to the Icinga command pipe. - -h Display this help and exit. - -i SERVICE_STATE_ID The associated service state id. - -n HOST_NAME The associated host name. - -s SERVICE_NAME The associated service name. - EOF - } + -h Display this help and exit. + -i SERVICE_STATE_ID The associated service state id. + -n HOST_NAME The associated host name. + -s SERVICE_NAME The associated service name. +EOF +} - while getopts "hi:n:s:" opt; do - case "$opt" in - h) - show_help - exit 0 - ;; - i) - SERVICE_STATE_ID=$OPTARG - ;; - n) - HOST_NAME=$OPTARG - ;; - s) - SERVICE_NAME=$OPTARG - ;; - '?') - show_help - exit 0 - ;; - esac - done +while getopts "hi:n:s:" opt; do + case "$opt" in + h) + show_help + exit 0 + ;; + i) + SERVICE_STATE_ID=$OPTARG + ;; + n) + HOST_NAME=$OPTARG + ;; + s) + SERVICE_NAME=$OPTARG + ;; + '?') + show_help + exit 0 + ;; + esac +done - if [ -z "$SERVICE_STATE_ID" ]; then - show_help - printf "\n Error: -i required.\n" - exit 1 - fi +if [ -z "$SERVICE_STATE_ID" ]; then + show_help + printf "\n Error: -i required.\n" + exit 1 +fi - if [ -z "$HOST_NAME" ]; then - show_help - printf "\n Error: -n required.\n" - exit 1 - fi +if [ -z "$HOST_NAME" ]; then + show_help + printf "\n Error: -n required.\n" + exit 1 +fi - if [ -z "$SERVICE_NAME" ]; then - show_help - printf "\n Error: -s required.\n" - exit 1 - fi +if [ -z "$SERVICE_NAME" ]; then + show_help + printf "\n Error: -s required.\n" + exit 1 +fi - if [ "$SERVICE_STATE_ID" -gt 0 ]; then - echo "[`date +%s`] PROCESS_SERVICE_CHECK_RESULT;$HOST_NAME;$SERVICE_NAME;0;Auto-reset (`date +"%m-%d-%Y %T"`)." >> /var/run/icinga2/cmd/icinga2.cmd - fi +if [ "$SERVICE_STATE_ID" -gt 0 ]; then + echo "[`date +%s`] PROCESS_SERVICE_CHECK_RESULT;$HOST_NAME;$SERVICE_NAME;0;Auto-reset (`date +"%m-%d-%Y %T"`)." >> /var/run/icinga2/cmd/icinga2.cmd +fi +``` + +> **Note** +> +> Replace the deprecated command pipe EXEC statement with a curl call +> to the REST API action [process-check-result](12-icinga2-api.md#icinga2-api-actions-process-check-result). Finally create the `Service` and assign it: - apply Service "Coldstart" { - import "generic-service-custom" +``` +apply Service "Coldstart" { + import "generic-service-custom" - check_command = "dummy" - event_command = "coldstart-reset-event" + check_command = "dummy" + event_command = "coldstart-reset-event" - enable_notifications = 1 - enable_active_checks = 0 - enable_passive_checks = 1 - enable_flapping = 0 - volatile = 1 - enable_perfdata = 0 + enable_notifications = 1 + enable_active_checks = 0 + enable_passive_checks = 1 + enable_flapping = 0 + volatile = 1 + enable_perfdata = 0 - vars.dummy_state = 0 - vars.dummy_text = "Manual reset." + vars.dummy_state = 0 + vars.dummy_text = "Manual reset." - vars.sla = "24x7" + vars.sla = "24x7" - assign where (host.vars.os == "Linux" || host.vars.os == "Windows") - } + assign where (host.vars.os == "Linux" || host.vars.os == "Windows") +} +``` ### Complex SNMP Traps @@ -321,39 +379,109 @@ As long as the most recent passive update has occurred, the active check is bypa Add a directive in `snmptt.conf` - EVENT enterpriseSpecific "Status Events" Normal - FORMAT Enterprise specific trap - EXEC echo "[$@] PROCESS_SERVICE_CHECK_RESULT;$A;$1;$2;$3" >> /var/run/icinga2/cmd/icinga2.cmd - SDESC - An enterprise specific trap. - The varbinds in order denote the Icinga service name, state and text. - EDESC +``` +EVENT enterpriseSpecific "Status Events" Normal +FORMAT Enterprise specific trap +EXEC echo "[$@] PROCESS_SERVICE_CHECK_RESULT;$A;$1;$2;$3" >> /var/run/icinga2/cmd/icinga2.cmd +SDESC +An enterprise specific trap. +The varbinds in order denote the Icinga service name, state and text. +EDESC +``` 1. Define the `EVENT` as per your need using your actual oid. 2. The service name, state and text are extracted from the first three varbinds. This has the advantage of accommodating an unlimited set of use cases. +> **Note** +> +> Replace the deprecated command pipe EXEC statement with a curl call +> to the REST API action [process-check-result](12-icinga2-api.md#icinga2-api-actions-process-check-result). + Create a `Service` for the specific use case associated to the host. If the host matches and the first varbind value is `Backup`, SNMPTT will submit the corresponding passive update with the state and text from the second and third varbind: - object Service "Backup" { - import "generic-service-custom" +``` +object Service "Backup" { + import "generic-service-custom" - host_name = "host.domain.com" - check_command = "dummy" + host_name = "host.domain.com" + check_command = "dummy" - enable_notifications = 1 - enable_active_checks = 1 - enable_passive_checks = 1 - enable_flapping = 0 - volatile = 1 - max_check_attempts = 1 - check_interval = 87000 - enable_perfdata = 0 + enable_notifications = 1 + enable_active_checks = 1 + enable_passive_checks = 1 + enable_flapping = 0 + volatile = 1 + max_check_attempts = 1 + check_interval = 87000 + enable_perfdata = 0 + + vars.sla = "24x7" + vars.dummy_state = 2 + vars.dummy_text = "No passive check result received." +} +``` + + +## Agents sending Check Results via REST API + +Whenever the remote agent cannot run the Icinga agent, or a backup script +should just send its current state after finishing, you can use the [REST API](12-icinga2-api.md#icinga2-api) +as secure transport and send [passive external check results](08-advanced-topics.md#external-check-results). + +Use the [process-check-result](12-icinga2-api.md#icinga2-api-actions-process-check-result) API action to send the external passive check result. +You can either use `curl` or implement the HTTP requests in your preferred programming +language. Examples for API clients are available in [this chapter](12-icinga2-api.md#icinga2-api-clients). + +Feeding check results from remote hosts requires the host/service +objects configured on the master/satellite instance. + +## NSClient++ on Windows + +[NSClient++](https://nsclient.org/) works on both Windows and Linux platforms and is well +known for its magnificent Windows support. There are alternatives like the WMI interface, +but using `NSClient++` will allow you to run local scripts similar to check plugins fetching +the required output and performance counters. + +> **Tip** +> +> Best practice is to use the Icinga agent as secure execution +> bridge (`check_nt` and `check_nrpe` are considered insecure) +> and query the NSClient++ service [locally](06-distributed-monitoring.md#distributed-monitoring-windows-nscp). + +You can use the `check_nt` plugin from the Monitoring Plugins project to query NSClient++. +Icinga 2 provides the [nscp check command](10-icinga-template-library.md#plugin-check-command-nscp) for this: + +Example: + +``` +object Service "disk" { + import "generic-service" + + host_name = "remote-windows-host" + + check_command = "nscp" + + vars.nscp_variable = "USEDDISKSPACE" + vars.nscp_params = "c" + vars.nscp_warn = 70 + vars.nscp_crit = 80 +} +``` + +For details on the `NSClient++` configuration please refer to the [official documentation](https://docs.nsclient.org/). + +## WMI on Windows + +The most popular plugin is [check_wmi_plus](http://edcint.co.nz/checkwmiplus/). + +> Check WMI Plus uses the Windows Management Interface (WMI) to check for common services (cpu, disk, sevices, eventlog…) on Windows machines. It requires the open source wmi client for Linux. + +Community examples: + +* [Icinga 2 check_wmi_plus example by 18pct](http://18pct.com/icinga2-check_wmi_plus-example/) +* [Agent-less monitoring with WMI](https://www.devlink.de/linux/icinga2-nagios-agentless-monitoring-von-windows/) - vars.sla = "24x7" - vars.dummy_state = 2 - vars.dummy_text = "No passive check result received." - } diff --git a/doc/08-advanced-topics.md b/doc/08-advanced-topics.md index c51a303d7..fe427abfb 100644 --- a/doc/08-advanced-topics.md +++ b/doc/08-advanced-topics.md @@ -206,14 +206,6 @@ the `check_period` attribute. Or a notification should be sent to users or not, filtered by the `period` and `notification_period` configuration attributes for `Notification` and `User` objects. -> **Note** -> -> If you are familiar with Icinga 1.x, these time period definitions -> are called `legacy timeperiods` in Icinga 2. -> -> An Icinga 2 legacy timeperiod requires the `ITL` provided template ->`legacy-timeperiod`. - The `TimePeriod` attribute `ranges` may contain multiple directives, including weekdays, days of the month, and calendar dates. These types may overlap/override other types in your ranges dictionary. @@ -231,78 +223,97 @@ If you don't set any `check_period` or `notification_period` attribute on your configuration objects, Icinga 2 assumes `24x7` as time period as shown below. - object TimePeriod "24x7" { - import "legacy-timeperiod" - - display_name = "Icinga 2 24x7 TimePeriod" - ranges = { - "monday" = "00:00-24:00" - "tuesday" = "00:00-24:00" - "wednesday" = "00:00-24:00" - "thursday" = "00:00-24:00" - "friday" = "00:00-24:00" - "saturday" = "00:00-24:00" - "sunday" = "00:00-24:00" - } - } +``` +object TimePeriod "24x7" { + display_name = "Icinga 2 24x7 TimePeriod" + ranges = { + "monday" = "00:00-24:00" + "tuesday" = "00:00-24:00" + "wednesday" = "00:00-24:00" + "thursday" = "00:00-24:00" + "friday" = "00:00-24:00" + "saturday" = "00:00-24:00" + "sunday" = "00:00-24:00" + } +} +``` If your operation staff should only be notified during workhours, create a new timeperiod named `workhours` defining a work day from 09:00 to 17:00. - object TimePeriod "workhours" { - import "legacy-timeperiod" +``` +object TimePeriod "workhours" { + display_name = "Icinga 2 8x5 TimePeriod" + ranges = { + "monday" = "09:00-17:00" + "tuesday" = "09:00-17:00" + "wednesday" = "09:00-17:00" + "thursday" = "09:00-17:00" + "friday" = "09:00-17:00" + } +} +``` - display_name = "Icinga 2 8x5 TimePeriod" - ranges = { - "monday" = "09:00-17:00" - "tuesday" = "09:00-17:00" - "wednesday" = "09:00-17:00" - "thursday" = "09:00-17:00" - "friday" = "09:00-17:00" - } - } +### Across midnight -Furthermore if you wish to specify a notification period across midnight, +If you want to specify a notification period across midnight, you can define it the following way: - object Timeperiod "across-midnight" { - import "legacy-timeperiod" +``` +object Timeperiod "across-midnight" { + display_name = "Nightly Notification" + ranges = { + "saturday" = "22:00-24:00" + "sunday" = "00:00-03:00" + } +} +``` - display_name = "Nightly Notification" - ranges = { - "saturday" = "22:00-24:00" - "sunday" = "00:00-03:00" - } - } +Starting with v2.11 this can be shortened to using +the first day as start with an overlapping range into +the next day: + +``` +object Timeperiod "do-not-disturb" { + display_name = "Weekend DND" + ranges = { + "saturday" = "22:00-06:00" + } +} +``` + +### Across several days, weeks or months Below you can see another example for configuring timeperiods across several days, weeks or months. This can be useful when taking components offline for a distinct period of time. - object Timeperiod "standby" { - import "legacy-timeperiod" - - display_name = "Standby" - ranges = { - "2016-09-30 - 2016-10-30" = "00:00-24:00" - } - } +``` +object Timeperiod "standby" { + display_name = "Standby" + ranges = { + "2016-09-30 - 2016-10-30" = "00:00-24:00" + } +} +``` Please note that the spaces before and after the dash are mandatory. Once your time period is configured you can Use the `period` attribute to assign time periods to `Notification` and `Dependency` objects: - object Notification "mail" { - import "generic-notification" +``` +apply Notification "mail-icingaadmin" to Service { + import "mail-service-notification" + user_groups = host.vars.notification.mail.groups + users = host.vars.notification.mail.users - host_name = "localhost" + period = "workhours" - command = "mail-notification" - users = [ "icingaadmin" ] - period = "workhours" - } + assign where host.vars.notification.mail +} +``` ### Time Periods Inclusion and Exclusion @@ -319,53 +330,53 @@ preferred. The following example defines a time period called `holidays` where notifications should be suppressed: - object TimePeriod "holidays" { - import "legacy-timeperiod" - - ranges = { - "january 1" = "00:00-24:00" //new year's day - "july 4" = "00:00-24:00" //independence day - "december 25" = "00:00-24:00" //christmas - "december 31" = "18:00-24:00" //new year's eve (6pm+) - "2017-04-16" = "00:00-24:00" //easter 2017 - "monday -1 may" = "00:00-24:00" //memorial day (last monday in may) - "monday 1 september" = "00:00-24:00" //labor day (1st monday in september) - "thursday 4 november" = "00:00-24:00" //thanksgiving (4th thursday in november) - } - } +``` +object TimePeriod "holidays" { + ranges = { + "january 1" = "00:00-24:00" //new year's day + "july 4" = "00:00-24:00" //independence day + "december 25" = "00:00-24:00" //christmas + "december 31" = "18:00-24:00" //new year's eve (6pm+) + "2017-04-16" = "00:00-24:00" //easter 2017 + "monday -1 may" = "00:00-24:00" //memorial day (last monday in may) + "monday 1 september" = "00:00-24:00" //labor day (1st monday in september) + "thursday 4 november" = "00:00-24:00" //thanksgiving (4th thursday in november) + } +} +``` In addition to that the time period `weekends` defines an additional time window which should be excluded from notifications: - object TimePeriod "weekends-excluded" { - import "legacy-timeperiod" - - ranges = { - "saturday" = "00:00-09:00,18:00-24:00" - "sunday" = "00:00-09:00,18:00-24:00" - } - } +``` +object TimePeriod "weekends-excluded" { + ranges = { + "saturday" = "00:00-09:00,18:00-24:00" + "sunday" = "00:00-09:00,18:00-24:00" + } +} +``` The time period `prod-notification` defines the default time ranges and adds the excluded time period names as an array. - object TimePeriod "prod-notification" { - import "legacy-timeperiod" +``` +object TimePeriod "prod-notification" { + excludes = [ "holidays", "weekends-excluded" ] - excludes = [ "holidays", "weekends-excluded" ] + ranges = { + "monday" = "00:00-24:00" + "tuesday" = "00:00-24:00" + "wednesday" = "00:00-24:00" + "thursday" = "00:00-24:00" + "friday" = "00:00-24:00" + "saturday" = "00:00-24:00" + "sunday" = "00:00-24:00" + } +} +``` - ranges = { - "monday" = "00:00-24:00" - "tuesday" = "00:00-24:00" - "wednesday" = "00:00-24:00" - "thursday" = "00:00-24:00" - "friday" = "00:00-24:00" - "saturday" = "00:00-24:00" - "sunday" = "00:00-24:00" - } - } - -## External Check Results +## External Passive Check Results Hosts or services which do not actively execute a check plugin to receive the state and output are called "passive checks" or "external check results". @@ -393,12 +404,16 @@ In Icinga 2 active check freshness is enabled by default. It is determined by th The threshold is calculated based on the last check execution time for actively executed checks: - (last check execution time + check interval) > current time +``` +(last check execution time + check interval) > current time +``` If this host/service receives check results from an [external source](08-advanced-topics.md#external-check-results), the threshold is based on the last time a check result was received: - (last check result time + check interval) > current time +``` +(last check result time + check interval) > current time +``` > **Tip** > @@ -443,13 +458,12 @@ Icinga 2 supports optional detection of hosts and services that are "flapping". Flapping occurs when a service or host changes state too frequently, which would result in a storm of problem and recovery notifications. With flapping detection enabled a flapping notification will be sent while other notifications are -suppresed until it calms down after receiving the same status from checks a few times. Flapping detection can help detect - -configuration problems (wrong thresholds), troublesome services, or network problems. +suppressed until it calms down after receiving the same status from checks a few times. Flapping detection can help detect +configuration problems (wrong thresholds), troublesome services or network problems. Flapping detection can be enabled or disabled using the `enable_flapping` attribute. The `flapping_threshold_high` and `flapping_threshold_low` attributes allows to specify the thresholds that control -when a [host](09-object-types.md#objecttype-host) or [service](objecttype-service) is considered to be flapping. +when a [host](09-object-types.md#objecttype-host) or [service](09-object-types.md#objecttype-service) is considered to be flapping. The default thresholds are 30% for high and 25% for low. If the computed flapping value exceeds the high threshold a host or service is considered flapping until it drops below the low flapping threshold. @@ -466,7 +480,7 @@ Icinga 2 saves the last 20 state changes for every host and service. See the gra ![Icinga 2 Flapping State Timeline](images/advanced-topics/flapping-state-graph.png) -All the states ware weighted, with the most recent one being worth the most (1.15) and the 20th the least (0.8). The +All the states are weighted, with the most recent one being worth the most (1.15) and the 20th the least (0.8). The states in between are fairly distributed. The final flapping value are the weighted state changes divided by the total count of 20. @@ -515,7 +529,7 @@ System | Memory, Swap | [mem](10-icinga-template-library.md#plugin-contrib-co System | Hardware | [hpasm](10-icinga-template-library.md#plugin-contrib-command-hpasm), [ipmi-sensor](10-icinga-template-library.md#plugin-contrib-command-ipmi-sensor) System | Virtualization | [VMware](10-icinga-template-library.md#plugin-contrib-vmware), [esxi_hardware](10-icinga-template-library.md#plugin-contrib-command-esxi-hardware) System | Processes | [procs](10-icinga-template-library.md#plugin-check-command-processes), [service-windows](10-icinga-template-library.md#windows-plugins) (Windows Client) -System | System Activity Reports | [check_sar_perf](https://github.com/dnsmichi/icinga-plugins/blob/master/scripts/check_sar_perf.py) +System | System Activity Reports | [sar-perf](10-icinga-template-library.md#plugin-contrib-command-sar-perf) System | I/O | [iostat](10-icinga-template-library.md#plugin-contrib-command-iostat) System | Network interfaces | [nwc_health](10-icinga-template-library.md#plugin-contrib-command-nwc_health), [interfaces](10-icinga-template-library.md#plugin-contrib-command-interfaces) System | Users | [users](10-icinga-template-library.md#plugin-check-command-users), [users-windows](10-icinga-template-library.md#windows-plugins) (Windows Client) @@ -528,8 +542,8 @@ Database | MySQL | [mysql_health](10-icinga-template-library.md#plugin-contri Database | PostgreSQL | [postgres](10-icinga-template-library.md#plugin-contrib-command-postgres) Database | Housekeeping | Check the database size and growth and analyse metrics to examine trends. Database | DB IDO | [ido](10-icinga-template-library.md#itl-icinga-ido) (more below) -Webserver | Apache2, Nginx, etc. | [http](10-icinga-template-library.md#plugin-check-command-http), [apache_status](10-icinga-template-library.md#plugin-contrib-command-apache_status), [nginx_status](10-icinga-template-library.md#plugin-contrib-command-nginx_status) -Webserver | Certificates | [http](10-icinga-template-library.md#plugin-check-command-http) +Webserver | Apache2, Nginx, etc. | [http](10-icinga-template-library.md#plugin-check-command-http), [apache-status](10-icinga-template-library.md#plugin-contrib-command-apache-status), [nginx_status](10-icinga-template-library.md#plugin-contrib-command-nginx_status) +Webserver | Certificates | [http](10-icinga-template-library.md#plugin-check-command-http), [Icinga certificate monitoring](https://icinga.com/products/icinga-certificate-monitoring/) Webserver | Authorization | [http](10-icinga-template-library.md#plugin-check-command-http) Notifications | Mail (queue) | [smtp](10-icinga-template-library.md#plugin-check-command-smtp), [mailq](10-icinga-template-library.md#plugin-check-command-mailq) Notifications | SMS (GSM modem) | [check_sms3_status](https://exchange.icinga.com/netways/check_sms3status) @@ -564,7 +578,10 @@ apply Service "ido-mysql" { More specific database queries can be found in the [DB IDO](14-features.md#db-ido) chapter. Distributed setups should include specific [health checks](06-distributed-monitoring.md#distributed-monitoring-health-checks). -You might also want to add additional checks for SSL certificate expiration. + +You might also want to add additional checks for TLS certificate expiration. +This can be done using the [Icinga certificate monitoring](https://icinga.com/products/icinga-certificate-monitoring/) module. + ## Advanced Configuration Hints @@ -573,7 +590,7 @@ You might also want to add additional checks for SSL certificate expiration. [Apply rules](03-monitoring-basics.md#using-apply) can be used to create a rule set which is entirely based on host objects and their attributes. -In addition to that [apply for and custom attribute override](03-monitoring-basics.md#using-apply-for) +In addition to that [apply for and custom variable override](03-monitoring-basics.md#using-apply-for) extend the possibilities. The following example defines a dictionary on the host object which contains @@ -586,67 +603,71 @@ In addition to that you can optionally define the `ssl` attribute which enables Host definition: - object Host "webserver01" { - import "generic-host" - address = "192.168.56.200" - vars.os = "Linux" +``` +object Host "webserver01" { + import "generic-host" + address = "192.168.56.200" + vars.os = "Linux" - vars.webserver = { - instance["status"] = { - address = "192.168.56.201" - port = "80" - url = "/status" - } - instance["tomcat"] = { - address = "192.168.56.202" - port = "8080" - } - instance["icingaweb2"] = { - address = "192.168.56.210" - port = "443" - url = "/icingaweb2" - ssl = true - } - } + vars.webserver = { + instance["status"] = { + address = "192.168.56.201" + port = "80" + url = "/status" } + instance["tomcat"] = { + address = "192.168.56.202" + port = "8080" + } + instance["icingaweb2"] = { + address = "192.168.56.210" + port = "443" + url = "/icingaweb2" + ssl = true + } + } +} +``` Service apply for definitions: - apply Service "webserver_ping" for (instance => config in host.vars.webserver.instance) { - display_name = "webserver_" + instance - check_command = "ping4" +``` +apply Service "webserver_ping" for (instance => config in host.vars.webserver.instance) { + display_name = "webserver_" + instance + check_command = "ping4" - vars.ping_address = config.address + vars.ping_address = config.address - assign where host.vars.webserver.instance - } + assign where host.vars.webserver.instance +} - apply Service "webserver_port" for (instance => config in host.vars.webserver.instance) { - display_name = "webserver_" + instance + "_" + config.port - check_command = "tcp" +apply Service "webserver_port" for (instance => config in host.vars.webserver.instance) { + display_name = "webserver_" + instance + "_" + config.port + check_command = "tcp" - vars.tcp_address = config.address - vars.tcp_port = config.port + vars.tcp_address = config.address + vars.tcp_port = config.port - assign where host.vars.webserver.instance - } + assign where host.vars.webserver.instance +} - apply Service "webserver_url" for (instance => config in host.vars.webserver.instance) { - display_name = "webserver_" + instance + "_" + config.url - check_command = "http" +apply Service "webserver_url" for (instance => config in host.vars.webserver.instance) { + display_name = "webserver_" + instance + "_" + config.url + check_command = "http" - vars.http_address = config.address - vars.http_port = config.port - vars.http_uri = config.url + vars.http_address = config.address + vars.http_port = config.port + vars.http_uri = config.url - if (config.ssl) { - vars.http_ssl = config.ssl - } + if (config.ssl) { + vars.http_ssl = config.ssl + } - assign where config.url != "" - } + assign where config.url != "" +} +``` -The variables defined in the host dictionary are not using the typical custom attribute +The variables defined in the host dictionary are not using the typical custom variable prefix recommended for CheckCommand parameters. Instead they are re-used for multiple service checks in this example. In addition to defining check parameters this way, you can also enrich the `display_name` @@ -656,7 +677,7 @@ attribute with more details. This will be shown in in Icinga Web 2 for example. There is a limited scope where functions can be used as object attributes such as: -* As value for [Custom Attributes](03-monitoring-basics.md#custom-attributes-functions) +* As value for [Custom Variables](03-monitoring-basics.md#custom-variables-functions) * Returning boolean expressions for [set_if](08-advanced-topics.md#use-functions-command-arguments-setif) inside command arguments * Returning a [command](08-advanced-topics.md#use-functions-command-attribute) array inside command objects @@ -665,7 +686,7 @@ The other way around you can create objects dynamically using your own global fu > **Note** > > Functions called inside command objects share the same global scope as runtime macros. -> Therefore you can access host custom attributes like `host.vars.os`, or any other +> Therefore you can access host custom variables like `host.vars.os`, or any other > object attribute from inside the function definition used for [set_if](08-advanced-topics.md#use-functions-command-arguments-setif) or [command](08-advanced-topics.md#use-functions-command-attribute). Tips when implementing functions: @@ -681,7 +702,7 @@ inside the `icinga2.log` file depending in your log severity in objects and other functions. Keep in mind that these functions are not marked as side-effect-free and as such are not available via the REST API. -Add a new configuration file `functions.conf` and include it into the [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) +Add a new configuration file `functions.conf` and include it into the [icinga2.conf](04-configuration.md#icinga2-conf) configuration file in the very beginning, e.g. after `constants.conf`. You can also manage global functions inside `constants.conf` if you prefer. @@ -721,7 +742,7 @@ to connect to the REST API. ``` $ ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://root@localhost:5665/' -Icinga 2 (version: v2.8.1-373-g4bea6d25c) +Icinga 2 (version: v2.11.0) <1> => globals.state_to_string(1) "Warning" <2> => state_to_string(2) @@ -763,25 +784,27 @@ slightly unexpected way. The following example shows how to assign values depending on group membership. All hosts in the `slow-lan` host group use 300 as value for `ping_wrta`, all other hosts use 100. - globals.group_specific_value = function(group, group_value, non_group_value) { - return function() use (group, group_value, non_group_value) { - if (group in host.groups) { - return group_value - } else { - return non_group_value - } +``` +globals.group_specific_value = function(group, group_value, non_group_value) { + return function() use (group, group_value, non_group_value) { + if (group in host.groups) { + return group_value + } else { + return non_group_value } } +} - apply Service "ping4" { - import "generic-service" - check_command = "ping4" +apply Service "ping4" { + import "generic-service" + check_command = "ping4" - vars.ping_wrta = group_specific_value("slow-lan", 300, 100) - vars.ping_crta = group_specific_value("slow-lan", 500, 200) + vars.ping_wrta = group_specific_value("slow-lan", 300, 100) + vars.ping_crta = group_specific_value("slow-lan", 500, 200) - assign where true - } + assign where true +} +``` #### Use Functions in Assign Where Expressions @@ -797,81 +820,85 @@ The following example requires the host `myprinter` being added to the host group `printers-lexmark` but only if the host uses a template matching the name `lexmark*`. - template Host "lexmark-printer-host" { - vars.printer_type = "Lexmark" +``` +template Host "lexmark-printer-host" { + vars.printer_type = "Lexmark" +} + +object Host "myprinter" { + import "generic-host" + import "lexmark-printer-host" + + address = "192.168.1.1" +} + +/* register a global function for the assign where call */ +globals.check_host_templates = function(host, search) { + /* iterate over all host templates and check if the search matches */ + for (tmpl in host.templates) { + if (match(search, tmpl)) { + return true } + } - object Host "myprinter" { - import "generic-host" - import "lexmark-printer-host" - - address = "192.168.1.1" - } - - /* register a global function for the assign where call */ - globals.check_host_templates = function(host, search) { - /* iterate over all host templates and check if the search matches */ - for (tmpl in host.templates) { - if (match(search, tmpl)) { - return true - } - } - - /* nothing matched */ - return false - } - - object HostGroup "printers-lexmark" { - display_name = "Lexmark Printers" - /* call the global function and pass the arguments */ - assign where check_host_templates(host, "lexmark*") - } + /* nothing matched */ + return false +} +object HostGroup "printers-lexmark" { + display_name = "Lexmark Printers" + /* call the global function and pass the arguments */ + assign where check_host_templates(host, "lexmark*") +} +``` Take a different more complex example: All hosts with the -custom attribute `vars_app` as nested dictionary should be +custom variable `vars_app` as nested dictionary should be added to the host group `ABAP-app-server`. But only if the `app_type` for all entries is set to `ABAP`. It could read as wildcard match for nested dictionaries: +``` where host.vars.vars_app["*"].app_type == "ABAP" +``` The solution for this problem is to register a global function which checks the `app_type` for all hosts with the `vars_app` dictionary. - object Host "appserver01" { - check_command = "dummy" - vars.vars_app["ABC"] = { app_type = "ABAP" } - } - object Host "appserver02" { - check_command = "dummy" - vars.vars_app["DEF"] = { app_type = "ABAP" } +``` +object Host "appserver01" { + check_command = "dummy" + vars.vars_app["ABC"] = { app_type = "ABAP" } +} +object Host "appserver02" { + check_command = "dummy" + vars.vars_app["DEF"] = { app_type = "ABAP" } +} + +globals.check_app_type = function(host, type) { + /* ensure that other hosts without the custom variable do not match */ + if (typeof(host.vars.vars_app) != Dictionary) { + return false + } + + /* iterate over the vars_app dictionary */ + for (key => val in host.vars.vars_app) { + /* if the value is a dictionary and if contains the app_type being the requested type */ + if (typeof(val) == Dictionary && val.app_type == type) { + return true } + } - globals.check_app_type = function(host, type) { - /* ensure that other hosts without the custom attribute do not match */ - if (typeof(host.vars.vars_app) != Dictionary) { - return false - } - - /* iterate over the vars_app dictionary */ - for (key => val in host.vars.vars_app) { - /* if the value is a dictionary and if contains the app_type being the requested type */ - if (typeof(val) == Dictionary && val.app_type == type) { - return true - } - } - - /* nothing matched */ - return false - } - - object HostGroup "ABAP-app-server" { - assign where check_app_type(host, "ABAP") - } + /* nothing matched */ + return false +} +object HostGroup "ABAP-app-server" { + assign where check_app_type(host, "ABAP") +} +``` #### Use Functions in Command Arguments set_if @@ -886,35 +913,39 @@ multiple conditions and attributes. The following example was found on the community support channels. The user had defined a host dictionary named `compellent` with the key `disks`. This was then used inside service apply for rules. - object Host "dict-host" { - check_command = "check_compellent" - vars.compellent["disks"] = { - file = "/var/lib/check_compellent/san_disks.0.json", - checks = ["disks"] - } - } +``` +object Host "dict-host" { + check_command = "check_compellent" + vars.compellent["disks"] = { + file = "/var/lib/check_compellent/san_disks.0.json", + checks = ["disks"] + } +} +``` The more significant problem was to only add the command parameter `--disk` to the plugin call when the dictionary `compellent` contains the key `disks`, and omit it if not found. By defining `set_if` as [abbreviated lambda function](17-language-reference.md#nullary-lambdas) -and evaluating the host custom attribute `compellent` containing the `disks` this problem was +and evaluating the host custom variable `compellent` containing the `disks` this problem was solved like this: - object CheckCommand "check_compellent" { - command = [ "/usr/bin/check_compellent" ] - arguments = { - "--disks" = { - set_if = {{ - var host_vars = host.vars - log(host_vars) - var compel = host_vars.compellent - log(compel) - compel.contains("disks") - }} - } - } +``` +object CheckCommand "check_compellent" { + command = [ "/usr/bin/check_compellent" ] + arguments = { + "--disks" = { + set_if = {{ + var host_vars = host.vars + log(host_vars) + var compel = host_vars.compellent + log(compel) + compel.contains("disks") + }} } + } +} +``` This implementation uses the dictionary type method [contains](18-library-reference.md#dictionary-contains) and will fail if `host.vars.compellent` is not of the type `Dictionary`. @@ -922,35 +953,38 @@ Therefore you can extend the checks using the [typeof](17-language-reference.md# You can test the types using the `icinga2 console`: - # icinga2 console - Icinga (version: v2.3.0-193-g3eb55ad) - <1> => srv_vars.compellent["check_a"] = { file="outfile_a.json", checks = [ "disks", "fans" ] } - null - <2> => srv_vars.compellent["check_b"] = { file="outfile_b.json", checks = [ "power", "voltages" ] } - null - <3> => typeof(srv_vars.compellent) - type 'Dictionary' - <4> => +``` +# icinga2 console +Icinga (version: v2.3.0-193-g3eb55ad) +<1> => srv_vars.compellent["check_a"] = { file="outfile_a.json", checks = [ "disks", "fans" ] } +null +<2> => srv_vars.compellent["check_b"] = { file="outfile_b.json", checks = [ "power", "voltages" ] } +null +<3> => typeof(srv_vars.compellent) +type 'Dictionary' +<4> => +``` The more programmatic approach for `set_if` could look like this: - "--disks" = { - set_if = {{ - var srv_vars = service.vars - if(len(srv_vars) > 0) { - if (typeof(srv_vars.compellent) == Dictionary) { - return srv_vars.compellent.contains("disks") - } else { - log(LogInformationen, "checkcommand set_if", "custom attribute compellent_checks is not a dictionary, ignoring it.") - return false - } - } else { - log(LogWarning, "checkcommand set_if", "empty custom attributes") - return false - } - }} +``` + "--disks" = { + set_if = {{ + var srv_vars = service.vars + if(len(srv_vars) > 0) { + if (typeof(srv_vars.compellent) == Dictionary) { + return srv_vars.compellent.contains("disks") + } else { + log(LogInformationen, "checkcommand set_if", "custom variable compellent_checks is not a dictionary, ignoring it.") + return false + } + } else { + log(LogWarning, "checkcommand set_if", "empty custom variables") + return false } - + }} + } +``` #### Use Functions as Command Attribute @@ -959,53 +993,56 @@ or [EventCommands](09-object-types.md#objecttype-eventcommand) which does not re a returned checkresult including state/output. The following example was taken from the community support channels. The requirement was to -specify a custom attribute inside the notification apply rule and decide which notification +specify a custom variable inside the notification apply rule and decide which notification script to call based on that. - object User "short-dummy" { - } +``` +object User "short-dummy" { +} - object UserGroup "short-dummy-group" { - assign where user.name == "short-dummy" - } +object UserGroup "short-dummy-group" { + assign where user.name == "short-dummy" +} - apply Notification "mail-admins-short" to Host { - import "mail-host-notification" - command = "mail-host-notification-test" - user_groups = [ "short-dummy-group" ] - vars.short = true - assign where host.vars.notification.mail - } +apply Notification "mail-admins-short" to Host { + import "mail-host-notification" + command = "mail-host-notification-test" + user_groups = [ "short-dummy-group" ] + vars.short = true + assign where host.vars.notification.mail +} +``` The solution is fairly simple: The `command` attribute is implemented as function returning an array required by the caller Icinga 2. The local variable `mailscript` sets the default value for the notification scrip location. -If the notification custom attribute `short` is set, it will override the local variable `mailscript` +If the notification custom variable `short` is set, it will override the local variable `mailscript` with a new value. The `mailscript` variable is then used to compute the final notification command array being returned. You can omit the `log()` calls, they only help debugging. - object NotificationCommand "mail-host-notification-test" { - command = {{ - log("command as function") - var mailscript = "mail-host-notification-long.sh" - if (notification.vars.short) { - mailscript = "mail-host-notification-short.sh" - } - log("Running command") - log(mailscript) - - var cmd = [ SysconfDir + "/icinga2/scripts/" + mailscript ] - log(LogCritical, "me", cmd) - return cmd - }} - - env = { - } +``` +object NotificationCommand "mail-host-notification-test" { + command = {{ + log("command as function") + var mailscript = "mail-host-notification-long.sh" + if (notification.vars.short) { + mailscript = "mail-host-notification-short.sh" } + log("Running command") + log(mailscript) + var cmd = [ ConfigDir + "/scripts/" + mailscript ] + log(LogCritical, "me", cmd) + return cmd + }} + + env = { + } +} +``` ### Access Object Attributes at Runtime @@ -1077,8 +1114,6 @@ time of the day compared to the defined time period. ``` object TimePeriod "backup" { - import "legacy-timeperiod" - ranges = { monday = "02:00-03:00" tuesday = "02:00-03:00" @@ -1155,3 +1190,16 @@ Icinga 2 parses performance data strings returned by check plugins and makes the warn | Value | Warning threshold value. min | Value | Minimum value returned by the check. max | Value | Maximum value returned by the check. + +### NotificationResult + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + exit\_status | Number | The exit status returned by the check execution. + output | String | The notification command output. + execution\_endpoint | String | Name of the node executing the check. + command | Value | Array of command with shell-escaped arguments or command line string. + execution\_start | Timestamp | Check execution start time (as a UNIX timestamp). + execution\_end | Timestamp | Check execution end time (as a UNIX timestamp). + active | Boolean | Whether the result is from an active or passive check. + diff --git a/doc/09-object-types.md b/doc/09-object-types.md index 17b3444e1..6111ec0aa 100644 --- a/doc/09-object-types.md +++ b/doc/09-object-types.md @@ -1,4 +1,4 @@ -# Config Object Types +# Object Types This chapter provides an overview of all available config object types which can be instantiated using the `object` keyword. @@ -12,6 +12,14 @@ You should note that the `Timestamp` type is a `Number`. In addition to that `Object name` is an object reference to an existing object name as `String` type. +## Overview + +* [Monitoring Objects](09-object-types.md#object-types-monitoring) such as host, service, etc. +* [Runtime Objects](09-object-types.md#object-types-runtime) generated by Icinga itself. +* [Features](09-object-types.md#object-types-features) available via `icinga2 feature` CLI command. + +## Common Runtime Attributes + Configuration objects share these runtime attributes which cannot be modified by the user. You can access these attributes using the [Icinga 2 API](12-icinga2-api.md#icinga2-api-config-objects). @@ -27,74 +35,9 @@ the [Icinga 2 API](12-icinga2-api.md#icinga2-api-config-objects). package | String | [Configuration package name](12-icinga2-api.md#icinga2-api-config-management) this object belongs to. Local configuration is set to `_etc`, runtime created objects use `_api`. source\_location | Dictionary | Location information where the configuration files are stored. +## Monitoring Objects -## ApiListener - -ApiListener objects are used for distributed monitoring setups -and API usage specifying the certificate files used for ssl -authorization and additional restrictions. -This configuration object is available as [api feature](11-cli-commands.md#cli-command-feature). - -The `TicketSalt` constant must be defined in [constants.conf](04-configuring-icinga-2.md#constants-conf). - -Example: - -``` -object ApiListener "api" { - accept_commands = true - accept_config = true - - ticket_salt = TicketSalt -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------------------|-----------------------|---------------------------------- - cert\_path | String | **Deprecated.** Path to the public key. - key\_path | String | **Deprecated.** Path to the private key. - ca\_path | String | **Deprecated.** Path to the CA certificate file. - ticket\_salt | String | **Optional.** Private key for [CSR auto-signing](06-distributed-monitoring.md#distributed-monitoring-setup-csr-auto-signing). **Required** for a signing master instance. - crl\_path | String | **Optional.** Path to the CRL file. - bind\_host | String | **Optional.** The IP address the api listener should be bound to. Defaults to `0.0.0.0`. - bind\_port | Number | **Optional.** The port the api listener should be bound to. Defaults to `5665`. - accept\_config | Boolean | **Optional.** Accept zone configuration. Defaults to `false`. - accept\_commands | Boolean | **Optional.** Accept remote commands. Defaults to `false`. - cipher\_list | String | **Optional.** Cipher list that is allowed. For a list of available ciphers run `openssl ciphers`. Defaults to `ALL:!LOW:!WEAK:!MEDIUM:!EXP:!NULL`. - tls\_protocolmin | String | **Optional.** Minimum TLS protocol version. Must be one of `TLSv1`, `TLSv1.1` or `TLSv1.2`. Defaults to `TLSv1`. - access\_control\_allow\_origin | Array | **Optional.** Specifies an array of origin URLs that may access the API. [(MDN docs)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Origin) - access\_control\_allow\_credentials | Boolean | **Deprecated.** Indicates whether or not the actual request can be made using credentials. Defaults to `true`. [(MDN docs)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Credentials) - access\_control\_allow\_headers | String | **Deprecated.** Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request. Defaults to `Authorization`. [(MDN docs)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Headers) - access\_control\_allow\_methods | String | **Deprecated.** Used in response to a preflight request to indicate which HTTP methods can be used when making the actual request. Defaults to `GET, POST, PUT, DELETE`. [(MDN docs)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Methods) - -The attributes `access_control_allow_credentials`, `access_control_allow_headers` and `access_control_allow_methods` -are controlled by Icinga 2 and are not changeable by config any more. - - -The ApiListener type expects its certificate files to be in the following locations: - - Type | Location - ---------------------|------------------------------------- - Private key | `LocalStateDir + "/lib/icinga2/certs/" + NodeName + ".key"` - Certificate file | `LocalStateDir + "/lib/icinga2/certs/" + NodeName + ".crt"` - CA certificate file | `LocalStateDir + "/lib/icinga2/certs/ca.crt"` - -If the deprecated attributes `cert_path`, `key_path` and/or `ca_path` are specified Icinga 2 -copies those files to the new location in `LocalStateDir + "/lib/icinga2/certs"` unless the -file(s) there are newer. - -Please check the [upgrading chapter](16-upgrading-icinga-2.md#upgrading-to-2-8-certificate-paths) for more details. - -While Icinga 2 and the underlying OpenSSL library use sane and secure defaults, the attributes -`cipher_list` and `tls_protocolmin` can be used to increase communication security. A good source -for a more secure configuration is provided by the [Mozilla Wiki](https://wiki.mozilla.org/Security/Server_Side_TLS). -Ensure to use the same configuration for both attributes on **all** endpoints to avoid communication problems which -requires to use `cipher_list` compatible with the endpoint using the oldest version of the OpenSSL library. If using -other tools to connect to the API ensure also compatibility with them as this setting affects not only inter-cluster -communcation but also the REST API. - -## ApiUser +### ApiUser ApiUser objects are used for authentication against the [Icinga 2 API](12-icinga2-api.md#icinga2-api-authentication). @@ -112,22 +55,17 @@ Configuration Attributes: Name | Type | Description --------------------------|-----------------------|---------------------------------- password | String | **Optional.** Password string. Note: This attribute is hidden in API responses. - password\_hash | String | **Optional.** A hashed password string in the form of /etc/shadow. Note: This attribute is hidden in API responses. client\_cn | String | **Optional.** Client Common Name (CN). permissions | Array | **Required.** Array of permissions. Either as string or dictionary with the keys `permission` and `filter`. The latter must be specified as function. Available permissions are explained in the [API permissions](12-icinga2-api.md#icinga2-api-permissions) chapter. -## CheckCommand +### CheckCommand -A check command definition. Additional default command custom attributes can be +A check command definition. Additional default command custom variables can be defined here. -> **Note** -> -> Icinga 2 versions < 2.6.0 require the import of the [plugin-check-command](10-icinga-template-library.md#itl-plugin-check-command) template. - Example: ``` @@ -170,168 +108,47 @@ Configuration Attributes: --------------------------|-----------------------|---------------------------------- command | Array | **Required.** The command. This can either be an array of individual command arguments. Alternatively a string can be specified in which case the shell interpreter (usually /bin/sh) takes care of parsing the command. When using the "arguments" attribute this must be an array. Can be specified as function for advanced implementations. env | Dictionary | **Optional.** A dictionary of macros which should be exported as environment variables prior to executing the command. - vars | Dictionary | **Optional.** A dictionary containing custom attributes that are specific to this command. + vars | Dictionary | **Optional.** A dictionary containing custom variables that are specific to this command. timeout | Duration | **Optional.** The command timeout in seconds. Defaults to `1m`. arguments | Dictionary | **Optional.** A dictionary of command arguments. -### CheckCommand Arguments +#### CheckCommand Arguments Command arguments can be defined as key-value-pairs in the `arguments` -dictionary. If the argument requires additional configuration, for example -a `description` attribute or an optional condition, the value can be defined -as dictionary specifying additional options. - -Service: +dictionary. Best practice is to assign a dictionary as value which +provides additional details such as the `description` next to the `value`. ``` -vars.x_val = "My command argument value." -vars.have_x = "true" -``` - -CheckCommand: - -``` -arguments = { - "-X" = { - value = "$x_val$" - key = "-Xnew" /* optional, set a new key identifier */ - description = "My plugin requires this argument for doing X." - required = false /* optional, no error if not set */ - skip_key = false /* always use "-X " */ - set_if = "$have_x$" /* only set if variable defined and resolves to a numeric value. String values are not supported */ - order = -1 /* first position */ - repeat_key = true /* if `value` is an array, repeat the key as parameter: ... 'key' 'value[0]' 'key' 'value[1]' 'key' 'value[2]' ... */ + arguments = { + "--parameter" = { + description = "..." + value = "..." + } } - "-Y" = { - value = "$y_val$" - description = "My plugin requires this argument for doing Y." - required = false /* optional, no error if not set */ - skip_key = true /* don't prefix "-Y" only use "" */ - set_if = "$have_y$" /* only set if variable defined and resolves to a numeric value. String values are not supported */ - order = 0 /* second position */ - repeat_key = false /* if `value` is an array, do not repeat the key as parameter: ... 'key' 'value[0]' 'value[1]' 'value[2]' ... */ - } -} ``` +All available argument value entries are shown below: + Name | Type | Description --------------------------|-----------------------|---------------------------------- - value | String/Function | Optional argument value set by a [runtime macro string](03-monitoring-basics.md#runtime-macros) or a [function call](17-language-reference.md#functions). - key | String | Optional argument key overriding the key identifier. - description | String | Optional argument description. - required | Boolean | Required argument. Execution error if not set. Defaults to false (optional). - skip\_key | Boolean | Use the value as argument and skip the key. - set\_if | String/Function | Argument is added if the [runtime macro string](03-monitoring-basics.md#runtime-macros) resolves to a defined numeric or boolean value. String values are not supported. [Function calls](17-language-reference.md#functions) returning a value are supported too. - order | Number | Set if multiple arguments require a defined argument order. - repeat\_key | Boolean | If the argument value is an array, repeat the argument key, or not. Defaults to true (repeat). + value | String/Function | Optional argument value set by a [runtime macro string](03-monitoring-basics.md#runtime-macros) or a [function call](17-language-reference.md#functions). [More details](03-monitoring-basics.md#command-arguments-value). + description | String | Optional argument description. [More details](03-monitoring-basics.md#command-arguments-description). + required | Boolean | Required argument. Execution error if not set. Defaults to false (optional). [More details](03-monitoring-basics.md#command-arguments-required). + skip\_key | Boolean | Use the value as argument and skip the key. [More details](03-monitoring-basics.md#command-arguments-skip-key). + set\_if | String/Function | Argument is added if the [runtime macro string](03-monitoring-basics.md#runtime-macros) resolves to a defined numeric or boolean value. String values are not supported. [Function calls](17-language-reference.md#functions) returning a value are supported too. [More details](03-monitoring-basics.md#command-arguments-set-if). + order | Number | Set if multiple arguments require a defined argument order. The syntax is `..., -3, -2, -1, , 1, 2, 3, ...`. [More details](03-monitoring-basics.md#command-arguments-order). + repeat\_key | Boolean | If the argument value is an array, repeat the argument key, or not. Defaults to true (repeat). [More details](03-monitoring-basics.md#command-arguments-repeat-key). + key | String | Optional argument key overriding the key identifier. [More details](03-monitoring-basics.md#command-arguments-key). -Argument order: +`value` and `description` are commonly used, the other entries allow +to build more advanced CheckCommand objects and arguments. -``` -..., -3, -2, -1, , 1, 2, 3, ... -``` - -Argument array with `repeat_key = true`: - -``` -'key' 'value[0]' 'key' 'value[1]' 'key' 'value[2]' -``` - -Argument array with `repeat_key = false`: - -``` -'key' 'value[0]' 'value[1]' 'value[2]' -``` - -## CheckerComponent - -The checker component is responsible for scheduling active checks. -This configuration object is available as [checker feature](11-cli-commands.md#cli-command-feature). - -Example: - -``` -object CheckerComponent "checker" { } -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - concurrent\_checks | Number | **Optional and deprecated.** The maximum number of concurrent checks. Was replaced by global constant `MaxConcurrentChecks` which will be set if you still use `concurrent_checks`. - -## CheckResultReader - -Reads Icinga 1.x check result files from a directory. This functionality is provided -to help existing Icinga 1.x users and might be useful for migration scenarios. - -Example: - -``` -object CheckResultReader "reader" { - spool_dir = "/data/check-results" -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - spool\_dir | String | **Optional.** The directory which contains the check result files. Defaults to LocalStateDir + "/lib/icinga2/spool/checkresults/". - -## Comment - -Comments created at runtime are represented as objects. -Note: This is for reference only. You can create comments -with the [add-comment](12-icinga2-api.md#icinga2-api-actions-add-comment) API action. - -Example: - -``` -object Comment "localhost!my-comment" { - host_name = "localhost" - author = "icingaadmin" - text = "This is a comment." -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - host\_name | Object name | **Required.** The name of the host this comment belongs to. - service\_name | Object name | **Optional.** The short name of the service this comment belongs to. If omitted, this comment object is treated as host comment. - author | String | **Required.** The author's name. - text | String | **Required.** The comment text. - entry\_time | Timestamp | **Optional.** The UNIX timestamp when this comment was added. - entry\_type | Number | **Optional.** The comment type (`User` = 1, `Downtime` = 2, `Flapping` = 3, `Acknowledgement` = 4). - expire\_time | Timestamp | **Optional.** The comment's expire time as UNIX timestamp. - persistent | Boolean | **Optional.** Only evaluated for `entry_type` Acknowledgement. `true` does not remove the comment when the acknowledgement is removed. - -## CompatLogger - -Writes log files in a format that's compatible with Icinga 1.x. -This configuration object is available as [compatlog feature](14-features.md#compat-logging). - -Example: - -``` -object CompatLogger "compatlog" { - log_dir = "/var/log/icinga2/compat" - rotation_method = "DAILY" -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - log\_dir | String | **Optional.** Path to the compat log directory. Defaults to LocalStateDir + "/log/icinga2/compat". - rotation\_method | String | **Optional.** Specifies when to rotate log files. Can be one of "HOURLY", "DAILY", "WEEKLY" or "MONTHLY". Defaults to "HOURLY". +Please continue reading [here](03-monitoring-basics.md#command-arguments) for advanced usage and examples +for command arguments. -## Dependency +### Dependency Dependency objects are used to specify dependencies between hosts and services. Dependencies can be defined as Host-to-Host, Service-to-Service, Service-to-Host, or Host-to-Service @@ -391,12 +208,14 @@ Configuration Attributes: Available state filters: - OK - Warning - Critical - Unknown - Up - Down +``` +OK +Warning +Critical +Unknown +Up +Down +``` When using [apply rules](03-monitoring-basics.md#using-apply) for dependencies, you can leave out certain attributes which will be automatically determined by Icinga 2. @@ -435,96 +254,7 @@ Dependency objects have composite names, i.e. their names are based on the `chil name you specified. This means you can define more than one object with the same (short) name as long as one of the `child_host_name` and `child_service_name` attributes has a different value. -## Downtime - -Downtimes created at runtime are represented as objects. -You can create downtimes with the [schedule-downtime](12-icinga2-api.md#icinga2-api-actions-schedule-downtime) API action. - -Example: - -``` -object Downtime "my-downtime" { - host_name = "localhost" - author = "icingaadmin" - comment = "This is a downtime." - start_time = 1505312869 - end_time = 1505312924 -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - host\_name | Object name | **Required.** The name of the host this comment belongs to. - service\_name | Object name | **Optional.** The short name of the service this comment belongs to. If omitted, this comment object is treated as host comment. - author | String | **Required.** The author's name. - comment | String | **Required.** The comment text. - start\_time | Timestamp | **Required.** The start time as UNIX timestamp. - end\_time | Timestamp | **Required.** The end time as UNIX timestamp. - duration | Number | **Optional.** The duration as number. - entry\_time | Timestamp | **Optional.** The UNIX timestamp when this downtime was added. - fixed | Boolean | **Optional.** Whether the downtime is fixed (true) or flexible (false). Defaults to flexible. Details in the [advanced topics chapter](08-advanced-topics.md#fixed-flexible-downtimes). - triggers | Array of object names | **Optional.** List of downtimes which should be triggered by this downtime. - -Runtime Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - trigger\_time | Timestamp | The UNIX timestamp when this downtime was triggered. - triggered\_by | Object name | The name of the downtime this downtime was triggered by. - - -## ElasticsearchWriter - -Writes check result metrics and performance data to an Elasticsearch instance. -This configuration object is available as [elasticsearch feature](14-features.md#elasticsearch-writer). - -Example: - -``` -object ElasticsearchWriter "elasticsearch" { - host = "127.0.0.1" - port = 9200 - index = "icinga2" - - enable_send_perfdata = true - - flush_threshold = 1024 - flush_interval = 10 -} -``` - -The index is rotated daily, as is recommended by Elastic, meaning the index will be renamed to `$index-$d.$M.$y`. - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - host | String | **Required.** Elasticsearch host address. Defaults to `127.0.0.1`. - port | Number | **Required.** Elasticsearch port. Defaults to `9200`. - index | String | **Required.** Elasticsearch index name. Defaults to `icinga2`. - enable\_send\_perfdata | Boolean | **Optional.** Send parsed performance data metrics for check results. Defaults to `false`. - flush\_interval | Duration | **Optional.** How long to buffer data points before transferring to Elasticsearch. Defaults to `10s`. - flush\_threshold | Number | **Optional.** How many data points to buffer before forcing a transfer to Elasticsearch. Defaults to `1024`. - username | String | **Optional.** Basic auth username if Elasticsearch is hidden behind an HTTP proxy. - password | String | **Optional.** Basic auth password if Elasticsearch is hidden behind an HTTP proxy. - enable\_tls | Boolean | **Optional.** Whether to use a TLS stream. Defaults to `false`. Requires an HTTP proxy. - ca\_path | String | **Optional.** Path to CA certificate to validate the remote host. Requires `enable_tls` set to `true`. - cert\_path | String | **Optional.** Path to host certificate to present to the remote host for mutual verification. Requires `enable_tls` set to `true`. - key\_path | String | **Optional.** Path to host key to accompany the cert\_path. Requires `enable_tls` set to `true`. - -Note: If `flush_threshold` is set too low, this will force the feature to flush all data to Elasticsearch too often. -Experiment with the setting, if you are processing more than 1024 metrics per second or similar. - -Basic auth is supported with the `username` and `password` attributes. This requires an -HTTP proxy (Nginx, etc.) in front of the Elasticsearch instance. Check [this blogpost](https://blog.netways.de/2017/09/14/secure-elasticsearch-and-kibana-with-an-nginx-http-proxy/) -for an example. - -TLS for the HTTP proxy can be enabled with `enable_tls`. In addition to that -you can specify the certificates with the `ca_path`, `cert_path` and `cert_key` attributes. - -## Endpoint +### Endpoint Endpoint objects are used to specify connection information for remote Icinga 2 instances. More details can be found in the [distributed monitoring chapter](06-distributed-monitoring.md#distributed-monitoring). @@ -532,7 +262,7 @@ Icinga 2 instances. More details can be found in the [distributed monitoring cha Example: ``` -object Endpoint "icinga2-client1.localdomain" { +object Endpoint "icinga2-agent1.localdomain" { host = "192.168.56.111" port = 5665 log_duration = 1d @@ -542,7 +272,7 @@ object Endpoint "icinga2-client1.localdomain" { Example (disable replay log): ``` -object Endpoint "icinga2-client1.localdomain" { +object Endpoint "icinga2-agent1.localdomain" { host = "192.168.5.111" port = 5665 log_duration = 0 @@ -559,14 +289,10 @@ Configuration Attributes: Endpoint objects cannot currently be created with the API. -## EventCommand +### EventCommand An event command definition. -> **Note** -> -> Icinga 2 versions < 2.6.0 require the import of the [plugin-event-command](10-icinga-template-library.md#itl-plugin-event-command) template. - Example: ``` @@ -582,7 +308,7 @@ Configuration Attributes: --------------------------|-----------------------|---------------------------------- command | Array | **Required.** The command. This can either be an array of individual command arguments. Alternatively a string can be specified in which case the shell interpreter (usually /bin/sh) takes care of parsing the command. When using the "arguments" attribute this must be an array. Can be specified as function for advanced implementations. env | Dictionary | **Optional.** A dictionary of macros which should be exported as environment variables prior to executing the command. - vars | Dictionary | **Optional.** A dictionary containing custom attributes that are specific to this command. + vars | Dictionary | **Optional.** A dictionary containing custom variables that are specific to this command. timeout | Duration | **Optional.** The command timeout in seconds. Defaults to `1m`. arguments | Dictionary | **Optional.** A dictionary of command arguments. @@ -590,111 +316,15 @@ Command arguments can be used the same way as for [CheckCommand objects](09-obje More advanced examples for event command usage can be found [here](03-monitoring-basics.md#event-commands). -## ExternalCommandListener -Implements the Icinga 1.x command pipe which can be used to send commands to Icinga. -This configuration object is available as [command feature](14-features.md#external-commands). - -Example: - -``` -object ExternalCommandListener "command" { - command_path = "/var/run/icinga2/cmd/icinga2.cmd" -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - command\_path | String | **Optional.** Path to the command pipe. Defaults to RunDir + "/icinga2/cmd/icinga2.cmd". - - - -## FileLogger - -Specifies Icinga 2 logging to a file. -This configuration object is available as `mainlog` and `debuglog` [logging feature](14-features.md#logging). - -Example: - -``` -object FileLogger "debug-file" { - severity = "debug" - path = "/var/log/icinga2/debug.log" -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - path | String | **Required.** The log path. - severity | String | **Optional.** The minimum severity for this log. Can be "debug", "notice", "information", "warning" or "critical". Defaults to "information". - - -## GelfWriter - -Writes event log entries to a defined GELF receiver host (Graylog, Logstash). -This configuration object is available as [gelf feature](14-features.md#gelfwriter). - -Example: - -``` -object GelfWriter "gelf" { - host = "127.0.0.1" - port = 12201 -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - host | String | **Optional.** GELF receiver host address. Defaults to `127.0.0.1`. - port | Number | **Optional.** GELF receiver port. Defaults to `12201`. - source | String | **Optional.** Source name for this instance. Defaults to `icinga2`. - enable\_send\_perfdata | Boolean | **Optional.** Enable performance data for 'CHECK RESULT' events. - - -## GraphiteWriter - -Writes check result metrics and performance data to a defined -Graphite Carbon host. -This configuration object is available as [graphite feature](14-features.md#graphite-carbon-cache-writer). - -Example: - -``` -object GraphiteWriter "graphite" { - host = "127.0.0.1" - port = 2003 -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - host | String | **Optional.** Graphite Carbon host address. Defaults to `127.0.0.1`. - port | Number | **Optional.** Graphite Carbon port. Defaults to `2003`. - host\_name\_template | String | **Optional.** Metric prefix for host name. Defaults to `icinga2.$host.name$.host.$host.check_command$`. - service\_name\_template | String | **Optional.** Metric prefix for service name. Defaults to `icinga2.$host.name$.services.$service.name$.$service.check_command$`. - enable\_send\_thresholds | Boolean | **Optional.** Send additional threshold metrics. Defaults to `false`. - enable\_send\_metadata | Boolean | **Optional.** Send additional metadata metrics. Defaults to `false`. - -Additional usage examples can be found [here](14-features.md#graphite-carbon-cache-writer). - - - -## Host +### Host A host. Example: ``` -object Host "icinga2-client1.localdomain" { +object Host "icinga2-agent1.localdomain" { display_name = "Linux Client 1" address = "192.168.56.111" address6 = "2a00:1450:4001:815::2003" @@ -713,13 +343,13 @@ Configuration Attributes: address | String | **Optional.** The host's IPv4 address. Available as command runtime macro `$address$` if set. address6 | String | **Optional.** The host's IPv6 address. Available as command runtime macro `$address6$` if set. groups | Array of object names | **Optional.** A list of host groups this host belongs to. - vars | Dictionary | **Optional.** A dictionary containing custom attributes that are specific to this host. + vars | Dictionary | **Optional.** A dictionary containing custom variables that are specific to this host. check\_command | Object name | **Required.** The name of the check command. max\_check\_attempts | Number | **Optional.** The number of times a host is re-checked before changing into a hard state. Defaults to 3. - check\_period | Object name | **Optional.** The name of a time period which determines when this host should be checked. Not set by default. + check\_period | Object name | **Optional.** The name of a time period which determines when this host should be checked. Not set by default (effectively 24x7). check\_timeout | Duration | **Optional.** Check command timeout in seconds. Overrides the CheckCommand's `timeout` attribute. check\_interval | Duration | **Optional.** The check interval (in seconds). This interval is used for checks when the host is in a `HARD` state. Defaults to `5m`. - retry\_interval | Duration | **Optional.** The retry interval (in seconds). This interval is used for checks when the host is in a `SOFT` state. Defaults to `1m`. + retry\_interval | Duration | **Optional.** The retry interval (in seconds). This interval is used for checks when the host is in a `SOFT` state. Defaults to `1m`. Note: This does not affect the scheduling [after a passive check result](08-advanced-topics.md#check-result-freshness). enable\_notifications | Boolean | **Optional.** Whether notifications are enabled. Defaults to true. enable\_active\_checks | Boolean | **Optional.** Whether active checks are enabled. Defaults to true. enable\_passive\_checks | Boolean | **Optional.** Whether passive checks are enabled. Defaults to true. @@ -771,10 +401,15 @@ Runtime Attributes: last\_hard\_state | Number | The last hard state (0 = UP, 1 = DOWN). last\_state\_up | Timestamp | When the last UP state occurred (as a UNIX timestamp). last\_state\_down | Timestamp | When the last DOWN state occurred (as a UNIX timestamp). + last\_state\_unreachable | Timestamp | When the host was unreachable the last time (as a UNIX timestamp). + previous\_state\_change | Timestamp | Previous timestamp of `last_state_change` before processing a new check result. + severity | Number | [Severity](19-technical-concepts.md#technical-concepts-checks-severity) calculated value. + problem | Boolean | Whether the host is considered in a problem state type (NOT-UP). + handled | Boolean | Whether the host problem is handled (downtime or acknowledgement). -## HostGroup +### HostGroup A group of hosts. @@ -799,308 +434,9 @@ Configuration Attributes: display\_name | String | **Optional.** A short description of the host group. groups | Array of object names | **Optional.** An array of nested group names. -## IcingaApplication - -The IcingaApplication object is required to start Icinga 2. -The object name must be `app`. If the object configuration -is missing, Icinga 2 will automatically create an IcingaApplication -object. - -Example: - -``` -object IcingaApplication "app" { - enable_perfdata = false -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - enable\_notifications | Boolean | **Optional.** Whether notifications are globally enabled. Defaults to true. - enable\_event\_handlers | Boolean | **Optional.** Whether event handlers are globally enabled. Defaults to true. - enable\_flapping | Boolean | **Optional.** Whether flap detection is globally enabled. Defaults to true. - enable\_host\_checks | Boolean | **Optional.** Whether active host checks are globally enabled. Defaults to true. - enable\_service\_checks | Boolean | **Optional.** Whether active service checks are globally enabled. Defaults to true. - enable\_perfdata | Boolean | **Optional.** Whether performance data processing is globally enabled. Defaults to true. - vars | Dictionary | **Optional.** A dictionary containing custom attributes that are available globally. - -## IdoMySqlConnection - -IDO database adapter for MySQL. -This configuration object is available as [ido-mysql feature](14-features.md#db-ido). - -Example: - -``` -object IdoMysqlConnection "mysql-ido" { - host = "127.0.0.1" - port = 3306 - user = "icinga" - password = "icinga" - database = "icinga" - - cleanup = { - downtimehistory_age = 48h - contactnotifications_age = 31d - } -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - host | String | **Optional.** MySQL database host address. Defaults to `localhost`. - port | Number | **Optional.** MySQL database port. Defaults to `3306`. - socket\_path | String | **Optional.** MySQL socket path. - user | String | **Optional.** MySQL database user with read/write permission to the icinga database. Defaults to `icinga`. - password | String | **Optional.** MySQL database user's password. Defaults to `icinga`. - database | String | **Optional.** MySQL database name. Defaults to `icinga`. - enable\_ssl | Boolean | **Optional.** Use SSL. Defaults to false. Change to `true` in case you want to use any of the SSL options. - ssl\_key | String | **Optional.** MySQL SSL client key file path. - ssl\_cert | String | **Optional.** MySQL SSL certificate file path. - ssl\_ca | String | **Optional.** MySQL SSL certificate authority certificate file path. - ssl\_capath | String | **Optional.** MySQL SSL trusted SSL CA certificates in PEM format directory path. - ssl\_cipher | String | **Optional.** MySQL SSL list of allowed ciphers. - table\_prefix | String | **Optional.** MySQL database table prefix. Defaults to `icinga_`. - instance\_name | String | **Optional.** Unique identifier for the local Icinga 2 instance. Defaults to `default`. - instance\_description | String | **Optional.** Description for the Icinga 2 instance. - enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-db-ido). Defaults to "true". - failover\_timeout | Duration | **Optional.** Set the failover timeout in a [HA cluster](06-distributed-monitoring.md#distributed-monitoring-high-availability-db-ido). Must not be lower than 60s. Defaults to `60s`. - cleanup | Dictionary | **Optional.** Dictionary with items for historical table cleanup. - categories | Array | **Optional.** Array of information types that should be written to the database. - -Cleanup Items: - - Name | Type | Description - --------------------------------|-----------------------|---------------------------------- - acknowledgements\_age | Duration | **Optional.** Max age for acknowledgements table rows (entry\_time). Defaults to 0 (never). - commenthistory\_age | Duration | **Optional.** Max age for commenthistory table rows (entry\_time). Defaults to 0 (never). - contactnotifications\_age | Duration | **Optional.** Max age for contactnotifications table rows (start\_time). Defaults to 0 (never). - contactnotificationmethods\_age | Duration | **Optional.** Max age for contactnotificationmethods table rows (start\_time). Defaults to 0 (never). - downtimehistory\_age | Duration | **Optional.** Max age for downtimehistory table rows (entry\_time). Defaults to 0 (never). - eventhandlers\_age | Duration | **Optional.** Max age for eventhandlers table rows (start\_time). Defaults to 0 (never). - externalcommands\_age | Duration | **Optional.** Max age for externalcommands table rows (entry\_time). Defaults to 0 (never). - flappinghistory\_age | Duration | **Optional.** Max age for flappinghistory table rows (event\_time). Defaults to 0 (never). - hostchecks\_age | Duration | **Optional.** Max age for hostalives table rows (start\_time). Defaults to 0 (never). - logentries\_age | Duration | **Optional.** Max age for logentries table rows (logentry\_time). Defaults to 0 (never). - notifications\_age | Duration | **Optional.** Max age for notifications table rows (start\_time). Defaults to 0 (never). - processevents\_age | Duration | **Optional.** Max age for processevents table rows (event\_time). Defaults to 0 (never). - statehistory\_age | Duration | **Optional.** Max age for statehistory table rows (state\_time). Defaults to 0 (never). - servicechecks\_age | Duration | **Optional.** Max age for servicechecks table rows (start\_time). Defaults to 0 (never). - systemcommands\_age | Duration | **Optional.** Max age for systemcommands table rows (start\_time). Defaults to 0 (never). - -Data Categories: - - Name | Description | Required by - ---------------------|------------------------|-------------------- - DbCatConfig | Configuration data | Icinga Web 2 - DbCatState | Current state data | Icinga Web 2 - DbCatAcknowledgement | Acknowledgements | Icinga Web 2 - DbCatComment | Comments | Icinga Web 2 - DbCatDowntime | Downtimes | Icinga Web 2 - DbCatEventHandler | Event handler data | Icinga Web 2 - DbCatExternalCommand | External commands | -- - DbCatFlapping | Flap detection data | Icinga Web 2 - DbCatCheck | Check results | -- - DbCatLog | Log messages | -- - DbCatNotification | Notifications | Icinga Web 2 - DbCatProgramStatus | Program status data | Icinga Web 2 - DbCatRetention | Retention data | Icinga Web 2 - DbCatStateHistory | Historical state data | Icinga Web 2 - -The default value for `categories` includes everything required -by Icinga Web 2 in the table above. - -In addition to the category flags listed above the `DbCatEverything` -flag may be used as a shortcut for listing all flags. - -## IdoPgsqlConnection - -IDO database adapter for PostgreSQL. -This configuration object is available as [ido-pgsql feature](14-features.md#db-ido). - -Example: - -``` -object IdoPgsqlConnection "pgsql-ido" { - host = "127.0.0.1" - port = 5432 - user = "icinga" - password = "icinga" - database = "icinga" - - cleanup = { - downtimehistory_age = 48h - contactnotifications_age = 31d - } -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - host | String | **Optional.** PostgreSQL database host address. Defaults to `localhost`. - port | Number | **Optional.** PostgreSQL database port. Defaults to `5432`. - user | String | **Optional.** PostgreSQL database user with read/write permission to the icinga database. Defaults to `icinga`. - password | String | **Optional.** PostgreSQL database user's password. Defaults to `icinga`. - database | String | **Optional.** PostgreSQL database name. Defaults to `icinga`. - ssl\_mode | String | **Optional.** Enable SSL connection mode. Value must be set according to the [sslmode setting](https://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNSTRING): `prefer`, `require`, `verify-ca`, `verify-full`, `allow`, `disable`. - ssl\_key | String | **Optional.** PostgreSQL SSL client key file path. - ssl\_cert | String | **Optional.** PostgreSQL SSL certificate file path. - ssl\_ca | String | **Optional.** PostgreSQL SSL certificate authority certificate file path. - table\_prefix | String | **Optional.** PostgreSQL database table prefix. Defaults to `icinga_`. - instance\_name | String | **Optional.** Unique identifier for the local Icinga 2 instance. Defaults to `default`. - instance\_description | String | **Optional.** Description for the Icinga 2 instance. - enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-db-ido). Defaults to "true". - failover\_timeout | Duration | **Optional.** Set the failover timeout in a [HA cluster](06-distributed-monitoring.md#distributed-monitoring-high-availability-db-ido). Must not be lower than 60s. Defaults to `60s`. - cleanup | Dictionary | **Optional.** Dictionary with items for historical table cleanup. - categories | Array | **Optional.** Array of information types that should be written to the database. - -Cleanup Items: - - Name | Type | Description - --------------------------------|-----------------------|---------------------------------- - acknowledgements\_age | Duration | **Optional.** Max age for acknowledgements table rows (entry\_time). Defaults to 0 (never). - commenthistory\_age | Duration | **Optional.** Max age for commenthistory table rows (entry\_time). Defaults to 0 (never). - contactnotifications\_age | Duration | **Optional.** Max age for contactnotifications table rows (start\_time). Defaults to 0 (never). - contactnotificationmethods\_age | Duration | **Optional.** Max age for contactnotificationmethods table rows (start\_time). Defaults to 0 (never). - downtimehistory\_age | Duration | **Optional.** Max age for downtimehistory table rows (entry\_time). Defaults to 0 (never). - eventhandlers\_age | Duration | **Optional.** Max age for eventhandlers table rows (start\_time). Defaults to 0 (never). - externalcommands\_age | Duration | **Optional.** Max age for externalcommands table rows (entry\_time). Defaults to 0 (never). - flappinghistory\_age | Duration | **Optional.** Max age for flappinghistory table rows (event\_time). Defaults to 0 (never). - hostchecks\_age | Duration | **Optional.** Max age for hostalives table rows (start\_time). Defaults to 0 (never). - logentries\_age | Duration | **Optional.** Max age for logentries table rows (logentry\_time). Defaults to 0 (never). - notifications\_age | Duration | **Optional.** Max age for notifications table rows (start\_time). Defaults to 0 (never). - processevents\_age | Duration | **Optional.** Max age for processevents table rows (event\_time). Defaults to 0 (never). - statehistory\_age | Duration | **Optional.** Max age for statehistory table rows (state\_time). Defaults to 0 (never). - servicechecks\_age | Duration | **Optional.** Max age for servicechecks table rows (start\_time). Defaults to 0 (never). - systemcommands\_age | Duration | **Optional.** Max age for systemcommands table rows (start\_time). Defaults to 0 (never). - -Data Categories: - - Name | Description | Required by - ---------------------|------------------------|-------------------- - DbCatConfig | Configuration data | Icinga Web 2 - DbCatState | Current state data | Icinga Web 2 - DbCatAcknowledgement | Acknowledgements | Icinga Web 2 - DbCatComment | Comments | Icinga Web 2 - DbCatDowntime | Downtimes | Icinga Web 2 - DbCatEventHandler | Event handler data | Icinga Web 2 - DbCatExternalCommand | External commands | -- - DbCatFlapping | Flap detection data | Icinga Web 2 - DbCatCheck | Check results | -- - DbCatLog | Log messages | -- - DbCatNotification | Notifications | Icinga Web 2 - DbCatProgramStatus | Program status data | Icinga Web 2 - DbCatRetention | Retention data | Icinga Web 2 - DbCatStateHistory | Historical state data | Icinga Web 2 - -The default value for `categories` includes everything required -by Icinga Web 2 in the table above. - -In addition to the category flags listed above the `DbCatEverything` -flag may be used as a shortcut for listing all flags. - -## InfluxdbWriter - -Writes check result metrics and performance data to a defined InfluxDB host. -This configuration object is available as [influxdb feature](14-features.md#influxdb-writer). - -Example: - -``` -object InfluxdbWriter "influxdb" { - host = "127.0.0.1" - port = 8086 - database = "icinga2" - - flush_threshold = 1024 - flush_interval = 10s - - host_template = { - measurement = "$host.check_command$" - tags = { - hostname = "$host.name$" - } - } - service_template = { - measurement = "$service.check_command$" - tags = { - hostname = "$host.name$" - service = "$service.name$" - } - } -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - host | String | **Required.** InfluxDB host address. Defaults to `127.0.0.1`. - port | Number | **Required.** InfluxDB HTTP port. Defaults to `8086`. - database | String | **Required.** InfluxDB database name. Defaults to `icinga2`. - username | String | **Optional.** InfluxDB user name. Defaults to `none`. - password | String | **Optional.** InfluxDB user password. Defaults to `none`. - ssl\_enable | Boolean | **Optional.** Whether to use a TLS stream. Defaults to `false`. - ssl\_ca\_cert | String | **Optional.** Path to CA certificate to validate the remote host. - ssl\_cert | String | **Optional.** Path to host certificate to present to the remote host for mutual verification. - ssl\_key | String | **Optional.** Path to host key to accompany the ssl\_cert. - host\_template | String | **Required.** Host template to define the InfluxDB line protocol. - service\_template | String | **Required.** Service template to define the influxDB line protocol. - enable\_send\_thresholds | Boolean | **Optional.** Whether to send warn, crit, min & max tagged data. - enable\_send\_metadata | Boolean | **Optional.** Whether to send check metadata e.g. states, execution time, latency etc. - flush\_interval | Duration | **Optional.** How long to buffer data points before transferring to InfluxDB. Defaults to `10s`. - flush\_threshold | Number | **Optional.** How many data points to buffer before forcing a transfer to InfluxDB. Defaults to `1024`. - -Note: If `flush_threshold` is set too low, this will always force the feature to flush all data -to InfluxDB. Experiment with the setting, if you are processing more than 1024 metrics per second -or similar. - -## LiveStatusListener - -Livestatus API interface available as TCP or UNIX socket. Historical table queries -require the [CompatLogger](09-object-types.md#objecttype-compatlogger) feature enabled -pointing to the log files using the `compat_log_path` configuration attribute. -This configuration object is available as [livestatus feature](14-features.md#setting-up-livestatus). - -Examples: - -``` -object LivestatusListener "livestatus-tcp" { - socket_type = "tcp" - bind_host = "127.0.0.1" - bind_port = "6558" -} - -object LivestatusListener "livestatus-unix" { - socket_type = "unix" - socket_path = "/var/run/icinga2/cmd/livestatus" -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - socket\_type | String | **Optional.** Specifies the socket type. Can be either `tcp` or `unix`. Defaults to `unix`. - bind\_host | String | **Optional.** Only valid when `socket_type` is set to `tcp`. Host address to listen on for connections. Defaults to `127.0.0.1`. - bind\_port | Number | **Optional.** Only valid when `socket_type` is set to `tcp`. Port to listen on for connections. Defaults to `6558`. - socket\_path | String | **Optional.** Only valid when `socket_type` is set to `unix`. Specifies the path to the UNIX socket file. Defaults to RunDir + "/icinga2/cmd/livestatus". - compat\_log\_path | String | **Optional.** Path to Icinga 1.x log files. Required for historical table queries. Requires `CompatLogger` feature enabled. Defaults to LocalStateDir + "/log/icinga2/compat" - -> **Note** -> -> UNIX sockets are not supported on Windows. - - -## Notification +### Notification Notification objects are used to specify how users should be notified in case of host and service state changes and other events. @@ -1135,40 +471,46 @@ Configuration Attributes: --------------------------|-----------------------|---------------------------------- host\_name | Object name | **Required.** The name of the host this notification belongs to. service\_name | Object name | **Optional.** The short name of the service this notification belongs to. If omitted, this notification object is treated as host notification. - vars | Dictionary | **Optional.** A dictionary containing custom attributes that are specific to this notification object. + vars | Dictionary | **Optional.** A dictionary containing custom variables that are specific to this notification object. users | Array of object names | **Required.** A list of user names who should be notified. **Optional.** if the `user_groups` attribute is set. user\_groups | Array of object names | **Required.** A list of user group names who should be notified. **Optional.** if the `users` attribute is set. times | Dictionary | **Optional.** A dictionary containing `begin` and `end` attributes for the notification. command | Object name | **Required.** The name of the notification command which should be executed when the notification is triggered. interval | Duration | **Optional.** The notification interval (in seconds). This interval is used for active notifications. Defaults to 30 minutes. If set to 0, [re-notifications](03-monitoring-basics.md#disable-renotification) are disabled. - period | Object name | **Optional.** The name of a time period which determines when this notification should be triggered. Not set by default. + period | Object name | **Optional.** The name of a time period which determines when this notification should be triggered. Not set by default (effectively 24x7). zone | Object name | **Optional.** The zone this object is a member of. Please read the [distributed monitoring](06-distributed-monitoring.md#distributed-monitoring) chapter for details. types | Array | **Optional.** A list of type filters when this notification should be triggered. By default everything is matched. - states | Array | **Optional.** A list of state filters when this notification should be triggered. By default everything is matched. + states | Array | **Optional.** A list of state filters when this notification should be triggered. By default everything is matched. Note that the states filter is ignored for notifications of type Acknowledgement! Available notification state filters for Service: - OK - Warning - Critical - Unknown +``` +OK +Warning +Critical +Unknown +``` Available notification state filters for Host: - Up - Down +``` +Up +Down +``` Available notification type filters: - DowntimeStart - DowntimeEnd - DowntimeRemoved - Custom - Acknowledgement - Problem - Recovery - FlappingStart - FlappingEnd +``` +DowntimeStart +DowntimeEnd +DowntimeRemoved +Custom +Acknowledgement +Problem +Recovery +FlappingStart +FlappingEnd +``` Runtime Attributes: @@ -1180,19 +522,15 @@ Runtime Attributes: last\_problem\_notification | Timestamp | When the last notification was sent for a problem (as a UNIX timestamp). -## NotificationCommand +### NotificationCommand A notification command definition. -> **Note** -> -> Icinga 2 versions < 2.6.0 require the import of the [plugin-notification-command](10-icinga-template-library.md#itl-plugin-notification-command) template. - Example: ``` object NotificationCommand "mail-service-notification" { - command = [ SysconfDir + "/icinga2/scripts/mail-service-notification.sh" ] + command = [ ConfigDir + "/scripts/mail-service-notification.sh" ] arguments += { "-4" = { @@ -1270,7 +608,7 @@ Configuration Attributes: --------------------------|-----------------------|---------------------------------- command | Array | **Required.** The command. This can either be an array of individual command arguments. Alternatively a string can be specified in which case the shell interpreter (usually /bin/sh) takes care of parsing the command. When using the "arguments" attribute this must be an array. Can be specified as function for advanced implementations. env | Dictionary | **Optional.** A dictionary of macros which should be exported as environment variables prior to executing the command. - vars | Dictionary | **Optional.** A dictionary containing custom attributes that are specific to this command. + vars | Dictionary | **Optional.** A dictionary containing custom variables that are specific to this command. timeout | Duration | **Optional.** The command timeout in seconds. Defaults to `1m`. arguments | Dictionary | **Optional.** A dictionary of command arguments. @@ -1278,83 +616,7 @@ Command arguments can be used the same way as for [CheckCommand objects](09-obje More details on specific attributes can be found in [this chapter](03-monitoring-basics.md#notification-commands). -## NotificationComponent - -The notification component is responsible for sending notifications. -This configuration object is available as [notification feature](11-cli-commands.md#cli-command-feature). - -Example: - -``` -object NotificationComponent "notification" { } -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-notifications). Disabling this currently only affects reminder notifications. Defaults to "true". - -## OpenTsdbWriter - -Writes check result metrics and performance data to [OpenTSDB](http://opentsdb.net). -This configuration object is available as [opentsdb feature](14-features.md#opentsdb-writer). - -Example: - -``` -object OpenTsdbWriter "opentsdb" { - host = "127.0.0.1" - port = 4242 - -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - host | String | **Optional.** OpenTSDB host address. Defaults to `127.0.0.1`. - port | Number | **Optional.** OpenTSDB port. Defaults to `4242`. - - -## PerfdataWriter - -Writes check result performance data to a defined path using macro -pattern consisting of custom attributes and runtime macros. -This configuration object is available as [perfdata feature](14-features.md#writing-performance-data-files). - -Example: - -``` -object PerfdataWriter "perfdata" { - host_perfdata_path = "/var/spool/icinga2/perfdata/host-perfdata" - - service_perfdata_path = "/var/spool/icinga2/perfdata/service-perfdata" - - host_format_template = "DATATYPE::HOSTPERFDATA\tTIMET::$icinga.timet$\tHOSTNAME::$host.name$\tHOSTPERFDATA::$host.perfdata$\tHOSTCHECKCOMMAND::$host.check_command$\tHOSTSTATE::$host.state$\tHOSTSTATETYPE::$host.state_type$" - service_format_template = "DATATYPE::SERVICEPERFDATA\tTIMET::$icinga.timet$\tHOSTNAME::$host.name$\tSERVICEDESC::$service.name$\tSERVICEPERFDATA::$service.perfdata$\tSERVICECHECKCOMMAND::$service.check_command$\tHOSTSTATE::$host.state$\tHOSTSTATETYPE::$host.state_type$\tSERVICESTATE::$service.state$\tSERVICESTATETYPE::$service.state_type$" - - rotation_interval = 15s -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - host\_perfdata\_path | String | **Optional.** Path to the host performance data file. Defaults to LocalStateDir + "/spool/icinga2/perfdata/host-perfdata". - service\_perfdata\_path | String | **Optional.** Path to the service performance data file. Defaults to LocalStateDir + "/spool/icinga2/perfdata/service-perfdata". - host\_temp\_path | String | **Optional.** Path to the temporary host file. Defaults to LocalStateDir + "/spool/icinga2/tmp/host-perfdata". - service\_temp\_path | String | **Optional.** Path to the temporary service file. Defaults to LocalStateDir + "/spool/icinga2/tmp/service-perfdata". - host\_format\_template | String | **Optional.** Host Format template for the performance data file. Defaults to a template that's suitable for use with PNP4Nagios. - service\_format\_template | String | **Optional.** Service Format template for the performance data file. Defaults to a template that's suitable for use with PNP4Nagios. - rotation\_interval | Duration | **Optional.** Rotation interval for the files specified in `{host,service}_perfdata_path`. Defaults to `30s`. - -When rotating the performance data file the current UNIX timestamp is appended to the path specified -in `host_perfdata_path` and `service_perfdata_path` to generate a unique filename. - - -## ScheduledDowntime +### ScheduledDowntime ScheduledDowntime objects can be used to set up recurring downtimes for hosts/services. @@ -1396,6 +658,7 @@ Configuration Attributes: fixed | Boolean | **Optional.** Whether this is a fixed downtime. Defaults to `true`. duration | Duration | **Optional.** How long the downtime lasts. Only has an effect for flexible (non-fixed) downtimes. ranges | Dictionary | **Required.** A dictionary containing information which days and durations apply to this timeperiod. + child\_options | String | **Optional.** Schedule child downtimes. `DowntimeNoChildren` does not do anything, `DowntimeTriggeredChildren` schedules child downtimes triggered by this downtime, `DowntimeNonTriggeredChildren` schedules non-triggered downtimes. Defaults to `DowntimeNoChildren`. ScheduledDowntime objects have composite names, i.e. their names are based on the `host_name` and `service_name` attributes and the @@ -1404,7 +667,7 @@ with the same (short) name as long as one of the `host_name` and `service_name` attributes has a different value. -## Service +### Service Service objects describe network services and how they should be checked by Icinga 2. @@ -1443,13 +706,13 @@ Configuration Attributes: display\_name | String | **Optional.** A short description of the service. host\_name | Object name | **Required.** The host this service belongs to. There must be a `Host` object with that name. groups | Array of object names | **Optional.** The service groups this service belongs to. - vars | Dictionary | **Optional.** A dictionary containing custom attributes that are specific to this service. + vars | Dictionary | **Optional.** A dictionary containing custom variables that are specific to this service. check\_command | Object name | **Required.** The name of the check command. max\_check\_attempts | Number | **Optional.** The number of times a service is re-checked before changing into a hard state. Defaults to 3. - check\_period | Object name | **Optional.** The name of a time period which determines when this service should be checked. Not set by default. + check\_period | Object name | **Optional.** The name of a time period which determines when this service should be checked. Not set by default (effectively 24x7). check\_timeout | Duration | **Optional.** Check command timeout in seconds. Overrides the CheckCommand's `timeout` attribute. check\_interval | Duration | **Optional.** The check interval (in seconds). This interval is used for checks when the service is in a `HARD` state. Defaults to `5m`. - retry\_interval | Duration | **Optional.** The retry interval (in seconds). This interval is used for checks when the service is in a `SOFT` state. Defaults to `1m`. + retry\_interval | Duration | **Optional.** The retry interval (in seconds). This interval is used for checks when the service is in a `SOFT` state. Defaults to `1m`. Note: This does not affect the scheduling [after a passive check result](08-advanced-topics.md#check-result-freshness). enable\_notifications | Boolean | **Optional.** Whether notifications are enabled. Defaults to `true`. enable\_active\_checks | Boolean | **Optional.** Whether active checks are enabled. Defaults to `true`. enable\_passive\_checks | Boolean | **Optional.** Whether passive checks are enabled. Defaults to `true`. @@ -1494,7 +757,7 @@ Runtime Attributes: downtime\_depth | Number | Whether the service has one or more active downtimes. flapping\_last\_change | Timestamp | When the last flapping change occurred (as a UNIX timestamp). flapping\_current | Number | Current flapping value in percent (see flapping\_thresholds) - flapping | Boolean | Whether the host is flapping between states. + flapping | Boolean | Whether the service is flapping between states. state | Number | The current state (0 = OK, 1 = WARNING, 2 = CRITICAL, 3 = UNKNOWN). last\_state | Number | The previous state (0 = OK, 1 = WARNING, 2 = CRITICAL, 3 = UNKNOWN). last\_hard\_state | Number | The last hard state (0 = OK, 1 = WARNING, 2 = CRITICAL, 3 = UNKNOWN). @@ -1502,9 +765,14 @@ Runtime Attributes: last\_state\_warning | Timestamp | When the last WARNING state occurred (as a UNIX timestamp). last\_state\_critical | Timestamp | When the last CRITICAL state occurred (as a UNIX timestamp). last\_state\_unknown | Timestamp | When the last UNKNOWN state occurred (as a UNIX timestamp). + last\_state\_unreachable | Timestamp | When the service was unreachable the last time (as a UNIX timestamp). + previous\_state\_change | Timestamp | Previous timestamp of `last_state_change` before processing a new check result. + severity | Number | [Severity](19-technical-concepts.md#technical-concepts-checks-severity) calculated value. + problem | Boolean | Whether the service is considered in a problem state type (NOT-OK). + handled | Boolean | Whether the service problem is handled (downtime or acknowledgement). -## ServiceGroup +### ServiceGroup A group of services. @@ -1528,84 +796,12 @@ Configuration Attributes: groups | Array of object names | **Optional.** An array of nested group names. -## StatusDataWriter -Periodically writes status and configuration data files which are used by third-party tools. -This configuration object is available as [statusdata feature](14-features.md#status-data). - -Example: - -``` -object StatusDataWriter "status" { - status_path = "/var/cache/icinga2/status.dat" - objects_path = "/var/cache/icinga2/objects.cache" - update_interval = 30s -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - status\_path | String | **Optional.** Path to the `status.dat` file. Defaults to LocalStateDir + "/cache/icinga2/status.dat". - objects\_path | String | **Optional.** Path to the `objects.cache` file. Defaults to LocalStateDir + "/cache/icinga2/objects.cache". - update\_interval | Duration | **Optional.** The interval in which the status files are updated. Defaults to `15s`. - - -## SyslogLogger - -Specifies Icinga 2 logging to syslog. -This configuration object is available as `syslog` [logging feature](14-features.md#logging). - -Example: - -``` -object SyslogLogger "syslog" { - severity = "warning" -} -``` - -Configuration Attributes: - - Name | Type | Description - --------------------------|-----------------------|---------------------------------- - severity | String | **Optional.** The minimum severity for this log. Can be "debug", "notice", "information", "warning" or "critical". Defaults to "warning". - facility | String | **Optional.** Defines the facility to use for syslog entries. This can be a facility constant like `FacilityDaemon`. Defaults to `FacilityUser`. - -Facility Constants: - - Name | Facility | Description - ---------------------|---------------|---------------- - FacilityAuth | LOG\_AUTH | The authorization system. - FacilityAuthPriv | LOG\_AUTHPRIV | The same as `FacilityAuth`, but logged to a file readable only by selected individuals. - FacilityCron | LOG\_CRON | The cron daemon. - FacilityDaemon | LOG\_DAEMON | System daemons that are not provided for explicitly by other facilities. - FacilityFtp | LOG\_FTP | The file transfer protocol daemons. - FacilityKern | LOG\_KERN | Messages generated by the kernel. These cannot be generated by any user processes. - FacilityLocal0 | LOG\_LOCAL0 | Reserved for local use. - FacilityLocal1 | LOG\_LOCAL1 | Reserved for local use. - FacilityLocal2 | LOG\_LOCAL2 | Reserved for local use. - FacilityLocal3 | LOG\_LOCAL3 | Reserved for local use. - FacilityLocal4 | LOG\_LOCAL4 | Reserved for local use. - FacilityLocal5 | LOG\_LOCAL5 | Reserved for local use. - FacilityLocal6 | LOG\_LOCAL6 | Reserved for local use. - FacilityLocal7 | LOG\_LOCAL7 | Reserved for local use. - FacilityLpr | LOG\_LPR | The line printer spooling system. - FacilityMail | LOG\_MAIL | The mail system. - FacilityNews | LOG\_NEWS | The network news system. - FacilitySyslog | LOG\_SYSLOG | Messages generated internally by syslogd. - FacilityUser | LOG\_USER | Messages generated by user processes. This is the default facility identifier if none is specified. - FacilityUucp | LOG\_UUCP | The UUCP system. - -## TimePeriod +### TimePeriod Time periods can be used to specify when hosts/services should be checked or to limit when notifications should be sent out. -> **Note** -> -> Icinga 2 versions < 2.6.0 require the import of the [legacy-timeperiod](10-icinga-template-library.md#itl-legacy-timeperiod) template. - Examples: ``` @@ -1662,7 +858,7 @@ Runtime Attributes: is\_inside | Boolean | Whether we're currently inside this timeperiod. -## User +### User A user. @@ -1686,24 +882,28 @@ object User "icingaadmin" { Available notification state filters: - OK - Warning - Critical - Unknown - Up - Down +``` +OK +Warning +Critical +Unknown +Up +Down +``` Available notification type filters: - DowntimeStart - DowntimeEnd - DowntimeRemoved - Custom - Acknowledgement - Problem - Recovery - FlappingStart - FlappingEnd +``` +DowntimeStart +DowntimeEnd +DowntimeRemoved +Custom +Acknowledgement +Problem +Recovery +FlappingStart +FlappingEnd +``` Configuration Attributes: @@ -1712,10 +912,10 @@ Configuration Attributes: display\_name | String | **Optional.** A short description of the user. email | String | **Optional.** An email string for this user. Useful for notification commands. pager | String | **Optional.** A pager string for this user. Useful for notification commands. - vars | Dictionary | **Optional.** A dictionary containing custom attributes that are specific to this user. + vars | Dictionary | **Optional.** A dictionary containing custom variables that are specific to this user. groups | Array of object names | **Optional.** An array of group names. - enable\_notifications | Boolean | **Optional.** Whether notifications are enabled for this user. - period | Object name | **Optional.** The name of a time period which determines when a notification for this user should be triggered. Not set by default. + enable\_notifications | Boolean | **Optional.** Whether notifications are enabled for this user. Defaults to true. + period | Object name | **Optional.** The name of a time period which determines when a notification for this user should be triggered. Not set by default (effectively 24x7). types | Array | **Optional.** A set of type filters when a notification for this user should be triggered. By default everything is matched. states | Array | **Optional.** A set of state filters when a notification for this should be triggered. By default everything is matched. @@ -1725,7 +925,7 @@ Runtime Attributes: --------------------------|-----------------------|---------------------------------- last\_notification | Timestamp | When the last notification was sent for this user (as a UNIX timestamp). -## UserGroup +### UserGroup A user group. @@ -1749,7 +949,7 @@ Configuration Attributes: groups | Array of object names | **Optional.** An array of nested group names. -## Zone +### Zone Zone objects are used to specify which Icinga 2 instances are located in a zone. Please read the [distributed monitoring chapter](06-distributed-monitoring.md#distributed-monitoring) for additional details. @@ -1772,7 +972,847 @@ Configuration Attributes: Name | Type | Description --------------------------|-----------------------|---------------------------------- endpoints | Array of object names | **Optional.** Array of endpoint names located in this zone. - parent | Object name | **Optional.** The name of the parent zone. + parent | Object name | **Optional.** The name of the parent zone. (Do not specify a global zone) global | Boolean | **Optional.** Whether configuration files for this zone should be [synced](06-distributed-monitoring.md#distributed-monitoring-global-zone-config-sync) to all endpoints. Defaults to `false`. Zone objects cannot currently be created with the API. + + +## Runtime Objects + +These objects are generated at runtime by the daemon +from API actions. Downtime objects are also created +by ScheduledDowntime objects. + +### Comment + +Comments created at runtime are represented as objects. +Note: This is for reference only. You can create comments +with the [add-comment](12-icinga2-api.md#icinga2-api-actions-add-comment) API action. + +Example: + +``` +object Comment "my-comment" { + host_name = "localhost" + author = "icingaadmin" + text = "This is a comment." + entry_time = 1234567890 +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + host\_name | Object name | **Required.** The name of the host this comment belongs to. + service\_name | Object name | **Optional.** The short name of the service this comment belongs to. If omitted, this comment object is treated as host comment. + author | String | **Required.** The author's name. + text | String | **Required.** The comment text. + entry\_time | Timestamp | **Optional.** The UNIX timestamp when this comment was added. If omitted, the entry time is volatile! + entry\_type | Number | **Optional.** The comment type (`User` = 1, `Downtime` = 2, `Flapping` = 3, `Acknowledgement` = 4). + expire\_time | Timestamp | **Optional.** The comment's expire time as UNIX timestamp. + persistent | Boolean | **Optional.** Only evaluated for `entry_type` Acknowledgement. `true` does not remove the comment when the acknowledgement is removed. + +### Downtime + +Downtimes created at runtime are represented as objects. +You can create downtimes with the [schedule-downtime](12-icinga2-api.md#icinga2-api-actions-schedule-downtime) API action. + +Example: + +``` +object Downtime "my-downtime" { + host_name = "localhost" + author = "icingaadmin" + comment = "This is a downtime." + start_time = 1505312869 + end_time = 1505312924 +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + host\_name | Object name | **Required.** The name of the host this comment belongs to. + service\_name | Object name | **Optional.** The short name of the service this comment belongs to. If omitted, this comment object is treated as host comment. + author | String | **Required.** The author's name. + comment | String | **Required.** The comment text. + start\_time | Timestamp | **Required.** The start time as UNIX timestamp. + end\_time | Timestamp | **Required.** The end time as UNIX timestamp. + duration | Number | **Optional.** The duration as number. + entry\_time | Timestamp | **Optional.** The UNIX timestamp when this downtime was added. + fixed | Boolean | **Optional.** Whether the downtime is fixed (true) or flexible (false). Defaults to flexible. Details in the [advanced topics chapter](08-advanced-topics.md#fixed-flexible-downtimes). + triggers | Array of object names | **Optional.** List of downtimes which should be triggered by this downtime. + +Runtime Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + trigger\_time | Timestamp | The UNIX timestamp when this downtime was triggered. + triggered\_by | Object name | The name of the downtime this downtime was triggered by. + + + +## Features + +### ApiListener + +ApiListener objects are used for distributed monitoring setups +and API usage specifying the certificate files used for ssl +authorization and additional restrictions. +This configuration object is available as [api feature](11-cli-commands.md#cli-command-feature). + +The `TicketSalt` constant must be defined in [constants.conf](04-configuration.md#constants-conf). + +Example: + +``` +object ApiListener "api" { + accept_commands = true + accept_config = true + + ticket_salt = TicketSalt +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------------------|-----------------------|---------------------------------- + cert\_path | String | **Deprecated.** Path to the public key. + key\_path | String | **Deprecated.** Path to the private key. + ca\_path | String | **Deprecated.** Path to the CA certificate file. + ticket\_salt | String | **Optional.** Private key for [CSR auto-signing](06-distributed-monitoring.md#distributed-monitoring-setup-csr-auto-signing). **Required** for a signing master instance. + crl\_path | String | **Optional.** Path to the CRL file. + bind\_host | String | **Optional.** The IP address the api listener should be bound to. If not specified, the ApiListener is bound to `::` and listens for both IPv4 and IPv6 connections. + bind\_port | Number | **Optional.** The port the api listener should be bound to. Defaults to `5665`. + accept\_config | Boolean | **Optional.** Accept zone configuration. Defaults to `false`. + accept\_commands | Boolean | **Optional.** Accept remote commands. Defaults to `false`. + max\_anonymous\_clients | Number | **Optional.** Limit the number of anonymous client connections (not configured endpoints and signing requests). + cipher\_list | String | **Optional.** Cipher list that is allowed. For a list of available ciphers run `openssl ciphers`. Defaults to `ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384:AES128-GCM-SHA256`. + tls\_protocolmin | String | **Optional.** Minimum TLS protocol version. Since v2.11, only `TLSv1.2` is supported. Defaults to `TLSv1.2`. + tls\_handshake\_timeout | Number | **Optional.** TLS Handshake timeout. Defaults to `10s`. + access\_control\_allow\_origin | Array | **Optional.** Specifies an array of origin URLs that may access the API. [(MDN docs)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Origin) + access\_control\_allow\_credentials | Boolean | **Deprecated.** Indicates whether or not the actual request can be made using credentials. Defaults to `true`. [(MDN docs)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Credentials) + access\_control\_allow\_headers | String | **Deprecated.** Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request. Defaults to `Authorization`. [(MDN docs)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Headers) + access\_control\_allow\_methods | String | **Deprecated.** Used in response to a preflight request to indicate which HTTP methods can be used when making the actual request. Defaults to `GET, POST, PUT, DELETE`. [(MDN docs)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Methods) + environment | String | **Optional.** Used as suffix in TLS SNI extension name; default from constant `ApiEnvironment`, which is empty. + +The attributes `access_control_allow_credentials`, `access_control_allow_headers` and `access_control_allow_methods` +are controlled by Icinga 2 and are not changeable by config any more. + + +The ApiListener type expects its certificate files to be in the following locations: + + Type | Location + ---------------------|------------------------------------- + Private key | `DataDir + "/certs/" + NodeName + ".key"` + Certificate file | `DataDir + "/certs/" + NodeName + ".crt"` + CA certificate file | `DataDir + "/certs/ca.crt"` + +If the deprecated attributes `cert_path`, `key_path` and/or `ca_path` are specified Icinga 2 +copies those files to the new location in `DataDir + "/certs"` unless the +file(s) there are newer. + +Please check the [upgrading chapter](16-upgrading-icinga-2.md#upgrading-to-2-8-certificate-paths) for more details. + +While Icinga 2 and the underlying OpenSSL library use sane and secure defaults, the attributes +`cipher_list` and `tls_protocolmin` can be used to increase communication security. A good source +for a more secure configuration is provided by the [Mozilla Wiki](https://wiki.mozilla.org/Security/Server_Side_TLS). +Ensure to use the same configuration for both attributes on **all** endpoints to avoid communication problems which +requires to use `cipher_list` compatible with the endpoint using the oldest version of the OpenSSL library. If using +other tools to connect to the API ensure also compatibility with them as this setting affects not only inter-cluster +communcation but also the REST API. + +### CheckerComponent + +The checker component is responsible for scheduling active checks. +This configuration object is available as [checker feature](11-cli-commands.md#cli-command-feature). + +Example: + +``` +object CheckerComponent "checker" { } +``` + +In order to limit the concurrent checks on a master/satellite endpoint, +use [MaxConcurrentChecks](17-language-reference.md#icinga-constants-global-config) constant. +This also applies to an agent as command endpoint where the checker +feature is disabled. + +### CheckResultReader + +Reads Icinga 1.x check result files from a directory. This functionality is provided +to help existing Icinga 1.x users and might be useful for migration scenarios. + +> **Note** +> +> This feature is DEPRECATED and will be removed in future releases. +> Check the [roadmap](https://github.com/Icinga/icinga2/milestones). + +Example: + +``` +object CheckResultReader "reader" { + spool_dir = "/data/check-results" +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + spool\_dir | String | **Optional.** The directory which contains the check result files. Defaults to DataDir + "/spool/checkresults/". + +### CompatLogger + +Writes log files in a format that's compatible with Icinga 1.x. +This configuration object is available as [compatlog feature](14-features.md#compat-logging). + +> **Note** +> +> This feature is DEPRECATED and will be removed in future releases. +> Check the [roadmap](https://github.com/Icinga/icinga2/milestones). + +Example: + +``` +object CompatLogger "compatlog" { + log_dir = "/var/log/icinga2/compat" + rotation_method = "DAILY" +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + log\_dir | String | **Optional.** Path to the compat log directory. Defaults to LogDir + "/compat". + rotation\_method | String | **Optional.** Specifies when to rotate log files. Can be one of "HOURLY", "DAILY", "WEEKLY" or "MONTHLY". Defaults to "HOURLY". + + +### ElasticsearchWriter + +Writes check result metrics and performance data to an Elasticsearch instance. +This configuration object is available as [elasticsearch feature](14-features.md#elasticsearch-writer). + +Example: + +``` +object ElasticsearchWriter "elasticsearch" { + host = "127.0.0.1" + port = 9200 + index = "icinga2" + + enable_send_perfdata = true + + flush_threshold = 1024 + flush_interval = 10 +} +``` + +The index is rotated daily, as is recommended by Elastic, meaning the index will be renamed to `$index-$d.$M.$y`. + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + host | String | **Required.** Elasticsearch host address. Defaults to `127.0.0.1`. + port | Number | **Required.** Elasticsearch port. Defaults to `9200`. + index | String | **Required.** Elasticsearch index name. Defaults to `icinga2`. + enable\_send\_perfdata | Boolean | **Optional.** Send parsed performance data metrics for check results. Defaults to `false`. + flush\_interval | Duration | **Optional.** How long to buffer data points before transferring to Elasticsearch. Defaults to `10s`. + flush\_threshold | Number | **Optional.** How many data points to buffer before forcing a transfer to Elasticsearch. Defaults to `1024`. + username | String | **Optional.** Basic auth username if Elasticsearch is hidden behind an HTTP proxy. + password | String | **Optional.** Basic auth password if Elasticsearch is hidden behind an HTTP proxy. + enable\_tls | Boolean | **Optional.** Whether to use a TLS stream. Defaults to `false`. Requires an HTTP proxy. + ca\_path | String | **Optional.** Path to CA certificate to validate the remote host. Requires `enable_tls` set to `true`. + cert\_path | String | **Optional.** Path to host certificate to present to the remote host for mutual verification. Requires `enable_tls` set to `true`. + key\_path | String | **Optional.** Path to host key to accompany the cert\_path. Requires `enable_tls` set to `true`. + enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-features). Defaults to `false`. + +Note: If `flush_threshold` is set too low, this will force the feature to flush all data to Elasticsearch too often. +Experiment with the setting, if you are processing more than 1024 metrics per second or similar. + +Basic auth is supported with the `username` and `password` attributes. This requires an +HTTP proxy (Nginx, etc.) in front of the Elasticsearch instance. Check [this blogpost](https://blog.netways.de/2017/09/14/secure-elasticsearch-and-kibana-with-an-nginx-http-proxy/) +for an example. + +TLS for the HTTP proxy can be enabled with `enable_tls`. In addition to that +you can specify the certificates with the `ca_path`, `cert_path` and `cert_key` attributes. + +### ExternalCommandListener + +Implements the Icinga 1.x command pipe which can be used to send commands to Icinga. +This configuration object is available as [command feature](14-features.md#external-commands). + +> **Note** +> +> This feature is DEPRECATED and will be removed in future releases. +> Check the [roadmap](https://github.com/Icinga/icinga2/milestones). + +Example: + +``` +object ExternalCommandListener "command" { + command_path = "/var/run/icinga2/cmd/icinga2.cmd" +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + command\_path | String | **Optional.** Path to the command pipe. Defaults to RunDir + "/icinga2/cmd/icinga2.cmd". + + + +### FileLogger + +Specifies Icinga 2 logging to a file. +This configuration object is available as `mainlog` and `debuglog` [logging feature](14-features.md#logging). + +Example: + +``` +object FileLogger "debug-file" { + severity = "debug" + path = "/var/log/icinga2/debug.log" +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + path | String | **Required.** The log path. + severity | String | **Optional.** The minimum severity for this log. Can be "debug", "notice", "information", "warning" or "critical". Defaults to "information". + + +### GelfWriter + +Writes event log entries to a defined GELF receiver host (Graylog, Logstash). +This configuration object is available as [gelf feature](14-features.md#gelfwriter). + +Example: + +``` +object GelfWriter "gelf" { + host = "127.0.0.1" + port = 12201 +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + host | String | **Optional.** GELF receiver host address. Defaults to `127.0.0.1`. + port | Number | **Optional.** GELF receiver port. Defaults to `12201`. + source | String | **Optional.** Source name for this instance. Defaults to `icinga2`. + enable\_send\_perfdata | Boolean | **Optional.** Enable performance data for 'CHECK RESULT' events. + enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-features). Defaults to `false`. + enable\_tls | Boolean | **Optional.** Whether to use a TLS stream. Defaults to `false`. + ca\_path | String | **Optional.** Path to CA certificate to validate the remote host. Requires `enable_tls` set to `true`. + cert\_path | String | **Optional.** Path to host certificate to present to the remote host for mutual verification. Requires `enable_tls` set to `true`. + key\_path | String | **Optional.** Path to host key to accompany the cert\_path. Requires `enable_tls` set to `true`. + +### GraphiteWriter + +Writes check result metrics and performance data to a defined +Graphite Carbon host. +This configuration object is available as [graphite feature](14-features.md#graphite-carbon-cache-writer). + +Example: + +``` +object GraphiteWriter "graphite" { + host = "127.0.0.1" + port = 2003 +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + host | String | **Optional.** Graphite Carbon host address. Defaults to `127.0.0.1`. + port | Number | **Optional.** Graphite Carbon port. Defaults to `2003`. + host\_name\_template | String | **Optional.** Metric prefix for host name. Defaults to `icinga2.$host.name$.host.$host.check_command$`. + service\_name\_template | String | **Optional.** Metric prefix for service name. Defaults to `icinga2.$host.name$.services.$service.name$.$service.check_command$`. + enable\_send\_thresholds | Boolean | **Optional.** Send additional threshold metrics. Defaults to `false`. + enable\_send\_metadata | Boolean | **Optional.** Send additional metadata metrics. Defaults to `false`. + enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-features). Defaults to `false`. + +Additional usage examples can be found [here](14-features.md#graphite-carbon-cache-writer). + + +### IcingaApplication + +The IcingaApplication object is required to start Icinga 2. +The object name must be `app`. If the object configuration +is missing, Icinga 2 will automatically create an IcingaApplication +object. + +Example: + +``` +object IcingaApplication "app" { + enable_perfdata = false +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + enable\_notifications | Boolean | **Optional.** Whether notifications are globally enabled. Defaults to true. + enable\_event\_handlers | Boolean | **Optional.** Whether event handlers are globally enabled. Defaults to true. + enable\_flapping | Boolean | **Optional.** Whether flap detection is globally enabled. Defaults to true. + enable\_host\_checks | Boolean | **Optional.** Whether active host checks are globally enabled. Defaults to true. + enable\_service\_checks | Boolean | **Optional.** Whether active service checks are globally enabled. Defaults to true. + enable\_perfdata | Boolean | **Optional.** Whether performance data processing is globally enabled. Defaults to true. + vars | Dictionary | **Optional.** A dictionary containing custom variables that are available globally. + environment | String | **Optional.** Specify the Icinga environment. This overrides the `Environment` constant specified in the configuration or on the CLI with `--define`. Defaults to empty. + +### IdoMySqlConnection + +IDO database adapter for MySQL. +This configuration object is available as [ido-mysql feature](14-features.md#db-ido). + +Example: + +``` +object IdoMysqlConnection "mysql-ido" { + host = "127.0.0.1" + port = 3306 + user = "icinga" + password = "icinga" + database = "icinga" + + cleanup = { + downtimehistory_age = 48h + contactnotifications_age = 31d + } +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + host | String | **Optional.** MySQL database host address. Defaults to `localhost`. + port | Number | **Optional.** MySQL database port. Defaults to `3306`. + socket\_path | String | **Optional.** MySQL socket path. + user | String | **Optional.** MySQL database user with read/write permission to the icinga database. Defaults to `icinga`. + password | String | **Optional.** MySQL database user's password. Defaults to `icinga`. + database | String | **Optional.** MySQL database name. Defaults to `icinga`. + enable\_ssl | Boolean | **Optional.** Use SSL. Defaults to false. Change to `true` in case you want to use any of the SSL options. + ssl\_key | String | **Optional.** MySQL SSL client key file path. + ssl\_cert | String | **Optional.** MySQL SSL certificate file path. + ssl\_ca | String | **Optional.** MySQL SSL certificate authority certificate file path. + ssl\_capath | String | **Optional.** MySQL SSL trusted SSL CA certificates in PEM format directory path. + ssl\_cipher | String | **Optional.** MySQL SSL list of allowed ciphers. + table\_prefix | String | **Optional.** MySQL database table prefix. Defaults to `icinga_`. + instance\_name | String | **Optional.** Unique identifier for the local Icinga 2 instance, used for multiple Icinga 2 clusters writing to the same database. Defaults to `default`. + instance\_description | String | **Optional.** Description for the Icinga 2 instance. + enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-db-ido). Defaults to `true`. + failover\_timeout | Duration | **Optional.** Set the failover timeout in a [HA cluster](06-distributed-monitoring.md#distributed-monitoring-high-availability-db-ido). Must not be lower than 30s. Defaults to `30s`. + cleanup | Dictionary | **Optional.** Dictionary with items for historical table cleanup. + categories | Array | **Optional.** Array of information types that should be written to the database. + +Cleanup Items: + + Name | Type | Description + --------------------------------|-----------------------|---------------------------------- + acknowledgements\_age | Duration | **Optional.** Max age for acknowledgements table rows (entry\_time). Defaults to 0 (never). + commenthistory\_age | Duration | **Optional.** Max age for commenthistory table rows (entry\_time). Defaults to 0 (never). + contactnotifications\_age | Duration | **Optional.** Max age for contactnotifications table rows (start\_time). Defaults to 0 (never). + contactnotificationmethods\_age | Duration | **Optional.** Max age for contactnotificationmethods table rows (start\_time). Defaults to 0 (never). + downtimehistory\_age | Duration | **Optional.** Max age for downtimehistory table rows (entry\_time). Defaults to 0 (never). + eventhandlers\_age | Duration | **Optional.** Max age for eventhandlers table rows (start\_time). Defaults to 0 (never). + externalcommands\_age | Duration | **Optional.** Max age for externalcommands table rows (entry\_time). Defaults to 0 (never). + flappinghistory\_age | Duration | **Optional.** Max age for flappinghistory table rows (event\_time). Defaults to 0 (never). + hostchecks\_age | Duration | **Optional.** Max age for hostalives table rows (start\_time). Defaults to 0 (never). + logentries\_age | Duration | **Optional.** Max age for logentries table rows (logentry\_time). Defaults to 0 (never). + notifications\_age | Duration | **Optional.** Max age for notifications table rows (start\_time). Defaults to 0 (never). + processevents\_age | Duration | **Optional.** Max age for processevents table rows (event\_time). Defaults to 0 (never). + statehistory\_age | Duration | **Optional.** Max age for statehistory table rows (state\_time). Defaults to 0 (never). + servicechecks\_age | Duration | **Optional.** Max age for servicechecks table rows (start\_time). Defaults to 0 (never). + systemcommands\_age | Duration | **Optional.** Max age for systemcommands table rows (start\_time). Defaults to 0 (never). + +Data Categories: + + Name | Description | Required by + ---------------------|------------------------|-------------------- + DbCatConfig | Configuration data | Icinga Web 2 + DbCatState | Current state data | Icinga Web 2 + DbCatAcknowledgement | Acknowledgements | Icinga Web 2 + DbCatComment | Comments | Icinga Web 2 + DbCatDowntime | Downtimes | Icinga Web 2 + DbCatEventHandler | Event handler data | Icinga Web 2 + DbCatExternalCommand | External commands | -- + DbCatFlapping | Flap detection data | Icinga Web 2 + DbCatCheck | Check results | -- + DbCatLog | Log messages | -- + DbCatNotification | Notifications | Icinga Web 2 + DbCatProgramStatus | Program status data | Icinga Web 2 + DbCatRetention | Retention data | Icinga Web 2 + DbCatStateHistory | Historical state data | Icinga Web 2 + +The default value for `categories` includes everything required +by Icinga Web 2 in the table above. + +In addition to the category flags listed above the `DbCatEverything` +flag may be used as a shortcut for listing all flags. + +Runtime Attributes: + + Name | Type | Description + ----------------------------|-----------------------|----------------- + last\_failover | Timestamp | When the last failover happened for this connection (only available with `enable_ha = true`. + +### IdoPgsqlConnection + +IDO database adapter for PostgreSQL. +This configuration object is available as [ido-pgsql feature](14-features.md#db-ido). + +Example: + +``` +object IdoPgsqlConnection "pgsql-ido" { + host = "127.0.0.1" + port = 5432 + user = "icinga" + password = "icinga" + database = "icinga" + + cleanup = { + downtimehistory_age = 48h + contactnotifications_age = 31d + } +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + host | String | **Optional.** PostgreSQL database host address. Defaults to `localhost`. + port | Number | **Optional.** PostgreSQL database port. Defaults to `5432`. + user | String | **Optional.** PostgreSQL database user with read/write permission to the icinga database. Defaults to `icinga`. + password | String | **Optional.** PostgreSQL database user's password. Defaults to `icinga`. + database | String | **Optional.** PostgreSQL database name. Defaults to `icinga`. + ssl\_mode | String | **Optional.** Enable SSL connection mode. Value must be set according to the [sslmode setting](https://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNSTRING): `prefer`, `require`, `verify-ca`, `verify-full`, `allow`, `disable`. + ssl\_key | String | **Optional.** PostgreSQL SSL client key file path. + ssl\_cert | String | **Optional.** PostgreSQL SSL certificate file path. + ssl\_ca | String | **Optional.** PostgreSQL SSL certificate authority certificate file path. + table\_prefix | String | **Optional.** PostgreSQL database table prefix. Defaults to `icinga_`. + instance\_name | String | **Optional.** Unique identifier for the local Icinga 2 instance, used for multiple Icinga 2 clusters writing to the same database. Defaults to `default`. + instance\_description | String | **Optional.** Description for the Icinga 2 instance. + enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-db-ido). Defaults to `true`. + failover\_timeout | Duration | **Optional.** Set the failover timeout in a [HA cluster](06-distributed-monitoring.md#distributed-monitoring-high-availability-db-ido). Must not be lower than 30s. Defaults to `30s`. + cleanup | Dictionary | **Optional.** Dictionary with items for historical table cleanup. + categories | Array | **Optional.** Array of information types that should be written to the database. + +Cleanup Items: + + Name | Type | Description + --------------------------------|-----------------------|---------------------------------- + acknowledgements\_age | Duration | **Optional.** Max age for acknowledgements table rows (entry\_time). Defaults to 0 (never). + commenthistory\_age | Duration | **Optional.** Max age for commenthistory table rows (entry\_time). Defaults to 0 (never). + contactnotifications\_age | Duration | **Optional.** Max age for contactnotifications table rows (start\_time). Defaults to 0 (never). + contactnotificationmethods\_age | Duration | **Optional.** Max age for contactnotificationmethods table rows (start\_time). Defaults to 0 (never). + downtimehistory\_age | Duration | **Optional.** Max age for downtimehistory table rows (entry\_time). Defaults to 0 (never). + eventhandlers\_age | Duration | **Optional.** Max age for eventhandlers table rows (start\_time). Defaults to 0 (never). + externalcommands\_age | Duration | **Optional.** Max age for externalcommands table rows (entry\_time). Defaults to 0 (never). + flappinghistory\_age | Duration | **Optional.** Max age for flappinghistory table rows (event\_time). Defaults to 0 (never). + hostchecks\_age | Duration | **Optional.** Max age for hostalives table rows (start\_time). Defaults to 0 (never). + logentries\_age | Duration | **Optional.** Max age for logentries table rows (logentry\_time). Defaults to 0 (never). + notifications\_age | Duration | **Optional.** Max age for notifications table rows (start\_time). Defaults to 0 (never). + processevents\_age | Duration | **Optional.** Max age for processevents table rows (event\_time). Defaults to 0 (never). + statehistory\_age | Duration | **Optional.** Max age for statehistory table rows (state\_time). Defaults to 0 (never). + servicechecks\_age | Duration | **Optional.** Max age for servicechecks table rows (start\_time). Defaults to 0 (never). + systemcommands\_age | Duration | **Optional.** Max age for systemcommands table rows (start\_time). Defaults to 0 (never). + +Data Categories: + + Name | Description | Required by + ---------------------|------------------------|-------------------- + DbCatConfig | Configuration data | Icinga Web 2 + DbCatState | Current state data | Icinga Web 2 + DbCatAcknowledgement | Acknowledgements | Icinga Web 2 + DbCatComment | Comments | Icinga Web 2 + DbCatDowntime | Downtimes | Icinga Web 2 + DbCatEventHandler | Event handler data | Icinga Web 2 + DbCatExternalCommand | External commands | -- + DbCatFlapping | Flap detection data | Icinga Web 2 + DbCatCheck | Check results | -- + DbCatLog | Log messages | -- + DbCatNotification | Notifications | Icinga Web 2 + DbCatProgramStatus | Program status data | Icinga Web 2 + DbCatRetention | Retention data | Icinga Web 2 + DbCatStateHistory | Historical state data | Icinga Web 2 + +The default value for `categories` includes everything required +by Icinga Web 2 in the table above. + +In addition to the category flags listed above the `DbCatEverything` +flag may be used as a shortcut for listing all flags. + +Runtime Attributes: + + Name | Type | Description + ----------------------------|-----------------------|----------------- + last\_failover | Timestamp | When the last failover happened for this connection (only available with `enable_ha = true`. + +### InfluxdbWriter + +Writes check result metrics and performance data to a defined InfluxDB host. +This configuration object is available as [influxdb feature](14-features.md#influxdb-writer). + +Example: + +``` +object InfluxdbWriter "influxdb" { + host = "127.0.0.1" + port = 8086 + database = "icinga2" + + flush_threshold = 1024 + flush_interval = 10s + + host_template = { + measurement = "$host.check_command$" + tags = { + hostname = "$host.name$" + } + } + service_template = { + measurement = "$service.check_command$" + tags = { + hostname = "$host.name$" + service = "$service.name$" + } + } +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + host | String | **Required.** InfluxDB host address. Defaults to `127.0.0.1`. + port | Number | **Required.** InfluxDB HTTP port. Defaults to `8086`. + database | String | **Required.** InfluxDB database name. Defaults to `icinga2`. + username | String | **Optional.** InfluxDB user name. Defaults to `none`. + password | String | **Optional.** InfluxDB user password. Defaults to `none`. + ssl\_enable | Boolean | **Optional.** Whether to use a TLS stream. Defaults to `false`. + ssl\_ca\_cert | String | **Optional.** Path to CA certificate to validate the remote host. + ssl\_cert | String | **Optional.** Path to host certificate to present to the remote host for mutual verification. + ssl\_key | String | **Optional.** Path to host key to accompany the ssl\_cert. + host\_template | String | **Required.** Host template to define the InfluxDB line protocol. + service\_template | String | **Required.** Service template to define the influxDB line protocol. + enable\_send\_thresholds | Boolean | **Optional.** Whether to send warn, crit, min & max tagged data. + enable\_send\_metadata | Boolean | **Optional.** Whether to send check metadata e.g. states, execution time, latency etc. + flush\_interval | Duration | **Optional.** How long to buffer data points before transferring to InfluxDB. Defaults to `10s`. + flush\_threshold | Number | **Optional.** How many data points to buffer before forcing a transfer to InfluxDB. Defaults to `1024`. + enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-features). Defaults to `false`. + +Note: If `flush_threshold` is set too low, this will always force the feature to flush all data +to InfluxDB. Experiment with the setting, if you are processing more than 1024 metrics per second +or similar. + + + +### LiveStatusListener + +Livestatus API interface available as TCP or UNIX socket. Historical table queries +require the [CompatLogger](09-object-types.md#objecttype-compatlogger) feature enabled +pointing to the log files using the `compat_log_path` configuration attribute. +This configuration object is available as [livestatus feature](14-features.md#setting-up-livestatus). + +Examples: + +``` +object LivestatusListener "livestatus-tcp" { + socket_type = "tcp" + bind_host = "127.0.0.1" + bind_port = "6558" +} + +object LivestatusListener "livestatus-unix" { + socket_type = "unix" + socket_path = "/var/run/icinga2/cmd/livestatus" +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + socket\_type | String | **Optional.** Specifies the socket type. Can be either `tcp` or `unix`. Defaults to `unix`. + bind\_host | String | **Optional.** Only valid when `socket_type` is set to `tcp`. Host address to listen on for connections. Defaults to `127.0.0.1`. + bind\_port | Number | **Optional.** Only valid when `socket_type` is set to `tcp`. Port to listen on for connections. Defaults to `6558`. + socket\_path | String | **Optional.** Only valid when `socket_type` is set to `unix`. Specifies the path to the UNIX socket file. Defaults to RunDir + "/icinga2/cmd/livestatus". + compat\_log\_path | String | **Optional.** Path to Icinga 1.x log files. Required for historical table queries. Requires `CompatLogger` feature enabled. Defaults to LogDir + "/compat" + +> **Note** +> +> UNIX sockets are not supported on Windows. + +### NotificationComponent + +The notification component is responsible for sending notifications. +This configuration object is available as [notification feature](11-cli-commands.md#cli-command-feature). + +Example: + +``` +object NotificationComponent "notification" { } +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-notifications). Disabling this currently only affects reminder notifications. Defaults to "true". + +### OpenTsdbWriter + +Writes check result metrics and performance data to [OpenTSDB](http://opentsdb.net). +This configuration object is available as [opentsdb feature](14-features.md#opentsdb-writer). + +Example: + +``` +object OpenTsdbWriter "opentsdb" { + host = "127.0.0.1" + port = 4242 +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + host | String | **Optional.** OpenTSDB host address. Defaults to `127.0.0.1`. + port | Number | **Optional.** OpenTSDB port. Defaults to `4242`. + enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-features). Defaults to `false`. + + +### PerfdataWriter + +Writes check result performance data to a defined path using macro +pattern consisting of custom variables and runtime macros. +This configuration object is available as [perfdata feature](14-features.md#writing-performance-data-files). + +Example: + +``` +object PerfdataWriter "perfdata" { + host_perfdata_path = "/var/spool/icinga2/perfdata/host-perfdata" + + service_perfdata_path = "/var/spool/icinga2/perfdata/service-perfdata" + + host_format_template = "DATATYPE::HOSTPERFDATA\tTIMET::$icinga.timet$\tHOSTNAME::$host.name$\tHOSTPERFDATA::$host.perfdata$\tHOSTCHECKCOMMAND::$host.check_command$\tHOSTSTATE::$host.state$\tHOSTSTATETYPE::$host.state_type$" + service_format_template = "DATATYPE::SERVICEPERFDATA\tTIMET::$icinga.timet$\tHOSTNAME::$host.name$\tSERVICEDESC::$service.name$\tSERVICEPERFDATA::$service.perfdata$\tSERVICECHECKCOMMAND::$service.check_command$\tHOSTSTATE::$host.state$\tHOSTSTATETYPE::$host.state_type$\tSERVICESTATE::$service.state$\tSERVICESTATETYPE::$service.state_type$" + + rotation_interval = 15s +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + host\_perfdata\_path | String | **Optional.** Path to the host performance data file. Defaults to SpoolDir + "/perfdata/host-perfdata". + service\_perfdata\_path | String | **Optional.** Path to the service performance data file. Defaults to SpoolDir + "/perfdata/service-perfdata". + host\_temp\_path | String | **Optional.** Path to the temporary host file. Defaults to SpoolDir + "/tmp/host-perfdata". + service\_temp\_path | String | **Optional.** Path to the temporary service file. Defaults to SpoolDir + "/tmp/service-perfdata". + host\_format\_template | String | **Optional.** Host Format template for the performance data file. Defaults to a template that's suitable for use with PNP4Nagios. + service\_format\_template | String | **Optional.** Service Format template for the performance data file. Defaults to a template that's suitable for use with PNP4Nagios. + rotation\_interval | Duration | **Optional.** Rotation interval for the files specified in `{host,service}_perfdata_path`. Defaults to `30s`. + enable\_ha | Boolean | **Optional.** Enable the high availability functionality. Only valid in a [cluster setup](06-distributed-monitoring.md#distributed-monitoring-high-availability-features). Defaults to `false`. + +When rotating the performance data file the current UNIX timestamp is appended to the path specified +in `host_perfdata_path` and `service_perfdata_path` to generate a unique filename. + + +### StatusDataWriter + +Periodically writes status and configuration data files which are used by third-party tools. +This configuration object is available as [statusdata feature](14-features.md#status-data). + +> **Note** +> +> This feature is DEPRECATED and will be removed in future releases. +> Check the [roadmap](https://github.com/Icinga/icinga2/milestones). + +Example: + +``` +object StatusDataWriter "status" { + status_path = "/var/cache/icinga2/status.dat" + objects_path = "/var/cache/icinga2/objects.cache" + update_interval = 30s +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + status\_path | String | **Optional.** Path to the `status.dat` file. Defaults to CacheDir + "/status.dat". + objects\_path | String | **Optional.** Path to the `objects.cache` file. Defaults to CacheDir + "/objects.cache". + update\_interval | Duration | **Optional.** The interval in which the status files are updated. Defaults to `15s`. + +### SyslogLogger + +Specifies Icinga 2 logging to syslog. +This configuration object is available as `syslog` [logging feature](14-features.md#logging). + +Example: + +``` +object SyslogLogger "syslog" { + severity = "warning" +} +``` + +Configuration Attributes: + + Name | Type | Description + --------------------------|-----------------------|---------------------------------- + severity | String | **Optional.** The minimum severity for this log. Can be "debug", "notice", "information", "warning" or "critical". Defaults to "warning". + facility | String | **Optional.** Defines the facility to use for syslog entries. This can be a facility constant like `FacilityDaemon`. Defaults to `FacilityUser`. + +Facility Constants: + + Name | Facility | Description + ---------------------|---------------|---------------- + FacilityAuth | LOG\_AUTH | The authorization system. + FacilityAuthPriv | LOG\_AUTHPRIV | The same as `FacilityAuth`, but logged to a file readable only by selected individuals. + FacilityCron | LOG\_CRON | The cron daemon. + FacilityDaemon | LOG\_DAEMON | System daemons that are not provided for explicitly by other facilities. + FacilityFtp | LOG\_FTP | The file transfer protocol daemons. + FacilityKern | LOG\_KERN | Messages generated by the kernel. These cannot be generated by any user processes. + FacilityLocal0 | LOG\_LOCAL0 | Reserved for local use. + FacilityLocal1 | LOG\_LOCAL1 | Reserved for local use. + FacilityLocal2 | LOG\_LOCAL2 | Reserved for local use. + FacilityLocal3 | LOG\_LOCAL3 | Reserved for local use. + FacilityLocal4 | LOG\_LOCAL4 | Reserved for local use. + FacilityLocal5 | LOG\_LOCAL5 | Reserved for local use. + FacilityLocal6 | LOG\_LOCAL6 | Reserved for local use. + FacilityLocal7 | LOG\_LOCAL7 | Reserved for local use. + FacilityLpr | LOG\_LPR | The line printer spooling system. + FacilityMail | LOG\_MAIL | The mail system. + FacilityNews | LOG\_NEWS | The network news system. + FacilitySyslog | LOG\_SYSLOG | Messages generated internally by syslogd. + FacilityUser | LOG\_USER | Messages generated by user processes. This is the default facility identifier if none is specified. + FacilityUucp | LOG\_UUCP | The UUCP system. + + + diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 0da925058..bbd3e824b 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -23,9 +23,11 @@ You are advised to create your own CheckCommand definitions in ## Generic Templates -By default the generic templates are included in the [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) configuration file: +By default the generic templates are included in the [icinga2.conf](04-configuration.md#icinga2-conf) configuration file: - include +``` +include +``` These templates are imported by the provided example configuration. @@ -74,9 +76,9 @@ plugin scripts. ### icinga Check command for the built-in `icinga` check. This check returns performance -data for the current Icinga instance and optionally allows for minimum version checks. +data for the current Icinga instance, reports as warning if the last reload failed and optionally allows for minimum version checks. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -----------------------|--------------- @@ -93,7 +95,7 @@ The `cluster` check command does not support any vars. Check command for the built-in `cluster-zone` check. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -----------------------|--------------- @@ -105,7 +107,7 @@ cluster\_lag\_critical | **Optional.** Critical threshold for log lag in seconds Check command for the built-in `ido` check. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --------------------------------|----------------------------- @@ -125,7 +127,7 @@ or [runtime object checks](08-advanced-topics.md#access-object-attributes-at-run In contrast to the [check_dummy](https://www.monitoring-plugins.org/doc/man/check_dummy.html) plugin, Icinga 2 implements a light-weight in memory check with 2.9+. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -136,7 +138,7 @@ dummy\_text | **Optional.** Plugin output. Defaults to "Check was successfu Specialised check command object for passive checks which uses the functionality of the "dummy" check command with appropriate default values. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -158,6 +160,15 @@ Check command for the built-in `exception` check. This check throws an exception For test and demo purposes only. The `exception` check command does not support any vars. +### sleep + +Check command for the built-in `sleep` check. This allows to use sleep for testing +and debugging only. + +Name | Description +----------------|-------------- +sleep\_time | **Optional.** The duration of the sleep in seconds. Defaults to 1s. + @@ -166,7 +177,7 @@ any vars. The Plugin Check Commands provides example configuration for plugin check commands provided by the [Monitoring Plugins](https://www.monitoring-plugins.org) project. -By default the Plugin Check Commands are included in the [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) configuration +By default the Plugin Check Commands are included in the [icinga2.conf](04-configuration.md#icinga2-conf) configuration file: include @@ -182,10 +193,10 @@ which contains the path of the plugins from the Monitoring Plugins project. ### apt -The plugin [apt](https://www.monitoring-plugins.org/doc/index.html) checks for software updates on systems that use +The plugin [apt](https://www.monitoring-plugins.org/doc/man/check_apt.html) checks for software updates on systems that use package management systems based on the apt-get(8) command found in Debian based systems. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -197,6 +208,7 @@ apt_exclude | **Optional.** Exclude packages matching REGEXP from th apt_critical | **Optional.** If the full package information of any of the upgradable packages match this REGEXP, the plugin will return CRITICAL status. Can be specified multiple times. apt_timeout | **Optional.** Seconds before plugin times out (default: 10). apt_only_critical | **Optional.** Only warn about critical upgrades. +apt_list | **Optional.** List packages available for upgrade. ### breeze @@ -204,7 +216,7 @@ apt_only_critical | **Optional.** Only warn about critical upgrades. The [check_breeze](https://www.monitoring-plugins.org/doc/man/check_breeze.html) plugin reports the signal strength of a Breezecom wireless equipment. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -----------------|--------------------------------- @@ -219,7 +231,7 @@ breeze_critical | **Required.** Percentage strength below which a WARNING statu The [check_by_ssh](https://www.monitoring-plugins.org/doc/man/check_by_ssh.html) plugin uses SSH to execute commands on a remote host. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------- | -------------- @@ -244,7 +256,7 @@ by_ssh_skip_stderr | **Optional.** Ignore all or (if specified) first n lines on The [check_clamd](https://www.monitoring-plugins.org/doc/man/check_clamd.html) plugin tests CLAMD connections with the specified host (or unix socket). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -------------------|-------------- @@ -275,7 +287,7 @@ clamd_ipv6 | **Optional.** Use IPv6 connection. Defaults to false. The [check_dhcp](https://www.monitoring-plugins.org/doc/man/check_dhcp.html) plugin tests the availability of DHCP servers on a network. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -292,7 +304,7 @@ dhcp_unicast | **Optional.** Whether to use unicast requests. Defaults to fal The [check_dig](https://www.monitoring-plugins.org/doc/man/check_dig.html) plugin test the DNS service on the specified host using dig. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------|-------------- @@ -316,7 +328,7 @@ The [check_disk](https://www.monitoring-plugins.org/doc/man/check_disk.html) plu checks the amount of used disk space on a mounted file system and generates an alert if free space is less than one of the threshold values. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --------------------|------------------------ @@ -345,14 +357,15 @@ disk\_ignore\_eregi\_path | **Optional.** Regular expression to ignore selected disk\_ignore\_ereg\_path | **Optional.** Regular expression to ignore selected path or partition. Multiple regular expression strings must be defined as array. disk\_timeout | **Optional.** Seconds before connection times out (default: 10). disk\_units | **Optional.** Choose bytes, kB, MB, GB, TB (default: MB). -disk\_exclude\_type | **Optional.** Ignore all filesystems of indicated type. Multiple regular expression strings must be defined as array. Defaults to "none", "tmpfs", "sysfs", "proc", "configfs", "devtmpfs", "devfs", "mtmfs", "tracefs", "cgroup", "fuse.gvfsd-fuse", "fuse.gvfs-fuse-daemon", "fdescfs", "overlay", "nsfs". +disk\_exclude\_type | **Optional.** Ignore all filesystems of indicated type. Multiple regular expression strings must be defined as array. Defaults to "none", "tmpfs", "sysfs", "proc", "configfs", "devtmpfs", "devfs", "mtmfs", "tracefs", "cgroup", "fuse.gvfsd-fuse", "fuse.gvfs-fuse-daemon", "fdescfs", "overlay", "nsfs", "squashfs". +disk\_include\_type | **Optional.** Check only filesystems of indicated type. Multiple regular expression strings must be defined as array. ### disk_smb The [check_disk_smb](https://www.monitoring-plugins.org/doc/man/check_disk_smb.html) plugin uses the `smbclient` binary to check SMB shares. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|------------------------ @@ -373,7 +386,7 @@ uses the nslookup program to obtain the IP address for the given host/domain que An optional DNS server to use may be specified. If no DNS server is specified, the default server(s) specified in `/etc/resolv.conf` will be used. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------|-------------- @@ -394,7 +407,7 @@ dns_timeout | **Optional.** Seconds before connection times out. Defaul The [check_file_age](https://www.monitoring-plugins.org/doc/man/check_file_age.html) plugin checks a file's size and modification time to make sure it's not empty and that it's sufficiently recent. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -----------------------|-------------------------------------------------------------------------------------------------------- @@ -411,7 +424,7 @@ file_age_ignoremissing | **Optional.** Return OK if the file does not exist. Def The [check_flexlm](https://www.monitoring-plugins.org/doc/man/check_flexlm.html) plugin checks available flexlm license managers. Requires the `lmstat` command. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -------------------|---------------------------------------------------------- @@ -427,7 +440,7 @@ necessary to set the `suid` flag on `fping`. This CheckCommand expects an IPv4 address. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -452,7 +465,7 @@ necessary to set the `suid` flag on `fping`. This CheckCommand expects an IPv6 address. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -474,7 +487,7 @@ fping_source_interface | **Optional.** The source interface name. The [check_ftp](https://www.monitoring-plugins.org/doc/man/check_ftp.html) plugin tests FTP connections with the specified host (or unix socket). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -------------------|-------------- @@ -508,7 +521,7 @@ This plugin uses the 'qstat' command, the popular game server status query tool. If you don't have the package installed, you will need to [download](http://www.activesw.com/people/steve/qstat.html) or install the package `quakestat` before you can use this plugin. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -------------------|------------------- @@ -529,7 +542,7 @@ Check command object for the [check_ping](https://www.monitoring-plugins.org/doc plugin with host check default values. This variant uses the host's `address` attribute if available and falls back to using the `address6` attribute if the `address` attribute is not set. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -547,7 +560,7 @@ ping_timeout | **Optional.** The plugin timeout in seconds. Defaults to 0 (no Check command object for the [check_ping](https://www.monitoring-plugins.org/doc/man/check_ping.html) plugin with host check default values. This variant uses the host's `address` attribute. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -565,7 +578,7 @@ ping_timeout | **Optional.** The plugin timeout in seconds. Defaults to 0 (no Check command object for the [check_ping](https://www.monitoring-plugins.org/doc/man/check_ping.html) plugin with host check default values. This variant uses the host's `address6` attribute. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -584,7 +597,7 @@ The [check_hpjd](https://www.monitoring-plugins.org/doc/man/check_hpjd.html) plu tests the state of an HP printer with a JetDirect card. Net-snmp must be installed on the computer running the plugin. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -600,7 +613,9 @@ tests the HTTP service on the specified host. It can test normal (http) and secu (https) servers, follow redirects, search for strings and regular expressions, check connection times, and report on certificate expiration times. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +The plugin can either test the HTTP response of a server, or if `http_certificate` is set to a non-empty value, the TLS certificate age for a HTTPS host. + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------------------|--------------------------------- @@ -659,7 +674,7 @@ The main difference is that check_ping executes the system's ping(1) command and parses its output while `check_icmp` talks ICMP itself. `check_icmp` must be installed with `setuid` root. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -683,7 +698,7 @@ icmp_ttl | **Optional.** The TTL on outgoing packets. The [check_imap](https://www.monitoring-plugins.org/doc/man/check_imap.html) plugin tests IMAP connections with the specified host (or unix socket). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------------|-------------- @@ -716,7 +731,7 @@ can be used to check LDAP servers. The plugin can also be used for monitoring ldaps connections instead of the deprecated `check_ldaps`. This can be ensured by enabling `ldap_starttls` or `ldap_ssl`. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -742,7 +757,7 @@ ldap_verbose | **Optional.** Show details for command-line debugging (disabled The [check_load](https://www.monitoring-plugins.org/doc/man/check_load.html) plugin tests the current system load average. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -759,7 +774,7 @@ load_percpu | **Optional.** Divide the load averages by the number of CPUs ( The [check_mailq](https://www.monitoring-plugins.org/doc/man/check_mailq.html) plugin checks the number of messages in the mail queue (supports multiple sendmail queues, qmail). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -776,7 +791,7 @@ mailq_sudo | **Optional.** Use sudo to execute the mailq command. The [check_mysql](https://www.monitoring-plugins.org/doc/man/check_mysql.html) plugin tests connections to a MySQL server. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|--------------------------------------------------------------- @@ -809,7 +824,7 @@ The result from the query should be numeric. For extra security, create a user w **Note**: You must specify `mysql_query_password` with an empty string to force an empty password, overriding any my.cnf settings. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|--------------------------------------------------------------- @@ -831,7 +846,7 @@ The [negate](https://www.monitoring-plugins.org/doc/man/negate.html) plugin negates the status of a plugin (returns OK for CRITICAL and vice-versa). Additional switches can be used to control which state becomes what. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------------|--------------------------------------------------------------- @@ -847,11 +862,11 @@ negate_arguments | **Optional.** Arguments for the negated command. ### nrpe -The `check_nrpe` plugin can be used to query an [NRPE](https://docs.icinga.com/latest/en/nrpe.html) +The `check_nrpe` plugin can be used to query an [NRPE](https://icinga.com/docs/icinga1/latest/en/nrpe.html) server or [NSClient++](https://www.nsclient.org). **Note**: This plugin is considered insecure/deprecated. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -872,7 +887,7 @@ nrpe_version_2 | **Optional.** Use this if you want to connect using NRPE v2 pro The [check_nt](https://www.monitoring-plugins.org/doc/man/check_nt.html) plugin collects data from the [NSClient++](https://www.nsclient.org) service. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -894,7 +909,7 @@ checks the clock offset between the local host and a remote NTP server. **Note**: If you want to monitor an NTP server, please use `ntp_peer`. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -916,7 +931,7 @@ checks the health of an NTP server. It supports checking the offset with the syn jitter and stratum. This plugin will not check the clock offset between the local host and NTP server; please use `ntp_time` for that purpose. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -944,7 +959,7 @@ If a query is specified using the `pgsql_query` attribute, it will be executed a connecting to the server. The result from the query has to be numeric in order to compare it against the query thresholds if set. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|--------------------------------------------------------------- @@ -970,7 +985,7 @@ round trip average (milliseconds). This command uses the host's `address` attribute if available and falls back to using the `address6` attribute if the `address` attribute is not set. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -992,7 +1007,7 @@ round trip average (milliseconds). This command uses the host's `address` attribute if not explicitly specified using the `ping_address` attribute. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -1013,7 +1028,7 @@ round trip average (milliseconds). This command uses the host's `address6` attribute if not explicitly specified using the `ping_address` attribute. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -1031,7 +1046,7 @@ ping_timeout | **Optional.** The plugin timeout in seconds. Defaults to 0 (no The [check_pop](https://www.monitoring-plugins.org/doc/man/check_pop.html) plugin tests POP connections with the specified host (or unix socket). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------|-------------- @@ -1063,7 +1078,7 @@ checks all processes and generates WARNING or CRITICAL states if the specified metric is outside the required threshold ranges. The metric defaults to number of processes. Search filters can be applied to limit the processes to check. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------|-------------- @@ -1097,7 +1112,7 @@ typically be executed at regular predictable intervals. Please be sure that the password used does not allow access to sensitive system resources. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -------------------|-------------- @@ -1117,7 +1132,7 @@ radius_timeout | **Optional.** The number of seconds before connection times The [check_rpc](https://www.monitoring-plugins.org/doc/man/check_rpc.html) plugin tests if a service is registered and running using `rpcinfo -H host -C rpc_command`. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --- | --- @@ -1134,7 +1149,7 @@ rpc_verbose | **Optional.** Show verbose output. Defaults to false. The [check_simap](https://www.monitoring-plugins.org/doc/man/check_simap.html) plugin tests SIMAP connections with the specified host (or unix socket). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -----------------------|-------------- @@ -1163,7 +1178,7 @@ simap_ipv6 | **Optional.** Use IPv6 connection. Defaults to false. The [check_ide_smart](https://www.monitoring-plugins.org/doc/man/check_ide_smart.html) plugin checks a local hard drive with the (Linux specific) SMART interface. Requires installation of `smartctl`. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -1175,7 +1190,7 @@ smart_device | **Required.** The name of a local hard drive to monitor. The [check_smtp](https://www.monitoring-plugins.org/doc/man/check_smtp.html) plugin will attempt to open an SMTP connection with the host. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------------|-------------- @@ -1206,7 +1221,7 @@ checks the status of remote machines and obtains system information via SNMP. **Note**: This plugin uses the `snmpget` command included with the NET-SNMP package. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --------------------|-------------- @@ -1238,7 +1253,7 @@ snmp_perf_oids | **Optional.** Label performance data with OIDs instead of Check command object for the [check_snmp](https://www.monitoring-plugins.org/doc/man/check_snmp.html) plugin, using SNMPv3 authentication and encryption options. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------|-------------- @@ -1247,6 +1262,7 @@ snmpv3_getnext | **Optional.** Use SNMP GETNEXT instead of SNMP GET. snmpv3_seclevel | **Optional.** The security level. Defaults to authPriv. snmpv3_auth_alg | **Optional.** The authentication algorithm. Defaults to SHA. snmpv3_user | **Required.** The username to log in with. +snmpv3_context | **Optional.** The SNMPv3 context. snmpv3_auth_key | **Required,** The authentication key. Required if `snmpv3_seclevel` is set to `authPriv` otherwise optional. snmpv3_priv_key | **Required.** The encryption key. snmpv3_oid | **Required.** The SNMP OID. @@ -1268,7 +1284,7 @@ snmpv3_timeout | **Optional.** The command timeout in seconds. Defaults to Check command object for the [check_snmp](https://www.monitoring-plugins.org/doc/man/check_snmp.html) plugin, using the uptime OID by default. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -1282,7 +1298,7 @@ snmp_community | **Optional.** The SNMP community. Defaults to "public". The [check_spop](https://www.monitoring-plugins.org/doc/man/check_spop.html) plugin tests SPOP connections with the specified host (or unix socket). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------------|-------------- @@ -1312,7 +1328,7 @@ spop_ipv6 | **Optional.** Use IPv6 connection. Defaults to false. The [check_ssh](https://www.monitoring-plugins.org/doc/man/check_ssh.html) plugin connects to an SSH server at a specified host and port. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -1328,7 +1344,7 @@ ssh_ipv6 | **Optional.** Use IPv6 connection. Defaults to false. Check command object for the [check_tcp](https://www.monitoring-plugins.org/doc/man/check_tcp.html) plugin, using ssl-related options. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------------|-------------- @@ -1345,7 +1361,7 @@ ssl_sni | **Optional.** The `server_name` that is send to The [check_ssmtp](https://www.monitoring-plugins.org/doc/man/check_ssmtp.html) plugin tests SSMTP connections with the specified host (or unix socket). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -----------------------|-------------- @@ -1375,7 +1391,7 @@ ssmtp_ipv6 | **Optional.** Use IPv6 connection. Defaults to false. The [check_swap](https://www.monitoring-plugins.org/doc/man/check_swap.html) plugin checks the swap space on a local machine. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -1391,7 +1407,7 @@ swap_noswap | **Optional.** Resulting state when there is no swap regardless The [check_tcp](https://www.monitoring-plugins.org/doc/man/check_tcp.html) plugin tests TCP connections with the specified host (or unix socket). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -1422,7 +1438,7 @@ tcp_ipv6 | **Optional.** Use IPv6 connection. Defaults to false. The [check_udp](https://www.monitoring-plugins.org/doc/man/check_udp.html) plugin tests UDP connections with the specified host (or unix socket). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -1441,7 +1457,7 @@ The [check_ups](https://www.monitoring-plugins.org/doc/man/check_ups.html) plugi tests the UPS service on the specified host. [Network UPS Tools](http://www.networkupstools.org) must be running for this plugin to work. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -1461,7 +1477,7 @@ The [check_users](https://www.monitoring-plugins.org/doc/man/check_users.html) p checks the number of users currently logged in on the local system and generates an error if the number exceeds the thresholds specified. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -1469,6 +1485,21 @@ users_wgreater | **Optional.** The user count warning threshold. Defaults to 20 users_cgreater | **Optional.** The user count critical threshold. Defaults to 50. +### uptime + +The [check_uptime](https://www.monitoring-plugins.org/doc/man/check_uptime.html) plugin +checks the uptime of the system using /proc/uptime. + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +----------------|-------------- +uptime_warning | **Required.** Min. number of uptime to generate warning (-w 30m). Defaults to 30m. +uptime_critical | **Required.** Min. number of uptime to generate critical alert (-c 15m). Defaults to 15m. +uptime_for | **Optional.** Show uptime in a pretty format (Running for x weeks, x days, ...). Defaults to false. +uptime_since | **Optional.** Show last boot in yyyy-mm-dd HH:MM:SS format (output from 'uptime -s'). Defaults to false. + + ## Windows Plugins for Icinga 2 @@ -1504,7 +1535,7 @@ The data collection is instant and free disk space (default, see `disk_win_show_ > Percentage based thresholds can be used by adding a '%' to the threshold > value. -Custom attributes: +Custom variables: Name | Description :---------------------|:------------ @@ -1520,7 +1551,7 @@ disk\_win\_show\_used | **Optional**. Use used instead of free space. Check command object for the `check_load.exe` plugin. This plugin collects the inverse of the performance counter `\Processor(_Total)\% Idle Time` two times, with a wait time of one second between the collection. To change this wait time use [`perfmon-windows`](10-icinga-template-library.md#windows-plugins-load-windows). -Custom attributes: +Custom variables: Name | Description :---------------|:------------ @@ -1539,13 +1570,14 @@ The memory collection is instant and free memory is used for threshold computati > value. Keep in mind that memory\_win\_unit is applied before the > value is calculated. -Custom attributes: +Custom variables: Name | Description :-----------------|:------------ memory\_win\_warn | **Optional**. The warning threshold. Defaults to "10%". memory\_win\_crit | **Optional**. The critical threshold. Defaults to "5%". memory\_win\_unit | **Optional**. The unit to display the received value in, thresholds are interpreted in this unit. Defaults to "mb" (megabyte), possible values are: b, kb, mb, gb and tb. +memory\_win\_show\_used | **Optional**. Show used memory instead of the free memory. ### network-windows @@ -1553,7 +1585,7 @@ memory\_win\_unit | **Optional**. The unit to display the received value in, thr Check command object for the `check_network.exe` plugin. Collects the total Bytes inbound and outbound for all interfaces in one second, to itemise interfaces or use a different collection interval use [`perfmon-windows`](10-icinga-template-library.md#windows-plugins-load-windows). -Custom attributes: +Custom variables: Name | Description :-------------------|:------------ @@ -1569,7 +1601,7 @@ This plugins allows to collect data from a Performance Counter. After the first To receive a list of possible Performance Counter Objects run `check_perfmon.exe --print-objects` and to view an objects instances and counters run `check_perfmon.exe --print-object-info -P "name of object"` -Custom attributes: +Custom variables: Name | Description :---------------------|:------------ @@ -1586,7 +1618,7 @@ perfmon\_win\_syntax | **Optional**. Use this in the performance output instead Check command object for the `check_ping.exe` plugin. ping-windows should automatically detect whether `ping_win_address` is an IPv4 or IPv6 address. If not, use ping4-windows and ping6-windows. Also note that check\_ping.exe waits at least `ping_win_timeout` milliseconds between the pings. -Custom attributes: +Custom variables: Name | Description :------------------|:------------ @@ -1602,7 +1634,7 @@ ping\_win\_timeout | **Optional**. The timeout in milliseconds. Default: 1000 Check command object for `check_procs.exe` plugin. When using `procs_win_user` this plugins needs administrative privileges to access the processes of other users, to just enumerate them no additional privileges are required. -Custom attributes: +Custom variables: Name | Description :----------------|:------------ @@ -1616,7 +1648,7 @@ procs\_win\_user | **Optional**. Count this users processes. Check command object for `check_service.exe` plugin. This checks thresholds work different since the binary decision whether a service is running or not does not allow for three states. As a default `check_service.exe` will return CRITICAL when `service_win_service` is not running, the `service_win_warn` flag changes this to WARNING. -Custom attributes: +Custom variables: Name | Description :-------------------------|:------------ @@ -1630,14 +1662,14 @@ service\_win\_service | **Required**. Name of the service to check. Check command object for `check_swap.exe` plugin. The data collection is instant. -Custom attributes: - -Name | Description -:---------------|:------------ -swap\_win\_warn | **Optional**. The warning threshold. Defaults to "10%". -swap\_win\_crit | **Optional**. The critical threshold. Defaults to "5%". -swap\_win\_unit | **Optional**. The unit to display the received value in, thresholds are interpreted in this unit. Defaults to "mb" (megabyte). +Custom variables: +Name | Description +:--------------- | :------------ +swap\_win\_warn | **Optional**. The warning threshold. Defaults to "10%". +swap\_win\_crit | **Optional**. The critical threshold. Defaults to "5%". +swap\_win\_unit | **Optional**. The unit to display the received value in, thresholds are interpreted in this unit. Defaults to "mb" (megabyte). +swap\_win\_show\_used | **Optional**. Show used swap instead of the free swap. ### update-windows @@ -1649,23 +1681,23 @@ Querying Microsoft for Windows updates can take multiple seconds to minutes. An > The Network Services Account which runs Icinga 2 by default does not have the required > permissions to run this check. -Custom attributes: +Custom variables: Name | Description :-------------------|:------------ -update\_win\_warn | **Optional**. If set, returns warning when important updates are available. -update\_win\_crit | **Optional**. If set, return critical when important updates that require a reboot are available. +update\_win\_warn | **Optional**. The warning threshold. +update\_win\_crit | **Optional**. The critical threshold. update\_win\_reboot | **Optional**. Set to treat 'may need update' as 'definitely needs update'. Please Note that this is true for almost every update and is therefore not recommended. +ignore\_reboot | **Optional**. Set to disable behavior of returning critical if any updates require a reboot. -In contrast to most other plugins, the values of check_update's custom attributes do not set thresholds, but just enable/disable the behavior described in the table above. -It can be enabled/disabled for example by setting them to "true" or "false", "1" or "0" would also work. -Thresholds will always be "1". +If a warning threshold is set but not a critical threshold, the critical threshold will be set to one greater than the set warning threshold. +Unless the `ignore_reboot` flag is set, if any updates require a reboot the plugin will return critical. > **Note** > -> If they are enabled, performance data will be shown in the web interface. -> If run without the optional parameters, the plugin will output critical if any important updates are available. +> If they are enabled, performance data will be shown in the web interface. +> If run without the optional parameters, the plugin will output critical if any important updates are available. ### uptime-windows @@ -1673,7 +1705,7 @@ Thresholds will always be "1". Check command object for `check_uptime.exe` plugin. Uses GetTickCount64 to get the uptime, so boot time is not included. -Custom attributes: +Custom variables: Name | Description :-----------------|:------------ @@ -1686,7 +1718,7 @@ uptime\_win\_unit | **Optional**. The unit to display the received value in, thr Check command object for `check_users.exe` plugin. -Custom attributes: +Custom variables: Name | Description :----------------|:------------ @@ -1714,7 +1746,7 @@ are not recommended with using the legacy HTTP API. `check_nscp_api` is part of the Icinga 2 plugins. This plugin is available for both, Windows and Linux/Unix. -Verify that the ITL CheckCommand is included in the [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) configuration file: +Verify that the ITL CheckCommand is included in the [icinga2.conf](04-configuration.md#icinga2-conf) configuration file: vim /etc/icinga2/icinga2.conf @@ -1733,7 +1765,7 @@ and integrate them into Icinga 2. The check plugin `check_nscp_api` can be integrated with the `nscp_api` CheckCommand object: -Custom attributes: +Custom variables: Name | Description :----------------------|:---------------------- @@ -1757,12 +1789,12 @@ check_cpu CRITICAL: critical(5m: 48%, 1m: 36%), 5s: 0% | 'total 5m'=48%;40;30 't Icinga 2 can use the `nscp client` command to run arbitrary NSClient++ checks locally on the client. You can enable these check commands by adding the following the include directive in your -[icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) configuration file: +[icinga2.conf](04-configuration.md#icinga2-conf) configuration file: include You can also optionally specify an alternative installation directory for NSClient++ by adding -the NscpPath constant in your [constants.conf](04-configuring-icinga-2.md#constants-conf) configuration +the NscpPath constant in your [constants.conf](04-configuration.md#constants-conf) configuration file: const NscpPath = "C:\\Program Files (x86)\\NSClient++" @@ -1774,7 +1806,7 @@ Note that it is not necessary to run NSClient++ as a Windows service for these c The check command object for NSClient++ is available as `nscp-local`. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------- @@ -1822,19 +1854,19 @@ nscp_memory_showall | **Optional.** Shows more details in plugin output, defau Check command object for the `check_os_version` NSClient++ plugin. -This command has the same custom attributes like the `nscp-local` check command. +This command has the same custom variables like the `nscp-local` check command. ### nscp-local-pagefile Check command object for the `check_pagefile` NSClient++ plugin. -This command has the same custom attributes like the `nscp-local` check command. +This command has the same custom variables like the `nscp-local` check command. ### nscp-local-process Check command object for the `check_process` NSClient++ plugin. -This command has the same custom attributes like the `nscp-local` check command. +This command has the same custom variables like the `nscp-local` check command. ### nscp-local-service @@ -1857,13 +1889,13 @@ nscp_service_showall | **Optional.** Shows more details in plugin output, defa Check command object for the `check_uptime` NSClient++ plugin. -This command has the same custom attributes like the `nscp-local` check command. +This command has the same custom variables like the `nscp-local` check command. ### nscp-local-version Check command object for the `check_version` NSClient++ plugin. -This command has the same custom attributes like the `nscp-local` check command. +This command has the same custom variables like the `nscp-local` check command. In addition to that the default value for `nscp_modules` is set to `[ "CheckHelpers" ]`. ### nscp-local-disk @@ -1872,7 +1904,8 @@ Check command object for the `check_drivesize` NSClient++ plugin. Name | Description -----------------------|------------------ -nscp_disk_drive | **Optional.** Drive character, default to all drives. +nscp_disk_drive | **Optional.** Drive character, default to all drives. Can be an array if multiple drives should be monitored. +nscp_disk_exclude | **Optional.** Drive character, default to none. Can be an array of drive characters if multiple drives should be excluded. nscp_disk_free | **Optional.** Switch between checking free space (free=true) or used space (free=false), default to false. nscp_disk_warning | **Optional.** Threshold for WARNING in percent or absolute (use MB, GB, ...), default to 80 (used) or 20 percent (free). nscp_disk_critical | **Optional.** Threshold for CRITICAL in percent or absolute (use MB, GB, ...), default to 90 (used) or 10 percent (free). @@ -1893,6 +1926,25 @@ nscp_counter_arguments | **Optional.** Additional arguments. nscp_counter_showall | **Optional.** Shows more details in plugin output, default to false. nscp_counter_perfsyntax | **Optional.** Apply performance data label, e.g. `Total Processor Time` to avoid special character problems. Defaults to `nscp_counter_name`. +### nscp-local-tasksched + +Check Command object for the `check_tasksched` NSClient++ plugin. +You can check for a single task or for a complete folder (and sub folders) of tasks. + +Name | Description +-----------------------|------------------ +nscp_tasksched_name | **Optional.** Name of the task to check. +nscp_tasksched_folder | **Optional.** The folder in which the tasks to check reside. +nscp_tasksched_recursive | **Optional.** Recurse sub folder, defaults to true. +nscp_tasksched_hidden | **Optional.** Look for hidden tasks, defaults to false. +nscp_tasksched_warning | **Optional.** Filter which marks items which generates a warning state, defaults to `exit_code != 0`. +nscp_tasksched_critical | **Optional.** Filter which marks items which generates a critical state, defaults to `exit_code < 0`. +nscp_tasksched_emptystate | **Optional.** Return status to use when nothing matched filter, defaults to warning. +nscp_tasksched_perfsyntax | **Optional.** Performance alias syntax., defaults to `%(title)` +nscp_tasksched_detailsyntax | **Optional.** Detail level syntax, defaults to `%(folder)/%(title): %(exit_code) != 0` +nscp_tasksched_arguments | **Optional.** Additional arguments. +nscp_tasksched_showall | **Optional.** Shows more details in plugin output, default to false. +nscp_modules | **Optional.** An array of NSClient++ modules to load. Defaults to `[ "CheckTaskSched" ]`. ## Plugin Check Commands for Manubulon SNMP @@ -1907,7 +1959,7 @@ The SNMP manubulon plugin check commands assume that the global constant named ` is set to the path where the Manubublon SNMP plugins are installed. You can enable these plugin check commands by adding the following the include directive in your -[icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) configuration file: +[icinga2.conf](04-configuration.md#icinga2-conf) configuration file: include @@ -1943,7 +1995,7 @@ You can enable these plugin check commands by adding the following the include d Check command object for the [check_snmp_env.pl](http://nagios.manubulon.com/snmp_env.html) plugin. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description @@ -1970,7 +2022,7 @@ snmp_timeout | **Optional.** The command timeout in seconds. Defaults Check command object for the [check_snmp_load.pl](http://nagios.manubulon.com/snmp_load.html) plugin. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description @@ -1997,7 +2049,7 @@ snmp_timeout | **Optional.** The command timeout in seconds. Defaults Check command object for the [check_snmp_mem.pl](http://nagios.manubulon.com/snmp_mem.html) plugin. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -2026,7 +2078,7 @@ snmp_timeout | **Optional.** The command timeout in seconds. Defaults Check command object for the [check_snmp_storage.pl](http://nagios.manubulon.com/snmp_storage.html) plugin. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -2045,7 +2097,9 @@ snmp_privpass | **Required.** SNMP version 3 priv password. No value d snmp_warn | **Optional.** The warning threshold. snmp_crit | **Optional.** The critical threshold. snmp_storage_name | **Optional.** Storage name. Default to regex "^/$$". More options available in the [snmp storage](http://nagios.manubulon.com/snmp_storage.html) documentation. +snmp_storage_type | **Optional.** Filter by storage type. Valid options are Other, Ram, VirtualMemory, FixedDisk, RemovableDisk, FloppyDisk, CompactDisk, RamDisk, FlashMemory, or NetworkDisk. No value defined as default. snmp_perf | **Optional.** Enable perfdata values. Defaults to true. +snmp_exclude | **Optional.** Select all storages except the one(s) selected by -m. No action on storage type selection. snmp_timeout | **Optional.** The command timeout in seconds. Defaults to 5 seconds. snmp_storage_olength | **Optional.** Max-size of the SNMP message, usefull in case of Too Long responses. @@ -2053,7 +2107,7 @@ snmp_storage_olength | **Optional.** Max-size of the SNMP message, usefull in ca Check command object for the [check_snmp_int.pl](http://nagios.manubulon.com/snmp_int.html) plugin. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------------------|-------------- @@ -2094,7 +2148,7 @@ snmp_timeout | **Optional.** The command timeout in seconds. Defa Check command object for the [check_snmp_process.pl](http://nagios.manubulon.com/snmp_process.html) plugin. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------------|-------------- @@ -2125,7 +2179,7 @@ snmp_process_cpu_threshold | **Optional.** Defines the warning and critical thre Check command object for the [check_snmp_win.pl](http://nagios.manubulon.com/snmp_windows.html) plugin. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------------|-------------- @@ -2155,7 +2209,7 @@ contributed by community members. These check commands assume that the global constant named `PluginContribDir` is set to the path where the user installs custom plugins and can be enabled by -uncommenting the corresponding line in [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf): +uncommenting the corresponding line in [icinga2.conf](04-configuration.md#icinga2-conf): ``` vim /etc/icinga2/icinga2.conf @@ -2165,6 +2219,58 @@ include This is enabled by default since Icinga 2 2.5.0. +### Big Data + +This category contains plugins for various Big Data systems. + +#### cloudera_service_status + +The [cloudera_service_status](https://github.com/miso231/icinga2-cloudera-plugin) plugin +uses Cloudera Manager API to monitor cluster services + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +----------------------|----------------------------------------------------------------- +cloudera_host | **Required.** Hostname of cloudera server. +cloudera_port | **Optional.** Port where cloudera is listening. Defaults to 443. +cloudera_user | **Required.** The username for the API connection. +cloudera_pass | **Required.** The password for the API connection. +cloudera_api_version | **Required.** API version of cloudera. +cloudera_cluster | **Required.** The cluster name in cloudera manager. +cloudera_service | **Required.** Name of cluster service to be checked. +cloudera_verify_ssl | **Optional.** Verify SSL. Defaults to true. + +#### cloudera_hdfs_space + +The [cloudera_hdfs_space](https://github.com/miso231/icinga2-cloudera-plugin) plugin +connects to Hadoop Namenode and gets used capacity of selected disk + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +--------------------------|----------------------------------------------------------------- +cloudera_hdfs_space_host | **Required.** Namenode host to connect to. +cloudera_hdfs_space_port | **Optional.** Namenode port (default 50070). +cloudera_hdfs_space_disk | **Required.** HDFS disk to check. +cloudera_hdfs_space_warn | **Required.** Warning threshold in percent. +cloudera_hdfs_space_crit | **Required.** Critical threshold in percent. + +#### cloudera_hdfs_files + +The [cloudera_hdfs_files](https://github.com/miso231/icinga2-cloudera-plugin) plugin +connects to Hadoop Namenode and gets total number of files on HDFS + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +--------------------------|----------------------------------------------------------------- +cloudera_hdfs_files_host | **Required.** Namenode host to connect to. +cloudera_hdfs_files_port | **Optional.** Namenode port (default 50070). +cloudera_hdfs_files_warn | **Required.** Warning threshold. +cloudera_hdfs_files_crit | **Required.** Critical threshold. +cloudera_hdfs_files_max | **Required.** Max files count that causes problems (default 140,000,000). + ### Databases This category contains plugins for various database servers. @@ -2177,11 +2283,11 @@ database. The Git repository is located on [GitHub](https://github.com/lausser/check_db2_health). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------------------|------------------------------------------------------------------------------------------------------------------------------ -db2_health_database | **Required.** The name of the database. (If it was catalogued locally, this parameter is the only you need. Otherwise you must specify database, hostname and port) +db2_health_database | **Required.** The name of the database. (If it was catalogued locally, this parameter and `db2_health_not_catalogued = false` are the only you need. Otherwise you must specify database, hostname and port) db2_health_username | **Optional.** The username for the database connection. db2_health_password | **Optional.** The password for the database connection. db2_health_port | **Optional.** The port where DB2 is listening. @@ -2197,6 +2303,7 @@ db2_health_maxinactivity | **Optional.** Used for the maximum amount of tim db2_health_mitigation | **Optional.** Classifies the severity of an offline tablespace. db2_health_lookback | **Optional.** How many days in the past db2_health check should look back to calculate exitcode. db2_health_report | **Optional.** Report can be used to output only the bad news. Possible values are "short", "long", "html". Defaults to `short`. +db2_health_not_catalogued | **Optional.** Set this variable to false if you want to use a catalogued locally database. Defaults to `true`. db2_health_env_db2_home | **Required.** Specifies the location of the db2 client libraries as environment variable `DB2_HOME`. Defaults to "/opt/ibm/db2/V10.5". db2_health_env_db2_version | **Optional.** Specifies the DB2 version as environment variable `DB2_VERSION`. @@ -2208,7 +2315,7 @@ uses the `DBD::Sybase` Perl library based on [FreeTDS](http://www.freetds.org/) The Git repository is located on [GitHub](https://github.com/lausser/check_mssql_health). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------------------|------------------------------------------------------------------------------------------------------------------------------ @@ -2258,7 +2365,7 @@ uses the `DBD::MySQL` Perl library to monitor a The Git repository is located on [GitHub](https://github.com/lausser/check_mysql_health). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------------------|------------------------------------------------------------------------------------------------------------------------------ @@ -2306,7 +2413,7 @@ uses the `DBD::Oracle` Perl library to monitor an [Oracle](https://www.oracle.co The Git repository is located on [GitHub](https://github.com/lausser/check_oracle_health). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------------------|------------------------------------------------------------------------------------------------------------------------------ @@ -2331,9 +2438,9 @@ Environment Macros: Name | Description --------------------|------------------------------------------------------------------------------------------------------------------------------------------ -ORACLE\_HOME | **Required.** Specifies the location of the oracle instant client libraries. Defaults to "/usr/lib/oracle/11.2/client64/lib". Can be overridden by setting the custom attribute `oracle_home`. -LD\_LIBRARY\_PATH | **Required.** Specifies the location of the oracle instant client libraries for the run-time shared library loader. Defaults to "/usr/lib/oracle/11.2/client64/lib". Can be overridden by setting the custom attribute `oracle_ld_library_path`. -TNS\_ADMIN | **Required.** Specifies the location of the tnsnames.ora including the database connection strings. Defaults to "/etc/icinga2/plugin-configs". Can be overridden by setting the custom attribute `oracle_tns_admin`. +ORACLE\_HOME | **Required.** Specifies the location of the oracle instant client libraries. Defaults to "/usr/lib/oracle/11.2/client64/lib". Can be overridden by setting the custom variable `oracle_home`. +LD\_LIBRARY\_PATH | **Required.** Specifies the location of the oracle instant client libraries for the run-time shared library loader. Defaults to "/usr/lib/oracle/11.2/client64/lib". Can be overridden by setting the custom variable `oracle_ld_library_path`. +TNS\_ADMIN | **Required.** Specifies the location of the tnsnames.ora including the database connection strings. Defaults to "/etc/icinga2/plugin-configs". Can be overridden by setting the custom variable `oracle_tns_admin`. #### postgres @@ -2342,7 +2449,7 @@ uses the `psql` binary to monitor a [PostgreSQL](https://www.postgresql.org/abou The Git repository is located on [GitHub](https://github.com/bucardo/check_postgres). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------------------|------------------------------------------------------------------------------------------------------------------------------ @@ -2372,7 +2479,7 @@ postgres_tempdir | **Optional.** Specify directory for temporary files. The The [check_mongodb.py](https://github.com/mzupan/nagios-plugin-mongodb) plugin uses the `pymongo` Python library to monitor a [MongoDB](https://docs.mongodb.com/manual/) instance. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------------------|------------------------------------------------------------------------------------------------------------------------------ @@ -2401,7 +2508,7 @@ mongodb_sampletime | **Optional.** Time used to sample number of p The [check_elasticsearch](https://github.com/anchor/nagios-plugin-elasticsearch) plugin uses the HTTP API to monitor an [Elasticsearch](https://www.elastic.co/products/elasticsearch) node. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -----------------------------|------------------------------------------------------------------------------------------------------- @@ -2419,7 +2526,7 @@ uses the `Redis` Perl library to monitor a [Redis](https://redis.io/) instance. measure response time, hitrate, memory utilization, check replication synchronization, etc. It is also possible to test data in a specified key and calculate averages or summaries on ranges. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -------------------------|-------------------------------------------------------------------------------------------------------------- @@ -2444,6 +2551,49 @@ redis_memory_utilization | **Optional.** This calculates percent of total memory redis_total_memory | **Optional.** Amount of memory on a system for memory utilization calculation. Use system memory or max_memory setting of redis. redis_replication_delay | **Optional.** Allows to set threshold on replication delay info. +#### proxysql + +The [check_proxysql](https://github.com/sysown/proxysql-nagios) plugin, +uses the `proxysql` binary to monitor [proxysql](https://proxysql.com/). + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +-----------------------------|---------------------------------------------------------------------------------- +proxysql_user | **Optional.** ProxySQL admin username (default=admin) +proxysql_password | **Optional.** ProxySQL admin password (default=admin) +proxysql_host | **Optional.** ProxySQL hostname / IP (default=127.0.0.1) +proxysql_port | **Optional.** ProxySQL admin port (default=6032) +proxysql_defaultfile | **Optional.** ProxySQL defaults file +proxysql_type | **Required.** ProxySQL check type (one of conns,hg,rules,status,var) +proxysql_name | **Optional.** ProxySQL variable name to check +proxysql_lower | **Optional.** Alert if ProxySQL value are LOWER than defined WARN / CRIT thresholds (only applies to 'var' check type) +proxysql_runtime | **Optional.** Force ProxySQL Nagios check to query the runtime_mysql_XXX tables rather than the mysql_XXX tables +proxysql_warning | **Optional.** Warning threshold +proxysql_critical | **Optional.** Critical threshold +proxysql\_include\_hostgroup | **Optional.** ProxySQL hostgroup(s) to include (only applies to '--type hg' checks, accepts comma-separated list) +proxysql\_ignore\_hostgroup | **Optional.** ProxySQL hostgroup(s) to ignore (only applies to '--type hg' checks, accepts comma-separated list) + +#### memcached + +The [check_memcached](https://packages.debian.org/stretch/nagios-plugins-contrib) plugin +is provided by the `nagios-plugin-contrib` package on Debian/Ubuntu. + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +--------------------------------|---------------------------------------------------------------------------------- +memcached_hostname | **Required.** Hostname or IP address (required) optional ':port' overrides -p +memcached_port | **Optional.** Port number (default: 11211) +memcached_verbose | **Optional.** verbose messages +memcached_keep | **Optional.** Keep up to this many items in the history object in memcached (default: 30) +memcached_minimum_stat_interval | **Optional.** Minimum time interval (in minutes) to use to analyse stats. (default: 30) +memcached_warning_hits_misses | **Optional.** Generate warning if quotient of hits/misses falls below this value (default: 2.0) +memcached_warning_evictions | **Optional.** Generate warning if number of evictions exceeds this threshold. 0=disable. (default: 10) +memcached_timeout | **Optional.** timeout in seconds (default: 1.0) +memcached_key | **Optional.** key name for history object (default: check_memcached) +memcached_expiry | **Optional.** expiry time in seconds for history object (default: 7200) +memcached_performance_output | **Optional.** output performance statistics as rate-per-minute figures (better suited to pnp4nagios) ### Hardware @@ -2461,13 +2611,13 @@ The plugin can run in two different ways: 1. Local execution using the `hpasmcli` command line tool. 2. Remote SNMP query which invokes the HP Insight Tools on the remote node. -You can either set or omit `hpasm_hostname` custom attribute and select the corresponding node. +You can either set or omit `hpasm_hostname` custom variable and select the corresponding node. The `hpasm_remote` attribute enables the plugin to execute remote SNMP queries if set to `true`. For compatibility reasons this attribute uses `true` as default value, and ensures that specifying the `hpasm_hostname` always enables remote checks. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --------------------------------|----------------------------------------------------------------------- @@ -2501,7 +2651,7 @@ check_by_ssh or similar, whichever suits your needs and particular taste. The plugin checks the health of the storage subsystem, power supplies, memory modules, temperature probes etc., and gives an alert if any of the components are faulty or operate outside normal parameters. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --------------------------------|----------------------------------------------------------------------- @@ -2531,12 +2681,53 @@ openmanage_timeout | **Optional.** Plugin timeout in seconds [default=30] openmanage_vdisk_critical | **Optional.** Make any alerts on virtual disks critical openmanage_warning | **Optional.** Custom temperature warning limits +#### lmsensors + +The [check_lmsensors](https://github.com/jackbenny/check_temp) plugin, +uses the `lm-sensors` binary to monitor temperature sensors. + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +------------------------|---------------------------------------------------------------------------------- +lmsensors_warning | **Required.** Exit with WARNING status if above INTEGER degrees +lmsensors_critical | **Required.** Exit with CRITICAL status if above INTEGER degrees +lmsensors_sensor | **Optional.** Set what to monitor, for example CPU or MB (or M/B). Check sensors for the correct word. Default is CPU. + +#### hddtemp + +The [check_hddtemp](https://github.com/vint21h/nagios-check-hddtemp) plugin, +uses the `hddtemp` binary to monitor hard drive temperature. + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +------------------------|---------------------------------------------------------------------------------- +hddtemp_server | **Required.** server name or address +hddtemp_port | **Optional.** port number +hddtemp_devices | **Optional.** comma separated devices list, or empty for all devices in hddtemp response +hddtemp_separator | **Optional.** hddtemp separator +hddtemp_warning | **Required.** warning temperature +hddtemp_critical | **Required.** critical temperature +hddtemp_timeout | **Optional.** receiving data from hddtemp operation network timeout +hddtemp_performance | **Optional.** If set, return performance data +hddtemp_quiet | **Optional.** If set, be quiet + +The following sane default value are specified: +``` +vars.hddtemp_server = "127.0.0.1" +vars.hddtemp_warning = 55 +vars.hddtemp_critical = 60 +vars.hddtemp_performance = true +vars.hddtemp_timeout = 5 +``` + #### adaptec-raid The [check_adaptec_raid](https://github.com/thomas-krenn/check_adaptec_raid) plugin uses the `arcconf` binary to monitor Adaptec RAID controllers. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --------------------------------|----------------------------------------------------------------------- @@ -2548,19 +2739,40 @@ arcconf_path | **Required.** Path to the `arcconf` binary, e. The [check_lsi_raid](https://github.com/thomas-krenn/check_lsi_raid) plugin uses the `storcli` binary to monitor MegaRAID RAID controllers. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --------------------------------|----------------------------------------------------------------------- -lsi_controller_number | **Required.** Controller number to monitor. -storcli_path | **Required.** Path to the `storcli` binary, e.g. "/usr/sbin/storcli". +lsi_controller_number | **Optional.** Controller number to monitor. +storcli_path | **Optional.** Path to the `storcli` binary, e.g. "/usr/sbin/storcli". +lsi_enclosure_id | **Optional.** Enclosure numbers to be checked, comma-separated. +lsi_ld_id | **Optional.** Logical devices to be checked, comma-separated. +lsi_pd_id | **Optional.** Physical devices to be checked, comma-separated. +lsi_temp_warning | **Optional.** RAID controller warning temperature. +lsi_temp_critical | **Optional.** RAID controller critical temperature. +lsi_pd_temp_warning | **Optional.** Disk warning temperature. +lsi_pd_temp_critical | **Optional.** Disk critical temperature. +lsi_bbu_temp_warning | **Optional.** Battery warning temperature. +lsi_bbu_temp_critical | **Optional.** Battery critical temperature. +lsi_cv_temp_warning | **Optional.** CacheVault warning temperature. +lsi_cv_temp_critical | **Optional.** CacheVault critical temperature. +lsi_ignored_media_errors | **Optional.** Warning threshold for media errors. +lsi_ignored_other_errors | **Optional.** Warning threshold for other errors. +lsi_ignored_predictive_fails | **Optional.** Warning threshold for predictive failures. +lsi_ignored_shield_counters | **Optional.** Warning threshold for shield counter. +lsi_ignored_bbm_counters | **Optional.** Warning threshold for BBM counter. +lsi_bbu | **Optional.** Define if BBU is present and it's state should be checked. +lsi_noenclosures | **Optional.** If set to true, does not check enclosures. +lsi_nosudo | **Optional.** If set to true, does not use sudo when running storcli. +lsi_nocleanlogs | **Optional.** If set to true, does not clean up the log files after executing storcli checks. + #### smart-attributes The [check_smart_attributes](https://github.com/thomas-krenn/check_smart_attributes) plugin uses the `smartctl` binary to monitor SMART values of SSDs and HDDs. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --------------------------------|----------------------------------------------------------------------- @@ -2579,7 +2791,7 @@ The user running Icinga 2 needs sufficient permissions to read the Icinga Web 2 This subcommand is provided by the [business process module](https://exchange.icinga.com/icinga/Business+Process) and executed as `icingacli businessprocess` CLI command. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------------------------|----------------------------------------------------------------------------------------- @@ -2592,13 +2804,46 @@ icingacli_businessprocess_statetype | **Optional.** Define which state typ This subcommand is provided by the [director module](https://github.com/Icinga/icingaweb2-module-director) > 1.4.2 and executed as `icingacli director health check`. Please refer to the [documentation](https://github.com/Icinga/icingaweb2-module-director/blob/master/doc/60-CLI.md#health-check-plugin) for all available sub-checks. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------------------------|----------------------------------------------------------------------------------------- icingacli_director_check | **Optional.** Run only a specific test suite. icingacli_director_db | **Optional.** Use a specific Icinga Web DB resource. +#### Elasticsearch + +This subcommand is provided by the [elasticsearch_module](https://github.com/Icinga/icingaweb2-module-elasticsearch) and executed as `icingacli elasticsearch check`. + +* The value of `icingacli_elasticsearch_instance` is the same like in the configuration of the module. +* The value of `icingacli_elasticsearch_filter` are filters for events in Icinga Web 2 syntax. e.g. `"beat.hostname=www.example.com" AND severity=critical` +* The thresholds are just numerical values. They get checked against how many events match the filter within the given timeframe. +* The value of `icingacli_elasticsearch_index` is an index pattern. e.g. `logstash*` + +Name | Description +------------------------------------------|----------------------------------------------------------------------------------------- +icingacli_elasticsearch_instance | **Required.** The Elasticsearch to connect to +icingacli_elasticsearch_index | **Required.** Index pattern to use when searching +icingacli_elasticsearch_critical | **Required.** Critical threshold +icingacli_elasticsearch_warning | **Required.** Warning threshold +icingacli_elasticsearch_filter | **Required.** Filter for events +icingacli_elasticsearch_from | **Optional.** Negative value of time to search from now (Default: -5m) + +#### x509 + +This subcommand is provided by the [x509 module](https://github.com/Icinga/icingaweb2-module-x509) and executed as `icingacli x509 check host`. Please refer to the [documentation](https://github.com/Icinga/icingaweb2-module-x509/blob/master/doc/10-Monitoring.md#host-check-command) for more information. + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +------------------------------------------|----------------------------------------------------------------------------------------- +icingacli_x509_ip | **Required.** A hosts IP address [or] +icingacli_x509_host | **Required.** A hosts name +icingacli_x509_port | **Optional.** The port to check in particular +icingacli_x509_warning | **Optional.** Less remaining time results in state WARNING (Default: 25%) +icingacli_x509_critical | **Optional.** Less remaining time results in state CRITICAL (Default: 10%) +icingacli_x509_allow_self_signed | **Optional.** Ignore if a certificate or its issuer has been self-signed (Default: false) + ### IPMI Devices This category includes all plugins for IPMI devices. @@ -2610,7 +2855,7 @@ uses the `ipmimonitoring` binary to monitor sensor data for IPMI devices. Please read the [documentation](https://www.thomas-krenn.com/en/wiki/IPMI_Sensor_Monitoring_Plugin) for installation and configuration details. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------------------|----------------------------------------------------------------------------------------------------- @@ -2633,12 +2878,13 @@ ipmi_no_sel_checking | **Optional.** Turn off system event log check ipmi_no_thresholds | **Optional.** Turn off performance data thresholds from output-sensor-thresholds. ipmi_verbose | **Optional.** Be Verbose multi line output, also with additional details for warnings. ipmi_debug | **Optional.** Be Verbose debugging output, followed by normal multi line output. +ipmi_unify_file | **Optional.** Path to the unify file to unify sensor names. #### ipmi-alive The `ipmi-alive` check commands allows you to create a ping check for the IPMI Interface. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------------------|----------------------------------------------------------------------------------------------------- @@ -2676,6 +2922,38 @@ logstash_inflight_crit | **Optional.** Critical threshold of inflight events logstash_cpu_warn | **Optional.** Warning threshold for cpu usage in percent. logstash_cpu_crit | **Optional.** Critical threshold for cpu usage in percent. +#### logfiles + +The [logfiles](https://labs.consol.de/nagios/check_logfiles/) plugin finds +specified patterns in log files. + +Name | Description +----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +logfiles_tag | **Optional.** A short unique descriptor for this search. It will appear in the output of the plugin and is used to separare the different services. +logfiles_logfile | **Optional.** This is the name of the log file you want to scan. +logfiles_rotation | **Optional.** This is the method how log files are rotated. One of the predefined methods or a regular expression, which helps identify the rotated archives. If this key is missing, check_logfiles assumes that the log file will be simply overwritten instead of rotated. +logfiles_critical_pattern | **Optional.** A regular expression which will trigger a critical error. +logfiles_warning_pattern | **Optional.** A regular expression which will trigger a warning error. +logfiles_critical_exception | **Optional.** A regular expression, the exceptions which are not counted as critical errors. +logfiles_warning_exception | **Optional.** A regular expression, the exceptions which are not counted as warning errors. +logfiles_ok_pattern | **Optional.** A regular expression which resets the error counters. +logfiles_no_protocol | **Optional.** Normally all the matched lines are written into a protocol file with this file’s name appearing in the plugin’s output. This option switches this off. +logfiles_syslog_server | **Optional.** With this option you limit the pattern matching to lines originating from the host check_logfiles is running on. +logfiles_syslog_client | **Optional.** With this option you limit the pattern matching to lines originating from the host named in this option. +logfiles_sticky | **Optional.** Errors are propagated through successive runs. +logfiles_unstick | **Optional.** Resets sticky errors. +logfiles_config | **Optional.** The name of a configuration file. +logfiles_configdir | **Optional.** The name of a configuration directory. Configfiles ending in .cfg or .conf are (recursively) imported. +logfiles_searches | **Optional.** A list of tags of those searches which are to be run. Using this parameter, not all searches listed in the config file are run, but only those selected. +logfiles_selectedsearches | **Optional.** A list of tags of those searches which are to be run. Using this parameter, not all searches listed in the config file are run, but only those selected. +logfiles_report | **Optional.** This option turns on multiline output (Default: off). The setting html generates a table which display the last hits in the service details view. Possible values are: short, long, html or off. +logfiles_max_length | **Optional.** With this parameter long lines are truncated (Default: off). Some programs (e.g. TrueScan) generate entries in the eventlog of such a length, that the output of the plugin becomes longer than 1024 characters. NSClient++ discards these. +logfiles_winwarncrit | **Optional.** With this parameter messages in the eventlog are classified by the type WARNING/ERROR (Default: off). Replaces or complements warning/criticalpattern. +logfiles_run_unique | **Optional.** This parameter prevents check_logfiles from starting when there’s already another instance using the same config file. (exits with UNKNOWN). +logfiles_timeout | **Optional.** This parameter causes an abort of a running search after a defined number of seconds. It is an aborted in a controlled manner, so that the lines which have been read so far, are used for the computation of the final result. +logfiles_warning | **Optional.** Complex handler-scripts can be provided with a warning-parameter this way. Inside the scripts the value is accessible as the macro CL_WARNING. +logfiles_critical | **Optional.** Complex handler-scripts can be provided with a critical-parameter this way. Inside the scripts the value is accessible as the macro CL_CRITICAL. + ### Metrics @@ -2686,7 +2964,7 @@ This category includes all plugins for metric-based checks. The [check_graphite](https://github.com/obfuscurity/nagios-scripts) plugin uses the `rest-client` Ruby library to monitor a [Graphite](https://graphiteapp.org) instance. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------------------|----------------------------------------------------------------------------------------------------- @@ -2713,7 +2991,7 @@ generates a html page containing information about the monitored node and all of The Git repository is located on [GitHub](https://github.com/Tontonitch/interfacetable_v3t). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------------------|----------------------------------------------------------------------------------------------------- @@ -2784,12 +3062,13 @@ interfacetable_notype | **Optional.** Remove the interface type fo The [check_iftraffic](https://exchange.icinga.com/exchange/iftraffic) plugin checks the utilization of a given interface name using the SNMP protocol. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|--------------------------------------------------------- iftraffic_address | **Required.** Specifies the remote host. Defaults to "$address$". iftraffic_community | **Optional.** SNMP community. Defaults to "public'" if omitted. +iftraffic_version | **Optional.** SNMP version to use. Defaults to "1" if omitted. Requires v1.0.2+. iftraffic_interface | **Required.** Queried interface name. iftraffic_bandwidth | **Required.** Interface maximum speed in kilo/mega/giga/bits per second. iftraffic_units | **Optional.** Interface units can be one of these values: `g` (gigabits/s),`m` (megabits/s), `k` (kilobits/s),`b` (bits/s) @@ -2799,10 +3078,10 @@ iftraffic_max_counter | **Optional.** Maximum counter value of net devices in ki #### iftraffic64 -The [check_iftraffic64](https://exchange.icinga.com/exchange/iftraffic64) plugin +The [check_iftraffic64](https://exchange.icinga.com/exchange/check_iftraffic64) plugin checks the utilization of a given interface name using the SNMP protocol. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|--------------------------------------------------------- @@ -2820,7 +3099,7 @@ iftraffic64_max_counter | **Optional.** Maximum counter value of net devices in The [check_interfaces](https://git.netways.org/plugins/check_interfaces) plugin uses SNMP to monitor network interfaces and their utilization. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --------------------------|--------------------------------------------------------- @@ -2862,7 +3141,7 @@ Brocade ICX6610-24-HPOE, Cisco UC Telefonzeugs, FOUNDRY-SN-AGENT-MIB, FRITZ!BOX Juniper IVE, Pulse-Gateway MAG4610, Cisco IronPort AsyncOS, Foundry, etc. A complete list can be found in the plugin [documentation](https://labs.consol.de/nagios/check_nwc_health/index.html). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --------------------------------|--------------------------------------------------------- @@ -2881,6 +3160,7 @@ nwc_health_privpassword | **Optional.** The password for authPriv security le nwc_health_privprotocol | **Optional.** The private protocol for SNMPv3 (des\|aes\|aes128\|3des\|3desde). nwc_health_contextengineid | **Optional.** The context engine id for SNMPv3 (10 to 64 hex characters). nwc_health_contextname | **Optional.** The context name for SNMPv3 (empty represents the default context). +nwc_health_community2 | **Optional.** SNMP community which can be used to switch the context during runtime. nwc_health_name | **Optional.** The name of an interface (ifDescr). nwc_health_drecksptkdb | **Optional.** This parameter must be used instead of --name, because Devel::ptkdb is stealing the latter from the command line. nwc_health_alias | **Optional.** The alias name of a 64bit-interface (ifAlias) @@ -2890,6 +3170,7 @@ nwc_health_ifspeedout | **Optional.** Override the ifspeed oid of an interface nwc_health_ifspeed | **Optional.** Override the ifspeed oid of an interface nwc_health_units | **Optional.** One of %, B, KB, MB, GB, Bit, KBi, MBi, GBi. (used for e.g. mode interface-usage) nwc_health_name2 | **Optional.** The secondary name of a component. +nwc_health_name3 | **Optional.** The tertiary name of a component. nwc_health_role | **Optional.** The role of this device in a hsrp group (active/standby/listen). nwc_health_report | **Optional.** Can be used to shorten the output. Possible values are: 'long' (default), 'short' (to shorten if available), or 'html' (to produce some html outputs if available) nwc_health_lookback | **Optional.** The amount of time you want to look back when calculating average rates. Use it for mode interface-errors or interface-usage. Without --lookback the time between two runs of check_nwc_health is the base for calculations. If you want your checkresult to be based for example on the past hour, use --lookback 3600. @@ -2908,6 +3189,103 @@ nwc_health_oids | **Optional.** A list of oids which are downloaded and writte nwc_health_offline | **Optional.** The maximum number of seconds since the last update of cache file before it is considered too old. nwc_health_multiline | **Optional.** Multiline output +#### printer_health + +The [check_printer_health](https://labs.consol.de/nagios/check_printer_health/index.html) plugin +uses SNMP to monitor printer. The plugin is able to generate supply statistics and check hardware. +A complete list can be found in the plugin [documentation](https://labs.consol.de/nagios/check_printer_health/index.html). + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +--------------------------------|--------------------------------------------------------- +printer_health_hostname | **Required.** The host's address. Defaults to "$address$" if the host's `address` attribute is set, "$address6$" otherwise. +printer_health_mode | **Required.** The plugin mode. A list of all available modes can be found in the [plugin documentation](https://labs.consol.de/nagios/check_printer_health/index.html). +printer_health_timeout | **Optional.** Seconds before plugin times out (default: 15) +printer_health_blacklist | **Optional.** Blacklist some (missing/failed) components. +printer_health_port | **Optional.** The SNMP port to use (default: 161). +printer_health_domain | **Optional.** The transport domain to use (default: udp/ipv4, other possible values: udp6, udp/ipv6, tcp, tcp4, tcp/ipv4, tcp6, tcp/ipv6). +printer_health_protocol | **Optional.** The SNMP protocol to use (default: 2c, other possibilities: 1,3). +printer_health_community | **Optional.** SNMP community of the server (SNMP v1/2 only). +printer_health_username | **Optional.** The securityName for the USM security model (SNMPv3 only). +printer_health_authpassword | **Optional.** The authentication password for SNMPv3. +printer_health_authprotocol | **Optional.** The authentication protocol for SNMPv3 (md5\|sha). +printer_health_privpassword | **Optional.** The password for authPriv security level. +printer_health_privprotocol | **Optional.** The private protocol for SNMPv3 (des\|aes\|aes128\|3des\|3desde). +printer_health_contextengineid | **Optional.** The context engine id for SNMPv3 (10 to 64 hex characters). +printer_health_contextname | **Optional.** The context name for SNMPv3 (empty represents the default context). +printer_health_community2 | **Optional.** SNMP community which can be used to switch the context during runtime. +printer_health_name | **Optional.** The name of an interface (ifDescr). +printer_health_regexp | **Optional.** A flag indicating that --name is a regular expression +printer_health_ifspeedin | **Optional.** Override the ifspeed oid of an interface (only inbound) +printer_health_ifspeedout | **Optional.** Override the ifspeed oid of an interface (only outbound) +printer_health_ifspeed | **Optional.** Override the ifspeed oid of an interface +printer_health_units | **Optional.** One of %, B, KB, MB, GB, Bit, KBi, MBi, GBi. (used for e.g. mode interface-usage) +printer_health_name2 | **Optional.** The secondary name of a component. +printer_health_name3 | **Optional.** The teritary name of a component. +printer_health_role | **Optional.** The role of this device in a hsrp group (active/standby/listen). +printer_health_report | **Optional.** Can be used to shorten the output. Possible values are: 'long' (default), 'short' (to shorten if available), or 'html' (to produce some html outputs if available) +printer_health_lookback | **Optional.** The amount of time you want to look back when calculating average rates. Use it for mode interface-errors or interface-usage. Without --lookback the time between two runs of `check_printer_health` is the base for calculations. If you want your checkresult to be based for example on the past hour, use --lookback 3600. +printer_health_warning | **Optional.** The warning threshold +printer_health_critical | **Optional.** The critical threshold +printer_health_warningx | **Optional.** The extended warning thresholds +printer_health_criticalx | **Optional.** The extended critical thresholds +printer_health_mitigation | **Optional.** The parameter allows you to change a critical error to a warning (1) or ok (0). +printer_health_selectedperfdata | **Optional.** The parameter allows you to limit the list of performance data. It's a perl regexp. Only matching perfdata show up in the output. +printer_health_morphperfdata | **Optional.** The parameter allows you to change performance data labels. It's a perl regexp and a substitution. --morphperfdata '(.*)ISATAP(.*)'='$1patasi$2' +printer_health_negate | **Optional.** The parameter allows you to map exit levels, such as warning=critical. +printer_health_mymodules-dyn-dir | **Optional.** A directory where own extensions can be found. +printer_health_servertype | **Optional.** The type of the network device: cisco (default). Use it if auto-detection is not possible. +printer_health_statefilesdir | **Optional.** An alternate directory where the plugin can save files. +printer_health_oids | **Optional.** A list of oids which are downloaded and written to a cache file. Use it together with --mode oidcache. +printer_health_offline | **Optional.** The maximum number of seconds since the last update of cache file before it is considered too old. +printer_health_multiline | **Optional.** Multiline output + + +### Network Services + +This category contains plugins which receive details about network services + +#### lsyncd + +The [check_lsyncd](https://github.com/ohitz/check_lsyncd) plugin, +uses the `lsyncd` status file to monitor [lsyncd](https://axkibe.github.io/lsyncd/). + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +------------------------|--------------------------------------------------------------------------- +lsyncd_statfile | **Optional.** Set status file path (default: /var/run/lsyncd.status). +lsyncd_warning | **Optional.** Warning if more than N delays (default: 10). +lsyncd_critical | **Optional.** Critical if more then N delays (default: 100). + +#### fail2ban + +The [check_fail2ban](https://github.com/fail2ban/fail2ban/tree/master/files/nagios) plugin +uses the `fail2ban-client` binary to monitor [fail2ban](http://www.fail2ban.org) jails. + +The plugin requires `sudo` permissions. +You can add a sudoers file to allow your monitoring user to use the plugin, i.e. edit /etc/sudoers.d/icinga and add: +``` +icinga ALL=(root) NOPASSWD:/usr/lib/nagios/plugins/check_fail2ban +``` + +and set the correct permissions: +```bash +chown -c root: /etc/sudoers.d/icinga +chmod -c 0440 /etc/sudoers.d/icinga +``` + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +------------------------|--------------------------------------------------------------------------- +fail2ban_display | **Optional.** To modify the output display, default is 'CHECK FAIL2BAN ACTIVITY' +fail2ban_path | **Optional.** Specify the path to the tw_cli binary, default value is /usr/bin/fail2ban-client +fail2ban_warning | **Optional.** Specify a warning threshold, default is 1 +fail2ban_critical | **Optional.** Specify a critical threshold, default is 2 +fail2ban_socket | **Optional.** Specify a socket path, default is unset +fail2ban_perfdata | **Optional.** If set to true, activate the perfdata output, default value for the plugin is set to true. ### Operating System @@ -2920,7 +3298,7 @@ The [check_mem.pl](https://github.com/justintime/nagios-plugins) plugin checks t memory usage on linux and unix hosts. It is able to count cache memory as free when compared to thresholds. More details can be found on [this blog entry](http://sysadminsjourney.com/content/2009/06/04/new-and-improved-checkmempl-nagios-plugin). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -------------|----------------------------------------------------------------------------------------------------------------------- @@ -2930,12 +3308,25 @@ mem_cache | **Optional.** If set to true, plugin will count cache as free mem mem_warning | **Required.** Specify the warning threshold as number interpreted as percent. mem_critical | **Required.** Specify the critical threshold as number interpreted as percent. +#### sar-perf + +The [check_sar_perf.py](https://github.com/dnsmichi/check-sar-perf) +plugin collects performance metrics from Linux hosts using the `sar` binary available in the `sysstat` package. + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +-----------------|----------------------------------------------------------------------------------------------------------------------- +sar_perf_profile | **Required.** Define the run profile: `pagestat`, `cpu`, `memory_util`, `memory_stat`, `io_transfer`, `queueln_load`, `swap_util`, `swap_stat`, `task`, `kernel`, `disk `. Can be a string or an array of multiple profiles. +sar_perf_disk | **Optional.** Disk name for the 'disk' profile. + + #### running_kernel The [check_running_kernel](https://packages.debian.org/stretch/nagios-plugins-contrib) plugin is provided by the `nagios-plugin-contrib` package on Debian/Ubuntu. -Custom attributes: +Custom variables: Name | Description ---------------------------|------------- @@ -2947,7 +3338,7 @@ The [check_iostats](https://github.com/dnsmichi/icinga-plugins/blob/master/scrip uses the `iostat` binary to monitor I/O on a Linux host. The default thresholds are rather high so you can use a grapher for baselining before setting your own. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------|----------------------------------------------------------------------------------------------------------------------- @@ -2967,7 +3358,7 @@ The [check_iostat](https://github.com/dnsmichi/icinga-plugins/blob/master/script uses the `iostat` binary to monitor disk I/O on a Linux host. The default thresholds are rather high so you can use a grapher for baselining before setting your own. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------|----------------------------------------------------------------------------------------------------------------------- @@ -2985,7 +3376,7 @@ The [check_yum](https://github.com/calestyo/check_yum) plugin checks the YUM pac management system for package updates. The plugin requires the `yum-plugin-security` package to differentiate between security and normal updates. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -3009,7 +3400,7 @@ The [glusterfs](https://www.unixadm.org/software/nagios-stuff/checks/check_glust is used to check the GlusterFS storage health on the server. The plugin requires `sudo` permissions. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -3021,6 +3412,52 @@ glusterfs_disk_critical | **Optional.** Return a critical error if disk usage glusterfs_inode_warning | **Optional.** Warn if inode usage is above *DISKWARN*. Defaults to 90 (percent). glusterfs_inode_critical | **Optional.** Return a critical error if inode usage is above *DISKCRIT*. Defaults to 95 (percent). +#### ceph + +The [ceph plugin](https://github.com/ceph/ceph-nagios-plugins) +is used to check the Ceph storage health on the server. + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +-----------------|--------------------------------------------------------- +ceph_exec_dir | **Optional.** Ceph executable. Default /usr/bin/ceph. +ceph_conf_file | **Optional.** Alternative ceph conf file. +ceph_mon_address | **Optional.** Ceph monitor address[:port]. +ceph_client_id | **Optional.** Ceph client id. +ceph_client_name | **Optional.** Ceph client name. +ceph_client_key | **Optional.** Ceph client keyring file. +ceph_whitelist | **Optional.** Whitelist regexp for ceph health warnings. +ceph_details | **Optional.** Run 'ceph health detail'. + +#### btrfs + +The [btrfs plugin](https://github.com/knorrie/python-btrfs/) +is used to check the btrfs storage health on the server. + +The plugin requires `sudo` permissions. +You can add a sudoers file to allow your monitoring user to use the plugin, i.e. edit /etc/sudoers.d/icinga and add: +``` +icinga ALL=(root) NOPASSWD:/usr/lib/nagios/plugins/check_btrfs +``` + +and set the correct permissions: +```bash +chown -c root: /etc/sudoers.d/icinga +chmod -c 0440 /etc/sudoers.d/icinga +``` + +[monitoring-plugins-btrfs](https://packages.debian.org/monitoring-plugins-btrfs) provide the necessary binary on debian/ubuntu. + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +-----------------|--------------------------------------------------------- +btrfs_awg | **Optional.** Exit with WARNING status if less than the specified amount of disk space (in GiB) is unallocated +btrfs_acg | **Optional.** Exit with CRITICAL status if less than the specified amount of disk space (in GiB) is unallocated +btrfs_awp | **Optional.** Exit with WARNING status if more than the specified percent of disk space is allocated +btrfs_acp | **Optional.** Exit with CRITICAL status if more than the specified percent of disk space is allocated +btrfs_mountpoint | **Required.** Path to the BTRFS mountpoint ### Virtualization @@ -3028,11 +3465,11 @@ This category includes all plugins for various virtualization technologies. #### esxi_hardware -The [check_esxi_hardware.py](https://www.claudiokuenzler.com/nagios-plugins/check_esxi_hardware.php) plugin +The [check_esxi_hardware.py](https://www.claudiokuenzler.com/monitoring-plugins/check_esxi_hardware.php) plugin uses the [pywbem](https://pywbem.github.io/pywbem/) Python library to monitor the hardware of ESXi servers through the [VMWare API](https://www.vmware.com/support/pubs/sdk_pubs.html) and CIM service. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -3042,7 +3479,8 @@ esxi_hardware_pass | **Required.** Password of the user. Can also be provid esxi_hardware_port | **Optional.** Specifies the CIM port to connect to. Defaults to 5989. esxi_hardware_vendor | **Optional.** Defines the vendor of the server: "auto", "dell", "hp", "ibm", "intel", "unknown" (default). esxi_hardware_html | **Optional.** Add web-links to hardware manuals for Dell servers (use your country extension). Only useful with **esxi_hardware_vendor** = dell. -esxi_hardware_ignore | **Optional.** Comma separated list of elements to ignore. +esxi_hardware_ignore | **Optional.** Comma separated list of CIM elements to ignore. +esxi_hardware_regex | **Optional.** Allow regular expression lookups of elements in ignore list. Defaults to false. esxi_hardware_perfdata | **Optional.** Add performcedata for graphers like PNP4Nagios to the output. Defaults to false. esxi_hardware_nopower | **Optional.** Do not collect power performance data, when **esxi_hardware_perfdata** is set to true. Defaults to false. esxi_hardware_novolts | **Optional.** Do not collect voltage performance data, when **esxi_hardware_perfdata** is set to true. Defaults to false. @@ -3059,7 +3497,7 @@ Check commands for the [check_vmware_esx](https://github.com/BaldMansMojo/check_ Check command object for the `check_vmware_esx` plugin. Shows all datastore volumes info. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3092,7 +3530,7 @@ vmware_crit | **Optional.** The critical threshold for volumes. Defa Check command object for the `check_vmware_esx` plugin. Shows all runtime info for the datacenter/Vcenter. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3115,7 +3553,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. List of vmware machines and their power state. BEWARE!! In larger environments systems can cause trouble displaying the informations needed due to the mass of data. Use **vmware_alertonly** to avoid this. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3143,7 +3581,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. List of VMware ESX hosts and their power state. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3171,7 +3609,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. List of VMware clusters and their states. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3199,7 +3637,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. All issues for the host. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3226,7 +3664,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. Overall object status (gray/green/red/yellow). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3249,7 +3687,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Vmware Tools status. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3279,7 +3717,7 @@ vmware_openvmtools | **Optional** Prevent CRITICAL state for installed and runni Check command object for the `check_vmware_esx` plugin. Simple check to verify a successful connection to VMware SOAP API. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3302,7 +3740,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Displays uptime of the VMware host. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3325,7 +3763,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. CPU usage in percentage. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3350,7 +3788,7 @@ vmware_crit | **Optional.** The critical threshold in percent. Defau Check command object for the `check_vmware_esx` plugin. Percentage of time that the virtual machine was ready, but could not get scheduled to run on the physical CPU. CPU ready time is dependent on the number of virtual machines on the host and their CPU loads. High or growing ready time can be a hint CPU bottlenecks. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3373,7 +3811,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. CPU time spent in wait state. The wait total includes time spent the CPU idle, CPU swap wait, and CPU I/O wait states. High or growing wait time can be a hint I/O bottlenecks. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3396,7 +3834,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Actively used CPU of the host, as a percentage of the total available CPU. Active CPU is approximately equal to the ratio of the used CPU to the available CPU. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3421,7 +3859,7 @@ vmware_crit | **Optional.** The critical threshold in percent. Defau Check command object for the `check_vmware_esx` plugin. All mem info(except overall and no thresholds). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3444,7 +3882,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Average mem usage in percentage. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3469,7 +3907,7 @@ vmware_crit | **Optional.** The critical threshold in percent. Defau Check command object for the `check_vmware_esx` plugin. Amount of machine memory used on the host. Consumed memory includes Includes memory used by the Service Console, the VMkernel vSphere services, plus the total consumed metrics for all running virtual machines in MB. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3494,7 +3932,7 @@ vmware_crit | **Optional.** The critical threshold in percent. No va Check command object for the `check_vmware_esx` plugin. Amount of memory that is used by swap. Sum of memory swapped of all powered on VMs and vSphere services on the host in MB. In case of an error all VMs with their swap used will be displayed. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3520,7 +3958,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. Additional mem used by VM Server in MB. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3545,7 +3983,7 @@ vmware_crit | **Optional.** The critical threshold in percent. No va Check command object for the `check_vmware_esx` plugin. The sum of all vmmemctl values in MB for all powered-on virtual machines, plus vSphere services on the host. If the balloon target value is greater than the balloon value, the VMkernel inflates the balloon, causing more virtual machine memory to be reclaimed. If the balloon target value is less than the balloon value, the VMkernel deflates the balloon, which allows the virtual machine to consume additional memory if needed (used by VM memory control driver). In case of an error all VMs with their vmmemctl values will be displayed. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3571,7 +4009,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. Shows net info. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3596,7 +4034,7 @@ vmware_isregexp | **Optional.** Treat blacklist expression as regexp. Check command object for the `check_vmware_esx` plugin. Overall network usage in KBps(Kilobytes per Second). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3621,7 +4059,7 @@ vmware_crit | **Optional.** The critical threshold in KBps(Kilobytes Check command object for the `check_vmware_esx` plugin. Data receive in KBps(Kilobytes per Second). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3646,7 +4084,7 @@ vmware_crit | **Optional.** The critical threshold in KBps(Kilobytes Check command object for the `check_vmware_esx` plugin. Data send in KBps(Kilobytes per Second). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3671,7 +4109,7 @@ vmware_crit | **Optional.** The critical threshold in KBps(Kilobytes Check command object for the `check_vmware_esx` plugin. Check all active NICs. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3696,7 +4134,7 @@ vmware_isregexp | **Optional.** Treat blacklist expression as regexp. Check command object for the `check_vmware_esx` plugin. Shows all datastore volumes info. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3729,7 +4167,7 @@ vmware_spaceleft | **Optional.** This has to be used in conjunction with Check command object for the `check_vmware_esx` plugin. Shows all disk io info. Without subselect no thresholds can be given. All I/O values are aggregated from historical intervals over the past 24 hours with a 5 minute sample rate. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3752,7 +4190,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Number of aborted SCSI commands. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3777,7 +4215,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Number of SCSI bus resets. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3802,7 +4240,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Average number of kilobytes read from the disk each second. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3827,7 +4265,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Average amount of time (ms) to process a SCSI read command issued from the Guest OS to the virtual machine. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3852,7 +4290,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Average number of kilobytes written to disk each second. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3877,7 +4315,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Average amount of time (ms) taken to process a SCSI write command issued by the Guest OS to the virtual machine. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3902,7 +4340,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Aggregated disk I/O rate. For hosts, this metric includes the rates for all virtual machines running on the host. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3927,7 +4365,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Average amount of time (ms) spent by VMkernel processing each SCSI command. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3952,7 +4390,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Average amount of time (ms) to complete a SCSI command from the physical device. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -3977,7 +4415,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Average amount of time (ms) spent in the VMkernel queue. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4002,7 +4440,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Average amount of time (ms) taken during the collection interval to process a SCSI command issued by the guest OS to the virtual machine. The sum of kernelWriteLatency and deviceWriteLatency. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4027,7 +4465,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. List vm's with attached host mounted media like cd,dvd or floppy drives. This is important for monitoring because a virtual machine with a mount cd or dvd drive can not be moved to another host. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4054,7 +4492,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. Shows host service info. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4081,7 +4519,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. Shows runtime info: VMs, overall status, connection state, health, storagehealth, temperature and sensor are represented as one value and without thresholds. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4098,13 +4536,16 @@ vmware_nosession | **Optional.** No auth session -- IT SHOULD BE USED FOR vmware_username | **Optional.** The username to connect to Host or vCenter server. No value defined as default. vmware_password | **Optional.** The username's password. No value defined as default. vmware_authfile | **Optional.** Use auth file instead username/password to session connect. No effect if **vmware_username** and **vmware_password** are defined
**Authentication file content:**
username=vmuser
password=p@ssw0rd +vmware_exclude | **Optional.** Blacklist VMs name. No value defined as default. +vmware_include | **Optional.** Whitelist VMs name. No value defined as default. +vmware_isregexp | **Optional.** Treat blacklist and whitelist expressions as regexp. **vmware-esx-soap-host-runtime-con** Check command object for the `check_vmware_esx` plugin. Shows connection state. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4127,7 +4568,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. List of VMware machines and their status. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4154,7 +4595,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. Overall object status (gray/green/red/yellow). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4177,7 +4618,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Checks cpu/storage/memory/sensor status. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4203,7 +4644,7 @@ vmware_isregexp | **Optional.** Treat blacklist and whitelist expression Check command object for the `check_vmware_esx` plugin. List all available sensors(use for listing purpose only). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4229,7 +4670,7 @@ vmware_isregexp | **Optional.** Treat blacklist and whitelist expression Check command object for the `check_vmware_esx` plugin. This is to avoid a double alarm if you use **vmware-esx-soap-host-runtime-health** and **vmware-esx-soap-host-runtime-storagehealth**. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4255,7 +4696,7 @@ vmware_isregexp | **Optional.** Treat blacklist and whitelist expression Check command object for the `check_vmware_esx` plugin. Local storage status check. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4282,7 +4723,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. Lists all temperature sensors. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4309,7 +4750,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. Lists all configuration issues for the host. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4336,7 +4777,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. Shows Host storage info. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4362,7 +4803,7 @@ vmware_isregexp | **Optional.** Treat blacklist and whitelist expression Check command object for the `check_vmware_esx` plugin. List host bus adapters. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4389,7 +4830,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. List SCSI logical units. The listing will include: LUN, canonical name of the disc, all of displayed name which is not part of the canonical name and status. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4416,7 +4857,7 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean Check command object for the `check_vmware_esx` plugin. List multipaths and the associated paths. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4445,7 +4886,7 @@ vmware_standbyok | **Optional.** For storage systems where a standby mult Check command object for the `check_vmware_esx` plugin. Shows all CPU usage info. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4470,7 +4911,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Percentage of time that the virtual machine was ready, but could not get scheduled to run on the physical CPU. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4496,7 +4937,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. CPU time spent in wait state. The wait total includes time spent the CPU idle, CPU swap wait, and CPU I/O wait states. High or growing wait time can be a hint I/O bottlenecks. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4522,7 +4963,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Amount of actively used virtual CPU, as a percentage of total available CPU. This is the host's view of the CPU usage, not the guest operating system view. It is the average CPU utilization over all available virtual CPUs in the virtual machine. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4548,7 +4989,7 @@ vmware_crit | **Optional.** Critical threshold in percent. Defaults Check command object for the `check_vmware_esx` plugin. Shows all memory info, except overall. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4572,7 +5013,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Average mem usage in percentage of configured virtual machine "physical" memory. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4599,7 +5040,7 @@ vmware_crit | **Optional.** Critical threshold in percent. Defaults Check command object for the `check_vmware_esx` plugin. Amount of guest physical memory in MB consumed by the virtual machine for guest memory. Consumed memory does not include overhead memory. It includes shared memory and memory that might be reserved, but not actually used. Use this metric for charge-back purposes.
**vm consumed memory = memory granted -- memory saved** -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4625,7 +5066,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Amount of guest physical memory that is currently reclaimed from the virtual machine through ballooning. This is the amount of guest physical memory that has been allocated and pinned by the balloon driver. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4652,7 +5093,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Shows net info. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4676,7 +5117,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Overall network usage in KBps(Kilobytes per Second). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4702,7 +5143,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Receive in KBps(Kilobytes per Second). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4728,7 +5169,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Send in KBps(Kilobytes per Second). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4754,7 +5195,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Shows all disk io info. Without subselect no thresholds can be given. All I/O values are aggregated from historical intervals over the past 24 hours with a 5 minute sample rate. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4778,7 +5219,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Average number of kilobytes read from the disk each second. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4804,7 +5245,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Average number of kilobytes written to disk each second. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4830,7 +5271,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Aggregated disk I/O rate. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4856,7 +5297,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Shows virtual machine runtime info. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4880,7 +5321,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Shows the connection state. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4904,7 +5345,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Shows virtual machine power state: poweredOn, poweredOff or suspended. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4928,7 +5369,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Overall object status (gray/green/red/yellow). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4952,7 +5393,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Console connections to virtual machine. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -4978,7 +5419,7 @@ vmware_crit | **Optional.** The critical threshold. No value defined Check command object for the `check_vmware_esx` plugin. Guest OS status. Needs VMware Tools installed and running. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -5001,7 +5442,7 @@ vmware_authfile | **Optional.** Use auth file instead username/password Check command object for the `check_vmware_esx` plugin. Guest OS status. VMware tools status. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -5026,7 +5467,7 @@ vmware_openvmtools | **Optional** Prevent CRITICAL state for installed and runni Check command object for the `check_vmware_esx` plugin. All issues for the virtual machine. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -5051,31 +5492,35 @@ vmware_multiline | **Optional.** Multiline output in overview. This mean This category includes all plugins for web-based checks. -#### apache_status +#### apache-status The [check_apache_status.pl](https://github.com/lbetz/check_apache_status) plugin uses the [/server-status](https://httpd.apache.org/docs/current/mod/mod_status.html) HTTP endpoint to monitor status metrics for the Apache webserver. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): -Name | Description -------------------------|---------------------------------------------------------------------------------- -apache_status_address | **Optional.** The host's address. Defaults to "$address$" if the host's `address` attribute is set, `address6` otherwise. -apache_status_port | **Optional.** the http port. -apache_status_url | **Optional.** URL to use, instead of the default (http://`apache_status_address`/server-status). -apache_status_ssl | **Optional.** set to use ssl connection -apache_status_timeout | **Optional.** timeout in seconds -apache_status_warning | **Optional.** Warning threshold (number of open slots, busy workers and idle workers that will cause a WARNING) like ':20,50,:50'. -apache_status_critical | **Optional.** Critical threshold (number of open slots, busy workers and idle workers that will cause a CRITICAL) like ':10,25,:20'. +Name | Description +--------------------------------|---------------------------------------------------------------------------------- +apache_status_address | **Optional.** Host address. Defaults to "$address$" if the host's `address` attribute is set, `address6` otherwise. +apache_status_port | **Optional.** HTTP port. +apache_status_uri | **Optional.** URL to use, instead of the default (http://`apache_status_address`/server-status). +apache_status_ssl | **Optional.** Set to use SSL connection. +apache_status_no_validate | **Optional.** Skip SSL certificate validation. +apache_status_username | **Optional.** Username for basic auth. +apache_status_password | **Optional.** Password for basic auth. +apache_status_timeout | **Optional.** Timeout in seconds. +apache_status_unreachable | **Optional.** Return CRITICAL if socket timed out or http code >= 500. +apache_status_warning | **Optional.** Warning threshold (number of open slots, busy workers and idle workers that will cause a WARNING) like ':20,50,:50'. +apache_status_critical | **Optional.** Critical threshold (number of open slots, busy workers and idle workers that will cause a CRITICAL) like ':10,25,:20'. -### cert +#### ssl_cert The [check_ssl_cert](https://github.com/matteocorti/check_ssl_cert) plugin uses the openssl binary (and optional curl) to check a X.509 certificate. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --------------------------|-------------- @@ -5117,7 +5562,7 @@ web application and queries Java message beans on an application server. It is part of the `JMX::Jmx4Perl` Perl module which includes detailed [documentation](http://search.cpan.org/~roland/jmx4perl/scripts/check_jmx4perl). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description -----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -5162,7 +5607,7 @@ jmx4perl_check | **Optional.** Name of a check configuration as de The [check_kdc](https://exchange.nagios.org/directory/Plugins/Security/check_kdc/details) plugin uses the Kerberos `kinit` binary to monitor Kerberos 5 KDC by acquiring a ticket. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------------------------------------------------------------------- @@ -5178,7 +5623,7 @@ The [check_nginx_status.pl](https://github.com/regilero/check_nginx_status) plug uses the [/nginx_status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) HTTP endpoint which provides metrics for monitoring Nginx. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description --------------------------------|---------------------------------------------------------------------------------- @@ -5203,7 +5648,7 @@ The [check_rbl](https://github.com/matteocorti/check_rbl) plugin uses the `Net::DNS` Perl library to check whether your SMTP server is blacklisted. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ----------------|-------------------------------------------------------------------------- @@ -5211,7 +5656,7 @@ rbl_hostname | **Optional.** The address or name of the SMTP server to check. De rbl_server | **Required** List of RBL servers as an array. rbl_warning | **Optional** Number of blacklisting servers for a warning. rbl_critical | **Optional** Number of blacklisting servers for a critical. -tbl_timeout | **Optional** Seconds before plugin times out (default: 15). +rbl_timeout | **Optional** Seconds before plugin times out (default: 15). #### squid @@ -5219,7 +5664,7 @@ tbl_timeout | **Optional** Seconds before plugin times out (default: 15). The [check_squid](https://exchange.icinga.com/exchange/check_squid) plugin uses the `squidclient` binary to monitor a [Squid proxy](http://www.squid-cache.org). -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|---------------------------------------------------------------------------------- @@ -5246,7 +5691,7 @@ acceptance, and regression tests. A test harness allows you to run many test cas and collect/report your results. WebInject offers real-time results display and may also be used for monitoring system response times. -Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): Name | Description ------------------------|-------------- @@ -5256,3 +5701,104 @@ webinject_no_output | **Optional.** Suppresses all output to STDOUT except t webinject_timeout | **Optional.** The value [given in seconds] will be compared to the global time elapsed to run all the tests. If the tests have all been successful, but have taken more time than the 'globaltimeout' value, a warning message is sent back to Icinga. webinject_report_type | **Optional.** This setting is used to enable output formatting that is compatible for use with specific external programs. The available values you can set this to are: nagios, mrtg, external and standard. webinject_testcase_file | **Optional.** When you launch WebInject in console mode, you can optionally supply an argument for a testcase file to run. It will look for this file in the directory that webinject.pl resides in. If no filename is passed from the command line, it will look in config.xml for testcasefile declarations. If no files are specified, it will look for a default file named 'testcases.xml' in the current [webinject] directory. If none of these are found, the engine will stop and give you an error. + +#### varnish + +The [check_varnish](https://github.com/varnish/varnish-nagios) plugin, +also available in the [monitoring-plugins-contrib](https://packages.debian.org/sid/nagios-plugins-contrib) on debian, +uses the `varnishstat` binary to monitor [varnish](https://varnish-cache.org/). + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +------------------------|---------------------------------------------------------------------------------- +varnish_name | **Optional.** Specify the Varnish instance name +varnish_param | **Optional.** Specify the parameter to check (see below). The default is 'ratio'. +varnish_critical | **Optional.** Set critical threshold: [@][lo:]hi +varnish_warning | **Optional.** Set warning threshold: [@][lo:]hi + +For *varnish_param*, all items reported by varnishstat(1) are available - use the +identifier listed in the left column by `varnishstat -l`. In +addition, the following parameters are available: + +Name | Description +------------------------|---------------------------------------------------------------------------------- +uptime | How long the cache has been running (in seconds) +ratio | The cache hit ratio expressed as a percentage of hits to hits + misses. Default thresholds are 95 and 90. +usage | Cache file usage as a percentage of the total cache space. + +#### haproxy + +The [check_haproxy](https://salsa.debian.org/nagios-team/pkg-nagios-plugins-contrib/blob/master/check_haproxy/check_haproxy) plugin, +also available in the [monitoring-plugins-contrib](https://packages.debian.org/nagios-plugins-contrib) on debian, +uses the `haproxy` csv statistics page to monitor [haproxy](http://www.haproxy.org/) response time. The plugin outputa performance data for backends sessions and statistics response time. + +This plugin need to access the csv statistics page. You can configure it in haproxy by adding a new frontend: +``` +frontend stats + bind 127.0.0.1:80 + stats enablestats + stats uri /stats +``` + +The statistics page will be available at `http://127.0.0.1/stats;csv;norefresh`. + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +------------------------|---------------------------------------------------------------------------------- +haproxy_username | **Optional.** Username for HTTP Auth +haproxy_password | **Optional.** Password for HTTP Auth +haproxy_url | **Required.** URL of the HAProxy csv statistics page. +haproxy_timeout | **Optional.** Seconds before plugin times out (default: 10) +haproxy_warning | **Optional.** Warning request time threshold (in seconds) +haproxy_critical | **Optional.** Critical request time threshold (in seconds) + +#### haproxy_status + +The [check_haproxy_status](https://github.com/jonathanio/monitoring-nagios-haproxy) plugin, +uses the `haproxy` statistics socket to monitor [haproxy](http://www.haproxy.org/) frontends/backends. + +This plugin need read/write access to the statistics socket with an operator level. You can configure it in the global section of haproxy to allow icinga user to use it: +``` +stats socket /run/haproxy/admin.sock user haproxy group icinga mode 660 level operator +``` + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +----------------------------|---------------------------------------------------------------------------------- +haproxy\_status\_default | **Optional.** Set/Override the defaults which will be applied to all checks (unless specifically set by --overrides). +haproxy\_status\_frontends | **Optional.** Enable checks for the frontends in HAProxy (that they're marked as OPEN and the session limits haven't been reached). +haproxy\_status\_nofrontends| **Optional.** Disable checks for the frontends in HAProxy (that they're marked as OPEN and the session limits haven't been reached). +haproxy\_status\_backends | **Optional.** Enable checks for the backends in HAProxy (that they have the required quorum of servers, and that the session limits haven't been reached). +haproxy\_status\_nobackends | **Optional.** Disable checks for the backends in HAProxy (that they have the required quorum of servers, and that the session limits haven't been reached). +haproxy\_status\_servers | **Optional.** Enable checks for the servers in HAProxy (that they haven't reached the limits for the sessions or for queues). +haproxy\_status\_noservers | **Optional.** Disable checks for the servers in HAProxy (that they haven't reached the limits for the sessions or for queues). +haproxy\_status\_overrides | **Optional.** Override the defaults for a particular frontend or backend, in the form {name}:{override}, where {override} is the same format as --defaults above. +haproxy\_status\_socket | **Required.** Path to the socket check_haproxy should connect to + +#### phpfpm_status + +The [check_phpfpm_status](http://github.com/regilero/check_phpfpm_status) plugin, +uses the `php-fpm` status page to monitor php-fpm. + +Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): + +Name | Description +--------------------------|---------------------------------------------------------------------------------- +phpfpm\_status\_hostname | **Required.** name or IP address of host to check +phpfpm\_status\_port | **Optional.** Http port, or Fastcgi port when using --fastcgi +phpfpm\_status\_url | **Optional.** Specific URL (only the path part of it in fact) to use, instead of the default /fpm-status +phpfpm\_status\_servername| **Optional.** ServerName, (host header of HTTP request) use it if you specified an IP in -H to match the good Virtualhost in your target +phpfpm\_status\_fastcgi | **Optional.** If set, connect directly to php-fpm via network or local socket, using fastcgi protocol instead of HTTP. +phpfpm\_status\_user | **Optional.** Username for basic auth +phpfpm\_status\_pass | **Optional.** Password for basic auth +phpfpm\_status\_realm | **Optional.** Realm for basic auth +phpfpm\_status\_debug | **Optional.** If set, debug mode (show http request response) +phpfpm\_status\_timeout | **Optional.** timeout in seconds (Default: 15) +phpfpm\_status\_ssl | **Optional.** Wether we should use HTTPS instead of HTTP. Note that you can give some extra parameters to this settings. Default value is 'TLSv1' but you could use things like 'TLSv1_1' or 'TLSV1_2' (or even 'SSLv23:!SSLv2:!SSLv3' for old stuff). +phpfpm\_status\_verifyssl | **Optional.** If set, verify certificate and hostname from ssl cert, default is 0 (no security), set it to 1 to really make SSL peer name and certificater checks. +phpfpm\_status\_cacert | **Optional.** Full path to the cacert.pem certificate authority used to verify ssl certificates (use with --verifyssl). if not given the cacert from Mozilla::CA cpan plugin will be used. +phpfpm\_status\_warn | **Optional.** MIN_AVAILABLE_PROCESSES,PROC_MAX_REACHED,QUEUE_MAX_REACHED number of available workers, or max states reached that will cause a warning. -1 for no warning +phpfpm\_status\_critical | **Optional.** MIN_AVAILABLE_PROCESSES,PROC_MAX_REACHED,QUEUE_MAX_REACHED number of available workers, or max states reached that will cause an error, -1 for no CRITICAL diff --git a/doc/11-cli-commands.md b/doc/11-cli-commands.md index d288c64f5..7eac4247c 100644 --- a/doc/11-cli-commands.md +++ b/doc/11-cli-commands.md @@ -13,17 +13,18 @@ options. ``` # icinga2 -icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0) +icinga2 - The Icinga 2 network monitoring daemon (version: v2.11.0) Usage: icinga2 [] Supported commands: * api setup (setup for API) - * api user (API user creation helper) * ca list (lists all certificate signing requests) + * ca restore (restores a removed certificate request) + * ca remove (removes an outstanding certificate request) * ca sign (signs an outstanding certificate request) - * console (Icinga console) + * console (Icinga debug console) * daemon (starts Icinga 2) * feature disable (disables specified feature) * feature enable (enables specified feature) @@ -37,7 +38,6 @@ Supported commands: * pki save-cert (saves another Icinga 2 instance's certificate) * pki sign-csr (signs a CSR) * pki ticket (generates a ticket) - * troubleshoot (collect information for troubleshooting) * variable get (gets a variable) * variable list (lists all variables) @@ -56,7 +56,7 @@ Global options: -X [ --script-debugger ] whether to enable the script debugger Report bugs at -Icinga home page: +Icinga home page: ``` @@ -136,20 +136,19 @@ added. ## CLI command: Api -Provides the helper functions `api setup` and `api user`. The first to enable the REST API, the second to create -ApiUser objects with hashed password strings. -More details in the [Icinga 2 API](12-icinga2-api.md#icinga2-api-setup) chapter. +Provides helper functions to enable and setup the +[Icinga 2 API](12-icinga2-api.md#icinga2-api-setup). + +### CLI command: Api Setup ``` -# icinga2 api --help -icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0) +# icinga2 api setup --help +icinga2 - The Icinga 2 network monitoring daemon (version: v2.11.0) Usage: - icinga2 [] + icinga2 api setup [] -Supported commands: - * api setup (setup for API) - * api user (API user creation helper) +Setup for Icinga 2 API. Global options: -h [ --help ] show this help message @@ -157,16 +156,19 @@ Global options: --color use VT100 color codes even when stdout is not a terminal -D [ --define ] arg define a constant - -a [ --app ] arg application library name (default: icinga) - -l [ --library ] arg load a library -I [ --include ] arg add include search directory -x [ --log-level ] arg specify the log level for the console log. The valid value is either debug, notice, information (default), warning, or critical -X [ --script-debugger ] whether to enable the script debugger +Command options: + --cn arg The certificate's common name + Report bugs at -Icinga home page: +Get support: +Documentation: +Icinga home page: ``` ## CLI command: Ca @@ -177,7 +179,7 @@ chapter. This CLI command is available since v2.8. ``` # icinga2 ca --help -icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0) +icinga2 - The Icinga 2 network monitoring daemon (version: v2.11.0) Usage: icinga2 [] @@ -185,6 +187,8 @@ Usage: Supported commands: * ca list (lists all certificate signing requests) * ca sign (signs an outstanding certificate request) + * ca restore (restores a removed certificate request) + * ca remove (removes an outstanding certificate request) Global options: -h [ --help ] show this help message @@ -201,7 +205,44 @@ Global options: -X [ --script-debugger ] whether to enable the script debugger Report bugs at -Icinga home page: +Icinga home page: +``` + + +### CLI command: Ca List + +``` +icinga2 ca list --help +icinga2 - The Icinga 2 network monitoring daemon (version: v2.11.0) + +Usage: + icinga2 ca list [] + +Lists pending certificate signing requests. + +Global options: + -h [ --help ] show this help message + -V [ --version ] show version information + --color use VT100 color codes even when stdout is not a + terminal + -D [ --define ] arg define a constant + -I [ --include ] arg add include search directory + -x [ --log-level ] arg specify the log level for the console log. + The valid value is either debug, notice, + information (default), warning, or critical + -X [ --script-debugger ] whether to enable the script debugger + +Command options: + --all List all certificate signing requests, including + signed. Note: Old requests are automatically + cleaned by Icinga after 1 week. + --removed List all removed CSRs (for use with 'ca restore') + --json encode output as JSON + +Report bugs at +Get support: +Documentation: +Icinga home page: ``` ## CLI command: Console @@ -211,7 +252,7 @@ e.g. to test [functions](17-language-reference.md#functions) in your local sandb ``` $ icinga2 console -Icinga 2 (version: v2.8.0) +Icinga 2 (version: v2.11.0) <1> => function test(name) { <1> .. log("Hello " + name) <1> .. } @@ -226,7 +267,7 @@ Further usage examples can be found in the [library reference](18-library-refere ``` # icinga2 console --help -icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0) +icinga2 - The Icinga 2 network monitoring daemon (version: v2.11.0) Usage: icinga2 console [] @@ -255,7 +296,7 @@ Command options: --sandbox enable sandbox mode Report bugs at -Icinga home page: +Icinga home page: ``` @@ -273,7 +314,7 @@ are required for executing config expressions and auto-completion. > **Note** > -> The debug console does not currently support SSL certificate verification. +> The debug console does not currently support TLS certificate verification. > > Runtime modifications are not validated and might cause the Icinga 2 > daemon to crash or behave in an unexpected way. Use these runtime changes @@ -294,19 +335,19 @@ Here's an example: ``` $ ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://root@localhost:5665/' -Icinga 2 (version: v2.8.0) +Icinga 2 (version: v2.11.0) <1> => ``` Once connected you can inspect variables and execute other expressions by entering them at the prompt: ``` -<1> => var h = get_host("icinga2-client1.localdomain") +<1> => var h = get_host("icinga2-agent1.localdomain") null <2> => h.last_check_result { active = true - check_source = "icinga2-client1.localdomain" + check_source = "icinga2-agent1.localdomain" command = [ "/usr/local/sbin/check_ping", "-H", "127.0.0.1", "-c", "5000,100%", "-w", "3000,80%" ] execution_end = 1446653527.174983 execution_start = 1446653523.152673 @@ -341,10 +382,10 @@ The `--syntax-only` option can be used in combination with `--eval` or `--file` to check a script for syntax errors. In this mode the script is parsed to identify syntax errors but not evaluated. -Here's an example that retrieves the command that was used by Icinga to check the `icinga2-client1.localdomain` host: +Here's an example that retrieves the command that was used by Icinga to check the `icinga2-agent1.localdomain` host: ``` -$ ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://root@localhost:5665/' --eval 'get_host("icinga2-client1.localdomain").last_check_result.command' | python -m json.tool +$ ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://root@localhost:5665/' --eval 'get_host("icinga2-agent1.localdomain").last_check_result.command' | python -m json.tool [ "/usr/local/sbin/check_ping", "-H", @@ -363,7 +404,7 @@ Furthermore it allows to run the [configuration validation](11-cli-commands.md#c ``` # icinga2 daemon --help -icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0) +icinga2 - The Icinga 2 network monitoring daemon (version: v2.11.0) Usage: icinga2 daemon [] @@ -389,11 +430,13 @@ Command options: -z [ --no-config ] start without a configuration file -C [ --validate ] exit after validating the configuration -e [ --errorlog ] arg log fatal errors to the specified log file (only - works in combination with --daemonize) + works in combination with --daemonize or + --close-stdio) -d [ --daemonize ] detach from the controlling terminal + --close-stdio do not log to stdout (or stderr) after startup Report bugs at -Icinga home page: +Icinga home page: ``` ### Config Files @@ -403,7 +446,7 @@ Configuration files are processed in the order they're specified on the command- When no configuration file is specified and the `--no-config` is not used Icinga 2 automatically falls back to using the configuration file -`SysconfDir + "/icinga2/icinga2.conf"` (where SysconfDir is usually `/etc`). +`ConfigDir + "/icinga2.conf"` (where ConfigDir is usually `/etc/icinga2`). ### Validation @@ -442,7 +485,7 @@ nodes in a [distributed monitoring](06-distributed-monitoring.md#distributed-mon ``` # icinga2 node --help -icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0) +icinga2 - The Icinga 2 network monitoring daemon (version: v2.11.0) Usage: icinga2 [] @@ -466,7 +509,7 @@ Global options: -X [ --script-debugger ] whether to enable the script debugger Report bugs at -Icinga home page: +Icinga home page: ``` ## CLI command: Object @@ -485,7 +528,7 @@ More information can be found in the [troubleshooting](15-troubleshooting.md#tro ``` # icinga2 object --help -icinga2 - The Icinga 2 network monitoring daemon (version: v2.7.1-196-g23e8a6253; debug) +icinga2 - The Icinga 2 network monitoring daemon (version: v2.11.0) Usage: icinga2 [] @@ -508,7 +551,7 @@ Global options: -X [ --script-debugger ] whether to enable the script debugger Report bugs at -Icinga home page: +Icinga home page: ``` ## CLI command: Pki @@ -527,7 +570,7 @@ You will need them in the [distributed monitoring chapter](06-distributed-monito ``` # icinga2 pki --help -icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0) +icinga2 - The Icinga 2 network monitoring daemon (version: v2.11.0) Usage: icinga2 [] @@ -555,51 +598,7 @@ Global options: -X [ --script-debugger ] whether to enable the script debugger Report bugs at -Icinga home page: -``` - -## CLI command: Troubleshoot - -Collects basic information like version, paths, log files and crash reports for troubleshooting -purposes and prints them to a file or the console. See [troubleshooting](15-troubleshooting.md#troubleshooting-information-required). - -Its output defaults to a file named `troubleshooting-[TIMESTAMP].log` so it won't overwrite older troubleshooting files. - -Keep in mind that this tool can not collect information from other icinga2 nodes, you will have to run it on -each of one of you instances. -This is only a tool to collect information to help others help you, it will not attempt to fix anything. - -``` -# icinga2 troubleshoot --help -icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0) - -Usage: - icinga2 troubleshoot [] - -Collect logs and other relevant information for troubleshooting purposes. - -Global options: - -h [ --help ] show this help message - -V [ --version ] show version information - --color use VT100 color codes even when stdout is not a - terminal - -D [ --define ] arg define a constant - -a [ --app ] arg application library name (default: icinga) - -l [ --library ] arg load a library - -I [ --include ] arg add include search directory - -x [ --log-level ] arg specify the log level for the console log. - The valid value is either debug, notice, - information (default), warning, or critical - -X [ --script-debugger ] whether to enable the script debugger - -Command options: - -c [ --console ] print to console instead of file - -o [ --output ] arg path to output file - --include-objects Print the whole objectfile (like `object list`) - --include-vars Print all Variables (like `variable list`) - -Report bugs at -Icinga home page: +Icinga home page: ``` ## CLI command: Variable @@ -608,7 +607,7 @@ Lists all configured variables (constants) in a similar fashion like [object lis ``` # icinga2 variable --help -icinga2 - The Icinga 2 network monitoring daemon (version: v2.8.0; debug) +icinga2 - The Icinga 2 network monitoring daemon (version: v2.11.0) Usage: icinga2 [] @@ -632,7 +631,7 @@ Global options: -X [ --script-debugger ] whether to enable the script debugger Report bugs at -Icinga home page: +Icinga home page: ``` ## Enabling/Disabling Features @@ -731,4 +730,3 @@ safely reload the Icinga 2 daemon. The `reload` action will send the `SIGHUP` signal to the Icinga 2 daemon which will validate the configuration in a separate process and not stop the other events like check execution, notifications, etc. - diff --git a/doc/12-icinga2-api.md b/doc/12-icinga2-api.md index c029aee0e..ccecc587f 100644 --- a/doc/12-icinga2-api.md +++ b/doc/12-icinga2-api.md @@ -1,4 +1,20 @@ -# Icinga 2 API +# REST API + +* [Setup](12-icinga2-api.md#icinga2-api-setup) +* [Introduction](12-icinga2-api.md#icinga2-api-introduction) +* Endpoints + * [Config Objects](12-icinga2-api.md#icinga2-api-config-objects) + * [Actions](12-icinga2-api.md#icinga2-api-actions) + * [Event Streams](12-icinga2-api.md#icinga2-api-event-streams) + * [Status and Statistics](12-icinga2-api.md#icinga2-api-status) + * [Config Management](12-icinga2-api.md#icinga2-api-config-management) + * [Types](12-icinga2-api.md#icinga2-api-types) + * [Templates](12-icinga2-api.md#icinga2-api-config-templates) + * [Variables](12-icinga2-api.md#icinga2-api-variables) + * [Debug Console](12-icinga2-api.md#icinga2-api-console) +* [API Clients](12-icinga2-api.md#icinga2-api-clients) + * [Programmatic Examples](12-icinga2-api.md#icinga2-api-clients-programmatic-examples) + ## Setting up the API @@ -7,39 +23,24 @@ You can run the CLI command `icinga2 api setup` to enable the certificates as well as a new API user `root` with an auto-generated password in the `/etc/icinga2/conf.d/api-users.conf` configuration file: - # icinga2 api setup +``` +# icinga2 api setup +``` Make sure to restart Icinga 2 to enable the changes you just made: - # service icinga2 restart +``` +# systemctl restart icinga2 +``` If you prefer to set up the API manually, you will have to perform the following steps: -* Set up X.509 certificates for Icinga 2 +* Set up X.509 TLS certificates for Icinga 2 * Enable the `api` feature (`icinga2 feature enable api`) * Create an `ApiUser` object for authentication The next chapter provides a quick overview of how you can use the API. -### Creating ApiUsers - -The CLI command `icinga2 api user` allows you to create an ApiUser object with a hashed password string, ready to be -added to your configuration. Example: - -``` -$ icinga2 api user --user icingaweb2 --password icinga -object ApiUser "icingaweb2" { - password_hash ="$5$d5f1a17ea308acb6$9e9fd5d24a9373a16e8811765cc5a5939687faf9ef8ed496db6e7f1d0ae9b2a9" - // client_cn = "" - - permissions = [ "*" ] -} -``` - -Optionally a salt can be provided with `--salt`, otherwise a random value will be used. When ApiUsers are stored this -way, even somebody able to read the configuration files won't be able to authenticate using this information. There is -no way to recover your password should you forget it, you'd need to create it anew. - ## Introduction The Icinga 2 API allows you to manage configuration objects @@ -77,13 +78,15 @@ Supported request methods: PUT | Create a new object. The PUT request must include all attributes required to create a new object. DELETE | Remove an object created by the API. The DELETE method is idempotent and does not require any check if the object actually exists. -All requests apart from `GET` require that the following `Accept` header is set: +All requests except `GET` require the following `Accept` header: - Accept: application/json +``` +Accept: application/json +``` Each URL is prefixed with the API version (currently "/v1"). -HTTP header size is limited to 8KB. +HTTP header size is limited to 8KB per request. ### Responses @@ -93,27 +96,38 @@ list. Depending on the number of affected objects in your request, the The output will be sent back as a JSON object: - - { - "results": [ - { - "code": 200.0, - "status": "Object was created." - } - ] - } +``` +{ + "results": [ + { + "code": 200.0, + "status": "Object was created." + } + ] +} +``` > **Tip** > -> You can use the [pretty](12-icinga2-api.md#icinga2-api-parameters-global) parameter to beautify the JSON response with Icinga v2.9+. +> You can use the [pretty](12-icinga2-api.md#icinga2-api-parameters-global) parameter to beautify the JSON response. You can also use [jq](https://stedolan.github.io/jq/) or `python -m json.tool` in combination with curl on the CLI. ``` +curl ... | jq curl ... | python -m json.tool ``` +jq also has additional filter capabilities, as shown in [this blogpost](https://www.netways.de/blog/2018/08/24/json-in-bequem/). + +``` +curl ... |jq '{name: .results[].name}' +``` + +For programmatic examples in various languages, check the chapter +[below](12-icinga2-api.md#icinga2-api-clients). + > **Note** > > Future versions of Icinga 2 might set additional fields. Your application @@ -140,45 +154,62 @@ was malformed. A status in the range of 500 generally means that there was a server-side problem and Icinga 2 is unable to process your request. +### Security + +* HTTPS only. +* TLS v1.2+ is required. +* TLS cipher lists are hardened [by default](09-object-types.md#objecttype-apilistener). +* Authentication is [required](12-icinga2-api.md#icinga2-api-authentication). + ### Authentication There are two different ways for authenticating against the Icinga 2 API: -* username and password using HTTP basic auth -* X.509 certificate +* Username and password using HTTP basic auth +* X.509 client certificate In order to configure a new API user you'll need to add a new [ApiUser](09-object-types.md#objecttype-apiuser) configuration object. In this example `root` will be the basic auth username and the `password` attribute contains the basic auth password. - # vim /etc/icinga2/conf.d/api-users.conf +``` +# vim /etc/icinga2/conf.d/api-users.conf - object ApiUser "root" { - password = "icinga" - } +object ApiUser "root" { + password = "icinga" +} +``` Alternatively you can use X.509 client certificates by specifying the `client_cn` the API should trust. The X.509 certificate has to be signed by the CA certificate that is configured in the [ApiListener](09-object-types.md#objecttype-apilistener) object. - # vim /etc/icinga2/conf.d/api-users.conf +``` +# vim /etc/icinga2/conf.d/api-users.conf - object ApiUser "root" { - client_cn = "CertificateCommonName" - } +object ApiUser "root" { + client_cn = "CertificateCommonName" +} +``` An `ApiUser` object can have both authentication methods configured. +#### Authentication Test + You can test authentication by sending a GET request to the API: - $ curl -k -s -u root:icinga 'https://localhost:5665/v1' +``` +$ curl -k -s -u root:icinga 'https://localhost:5665/v1' +``` In case you get an error message make sure to check the API user credentials. When using client certificates for authentication you'll need to pass your client certificate and private key to the curl call: - $ curl -k --cert example.localdomain.crt --key example.localdomain.key 'https://example.localdomain:5665/v1/status' +``` +$ curl -k --cert example.localdomain.crt --key example.localdomain.key 'https://example.localdomain:5665/v1/status' +``` In case of an error make sure to verify the client certificate and CA. @@ -186,7 +217,9 @@ The curl parameter `-k` disables certificate verification and should therefore only be used for testing. In order to securely check each connection you'll need to specify the trusted CA certificate using the curl parameter`--cacert`: - $ curl -u root:icinga --cacert ca.crt 'icinga2.node1.localdomain:5665/v1' +``` +$ curl -u root:icinga --cacert ca.crt 'icinga2.node1.localdomain:5665/v1' +``` Read the next chapter on [API permissions](12-icinga2-api.md#icinga2-api-permissions) in order to configure authorization settings for your newly created API user. @@ -204,34 +237,46 @@ The permission system mainly relies on the url scheme of the API endpoints (See Example for an API user with all permissions: - permissions = [ "*" ] +``` +permissions = [ "*" ] +``` Note that you can use wildcards to include all possible hierarchically lower items. Here's another example that only allows the user to perform read-only object queries for hosts and services: - permissions = [ "objects/query/Host", "objects/query/Service" ] +``` +permissions = [ "objects/query/Host", "objects/query/Service" ] +``` You can also further restrict permissions by specifying a filter expression. The filter expression has to be a [lambda function](17-language-reference.md#nullary-lambdas) which must return a boolean value. The following example allows the API user to query all hosts and services which have a -custom attribute `os` that matches the regular expression `^Linux`. +custom variable `os` that matches the regular expression `^Linux`. The [regex function](18-library-reference.md#global-functions-regex) is available as global function. - permissions = [ - { - permission = "objects/query/Host" - filter = {{ regex("^Linux", host.vars.os) }} - }, - { - permission = "objects/query/Service" - filter = {{ regex("^Linux", service.vars.os) }} - } - ] +``` +permissions = [ + { + permission = "objects/query/Host" + filter = {{ regex("^Linux", host.vars.os) }} + }, + { + permission = "objects/query/Service" + filter = {{ regex("^Linux", service.vars.os) }} + } +] +``` More information about filters can be found in the [filters](12-icinga2-api.md#icinga2-api-filters) chapter. +Prior to setting complex permissions, ensure to always [test](12-icinga2-api.md#icinga2-api-authentication-test) +them step by step. + + +#### Overview + Permissions are tied to a maximum HTTP request size to prevent abuse, responses sent by Icinga are not limited. An API user with all permissions ("\*") may send up to 512 MB regardless of the endpoint. @@ -255,6 +300,7 @@ Available permissions for specific URL endpoints: The required actions or types can be replaced by using a wildcard match ("\*"). + ### Parameters Depending on the request method there are two ways of passing parameters to the request: @@ -267,15 +313,27 @@ as query string, e.g. a space character becomes `%20`. Example for a URL-encoded query string: - /v1/objects/hosts?filter=match(%22example.localdomain*%22,host.name)&attrs=name&attrs=state +``` +/v1/objects/hosts?filter=match(%22example.localdomain*%22,host.name)&attrs=name&attrs=state +``` Here are the exact same query parameters as a JSON object: - { "filter": "match(\"example.localdomain*\",host.name)", "attrs": [ "host.name", "host.state" ] } +``` +{ "filter": "match(\"example.localdomain*\",host.name)", "attrs": [ "host.name", "host.state" ] } +``` The [match function](18-library-reference.md#global-functions-match) is available as global function in Icinga 2. +Whenever filters and other URL parameters don't work due to encoding issues, +consider passing them in the request body. For GET requests, this method is explained +[here](12-icinga2-api.md#icinga2-api-requests-method-override). + +You can use [jo](https://github.com/jpmens/jo) to format JSON strings on the shell. An example +for API actions shown [here](12-icinga2-api.md#icinga2-api-actions-unix-timestamps). + + ### Global Parameters Name | Description @@ -295,8 +353,6 @@ Example as JSON object: { "pretty": true } ``` -Both parameters have been added in Icinga 2 v2.9. - ### Request Method Override `GET` requests do not allow you to send a request body. In case you cannot pass everything as URL @@ -305,11 +361,29 @@ header. This comes in handy when you are using HTTP proxies disallowing `PUT` or Query an existing object by sending a `POST` request with `X-HTTP-Method-Override: GET` as request header: - $ curl -k -s -u 'root:icinga' -H 'Accept: application/json' -X POST -H 'X-HTTP-Method-Override: GET' 'https://localhost:5665/v1/objects/hosts' +``` +$ curl -k -s -u 'root:icinga' -H 'Accept: application/json' \ + -H 'X-HTTP-Method-Override: GET' -X POST \ + 'https://localhost:5665/v1/objects/hosts' +``` Delete an existing object by sending a `POST` request with `X-HTTP-Method-Override: DELETE` as request header: - $ curl -k -s -u 'root:icinga' -H 'Accept: application/json' -X POST -H 'X-HTTP-Method-Override: DELETE' 'https://localhost:5665/v1/objects/hosts/example.localdomain' +``` +$ curl -k -s -u 'root:icinga' -H 'Accept: application/json' \ + -H 'X-HTTP-Method-Override: DELETE' -X POST \ + 'https://localhost:5665/v1/objects/hosts/example.localdomain' +``` + +Query objects with complex filters. For a detailed introduction into filter, please +read the [following chapter](12-icinga2-api.md#icinga2-api-filters). + +``` +curl -k -s -u 'root:icinga' -H 'Accept: application/json' \ + -H 'X-HTTP-Method-Override: GET' -X POST \ + 'https://localhost:5665/v1/objects/services' \ + -d '{ "filter": "service.state==2 && match(\"ping*\",service.name)" }' +``` ### Filters @@ -318,11 +392,15 @@ Delete an existing object by sending a `POST` request with `X-HTTP-Method-Overri By default actions and queries operate on all objects unless further restricted by the user. For example, the following query returns all `Host` objects: - https://localhost:5665/v1/objects/hosts +``` +https://localhost:5665/v1/objects/hosts +``` If you're only interested in a single object, you can limit the output to that object by specifying its name: - https://localhost:5665/v1/objects/hosts?host=localhost +``` +https://localhost:5665/v1/objects/hosts?host=localhost +``` **The name of the URL parameter is the lower-case version of the type the query applies to.** For example, for `Host` objects the URL parameter therefore is `host`, for `Service` objects it is @@ -330,14 +408,18 @@ example, for `Host` objects the URL parameter therefore is `host`, for `Service` You can also specify multiple objects: - https://localhost:5665/v1/objects/hosts?hosts=first-host&hosts=second-host +``` +https://localhost:5665/v1/objects/hosts?hosts=first-host&hosts=second-host +``` Again -- like in the previous example -- the name of the URL parameter is the lower-case version of the type. However, because we're specifying multiple objects here the **plural form** of the type is used. When specifying names for objects which have composite names like for example services the full name has to be used: - https://localhost:5665/v1/objects/services?service=localhost!ping6 +``` +https://localhost:5665/v1/objects/services?service=localhost!ping6 +``` The full name of an object can be obtained by looking at the `__name` attribute. @@ -346,7 +428,11 @@ The full name of an object can be obtained by looking at the `__name` attribute. Most of the information provided in this chapter applies to both permission filters (as used when configuring `ApiUser` objects) and filters specified in queries. -Advanced filters allow users to filter objects using lambda expressions. The syntax for these filters is the same like for [apply rule expressions](03-monitoring-basics.md#using-apply-expressions). +Advanced filters allow users to filter objects using lambda expressions. +The syntax for these filters is the same like for [apply rule expressions](03-monitoring-basics.md#using-apply-expressions). + +The `filter` parameter can only be specified once, complex filters must +be defined once in the provided string value. > **Note** > @@ -355,15 +441,25 @@ Advanced filters allow users to filter objects using lambda expressions. The syn Example matching all services in NOT-OK state: - https://localhost:5665/v1/objects/services?filter=service.state!=ServiceOK +``` +https://localhost:5665/v1/objects/services?filter=service.state!=ServiceOK +``` Example [matching](18-library-reference.md#global-functions-match) all hosts by a name string pattern: - https://localhost:5665/v1/objects/hosts?filter=match("example.localdomain*",host.name) +``` +https://localhost:5665/v1/objects/hosts?filter=match("example.localdomain*",host.name) +``` Example for all hosts which are in the host group `linux-servers`: +``` +https://localhost:5665/v1/objects/hosts?filter="linux-servers" in host.groups +``` - https://localhost:5665/v1/objects/hosts?filter="linux-servers" in host.groups +> **Tip** +> +> Best practice for filters is to use [X-HTTP-Method-Override](12-icinga2-api.md#icinga2-api-requests-method-override) +> for GET requests and always pass them in the request body. User-specified filters are run in a sandbox environment which ensures that filters cannot modify Icinga's state, for example object attributes or global variables. @@ -385,19 +481,42 @@ Some queries can be performed for more than just one object type. One example is action which can be used for both hosts and services. When using advanced filters you will also have to specify the type using the `type` parameter: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/reschedule-check' \ - -d '{ "type": "Service", "filter": "service.name==\"ping6\"", "pretty": true }' +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST \ + 'https://localhost:5665/v1/actions/reschedule-check' \ + -d '{ "type": "Service", "filter": "service.name==\"ping6\"", "pretty": true }' +``` -When building filters you have to ensure that values such as -`"linux-servers"` are escaped properly according to the rules of the Icinga 2 configuration -language. +##### Filter Variables -To make using the API in scripts easier you can use the `filter_vars` attribute to specify -variables which should be made available to your filter expression. This way you don't have -to worry about escaping values: +Filter values need to be escaped in the same way as in the Icinga 2 DSL. - $ curl -k -s -u 'root:icinga' -H 'Accept: application/json' -H 'X-HTTP-Method-Override: GET' -X POST 'https://localhost:5665/v1/objects/hosts' \ - -d '{ "filter": "host.vars.os == os", "filter_vars": { "os": "Linux" }, "pretty": true }' +The example below is not valid: + +``` +-d '{ "type": "Host", "filter": ""linux-servers" in host.groups" }' +``` + +The double quotes need to be escaped with a preceeding backslash: + +``` +-d '{ "type": "Host", "filter": "\"linux-servers\" in host.groups" }' +``` + +You can use the `filter_vars` attribute to avoid additional escaping. +This follows the same principle as with parameter binding known from RDBMS. +Specify a placeholder variable inside the `filter` string, and actually +assign its value inside the `filter_vars` dictionary. + +That way you can also keep the `filter` string the same for different +requests with only changing the `filter_vars`. + +``` +$ curl -k -s -u 'root:icinga' -H 'Accept: application/json' \ + -H 'X-HTTP-Method-Override: GET' -X POST \ + 'https://localhost:5665/v1/objects/hosts' \ + -d '{ "filter": "group in host.groups", "filter_vars": { "group": "linux-servers" }, "pretty": true }' +``` We're using [X-HTTP-Method-Override](12-icinga2-api.md#icinga2-api-requests-method-override) here because the HTTP specification does not allow message bodies for GET requests. @@ -405,6 +524,16 @@ the HTTP specification does not allow message bodies for GET requests. The `filters_vars` attribute can only be used inside the request body, but not as a URL parameter because there is no way to specify a dictionary in a URL. +The example from [X-HTTP-Method-Override](12-icinga2-api.md#icinga2-api-requests-method-override) +can be enhanced to avoid additional parameter value escaping. + +``` +curl -k -s -u 'root:icinga' -H 'Accept: application/json' \ + -H 'X-HTTP-Method-Override: GET' -X POST \ + 'https://localhost:5665/v1/objects/services' \ + -d '{ "filter": "service.state==state && match(pattern,service.name)", "filter_vars": { "state": 2, "pattern": "ping*" } }' +``` + ## Config Objects Provides methods to manage configuration objects: @@ -440,7 +569,9 @@ a `GET` query to the `/v1/objects/` URL endpoint. ` @@ -498,17 +633,23 @@ information about the host when querying service objects. The following query retrieves all host attributes: - https://localhost:5665/v1/objects/services?joins=host +``` +https://localhost:5665/v1/objects/services?joins=host +``` Instead of requesting all host attributes you can also limit the output to specific attributes: - https://localhost:5665/v1/objects/services?joins=host.name&joins=host.address +``` +https://localhost:5665/v1/objects/services?joins=host.name&joins=host.address +``` You can request that all available joins are returned in the result set by using the `all_joins` query parameter. - https://localhost:5665/v1/objects/services?all_joins=1 +``` +https://localhost:5665/v1/objects/services?all_joins=1 +``` > **Note** > @@ -527,109 +668,133 @@ The following joins are available: Zones | parent Here's an example that retrieves all service objects for hosts which have had their `os` -custom attribute set to `Linux`. The result set contains the `display_name` and `check_command` +custom variable set to `Linux`. The result set contains the `display_name` and `check_command` attributes for the service. The query also returns the host's `name` and `address` attribute via a join: - $ curl -k -s -u root:icinga 'https://localhost:5665/v1/objects/services?attrs=display_name&attrs=check_command&joins=host.name&joins=host.address&filter=host.vars.os==%22Linux%22&pretty=1' +``` +$ curl -k -s -u root:icinga 'https://localhost:5665/v1/objects/services?attrs=display_name&attrs=check_command&joins=host.name&joins=host.address&filter=host.vars.os==%22Linux%22&pretty=1' - { - "results": [ - { - "attrs": { - "check_command": "ping4", - "display_name": "ping4" - }, - "joins": { - "host": { - "address": "192.168.1.1", - "name": "example.localdomain" - } - }, - "meta": {}, - "name": "example.localdomain!ping4", - "type": "Service" +{ + "results": [ + { + "attrs": { + "check_command": "ping4", + "display_name": "ping4" }, - { - "attrs": { - "check_command": "ssh", - "display_name": "ssh" - }, - "joins": { - "host": { - "address": "192.168.1.1", - "name": "example.localdomain" - } - }, - "meta": {}, - "name": "example.localdomain!ssh", - "type": "Service" - } - ] - } + "joins": { + "host": { + "address": "192.168.1.1", + "name": "example.localdomain" + } + }, + "meta": {}, + "name": "example.localdomain!ping4", + "type": "Service" + }, + { + "attrs": { + "check_command": "ssh", + "display_name": "ssh" + }, + "joins": { + "host": { + "address": "192.168.1.1", + "name": "example.localdomain" + } + }, + "meta": {}, + "name": "example.localdomain!ssh", + "type": "Service" + } + ] +} +``` + +> **Tip** +> +> Use [X-HTTP-Method-Override](12-icinga2-api.md#icinga2-api-requests-method-override) +> and pass everything in the request body like this: + +``` +$ curl -k -s -u 'root:icinga' -H 'Accept: application/json' \ + -H 'X-HTTP-Method-Override: GET' -X POST \ + 'https://localhost:5665/v1/objects/services' \ + -d '{ "attrs": [ "display_name", "check_command" ], "joins": [ "host.name", "host.address" ], "filter": "host.vars.os==\"Linux\"", "pretty": true }' +``` In case you want to fetch all [comments](09-object-types.md#objecttype-comment) for hosts and services, you can use the following query URL (similar example for downtimes): - https://localhost:5665/v1/objects/comments?joins=host&joins=service +``` +https://localhost:5665/v1/objects/comments?joins=host&joins=service +``` This is another example for listing all service objects which are unhandled problems (state is not OK and no downtime or acknowledgement set). We're using [X-HTTP-Method-Override](12-icinga2-api.md#icinga2-api-requests-method-override) here because we want to pass all query attributes in the request body. - $ curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Override: GET' -X POST 'https://127.0.0.1:5665/v1/objects/services' \ - -d '{ "joins": [ "host.name", "host.address" ], "attrs": [ "name", "state", "downtime_depth", "acknowledgement" ], "filter": "service.state != ServiceOK && service.downtime_depth == 0.0 && service.acknowledgement == 0.0", "pretty": true }' +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -H 'X-HTTP-Method-Override: GET' -X POST \ + 'https://127.0.0.1:5665/v1/objects/services' \ +-d '{ "joins": [ "host.name", "host.address" ], "attrs": [ "name", "state", "downtime_depth", "acknowledgement" ], "filter": "service.state != ServiceOK && service.downtime_depth == 0.0 && service.acknowledgement == 0.0", "pretty": true }' - { - "results": [ - { - "attrs": { - "acknowledgement": 0.0, - "downtime_depth": 0.0, - "name": "10807-service", - "state": 3.0 - }, - "joins": { - "host": { - "address": "", - "name": "10807-host" - } - }, - "meta": {}, - "name": "10807-host!10807-service", - "type": "Service" - } - ] - } +{ + "results": [ + { + "attrs": { + "acknowledgement": 0.0, + "downtime_depth": 0.0, + "name": "10807-service", + "state": 3.0 + }, + "joins": { + "host": { + "address": "", + "name": "10807-host" + } + }, + "meta": {}, + "name": "10807-host!10807-service", + "type": "Service" + } + ] +} +``` In order to list all acknowledgements without expire time, you query the `/v1/objects/comments` URL endpoint with `joins` and `filter` request parameters using the [X-HTTP-Method-Override](12-icinga2-api.md#icinga2-api-requests-method-override) method: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Override: GET' -X POST 'https://localhost:5665/v1/objects/comments' \ - -d '{ "joins": [ "service.name", "service.acknowledgement", "service.acknowledgement_expiry" ], "attrs": [ "author", "text" ], "filter": "service.acknowledgement!=0 && service.acknowledgement_expiry==0", "pretty": true }' +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -H 'X-HTTP-Method-Override: GET' -X POST \ + 'https://localhost:5665/v1/objects/comments' \ + -d '{ "joins": [ "service.name", "service.acknowledgement", "service.acknowledgement_expiry" ], "attrs": [ "author", "text" ], "filter": "service.acknowledgement!=0 && service.acknowledgement_expiry==0", "pretty": true }' - { - "results": [ - { - "attrs": { - "author": "icingaadmin", - "text": "maintenance work" - }, - "joins": { - "service": { - "__name": "example.localdomain!disk /", - "acknowledgement": 1.0, - "acknowledgement_expiry": 0.0 - } - }, - "meta": {}, - "name": "example.localdomain!disk /!example.localdomain-1495457222-0", - "type": "Comment" - } - ] - } +{ + "results": [ + { + "attrs": { + "author": "icingaadmin", + "text": "maintenance work" + }, + "joins": { + "service": { + "__name": "example.localdomain!disk /", + "acknowledgement": 1.0, + "acknowledgement_expiry": 0.0 + } + }, + "meta": {}, + "name": "example.localdomain!disk /!example.localdomain-1495457222-0", + "type": "Comment" + } + ] +} +``` ### Creating Config Objects @@ -647,50 +812,62 @@ the full name (e.g. `example.localdomain!http`) must be specified. If attributes are of the Dictionary type, you can also use the indexer format. This might be necessary to only override specific custom variables and keep all other existing custom variables (e.g. from templates): - "attrs": { "vars.os": "Linux" } +``` +"attrs": { "vars.os": "Linux" } +``` Example for creating the new host object `example.localdomain`: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X PUT 'https://localhost:5665/v1/objects/hosts/example.localdomain' \ - -d '{ "templates": [ "generic-host" ], "attrs": { "address": "192.168.1.1", "check_command": "hostalive", "vars.os" : "Linux" }, "pretty": true }' - { - "results": [ - { - "code": 200.0, - "status": "Object was created." - } - ] - } +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X PUT 'https://localhost:5665/v1/objects/hosts/example.localdomain' \ + -d '{ "templates": [ "generic-host" ], "attrs": { "address": "192.168.1.1", "check_command": "hostalive", "vars.os" : "Linux" }, "pretty": true }' +{ + "results": [ + { + "code": 200.0, + "status": "Object was created." + } + ] +} +``` If the configuration validation fails, the new object will not be created and the response body contains a detailed error message. The following example is missing the `check_command` attribute which is required for host objects: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X PUT 'https://localhost:5665/v1/objects/hosts/example.localdomain' \ - -d '{ "attrs": { "address": "192.168.1.1", "vars.os" : "Linux" }, "pretty": true }' - { - "results": [ - { - "code": 500.0, - "errors": [ - "Error: Validation failed for object 'example.localdomain' of type 'Host'; Attribute 'check_command': Attribute must not be empty." - ], - "status": "Object could not be created." - } - ] - } +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X PUT 'https://localhost:5665/v1/objects/hosts/example.localdomain' \ + -d '{ "attrs": { "address": "192.168.1.1", "vars.os" : "Linux" }, "pretty": true }' +{ + "results": [ + { + "code": 500.0, + "errors": [ + "Error: Validation failed for object 'example.localdomain' of type 'Host'; Attribute 'check_command': Attribute must not be empty." + ], + "status": "Object could not be created." + } + ] +} +``` Service objects must be created using their full name ("hostname!servicename") referencing an existing host object: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X PUT 'https://localhost:5665/v1/objects/services/example.localdomain!realtime-load' \ - -d '{ "templates": [ "generic-service" ], "attrs": { "check_command": "load", "check_interval": 1,"retry_interval": 1 } }' - +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X PUT 'https://localhost:5665/v1/objects/services/example.localdomain!realtime-load' \ + -d '{ "templates": [ "generic-service" ], "attrs": { "check_command": "load", "check_interval": 1,"retry_interval": 1 } }' +``` Example for a new CheckCommand object: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X PUT 'https://localhost:5665/v1/objects/checkcommands/mytest' \ - -d '{ "templates": [ "plugin-check-command" ], "attrs": { "command": [ "/usr/local/sbin/check_http" ], "arguments": { "-I": "$mytest_iparam$" } } }' - +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X PUT 'https://localhost:5665/v1/objects/checkcommands/mytest' \ + -d '{ "templates": [ "plugin-check-command" ], "attrs": { "command": [ "/usr/local/sbin/check_http" ], "arguments": { "-I": "$mytest_iparam$" } } }' +``` ### Modifying Objects @@ -701,38 +878,44 @@ parameters need to be passed inside the JSON body: -----------|------------|--------------------------- attrs | Dictionary | **Required.** Set specific object attributes for this [object type](09-object-types.md#object-types). -In addition to these parameters a [filter](12-icinga2-api.md#icinga2-api-filters) should be provided. +In addition to these parameters a [filter](12-icinga2-api.md#icinga2-api-filters) +parameter should be provided. > **Note**: > > Modified attributes do not trigger a re-evaluation of existing > static [apply rules](03-monitoring-basics.md#using-apply) and [group assignments](03-monitoring-basics.md#group-assign-intro). -> Delete and re-create the objects if you require such changes. +> Delete and re-create the objects if you require such changes or +> consider funding [this feature request](https://github.com/Icinga/icinga2/issues/4084). > > Furthermore you cannot modify templates which have already been resolved > during [object creation](12-icinga2-api.md#icinga2-api-config-objects-create). > There are attributes which can only be set for [PUT requests](12-icinga2-api.md#icinga2-api-config-objects-create) such as `groups` > or `zone`. A complete list of `no_user_modify` attributes can be fetched from the [types](12-icinga2-api.md#icinga2-api-types) URL endpoint. -If attributes are of the Dictionary type, you can also use the indexer format: +If attributes are of the [Dictionary](17-language-reference.md#dictionary) type, you can also use the indexer format: - "attrs": { "vars.os": "Linux" } +``` +"attrs": { "vars.os": "Linux" } +``` -The following example updates the `address` attribute and the custom attribute `os` for the `example.localdomain` host: - - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/objects/hosts/example.localdomain' \ - -d '{ "attrs": { "address": "192.168.1.2", "vars.os" : "Windows" }, "pretty": true }' - { - "results": [ - { - "code": 200.0, - "name": "example.localdomain", - "status": "Attributes updated.", - "type": "Host" - } - ] - } +The following example updates the `address` attribute and the custom variable `os` for the `example.localdomain` host: +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/objects/hosts/example.localdomain' \ + -d '{ "attrs": { "address": "192.168.1.2", "vars.os" : "Windows" }, "pretty": true }' +{ + "results": [ + { + "code": 200.0, + "name": "example.localdomain", + "status": "Attributes updated.", + "type": "Host" + } + ] +} +``` ### Deleting Objects @@ -747,92 +930,32 @@ In addition to these parameters a [filter](12-icinga2-api.md#icinga2-api-filters Example for deleting the host object `example.localdomain`: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X DELETE 'https://localhost:5665/v1/objects/hosts/example.localdomain?cascade=1&pretty=1' - { - "results": [ - { - "code": 200.0, - "name": "example.localdomain", - "status": "Object was deleted.", - "type": "Host" - } - ] - } - -## Config Templates - -Provides methods to manage configuration templates: - -* [querying templates](12-icinga2-api.md#icinga2-api-config-templates-query) - -Creation, modification and deletion of templates at runtime is not supported. - -### Querying Templates - -You can request information about configuration templates by sending -a `GET` query to the `/v1/templates/` URL endpoint. ` - -Provides methods to manage global variables: - -* [querying variables](12-icinga2-api.md#icinga2-api-variables-query) - -### Querying Variables - -You can request information about global variables by sending -a `GET` query to the `/v1/variables/` URL endpoint: - - $ curl -k -s -u root:icinga 'https://localhost:5665/v1/variables' - -A [filter](12-icinga2-api.md#icinga2-api-filters) may be provided for this query type. The -variable information object can be accessed in the filter using the `variable` variable. -The `filter` attribute is passed inside the request body thus requiring to use [X-HTTP-Method-Override](12-icinga2-api.md#icinga2-api-requests-method-override) -here. - - $ curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Override: GET' -X POST 'https://localhost:5661/v1/variables' \ - -d '{ "filter": "variable.type in [ \"String\", \"Number\" ]" }' - -Instead of using a filter you can optionally specify the variable name in the -URL path when querying a single variable: - - $ curl -k -s -u root:icinga 'https://localhost:5665/v1/variables/PrefixDir' - -The result set contains the type, name and value of the global variable. +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X DELETE 'https://localhost:5665/v1/objects/hosts/example.localdomain?cascade=1&pretty=1' +{ + "results": [ + { + "code": 200.0, + "name": "example.localdomain", + "status": "Object was deleted.", + "type": "Host" + } + ] +} +``` ## Actions There are several actions available for Icinga 2 provided by the `/v1/actions` URL endpoint. You can run actions by sending a `POST` request. -In case you have been using the [external commands](14-features.md#external-commands) -in the past, the API actions provide a similar interface with filter -capabilities for some of the more common targets which do not directly change -the configuration. +The following actions are also used by [Icinga Web 2](https://icinga.com/products/icinga-web-2/): + +* sending check results to Icinga from scripts, remote agents, etc. +* scheduling downtimes from external scripts or cronjobs +* acknowledging problems +* adding comments All actions return a 200 `OK` or an appropriate error code for each action performed on each object matching the supplied filter. @@ -842,7 +965,40 @@ notification on a program-wide basis must be applied by updating the [IcingaApplication object](12-icinga2-api.md#icinga2-api-config-objects) called `app`. - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/objects/icingaapplications/app' -d '{ "attrs": { "enable_notifications": false } }' +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/objects/icingaapplications/app' \ + -d '{ "attrs": { "enable_notifications": false } }' +``` + +### Unix Timestamp Handling + +If you don't want to write JSON manually, especially for adding the `start_time` +and `end_time` parameters, you can use [jo](https://github.com/jpmens/jo) to format this. + +``` +$ jo -p pretty=true type=Service filter="service.name==\"ping4\"" author=icingaadmin comment="IPv4 network maintenance" fixed=true start_time=$(date +%s -d "+0 hour") end_time=$(date +%s -d "+1 hour") +{ + "pretty": true, + "type": "Service", + "filter": "service.name==\"ping4\"", + "author": "icingaadmin", + "comment": "IPv4 network maintenance", + "fixed": true, + "start_time": 1557414097, + "end_time": 1557417697 +} +``` + +Now wrap this into the actual curl command: + +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/schedule-downtime' \ + -d "$(jo -p pretty=true type=Service filter="service.name==\"ping4\"" author=icingaadmin comment="IPv4 network maintanence" fixed=true start_time=$(date +%s -d "+0 hour") end_time=$(date +%s -d "+1 hour"))" +``` + +Note: This requires GNU date. On macOS, install `coreutils` from Homebrew and use `gdate`. ### process-check-result @@ -850,39 +1006,46 @@ Process a check result for a host or a service. Send a `POST` request to the URL endpoint `/v1/actions/process-check-result`. - Parameter | Type | Description - ------------------|--------------|-------------- - exit\_status | Number | **Required.** For services: 0=OK, 1=WARNING, 2=CRITICAL, 3=UNKNOWN, for hosts: 0=OK, 1=CRITICAL. - plugin\_output | String | **Required.** One or more lines of the plugin main output. Does **not** contain the performance data. - performance\_data | Array|String | **Optional.** The performance data as array of strings. The raw performance data string can be used too. - check\_command | Array | **Optional.** The first entry should be the check commands path, then one entry for each command line option followed by an entry for each of its argument. - check\_source | String | **Optional.** Usually the name of the `command_endpoint` - execution\_start | Timestamp | **Optional.** The timestamp where a script/process started its execution. - execution\_end | Timestamp | **Optional.** The timestamp where a script/process ended its execution. This timestamp is used in features to determine e.g. the metric timestamp. - ttl | Number | **Optional.** Time-to-live duration in seconds for this check result. The next expected check result is `now + ttl` where freshness checks are executed. + Parameter | Type | Description + ------------------ | -------------- | -------------- + exit\_status | Number | **Required.** For services: 0=OK, 1=WARNING, 2=CRITICAL, 3=UNKNOWN, for hosts: 0=OK, 1=CRITICAL. + plugin\_output | String | **Required.** One or more lines of the plugin main output. Does **not** contain the performance data. + performance\_data | Array|String | **Optional.** The performance data as array of strings. The raw performance data string can be used too. + check\_command | Array|String | **Optional.** The first entry should be the check commands path, then one entry for each command line option followed by an entry for each of its argument. Alternativly a single string can be used. + check\_source | String | **Optional.** Usually the name of the `command_endpoint` + execution\_start | Timestamp | **Optional.** The timestamp where a script/process started its execution. + execution\_end | Timestamp | **Optional.** The timestamp where a script/process ended its execution. This timestamp is used in features to determine e.g. the metric timestamp. + ttl | Number | **Optional.** Time-to-live duration in seconds for this check result. The next expected check result is `now + ttl` where freshness checks are executed. In addition to these parameters a [filter](12-icinga2-api.md#icinga2-api-filters) must be provided. The valid types for this action are `Host` and `Service`. -Example for the service `passive-ping6`: +Example for the service `passive-ping`: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/process-check-result?service=example.localdomain!passive-ping6' \ - -d '{ "exit_status": 2, "plugin_output": "PING CRITICAL - Packet loss = 100%", "performance_data": [ "rta=5000.000000ms;3000.000000;5000.000000;0.000000", "pl=100%;80;100;0" ], "check_source": "example.localdomain", "pretty": true }' +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/process-check-result' \ +-d '{ "type": "Service", "filter": "host.name==\"icinga2-master1.localdomain\" && service.name==\"passive-ping\"", "exit_status": 2, "plugin_output": "PING CRITICAL - Packet loss = 100%", "performance_data": [ "rta=5000.000000ms;3000.000000;5000.000000;0.000000", "pl=100%;80;100;0" ], "check_source": "example.localdomain", "pretty": true }' - { - "results": [ - { - "code": 200.0, - "status": "Successfully processed check result for object 'localdomain!passive-ping6'." - } - ] - } +{ + "results": [ + { + "code": 200.0, + "status": "Successfully processed check result for object 'icinga2-master1.localdomain!passive-ping'." + } + ] +} +``` + +You can avoid URL encoding of white spaces in object names by using the `filter` attribute in the request body. Example for using the `Host` type and filter by the host name: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/process-check-result' \ - -d '{ "filter": "host.name==\"example.localdomain\"", "type": "Host", "exit_status": 1, "plugin_output": "Host is not available." }' +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/process-check-result' \ + -d '{ "filter": "host.name==\"example.localdomain\"", "type": "Host", "exit_status": 1, "plugin_output": "Host is not available." }' +``` -You can avoid URL encoding of white spaces in object names by using the `filter` attribute in the request body. > **Note** > @@ -907,18 +1070,20 @@ The example reschedules all services with the name "ping6" to immediately perfor (`next_check` default), ignoring any time periods or whether active checks are allowed for the service (`force=true`). - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/reschedule-check' \ - -d '{ "type": "Service", "filter": "service.name==\"ping6\"", "force": true, "pretty": true }' - - { - "results": [ - { - "code": 200.0, - "status": "Successfully rescheduled check for object 'example.localdomain!ping6'." - } - ] - } +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/reschedule-check' \ + -d '{ "type": "Service", "filter": "service.name==\"ping6\"", "force": true, "pretty": true }' +{ + "results": [ + { + "code": 200.0, + "status": "Successfully rescheduled check for object 'icinga2-master1.localdomain!ping6'." + } + ] +} +``` ### send-custom-notification @@ -938,20 +1103,23 @@ In addition to these parameters a [filter](12-icinga2-api.md#icinga2-api-filters Example for a custom host notification announcing a global maintenance to host owners: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/send-custom-notification' \ - -d '{ "type": "Host", "author": "icingaadmin", "comment": "System is going down for maintenance", "force": true, "pretty": true }' +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/send-custom-notification' \ + -d '{ "type": "Host", "author": "icingaadmin", "comment": "System is going down for maintenance", "force": true, "pretty": true }' - { - "results": [ - { - "code": 200.0, - "status": "Successfully sent custom notification for object 'host0'." - }, - { - "code": 200.0, - "status": "Successfully sent custom notification for object 'host1'." - } - } +{ + "results": [ + { + "code": 200.0, + "status": "Successfully sent custom notification for object 'host0'." + }, + { + "code": 200.0, + "status": "Successfully sent custom notification for object 'host1'." + } +} +``` ### delay-notification @@ -970,20 +1138,23 @@ In addition to these parameters a [filter](12-icinga2-api.md#icinga2-api-filters Example: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/delay-notification' \ - -d '{ "type": "Service", "timestamp": 1446389894, "pretty": true }' +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/delay-notification' \ + -d '{ "type": "Service", "timestamp": 1446389894, "pretty": true }' - { - "results": [ - { - "code": 200.0, - "status": "Successfully delayed notifications for object 'host0!service0'." - }, - { - "code": 200.0, - "status": "Successfully delayed notifications for object 'host1!service1'." - } - } +{ + "results": [ + { + "code": 200.0, + "status": "Successfully delayed notifications for object 'host0!service0'." + }, + { + "code": 200.0, + "status": "Successfully delayed notifications for object 'host1!service1'." + } +} +``` ### acknowledge-problem @@ -1007,26 +1178,28 @@ In addition to these parameters a [filter](12-icinga2-api.md#icinga2-api-filters The following example acknowledges all services which are in a hard critical state and sends out a notification for them: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/acknowledge-problem?type=Service&filter=service.state==2&service.state_type=1' \ - -d '{ "author": "icingaadmin", "comment": "Global outage. Working on it.", "notify": true, "pretty": true }' - - { - "results": [ - { - "code": 200.0, - "status": "Successfully acknowledged problem for object 'example2.localdomain!ping4'." - }, - { - "code": 200.0, - "status": "Successfully acknowledged problem for object 'example.localdomain!ping4'." - } - } +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/acknowledge-problem' \ + -d '{ "type": "Service", "filter": "service.state==2&service.state_type=1", "author": "icingaadmin", "comment": "Global outage. Working on it.", "notify": true, "pretty": true }' +{ + "results": [ + { + "code": 200.0, + "status": "Successfully acknowledged problem for object 'icinga2-satellite1.localdomain!ping4'." + }, + { + "code": 200.0, + "status": "Successfully acknowledged problem for object 'icinga2-satellite2.localdomain!ping4'." + } +} +``` ### remove-acknowledgement Removes the acknowledgements for services or hosts. Once the acknowledgement has -been removed notifications will be sent out again. +been removed the next notification will be sent again. Send a `POST` request to the URL endpoint `/v1/actions/remove-acknowledgement`. @@ -1034,19 +1207,23 @@ A [filter](12-icinga2-api.md#icinga2-api-filters) must be provided. The valid ty The example removes all service acknowledgements: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/remove-acknowledgement?type=Service&pretty=1' +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/remove-acknowledgement' \ + -d '{ "type": "Service", "pretty": true }' - { - "results": [ - { - "code": 200.0, - "status": "Successfully removed acknowledgement for object 'host0!service0'." - }, - { - "code": 200.0, - "status": "Successfully removed acknowledgement for object 'example2.localdomain!aws-health'." - } - } +{ + "results": [ + { + "code": 200.0, + "status": "Successfully removed acknowledgement for object 'host0!service0'." + }, + { + "code": 200.0, + "status": "Successfully removed acknowledgement for object 'example2.localdomain!aws-health'." + } +} +``` ### add-comment @@ -1063,23 +1240,27 @@ In addition to these parameters a [filter](12-icinga2-api.md#icinga2-api-filters The following example adds a comment for all `ping4` services: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/add-comment?type=Service&filter=service.name==%22ping4%22' -d '{ "author": "icingaadmin", "comment": "Troubleticket #123456789 opened.", "pretty": true }' - { - "results": [ - { - "code": 200.0, - "legacy_id": 26.0, - "name": "example.localdomain!ping4!example.localdomain-1446824161-0", - "status": "Successfully added comment 'example.localdomain!ping4!example.localdomain-1446824161-0' for object 'example.localdomain!ping4'." - }, - { - "code": 200.0, - "legacy_id": 27.0, - "name": "example2.localdomain!ping4!example.localdomain-1446824161-1", - "status": "Successfully added comment 'example2.localdomain!ping4!example.localdomain-1446824161-1' for object 'example2.localdomain!ping4'." - } - ] - } +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/add-comment' \ + -d '{ "type": "Service", "filter": "service.name==\"ping4\"", "author": "icingaadmin", "comment": "Troubleticket #123456789 opened.", "pretty": true }' +{ + "results": [ + { + "code": 200.0, + "legacy_id": 26.0, + "name": "icinga2-satellite1.localdomain!ping4!7e7861c8-8008-4e8d-9910-2a0bb26921bd", + "status": "Successfully added comment 'icinga2-satellite1.localdomain!ping4!7e7861c8-8008-4e8d-9910-2a0bb26921bd' for object 'icinga2-satellite1.localdomain!ping4'." + }, + { + "code": 200.0, + "legacy_id": 27.0, + "name": "icinga2-satellite2.localdomain!ping4!9a4c43f5-9407-a536-18bf-4a6cc4b73a9f", + "status": "Successfully added comment 'icinga2-satellite2.localdomain!ping4!9a4c43f5-9407-a536-18bf-4a6cc4b73a9f' for object 'icinga2-satellite2.localdomain!ping4'." + } + ] +} +``` ### remove-comment @@ -1094,32 +1275,39 @@ A [filter](12-icinga2-api.md#icinga2-api-filters) must be provided. The valid ty Example for a simple filter using the `comment` URL parameter: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/remove-comment?comment=example2.localdomain!ping4!mbmif.local-1446986367-0&pretty=1' - { - "results": [ - { - "code": 200.0, - "status": "Successfully removed comment 'example2.localdomain!ping4!mbmif.local-1446986367-0'." - } - ] - } +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/remove-comment' \ + -d '{ "comment": "icinga2-satellite2.localdomain!ping4!9a4c43f5-9407-a536-18bf-4a6cc4b73a9f", "pretty": true }' +{ + "results": [ + { + "code": 200.0, + "status": "Successfully removed comment 'icinga2-satellite2.localdomain!ping4!9a4c43f5-9407-a536-18bf-4a6cc4b73a9f'." + } + ] +} +``` Example for removing all service comments using a service name filter for `ping4`: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/remove-comment?filter=service.name==%22ping4%22&type=Service&pretty=1' - { - "results": [ - { - "code": 200.0, - "status": "Successfully removed all comments for object 'example2.localdomain!ping4'." - }, - { - "code": 200.0, - "status": "Successfully removed all comments for object 'example.localdomain!ping4'." - } - ] - } - +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/remove-comment' + -d '{ "type": "Service", "filter": "service.name==\"ping4\"", "pretty": true }' +{ + "results": [ + { + "code": 200.0, + "status": "Successfully removed all comments for object 'icinga2-satellite1.localdomain!ping4'." + }, + { + "code": 200.0, + "status": "Successfully removed all comments for object 'icinga2-satellite2.localdomain!ping4'." + } + ] +} +``` ### schedule-downtime @@ -1135,30 +1323,53 @@ Send a `POST` request to the URL endpoint `/v1/actions/schedule-downtime`. end\_time | Timestamp | **Required.** Timestamp marking the end of the downtime. fixed | Boolean | **Optional.** Defaults to `true`. If true, the downtime is `fixed` otherwise `flexible`. See [downtimes](08-advanced-topics.md#downtimes) for more information. duration | Number | **Required for flexible downtimes.** Duration of the downtime in seconds if `fixed` is set to false. + all\_services | Boolean | **Optional for host downtimes.** Sets downtime for [all services](12-icinga2-api.md#icinga2-api-actions-schedule-downtime-host-all-services) for the matched host objects. If `child_options` are set, all child hosts and their services will schedule a downtime too. Defaults to `false`. trigger\_name | String | **Optional.** Sets the trigger for a triggered downtime. See [downtimes](08-advanced-topics.md#downtimes) for more information on triggered downtimes. - child\_options | Number | **Optional.** Schedule child downtimes. `0` does not do anything, `1` schedules child downtimes triggered by this downtime, `2` schedules non-triggered downtimes. Defaults to `0`. + child\_options| String | **Optional.** Schedule child downtimes. `DowntimeNoChildren` does not do anything, `DowntimeTriggeredChildren` schedules child downtimes triggered by this downtime, `DowntimeNonTriggeredChildren` schedules non-triggered downtimes. Defaults to `DowntimeNoChildren`. In addition to these parameters a [filter](12-icinga2-api.md#icinga2-api-filters) must be provided. The valid types for this action are `Host` and `Service`. -Example: +Example for scheduling a downtime for all `ping4` services: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/schedule-downtime?type=Service&filter=service.name==%22ping4%22' -d '{ "start_time": 1446388806, "end_time": 1446389806, "duration": 1000, "author": "icingaadmin", "comment": "IPv4 network maintenance", "pretty": true }' - { - "results": [ - { - "code": 200.0, - "legacy_id": 2.0, - "name": "example2.localdomain!ping4!example.localdomain-1446822004-0", - "status": "Successfully scheduled downtime 'example2.localdomain!ping4!example.localdomain-1446822004-0' for object 'example2.localdomain!ping4'." - }, - { - "code": 200.0, - "legacy_id": 3.0, - "name": "example.localdomain!ping4!example.localdomain-1446822004-1", - "status": "Successfully scheduled downtime 'example.localdomain!ping4!example.localdomain-1446822004-1' for object 'example.localdomain!ping4'." - } - ] - } +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/schedule-downtime' \ + -d '{ "type": "Service", "filter": "service.name==\"ping4\"", "start_time": 1446388806, "end_time": 1446389806, "duration": 1000, "author": "icingaadmin", "comment": "IPv4 network maintenance", "pretty": true }' +{ + "results": [ + { + "code": 200.0, + "legacy_id": 2.0, + "name": "icinga2-satellite1.localdomain!ping4!ecc5fa55-a5b8-4189-a013-a5d4bb47af34", + "status": "Successfully scheduled downtime 'icinga2-satellite1.localdomain!ping4!ecc5fa55-a5b8-4189-a013-a5d4bb47af34' for object 'icinga2-satellite1.localdomain!ping4'." + }, + { + "code": 200.0, + "legacy_id": 3.0, + "name": "icinga2-satellite2.localdomain!ping4!abc59032-4589-abcd-4567-ecf67856c347", + "status": "Successfully scheduled downtime 'icinga2-satellite2.localdomain!ping4!abc59032-4589-abcd-4567-ecf67856c347' for object 'icinga2-satellite2.localdomain!ping4'." + } + ] +} +``` + +In case you want to target just a single service on a host, modify the filter +like this: + +``` +"filter": "host.name==\"icinga2-satellite1.localdomain\" && service.name==\"ping4\"" +``` + +#### Schedule Host Downtime(s) with all Services + +Schedule a downtime for one (or multiple) hosts and all of their services. +Note the `all_services` attribute. + +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/schedule-downtime' \ + -d "$(jo -p pretty=true type=Host filter="match(\"*satellite*\", host.name)" all_services=true author=icingaadmin comment="Cluster upgrade maintenance" fixed=true start_time=$(date +%s -d "+0 hour") end_time=$(date +%s -d "+1 hour"))" +``` ### remove-downtime @@ -1173,54 +1384,65 @@ A [filter](12-icinga2-api.md#icinga2-api-filters) must be provided. The valid ty Example for a simple filter using the `downtime` URL parameter: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/remove-downtime?downtime=example.localdomain!ping4!mbmif.local-1446979168-6&pretty=1' - { - "results": [ - { - "code": 200.0, - "status": "Successfully removed downtime 'example.localdomain!ping4!mbmif.local-1446979168-6'." - } - ] - } +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/remove-downtime' \ + -d '{ "downtime": "icinga2-satellite2.localdomain!ping4!abc59032-4589-abcd-4567-ecf67856c347", "pretty": true }' +{ + "results": [ + { + "code": 200.0, + "status": "Successfully removed downtime 'icinga2-satellite2.localdomain!ping4!abc59032-4589-abcd-4567-ecf67856c347'." + } + ] +} +``` -Example for removing all host downtimes using a host name filter for `example.localdomain`: +Example for removing all host downtimes using a host name filter for `icinga2-satellite2.localdomain`: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/remove-downtime?filter=host.name==%22example.localdomain%22&type=Host&pretty=1' - { - "results": [ - { - "code": 200.0, - "status": "Successfully removed all downtimes for object 'example.localdomain'." - } - ] - } +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/remove-downtime' \ + -d '{ "type": "Host", "filter": "host.name==\"icinga2-satellite2.localdomain\"", "pretty": true }' +{ + "results": [ + { + "code": 200.0, + "status": "Successfully removed all downtimes for object 'icinga2-satellite2.localdomain'." + } + ] +} +``` 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](12-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" - }, - "pretty": true - }' +``` +$ 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": "icinga2-satellite1.localdomain", + "filterAuthor": "icingaadmin" + }, + "pretty": true +}' - { - "results": [ - { - "code": 200.0, - "status": "Successfully removed downtime 'example.localdomain!mbmif.local-1463043129-3'." - } - ] - } +{ + "results": [ + { + "code": 200.0, + "status": "Successfully removed downtime 'icinga2-satellite1.localdomain!ecc5fa55-a5b8-ef34-abcd-a5d41234af34'." + } + ] +} +``` ### shutdown-process -Shuts down Icinga2. May or may not return. +Shuts down Icinga. May or may not return. Send a `POST` request to the URL endpoint `/v1/actions/shutdown-process`. @@ -1228,20 +1450,23 @@ This action does not support a target type or filter. Example: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/shutdown-process?pretty=1' +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/shutdown-process?pretty=1' - { - "results": [ - { - "code": 200.0, - "status": "Shutting down Icinga 2." - } - ] - } +{ + "results": [ + { + "code": 200.0, + "status": "Shutting down Icinga 2." + } + ] +} +``` ### restart-process -Restarts Icinga2. May or may not return. +Restarts Icinga. May or may not return. Send a `POST` request to the URL endpoint `/v1/actions/restart-process`. @@ -1249,22 +1474,31 @@ This action does not support a target type or filter. Example: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/restart-process?pretty=1' +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/restart-process?pretty=1' - { - "results": [ - { - "code": 200.0, - "status": "Restarting Icinga 2." - } - ] - } +{ + "results": [ + { + "code": 200.0, + "status": "Restarting Icinga 2." + } + ] +} +``` ### generate-ticket Generates a PKI ticket for [CSR auto-signing](06-distributed-monitoring.md#distributed-monitoring-setup-csr-auto-signing). This can be used in combination with satellite/client setups requesting this ticket number. +> **Note** +> +> This must be used on the local host, or e.g. by a Puppet master. +> Doing so remotely may result in security issues with cluster +> trust relationships. + Send a `POST` request to the URL endpoint `/v1/actions/generate-ticket`. Parameter | Type | Description @@ -1273,21 +1507,31 @@ Send a `POST` request to the URL endpoint `/v1/actions/generate-ticket`. Example: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/generate-ticket' \ - -d '{ "cn": "icinga2-client1.localdomain", "pretty": true }' - { - "results": [ - { - "code": 200.0, - "status": "Generated PKI ticket '4f75d2ecd253575fe9180938ebff7cbca262f96e' for common name 'icinga2-client1.localdomain'.", - "ticket": "4f75d2ecd253575fe9180938ebff7cbca262f96e" - } - ] - } - +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/generate-ticket' \ + -d '{ "cn": "icinga2-agent1.localdomain", "pretty": true }' +{ + "results": [ + { + "code": 200.0, + "status": "Generated PKI ticket '4f75d2ecd253575fe9180938ebff7cbca262f96e' for common name 'icinga2-agent1.localdomain'.", + "ticket": "4f75d2ecd253575fe9180938ebff7cbca262f96e" + } + ] +} +``` ## Event Streams +Event streams can be used to receive check results, downtimes, comments, +acknowledgements, etc. as a "live stream" from Icinga. + +You can for example forward these types into your own backend. Process the +metrics and correlate them with notifications and state changes e.g. in Elasticsearch +with the help of [Icingabeat](https://icinga.com/docs/icingabeat/latest/). Another use +case are aligned events and creating/resolving tickets automatically in your ticket system. + You can subscribe to event streams by sending a `POST` request to the URL endpoint `/v1/events`. The following parameters need to be specified (either as URL parameters or in a JSON-encoded message body): @@ -1320,7 +1564,11 @@ being set. Example for all downtime events: - &types=DowntimeAdded&types=DowntimeRemoved&types=DowntimeTriggered +``` +&types=DowntimeAdded&types=DowntimeRemoved&types=DowntimeTriggered + +-d '{ "types": ["DowntimeAdded", "DowntimeRemoved", "DowntimeTriggered"] }' +``` #### Event Stream Type: CheckResult @@ -1352,6 +1600,7 @@ Example for all downtime events: timestamp | Timestamp | Unix timestamp when the event happened. host | String | [Host](09-object-types.md#objecttype-host) name. service | String | [Service](09-object-types.md#objecttype-service) name. Optional if this is a host notification. + command | String | [NotificationCommand](09-object-types.md#objecttype-notificationcommand) name. users | Array | List of notified [user](09-object-types.md#objecttype-user) names. notification\_type | String | [$notification.type$](03-monitoring-basics.md#notification-runtime-macros) runtime macro value. author | String | [$notification.author$](03-monitoring-basics.md#notification-runtime-macros) runtime macro value. @@ -1457,13 +1706,20 @@ Event streams can be filtered by attributes using the prefix `event.`. Example for the `CheckResult` type with the `exit_code` set to `2`: - &types=CheckResult&filter=event.check_result.exit_status==2 +``` +&types=CheckResult&filter=event.check_result.exit_status==2 + +-d '{ "types": "CheckResult", "filter": "event.check_result.exit_status==2" }' +``` Example for the `CheckResult` type with the service [matching](18-library-reference.md#global-functions-match) the string pattern "random\*": - &types=CheckResult&filter=match%28%22random*%22,event.service%29 +``` +&types=CheckResult&filter=match%28%22random*%22,event.service%29 +-d { "types": "CheckResult", "filter": "match(\"random*\", event.service)" } +``` ### Event Stream Response @@ -1472,12 +1728,15 @@ must support long-polling and HTTP/1.1. HTTP/1.0 is not supported. Example: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/events?queue=michi&types=CheckResult&filter=event.check_result.exit_status==2' - - {"check_result":{ ... },"host":"example.localdomain","service":"ping4","timestamp":1445421319.7226390839,"type":"CheckResult"} - {"check_result":{ ... },"host":"example.localdomain","service":"ping4","timestamp":1445421324.7226390839,"type":"CheckResult"} - {"check_result":{ ... },"host":"example.localdomain","service":"ping4","timestamp":1445421329.7226390839,"type":"CheckResult"} +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/events' \ + -d '{ "queue": "myqueue", "types": "CheckResult", "filter": "event.check_result.exit_status==2" }' +{"check_result":{ ... },"host":"example.localdomain","service":"ping4","timestamp":1445421319.7226390839,"type":"CheckResult"} +{"check_result":{ ... },"host":"example.localdomain","service":"ping4","timestamp":1445421324.7226390839,"type":"CheckResult"} +{"check_result":{ ... },"host":"example.localdomain","service":"ping4","timestamp":1445421329.7226390839,"type":"CheckResult"} +``` ## Status and Statistics @@ -1485,86 +1744,106 @@ Send a `GET` request to the URL endpoint `/v1/status` to retrieve status informa Example: - $ curl -k -s -u root:icinga 'https://localhost:5665/v1/status?pretty=1' - { - "results": [ - { - "name": "ApiListener", - "perfdata": [ ... ], - "status": [ ... ] - }, - ... - { - "name": "IcingaAplication", - "perfdata": [ ... ], - "status": [ ... ] - }, - ... - ] - } +``` +$ curl -k -s -u root:icinga 'https://localhost:5665/v1/status?pretty=1' +{ + "results": [ + { + "name": "ApiListener", + "perfdata": [ ... ], + "status": [ ... ] + }, + ... + { + "name": "IcingaAplication", + "perfdata": [ ... ], + "status": [ ... ] + }, + ... + ] +} +``` You can limit the output by specifying a status type in the URL, e.g. `IcingaApplication`: - $ curl -k -s -u root:icinga 'https://localhost:5665/v1/status/IcingaApplication?pretty=1' - { - "results": [ - { - "perfdata": [], - "status": { - "icingaapplication": { - "app": { - "enable_event_handlers": true, - "enable_flapping": true, - "enable_host_checks": true, - "enable_notifications": true, - "enable_perfdata": true, - "enable_service_checks": true, - "node_name": "example.localdomain", - "pid": 59819.0, - "program_start": 1443019345.093372, - "version": "v2.3.0-573-g380a131" - } +``` +$ curl -k -s -u root:icinga 'https://localhost:5665/v1/status/IcingaApplication?pretty=1' +{ + "results": [ + { + "perfdata": [], + "status": { + "icingaapplication": { + "app": { + "enable_event_handlers": true, + "enable_flapping": true, + "enable_host_checks": true, + "enable_notifications": true, + "enable_perfdata": true, + "enable_service_checks": true, + "node_name": "example.localdomain", + "pid": 59819.0, + "program_start": 1443019345.093372, + "version": "v2.3.0-573-g380a131" } } } - ] - } - + } + ] +} +``` ## Configuration Management -The main idea behind configuration management is to allow external applications -creating configuration packages and stages based on configuration files and +The main idea behind configuration management is that external applications +can create configuration packages and stages based on configuration files and directory trees. This replaces any additional SSH connection and whatnot to dump configuration files to Icinga 2 directly. + In case you are pushing a new configuration stage to a package, Icinga 2 will validate the configuration asynchronously and populate a status log which -can be fetched in a separated request. +can be fetched in a separated request. Once the validation succeeds, +a reload is triggered by default. + +This functionality was primarly developed for the [Icinga Director](https://icinga.com/docs/director/latest/) +but can be used with your own deployments too. It also solves the problem +with certain runtime objects (zones, endpoints) and can be used to +deploy global templates in [global cluster zones](06-distributed-monitoring.md#distributed-monitoring-global-zone-config-sync). -### Creating a Config Package +### Create a Config Package Send a `POST` request to a new config package called `example-cmdb` in this example. This -will create a new empty configuration package. +creates a new empty configuration package. - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST \ - 'https://localhost:5665/v1/config/packages/example-cmdb?pretty=1' - { - "results": [ - { - "code": 200.0, - "package": "example-cmdb", - "status": "Created package." - } - ] - } +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ +-X POST 'https://localhost:5665/v1/config/packages/example-cmdb?pretty=1' +{ + "results": [ + { + "code": 200.0, + "package": "example-cmdb", + "status": "Created package." + } + ] +} +``` -Package names starting with an underscore are reserved for internal packages and must not be used. +Package names with the `_` prefix are reserved for internal packages and must not be used. +You can recognize `_api`, `_etc` and `_cluster` when querying specific objects and packages. -### Uploading configuration for a Config Package +Each configuration object stores the package source in the `package` attribute. -Configuration files in packages are managed in stages. -Stages provide a way to maintain multiple configuration versions for a package. +### Create a Stage: Upload Configuration + +Configuration files in packages are managed in stages. Stages provide a way +to maintain multiple configuration versions for a package. Once a new stage +is deployed, the content is validated and set as active stage on success. + +On failure, the older stage remains active, and the caller can fetch the `startup.log` +from this stage deployment attempt to see what exactly failed. You can see that +in the Director's deployment log. Send a `POST` request to the URL endpoint `/v1/config/stages` and add the name of an existing configuration package to the URL path (e.g. `example-cmdb`). @@ -1581,30 +1860,36 @@ The file path requires one of these two directories inside its path: Example for a local configuration in the `conf.d` directory: - "files": { "conf.d/host1.conf": "object Host \"local-host\" { address = \"127.0.0.1\", check_command = \"hostalive\" }" } +``` +"files": { "conf.d/host1.conf": "object Host \"local-host\" { address = \"127.0.0.1\", check_command = \"hostalive\" }" } +``` Example for a host configuration inside the `satellite` zone in the `zones.d` directory: - "files": { "zones.d/satellite/host2.conf": "object Host \"satellite-host\" { address = \"192.168.1.100\", check_command = \"hostalive\" }" } +``` +"files": { "zones.d/satellite/host2.conf": "object Host \"satellite-host\" { address = \"192.168.1.100\", check_command = \"hostalive\" }" } +``` The example below will create a new file called `test.conf` in the `conf.d` directory. Note: This example contains an error (`chec_command`). This is intentional. - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST \ - -d '{ "files": { "conf.d/test.conf": "object Host \"cmdb-host\" { chec_command = \"dummy\" }" }, "pretty": true }' \ - 'https://localhost:5665/v1/config/stages/example-cmdb' - { - "results": [ - { - "code": 200.0, - "package": "example-cmdb", - "stage": "example.localdomain-1441625839-0", - "status": "Created stage. Icinga2 will reload." - } - ] - } +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST \ +-d '{ "files": { "conf.d/test.conf": "object Host \"cmdb-host\" { chec_command = \"dummy\" }" }, "pretty": true }' \ +'https://localhost:5665/v1/config/stages/example-cmdb' +{ + "results": [ + { + "code": 200.0, + "package": "example-cmdb", + "stage": "7e7861c8-8008-4e8d-9910-2a0bb26921bd", + "status": "Created stage. Reload triggered." + } + ] +} +``` The Icinga 2 API returns the `package` name this stage was created for, and also generates a unique name for the `stage` attribute you'll need for later requests. @@ -1618,7 +1903,7 @@ and its configuration objects will remain active. > > Old stages are not purged automatically. You can [remove stages](12-icinga2-api.md#icinga2-api-config-management-delete-config-stage) that are no longer in use. -Icinga 2 will create the following files in the configuration package +Icinga 2 creates the following files in the configuration package stage after configuration validation: File | Description @@ -1627,7 +1912,8 @@ stage after configuration validation: startup.log | Contains the [configuration validation](11-cli-commands.md#config-validation) output. You can [fetch these files](12-icinga2-api.md#icinga2-api-config-management-fetch-config-package-stage-files) -in order to verify that the new configuration was deployed successfully. +in order to verify that the new configuration was deployed successfully. Please follow the chapter below +to learn more about this. ### List Configuration Packages and their Stages @@ -1637,52 +1923,55 @@ A list of packages and their stages can be retrieved by sending a `GET` request The following example contains one configuration package `example-cmdb`. The package does not currently have an active stage. - $ curl -k -s -u root:icinga 'https://localhost:5665/v1/config/packages?pretty=1' - { - "results": [ - { - "active-stage": "", - "name": "example-cmdb", - "stages": [ - "example.localdomain-1441625839-0" - ] - } - ] - } +``` +$ curl -k -s -u root:icinga 'https://localhost:5665/v1/config/packages?pretty=1' +{ + "results": [ + { + "active-stage": "", + "name": "example-cmdb", + "stages": [ + "7e7861c8-8008-4e8d-9910-2a0bb26921bd" + ] + } + ] +} +``` - -### List Configuration Packages and their Stages +### List Configuration Package Stage Files In order to retrieve a list of files for a stage you can send a `GET` request to the URL endpoint `/v1/config/stages`. You need to include -the package name (`example-cmdb`) and stage name (`example.localdomain-1441625839-0`) in the URL: +the package name (`example-cmdb`) and stage name (`7e7861c8-8008-4e8d-9910-2a0bb26921bd`) in the URL: - $ curl -k -s -u root:icinga 'https://localhost:5665/v1/config/stages/example-cmdb/example.localdomain-1441625839-0?pretty=1' - { - "results": [ - ... - { - "name": "startup.log", - "type": "file" - }, - { - "name": "status", - "type": "file" - }, - { - "name": "conf.d", - "type": "directory" - }, - { - "name": "zones.d", - "type": "directory" - }, - { - "name": "conf.d/test.conf", - "type": "file" - } - ] - } +``` +$ curl -k -s -u root:icinga 'https://localhost:5665/v1/config/stages/example-cmdb/7e7861c8-8008-4e8d-9910-2a0bb26921bd?pretty=1' +{ + "results": [ +... + { + "name": "startup.log", + "type": "file" + }, + { + "name": "status", + "type": "file" + }, + { + "name": "conf.d", + "type": "directory" + }, + { + "name": "zones.d", + "type": "directory" + }, + { + "name": "conf.d/test.conf", + "type": "file" + } + ] +} +``` ### Fetch Configuration Package Stage Files @@ -1695,9 +1984,11 @@ the package name, the stage name and the relative path to the file to the URL pa The following example fetches the configuration file `conf.d/test.conf`: - $ curl -k -s -u root:icinga 'https://localhost:5665/v1/config/files/example-cmdb/example.localdomain-1441625839-0/conf.d/test.conf' +``` +$ curl -k -s -u root:icinga 'https://localhost:5665/v1/config/files/example-cmdb/7e7861c8-8008-4e8d-9910-2a0bb26921bd/conf.d/test.conf' - object Host "cmdb-host" { chec_command = "dummy" } +object Host "cmdb-host" { chec_command = "dummy" } +``` You can fetch a [list of existing files](12-icinga2-api.md#icinga2-api-config-management-list-config-package-stage-files) in a configuration stage and then specifically request their content. @@ -1711,17 +2002,19 @@ In order to check for validation errors you can fetch the `startup.log` file by sending a `GET` request to the URL endpoint `/v1/config/files`. You must include the package name, stage name and the `startup.log` in the URL path. - $ curl -k -s -u root:icinga 'https://localhost:5665/v1/config/files/example-cmdb/example.localdomain-1441133065-1/startup.log' - ... +``` +$ curl -k -s -u root:icinga 'https://localhost:5665/v1/config/files/example-cmdb/7e7861c8-8008-4e8d-9910-2a0bb26921bd/startup.log' +... - critical/config: Error: Attribute 'chec_command' does not exist. - Location: - /var/lib/icinga2/api/packages/example-cmdb/example.localdomain-1441133065-1/conf.d/test.conf(1): object Host "cmdb-host" { chec_command = "dummy" } - ^^^^^^^^^^^^^^^^^^^^^^ +critical/config: Error: Attribute 'chec_command' does not exist. +Location: +/var/lib/icinga2/api/packages/example-cmdb/7e7861c8-8008-4e8d-9910-2a0bb26921bd/conf.d/test.conf(1): object Host "cmdb-host" { chec_command = "dummy" } + ^^^^^^^^^^^^^^^^^^^^^^ - critical/config: 1 error +critical/config: 1 error +``` -The output is similar to the manual [configuration validation](11-cli-commands.md#config-validation). +The output is the exact as known from [configuration validation](11-cli-commands.md#config-validation). > **Note** > @@ -1734,20 +2027,21 @@ You can send a `DELETE` request to the URL endpoint `/v1/config/stages` in order to purge a configuration stage. You must include the package and stage name inside the URL path. -The following example removes the failed configuration stage `example.localdomain-1441133065-1` +The following example removes the failed configuration stage `7e7861c8-8008-4e8d-9910-2a0bb26921bd` in the `example-cmdb` configuration package: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X DELETE \ - 'https://localhost:5665/v1/config/stages/example-cmdb/example.localdomain-1441133065-1?pretty=1' - { - "results": [ - { - "code": 200.0, - "status": "Stage deleted." - } - ] - } - +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X DELETE 'https://localhost:5665/v1/config/stages/example-cmdb/7e7861c8-8008-4e8d-9910-2a0bb26921bd?pretty=1' +{ + "results": [ + { + "code": 200.0, + "status": "Stage deleted." + } + ] +} +``` ### Deleting Configuration Package @@ -1757,18 +2051,19 @@ with the package name in the URL path. This example entirely deletes the configuration package `example-cmdb`: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X DELETE \ - 'https://localhost:5665/v1/config/packages/example-cmdb?pretty=1' - { - "results": [ - { - "code": 200.0, - "package": "example-cmdb", - "status": "Deleted package." - } - ] - } - +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' -X DELETE \ +'https://localhost:5665/v1/config/packages/example-cmdb?pretty=1' +{ + "results": [ + { + "code": 200.0, + "package": "example-cmdb", + "status": "Deleted package." + } + ] +} +``` ## Types @@ -1788,43 +2083,131 @@ Each response entry in the results array contains the following attributes: In order to view a specific configuration object type specify its name inside the URL path: - $ curl -k -s -u root:icinga 'https://localhost:5665/v1/types/Object?pretty=1' - { - "results": [ - { - "abstract": false, - "fields": { - "type": { - "array_rank": 0.0, - "attributes": { - "config": false, - "navigation": false, - "no_user_modify": false, - "no_user_view": false, - "required": false, - "state": false - }, - "id": 0.0, - "type": "String" - } - }, - "name": "Object", - "plural_name": "Objects", - "prototype_keys": [ - "clone", - "notify_attribute", - "to_string" - ] - } - ] - } +``` +$ curl -k -s -u root:icinga 'https://localhost:5665/v1/types/Object?pretty=1' +{ + "results": [ + { + "abstract": false, + "fields": { + "type": { + "array_rank": 0.0, + "attributes": { + "config": false, + "navigation": false, + "no_user_modify": false, + "no_user_view": false, + "required": false, + "state": false + }, + "id": 0.0, + "type": "String" + } + }, + "name": "Object", + "plural_name": "Objects", + "prototype_keys": [ + "clone", + "notify_attribute", + "to_string" + ] + } + ] +} +``` +## Config Templates -## Console +Provides methods to manage configuration templates: + +* [querying templates](12-icinga2-api.md#icinga2-api-config-templates-query) + +Creation, modification and deletion of templates at runtime is not supported. + +### Querying Templates + +You can request information about configuration templates by sending +a `GET` query to the `/v1/templates/` URL endpoint. ` + +Provides methods to manage global variables: + +* [querying variables](12-icinga2-api.md#icinga2-api-variables-query) + +### Querying Variables + +You can request information about global variables by sending +a `GET` query to the `/v1/variables/` URL endpoint: + +``` +$ curl -k -s -u root:icinga 'https://localhost:5665/v1/variables' +``` + +A [filter](12-icinga2-api.md#icinga2-api-filters) may be provided for this query type. The +variable information object can be accessed in the filter using the `variable` variable. +The `filter` attribute is passed inside the request body thus requiring to use [X-HTTP-Method-Override](12-icinga2-api.md#icinga2-api-requests-method-override) +here. + +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -H 'X-HTTP-Method-Override: GET' -X POST \ + 'https://localhost:5661/v1/variables' \ + -d '{ "filter": "variable.type in [ \"String\", \"Number\" ]" }' +``` + +Instead of using a filter you can optionally specify the variable name in the +URL path when querying a single variable: + +``` +$ curl -k -s -u root:icinga 'https://localhost:5665/v1/variables/PrefixDir' +``` + +The result set contains the type, name and value of the global variable. + +## Debug Console You can inspect variables and execute other expressions by sending a `POST` request to the URL endpoint `/v1/console/execute-script`. In order to receive auto-completion suggestions, send a `POST` request to the URL endpoint `/v1/console/auto-complete-script`. +> **Note** +> +> This functionality is used by the [debug console](11-cli-commands.md#cli-command-console). Do not use this in production, unless +> you are aware of the fact that expressions and commands may crash the daemon, or lead into +> unwanted behaviour. Use this URL endpoint **read-only** when needed. + The following parameters need to be specified (either as URL parameters or in a JSON-encoded message body): Parameter | Type | Description @@ -1845,82 +2228,160 @@ If you specify a session identifier, the same script context can be reused for m Example for fetching the command line from the local host's last check result: - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/console/execute-script?command=get_host(NodeName).last_check_result.command&sandboxed=0&session=bb75fd7c-c686-407d-9688-582c04227756&pretty=1' - { - "results": [ - { - "code": 200.0, - "result": [ - "/usr/local/sbin/check_ping", - "-H", - "127.0.0.1", - "-c", - "5000,100%", - "-w", - "3000,80%" - ], - "status": "Executed successfully." - } - ] - } +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/console/execute-script?command=get_host(NodeName).last_check_result.command&sandboxed=0&session=bb75fd7c-c686-407d-9688-582c04227756&pretty=1' +{ + "results": [ + { + "code": 200.0, + "result": [ + "/usr/local/sbin/check_ping", + "-H", + "127.0.0.1", + "-c", + "5000,100%", + "-w", + "3000,80%" + ], + "status": "Executed successfully." + } + ] +} +``` Example for fetching auto-completion suggestions for the `Host.` type. This works in a similar fashion when pressing TAB inside the [console CLI command](11-cli-commands.md#cli-command-console): - $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/console/auto-complete-script?command=Host.&sandboxed=0&session=bb75fd7c-c686-407d-9688-582c04227756&pretty=1' - { - "results": [ - { - "code": 200.0, - "status": "Auto-completed successfully.", - "suggestions": [ - "Host.type", - "Host.name", - "Host.prototype", - "Host.base", - "Host.register_attribute_handler", - "Host.clone", - "Host.notify_attribute", - "Host.to_string" - ] - } - ] - } - +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/console/auto-complete-script?command=Host.&sandboxed=0&session=bb75fd7c-c686-407d-9688-582c04227756&pretty=1' +{ + "results": [ + { + "code": 200.0, + "status": "Auto-completed successfully.", + "suggestions": [ + "Host.type", + "Host.name", + "Host.prototype", + "Host.base", + "Host.register_attribute_handler", + "Host.clone", + "Host.notify_attribute", + "Host.to_string" + ] + } + ] +} +``` ## API Clients -There are a couple of existing clients which can be used with the Icinga 2 API: +After its initial release in 2015, community members +and developers have been working hard to add more REST API +clients and integrations into DevOps tools. -* [curl](https://curl.haxx.se/) or any other HTTP client really -* [Icinga 2 console (CLI command)](12-icinga2-api.md#icinga2-api-clients-cli-console) -* [Icinga Web 2 Director](https://www.icinga.com/products/icinga-web-2-modules/) - -Demo cases: - -* [Dashing](https://github.com/Icinga/dashing-icinga2) -* [API examples](https://github.com/Icinga/icinga2-api-examples) +* [Libraries](12-icinga2-api.md#icinga2-api-clients-libraries) +* [Status](12-icinga2-api.md#icinga2-api-clients-status) +* [Management](12-icinga2-api.md#icinga2-api-clients-management) +* [Event Streams](12-icinga2-api.md#icinga2-api-clients-event-streams) +* [Actions](12-icinga2-api.md#icinga2-api-clients-actions) +* [REST API Apps](12-icinga2-api.md#icinga2-api-clients-apps) Additional [programmatic examples](12-icinga2-api.md#icinga2-api-clients-programmatic-examples) will help you getting started using the Icinga 2 API in your environment. -### Icinga 2 Console +### Libraries -By default the [console CLI command](11-cli-commands.md#cli-command-console) evaluates -expressions in a local interpreter, i.e. independently from your Icinga 2 daemon. -Add the `--connect` parameter to debug and evaluate expressions via the API. +Name | Language | Description +------------------------------------------------------------------------------------------------|---------------|-------------------------------------------------------- +[ruby-icinga2](https://github.com/bodsch/ruby-icinga2) | Ruby | Ruby library +[python-icinga2_api](https://github.com/KevinHonka/Icinga2_Python_API) | Python | Python library +[python-icinga2-api](https://github.com/fmnisme/python-icinga2api) | Python | Python bindings for Icinga 2 interaction +[go-icinga2](https://github.com/xert/go-icinga2) | Golang | Golang functions and type definitions +[go-icinga2-api](https://github.com/lrsmith/go-icinga2-api/) | Golang | Golang implementation used inside the Terraform provider +[go-icinga2-client](https://github.com/Nexinto/go-icinga2-client) | Golang | Golang implementation for the Rancher integration. +[Monitoring::Icinga2::Client::REST](https://metacpan.org/release/THESEAL/Monitoring-Icinga2-Client-REST-2.0.0) | Perl | Perl bindings. +[Icinga 2 API in PHP](https://github.com/uniwue-rz/icinga2-api) | PHP | PHP implementation. For other examples, look into Icinga Web 2 and Director. -### API Clients Programmatic Examples +### Status -The programmatic examples use HTTP basic authentication and SSL certificate -verification. The CA file is expected in `pki/icinga2-ca.crt` -but you may adjust the examples for your likings. +Name | Language | Description +------------------------------------------------------------------------------------------------|---------------|-------------------------------------------------------- +[Dashing](https://github.com/dnsmichi/dashing-icinga2) | Ruby, HTML | Dashboard for Dashing querying the REST API for current host/service/global status +[InfluxDB Telegraf Input](https://github.com/influxdata/telegraf/blob/master/plugins/inputs/icinga2/README.md) | Golang | [Telegraf](https://github.com/influxdata/telegraf) is an agent written in Go for collecting, processing, aggregating, and writing metrics. +[Icinga Slack Bot](https://github.com/bb-Ricardo/icinga-slack-bot) | Python | It can be used to interact with Icinga2 from your Slack client. It uses the Icinga2 API to get Host/Service status details. Simple status filters can be used to narrow down the returned status list. +[Icinga 2 Slack Bot](https://github.com/mlabouardy/icinga2-slack-bot) | Golang | Query host/service details from a [Slack](https://slack.com/) channel +[icinga2bot](https://github.com/reikoNeko/icinga2bot) | Python | [Errbot](http://errbot.io/en/latest/user_guide/setup.html) plugin to fetch status and event stream information and forward to XMPP, IRC, etc. +[IcingaBusyLightAgent](https://github.com/stdevel/IcingaBusylightAgent) | C# | Notification Agent in Systray +[BitBar for OSX](https://getbitbar.com/plugins/Dev/Icinga2/icinga2.24m.py) | Python | macOS tray app for highlighting the host/service status +[Icinga 2 Multistatus](https://chrome.google.com/webstore/detail/icinga-multi-status/khabbhcojgkibdeipanmiphceeoiijal/related) | - | Chrome Extension +[Naglite4](https://github.com/wftech/icinga2-naglite4) | Python | Naglite3 rewrite using the Icinga 2 REST API. -The [request method](icinga2-api-requests) is `POST` using -[X-HTTP-Method-Override: GET](12-icinga2-api.md#icinga2-api-requests-method-override) -which allows you to send a JSON request body. The examples request -specific service attributes joined with host attributes. `attrs` -and `joins` are therefore specified as array. +### Manage Objects + +Name | Language | Description +------------------------------------------------------------------------------------------------|---------------|-------------------------------------------------------- +[Icinga Director](https://icinga.com/docs/director/latest) | PHP, JS | Icinga 2 configuration interface with a nice frontend, and automated imports for nearly any source. +[Terraform Provider](https://github.com/terraform-providers/terraform-provider-icinga2) | Golang | Register hosts from Terraform in Icinga 2. [Official docs](https://www.terraform.io/docs/providers/icinga2/index.html). +[Kube Icinga](https://github.com/gyselroth/kube-icinga) | Typescript | Monitor Kubernetes services / resources using icinga2 (including autodiscovery support) +[Logstash output for Icinga](https://www.icinga.com/products/integrations/elastic/) | Ruby | Forward check results and create objects from log events +[Foreman Smart Proxy Monitoring](https://github.com/theforeman/smart_proxy_monitoring) | Ruby | Smart Proxy extension for Foreman creating and deleting hosts and services in Icinga 2 +[Rancher integration](https://github.com/Nexinto/rancher-icinga) | Golang | Registers [Rancher](http://rancher.com/rancher/) resources in Icinga 2 for monitoring. +[AWS/EC2](https://github.com/Icinga/icinga2-api-examples/tree/master/aws-ec2) | Ruby | Example script for creating and deleting AWS instances in Icinga 2 +[Ansible Host Module](https://docs.ansible.com/ansible/latest/modules/icinga2_host_module.html) | Python | In progress, [Ansible Feature](https://docs.ansible.com/ansible/latest/modules/icinga2_feature_module.html#icinga2-feature-module) is also there. +[gocinga](https://gitlab.com/sambadevi/gocinga) | Golang | CLI Tool for Icinga, written in go + +### Event Streams + +Name | Language | Description +------------------------------------------------------------------------------------------------|---------------|-------------------------------------------------------- +[Elastic Icingabeat](https://icinga.com/docs/icingabeat/latest/) | Golang | Process events and send to Elasticsearch/Logstash outputs +[Request Tracker ticket integration](https://github.com/bytemine/icinga2rt) | Golang | Create and update RT tickets +[Logstash input event stream](https://github.com/bobapple/logstash-input-icinga_eventstream) | Ruby | Forward events as Logstash input +[Flapjack events](https://github.com/sol1/flapjack-icinga2) | Golang | Dumping events into Redis for Flapjack processing +[Stackstorm integration](https://github.com/StackStorm-Exchange/stackstorm-icinga2) | Python | Processing events and fetching status information +[NodeJS consumer](https://community.icinga.com/t/consume-api-event-stream/1010/6) | NodeJS | Example from our community :) + +### Actions + +Name | Language | Description +------------------------------------------------------------------------------------------------|---------------|-------------------------------------------------------- +[Icinga Web 2](https://icinga.com/docs/icingaweb2/latest/) | PHP | Trigger actions via command transport +[Logstash output for Icinga](https://www.icinga.com/products/integrations/elastic/) | Ruby | Forward check results and create objects from log events +[OTRS SystemMonitoring](https://github.com/OTRS/SystemMonitoring) | Perl | Acknowledge problems in Icinga 2 from OTRS tickets +[mqttwarn](https://github.com/jpmens/mqttwarn#icinga2) | Python | Forward check results from mqttwarn to Icinga 2 +[Lita handler](https://github.com/tuxmea/lita-icinga2) | Ruby | List, recheck and acknowledge through a #chatops bot called [Lita](https://github.com/litaio/lita) +[Sakuli forwarder](http://sakuli.readthedocs.io/en/latest/forwarder-icinga2api/) | Java | Forward check results from tests from [Sakuli](https://github.com/ConSol/sakuli) to Icinga 2 +[OpsGenie actions](https://www.opsgenie.com/docs/integrations/icinga2-integration) | Golang, Java | Integrate Icinga 2 into OpsGenie + + +### REST API Apps + +Name | Language | Description +------------------------------------------------------------------------------------------------|---------------|-------------------------------------------------------- +Browser plugins | - | [Postman for Chrome](https://www.getpostman.com), [RESTED for Firefox](https://addons.mozilla.org/en-US/firefox/addon/rested/) +[Postman](https://www.getpostman.com/) | - | App instead of browser plugin +[Cocoa Rest Client](http://mmattozzi.github.io/cocoa-rest-client/) | - | macOS app +[Paw for MacOS](https://paw.cloud) | (exported) | Paw is a full-featured HTTP client that lets you test and describe the APIs you build or consume. It has a beautiful native macOS interface to compose requests, inspect server responses, generate client code and export API definitions. + + +### Programmatic Examples + +The following languages are covered: + +* [Python](12-icinga2-api.md#icinga2-api-clients-programmatic-examples-python) +* [Ruby](12-icinga2-api.md#icinga2-api-clients-programmatic-examples-ruby) +* [PHP](12-icinga2-api.md#icinga2-api-clients-programmatic-examples-php) +* [Perl](12-icinga2-api.md#icinga2-api-clients-programmatic-examples-perl) +* [Golang](12-icinga2-api.md#icinga2-api-clients-programmatic-examples-golang) +* [Powershell](12-icinga2-api.md#icinga2-api-clients-programmatic-examples-powershell) + +The [request method](icinga2-api-requests) is `POST` using [X-HTTP-Method-Override: GET](12-icinga2-api.md#icinga2-api-requests-method-override) +which allows you to send a JSON request body. The examples request specific service +attributes joined with host attributes. `attrs` and `joins` are therefore specified +as array. The `filter` attribute [matches](18-library-reference.md#global-functions-match) on all services with `ping` in their name. @@ -1928,92 +2389,95 @@ on all services with `ping` in their name. The following example uses **Python** and the `requests` and `json` module: - # pip install requests - # pip install json +``` +# pip install requests +# pip install json - $ vim icinga2-api-example.py +$ vim icinga.py - #!/usr/bin/env python - - import requests, json - - # Replace 'localhost' with your FQDN and certificate CN - # for SSL verification - request_url = "https://localhost:5665/v1/objects/services" - headers = { - 'Accept': 'application/json', - 'X-HTTP-Method-Override': 'GET' - } - data = { - "attrs": [ "name", "state", "last_check_result" ], - "joins": [ "host.name", "host.state", "host.last_check_result" ], - "filter": "match(\"ping*\", service.name)", - } - - r = requests.post(request_url, - headers=headers, - auth=('root', 'icinga'), - data=json.dumps(data), - verify="pki/icinga2-ca.crt") - - print "Request URL: " + str(r.url) - print "Status code: " + str(r.status_code) - - if (r.status_code == 200): - print "Result: " + json.dumps(r.json()) - else: - print r.text - r.raise_for_status() +#!/usr/bin/env python - $ python icinga2-api-example.py +import requests, json +# Replace 'localhost' with your FQDN and certificate CN +# for TLS verification +request_url = "https://localhost:5665/v1/objects/services" +headers = { + 'Accept': 'application/json', + 'X-HTTP-Method-Override': 'GET' + } +data = { + "attrs": [ "name", "state", "last_check_result" ], + "joins": [ "host.name", "host.state", "host.last_check_result" ], + "filter": "match(\"ping*\", service.name)", +} + +r = requests.post(request_url, + headers=headers, + auth=('root', 'icinga'), + data=json.dumps(data), + verify="pki/icinga2-ca.crt") + +print "Request URL: " + str(r.url) +print "Status code: " + str(r.status_code) + +if (r.status_code == 200): + print "Result: " + json.dumps(r.json()) +else: + print r.text + r.raise_for_status() + +$ python icinga.py +``` #### Example API Client in Ruby The following example uses **Ruby** and the `rest_client` gem: - # gem install rest_client +``` +# gem install rest_client - $ vim icinga2-api-example.rb +$ vim icinga.rb - #!/usr/bin/ruby - - require 'rest_client' - - # Replace 'localhost' with your FQDN and certificate CN - # for SSL verification - request_url = "https://localhost:5665/v1/objects/services" - headers = { - "Accept" => "application/json", - "X-HTTP-Method-Override" => "GET" - } - data = { - "attrs" => [ "name", "state", "last_check_result" ], - "joins" => [ "host.name", "host.state", "host.last_check_result" ], - "filter" => "match(\"ping*\", service.name)", - } - - r = RestClient::Resource.new( - URI.encode(request_url), - :headers => headers, - :user => "root", - :password => "icinga", - :ssl_ca_file => "pki/icinga2-ca.crt") - - begin - response = r.post(data.to_json) - rescue => e - response = e.response - end - - puts "Status: " + response.code.to_s - if response.code == 200 - puts "Result: " + (JSON.pretty_generate JSON.parse(response.body)) - else - puts "Error: " + response - end +#!/usr/bin/ruby - $ ruby icinga2-api-example.rb +require 'rest_client' + +# Replace 'localhost' with your FQDN and certificate CN +# for TLS verification +request_url = "https://localhost:5665/v1/objects/services" +headers = { + "Accept" => "application/json", + "X-HTTP-Method-Override" => "GET" +} +data = { + "attrs" => [ "name", "state", "last_check_result" ], + "joins" => [ "host.name", "host.state", "host.last_check_result" ], + "filter" => "match(\"ping*\", service.name)", +} + +r = RestClient::Resource.new( + URI.encode(request_url), + :headers => headers, + :user => "root", + :password => "icinga", + :ssl_ca_file => "pki/icinga2-ca.crt") + +begin + response = r.post(data.to_json) +rescue => e + response = e.response +end + +puts "Status: " + response.code.to_s +if response.code == 200 + puts "Result: " + (JSON.pretty_generate JSON.parse(response.body)) +else + puts "Error: " + response +end + +$ ruby icinga.rb +``` A more detailed example can be found in the [Dashing demo](https://github.com/Icinga/dashing-icinga2). @@ -2021,100 +2485,252 @@ A more detailed example can be found in the [Dashing demo](https://github.com/Ic The following example uses **PHP** and its `curl` library: - $ vim icinga2-api-example.php +``` +$ vim icinga.php - #!/usr/bin/env php - array('name', 'state', 'last_check_result'), - joins => array('host.name', 'host.state', 'host.last_check_result'), - filter => 'match("ping*", service.name)', - ); - - $ch = curl_init(); - curl_setopt_array($ch, array( - CURLOPT_URL => $request_url, - CURLOPT_HTTPHEADER => $headers, - CURLOPT_USERPWD => $username . ":" . $password, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_CAINFO => "pki/icinga2-ca.crt", - CURLOPT_POST => count($data), - CURLOPT_POSTFIELDS => json_encode($data) - )); - - $response = curl_exec($ch); - if ($response === false) { - print "Error: " . curl_error($ch) . "(" . $response . ")\n"; - } - - $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - print "Status: " . $code . "\n"; - - if ($code == 200) { - $response = json_decode($response, true); - print_r($response); - } - ?> +#!/usr/bin/env php + array('name', 'state', 'last_check_result'), + joins => array('host.name', 'host.state', 'host.last_check_result'), + filter => 'match("ping*", service.name)', +); - $ php icinga2-api-example.php +$ch = curl_init(); +curl_setopt_array($ch, array( + CURLOPT_URL => $request_url, + CURLOPT_HTTPHEADER => $headers, + CURLOPT_USERPWD => $username . ":" . $password, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CAINFO => "pki/icinga2-ca.crt", + CURLOPT_POST => count($data), + CURLOPT_POSTFIELDS => json_encode($data) +)); + +$response = curl_exec($ch); +if ($response === false) { + print "Error: " . curl_error($ch) . "(" . $response . ")\n"; +} + +$code = curl_getinfo($ch, CURLINFO_HTTP_CODE); +curl_close($ch); +print "Status: " . $code . "\n"; + +if ($code == 200) { + $response = json_decode($response, true); + print_r($response); +} +?> + +$ php icinga.php +``` #### Example API Client in Perl The following example uses **Perl** and the `Rest::Client` module: - # perl -MCPAN -e 'install REST::Client' - # perl -MCPAN -e 'install JSON' - # perl -MCPAN -e 'install MIME::Base64' - # perl -MCPAN -e 'install Data::Dumper' +``` +# perl -MCPAN -e 'install REST::Client' +# perl -MCPAN -e 'install JSON' +# perl -MCPAN -e 'install MIME::Base64' +# perl -MCPAN -e 'install Data::Dumper' - $ vim icinga2-api-example.pl +$ vim icinga.pl - #!/usr/bin/env perl - - use strict; - use warnings; - use REST::Client; - use MIME::Base64; - use JSON; - use Data::Dumper; - - # Replace 'localhost' with your FQDN and certificate CN - # for SSL verification - my $request_host = "https://localhost:5665"; - my $userpass = "root:icinga"; - - my $client = REST::Client->new(); - $client->setHost($request_host); - $client->setCa("pki/icinga2-ca.crt"); - $client->addHeader("Accept", "application/json"); - $client->addHeader("X-HTTP-Method-Override", "GET"); - $client->addHeader("Authorization", "Basic " . encode_base64($userpass)); - my %json_data = ( - attrs => ['name', 'state', 'last_check_result'], - joins => ['host.name', 'host.state', 'host.last_check_result'], - filter => 'match("ping*", service.name)', - ); - my $data = encode_json(\%json_data); - $client->POST("/v1/objects/services", $data); - - my $status = $client->responseCode(); - print "Status: " . $status . "\n"; - my $response = $client->responseContent(); - if ($status == 200) { - print "Result: " . Dumper(decode_json($response)) . "\n"; - } else { - print "Error: " . $response . "\n"; +#!/usr/bin/env perl + +use strict; +use warnings; +use REST::Client; +use MIME::Base64; +use JSON; +use Data::Dumper; + +# Replace 'localhost' with your FQDN and certificate CN +# for TLS verification +my $request_host = "https://localhost:5665"; +my $userpass = "root:icinga"; + +my $client = REST::Client->new(); +$client->setHost($request_host); +$client->setCa("pki/icinga2-ca.crt"); +$client->addHeader("Accept", "application/json"); +$client->addHeader("X-HTTP-Method-Override", "GET"); +$client->addHeader("Authorization", "Basic " . encode_base64($userpass)); +my %json_data = ( + attrs => ['name', 'state', 'last_check_result'], + joins => ['host.name', 'host.state', 'host.last_check_result'], + filter => 'match("ping*", service.name)', +); +my $data = encode_json(\%json_data); +$client->POST("/v1/objects/services", $data); + +my $status = $client->responseCode(); +print "Status: " . $status . "\n"; +my $response = $client->responseContent(); +if ($status == 200) { + print "Result: " . Dumper(decode_json($response)) . "\n"; +} else { + print "Error: " . $response . "\n"; +} + +$ perl icinga.pl +``` + + +#### Example API Client in Golang + +Requires the Golang build chain. + +``` +$ vim icinga.go + +package main + +import ( + "bytes" + "crypto/tls" + "log" + "io/ioutil" + "net/http" +) + +func main() { + var urlBase= "https://localhost:5665" + var apiUser= "root" + var apiPass= "icinga" + + urlEndpoint := urlBase + "/v1/objects/services" + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + httpClient := &http.Client{Transport: tr} + + var requestBody = []byte(`{ + "attrs": [ "name", "state", "last_check_result" ], + "joins": [ "host.name", "host.state", "host.last_check_result" ], + "filter": "match(\"ping*\", service.name)" + }`) + + req, err := http.NewRequest("POST", urlEndpoint, bytes.NewBuffer(requestBody)) + req.Header.Set("Accept", "application/json") + req.Header.Set("X-HTTP-Method-Override", "GET") + + req.SetBasicAuth(apiUser, apiPass) + + resp, err := httpClient.Do(req) + if err != nil { + log.Fatal("Server error:", err) + return + } + defer resp.Body.Close() + + log.Print("Response status:", resp.Status) + + bodyBytes, _ := ioutil.ReadAll(resp.Body) + bodyString := string(bodyBytes) + + if resp.StatusCode == http.StatusOK { + log.Print("Result: " + bodyString) + } else { + log.Fatal(bodyString) + } +} +``` + +Build the binary: + +``` +go build icinga.go +./icinga +``` + +#### Example API Client in Powershell + +Requires Windows 10+ with Powershell 5+. + +Note: The workaround for self signed certificates is not considered +best practice. + +``` +# Workaround for self signed certificates +# https://stackoverflow.com/questions/36456104/invoke-restmethod-ignore-self-signed-certs +if (-not("dummy" -as [type])) { + add-type -TypeDefinition @" +using System; +using System.Net; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; + +public static class Dummy { + public static bool ReturnTrue(object sender, + X509Certificate certificate, + X509Chain chain, + SslPolicyErrors sslPolicyErrors) { return true; } + + public static RemoteCertificateValidationCallback GetDelegate() { + return new RemoteCertificateValidationCallback(Dummy.ReturnTrue); } +} +"@ +} - $ perl icinga2-api-example.pl +[System.Net.ServicePointManager]::ServerCertificateValidationCallback = [dummy]::GetDelegate() +$icingaApiHost = "localhost" +$icingaApiUser = "root" +$icingaApiPassword = "icinga" + +$requestUrl = "https://{0}:5665/v1/objects/services" -f $icingaApiHost + +$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $icingaApiUser, $icingaApiPassword))) +$httpAuthInfo = "Basic $base64AuthInfo" +$httpAcceptInfo = "application/json" + +$httpHeaders = @{ + "Authorization" = $httpAuthInfo + "Accept" = $httpAcceptInfo + "X-HTTP-Method-Override" = "GET" +} + +$attrs = @( "name", "state", "last_check_result" ) +$joins = @( "host.name", "host.state", "host.last_check_result") +$filter = 'match("ping*", service.name)' + +$data = @{ + "attrs" = $attrs + "joins" = $joins + "filter" = $filter +} + +$result = Invoke-RestMethod -Headers $httpHeaders -Uri $requestUrl -Method "POST" -Body ($data|ConvertTo-Json) + +foreach ($s in $result.results) { + Write-Host "Service " $s.attrs.name " on Host " $s.joins.host.name "State " $s.attrs.state " Output: " $s.attrs.last_check_result.output + # Debug + Write-Host "Debug: Attributes " $s.attrs | ConvertTo-Json + Write-Host "Debug: Joins Host" $s.joins.host | ConvertTo-Json + Write-Host "`n" +} +``` + +Run the Powershell ISE as administrator, and execute the script as you change it. + +![Icinga 2 API Windows Powershell ISE Script](images/api/icinga2_api_powershell_ise.png) + + +Alternatively, save the code and run it in Powershell: + +``` +.\icinga.ps1 +``` diff --git a/doc/13-addons.md b/doc/13-addons.md index 1d26de747..68325120d 100644 --- a/doc/13-addons.md +++ b/doc/13-addons.md @@ -1,37 +1,27 @@ -# Icinga 2 Addons +# Icinga 2 Addons and Integrations -## Graphing +For an uptodate overview of all integrations and modules, +please visit [https://icinga.com/products/](https://icinga.com/products/). -### PNP -[PNP](https://www.pnp4nagios.org) is a graphing addon. +## Icinga Reporting -[PNP](https://www.pnp4nagios.org) is an addon which adds a graphical representation of the performance data collected -by the monitoring plugins. The data is stored as rrd (round robin database) files. +The [Icinga Reporting Module](https://icinga.com/docs/reporting/latest/) +is the framework and foundation we created to handle data collected +by Icinga 2 and other data providers. By definition Icinga Reporting does not collect +or calculate any data. The framework processes usable data from data providers such as +Icinga’s IDO or Icinga Web 2 modules and makes them available in different formats. -Use your distribution's package manager to install the `pnp4nagios` package. +It can display the data directly within the Icinga web interface or export it to PDF, +JSON or CSV format. With scheduled reports you can receive the prepared data periodically +via email. -If you're planning to use it, configure it to use the -[bulk mode with npcd and npcdmod](https://docs.pnp4nagios.org/pnp-0.6/modes#bulk_mode_with_npcd_and_npcdmod) -in combination with Icinga 2's [PerfdataWriter](14-features.md#performance-data). NPCD collects the performance -data files which Icinga 2 generates. +![Icinga Reporting](images/addons/icinga_reporting.png) -Enable performance data writer in icinga 2 +Follow along in this [hands-on blog post](https://icinga.com/2019/06/17/icinga-reporting-hands-on/). - # icinga2 feature enable perfdata - -Configure npcd to use the performance data created by Icinga 2: - - vim /etc/pnp4nagios/npcd.cfg - -Set `perfdata_spool_dir = /var/spool/icinga2/perfdata` and restart the `npcd` daemon. - -There's also an Icinga Web 2 module for direct PNP graph integration -available at [Icinga Exchange](https://exchange.icinga.com/icinga/PNP). - -More information on [action_url as attribute](13-addons.md#addons-graphing-pnp-action-url) -and [graph template names](13-addons.md#addons-graphing-pnp-custom-templates). +## Graphs and Metrics ### Graphite @@ -45,15 +35,22 @@ Graphite consists of 3 software components: * whisper -- a simple database library for storing time-series data (similar in design to RRD) * graphite webapp -- a Django webapp that renders graphs on-demand using Cairo +You need to install Graphite first, then proceed with configuring it in Icinga 2. + Use the [GraphiteWriter](14-features.md#graphite-carbon-cache-writer) feature for sending real-time metrics from Icinga 2 to Graphite. - # icinga2 feature enable graphite - -There are Graphite addons available for collecting the performance data files too (e.g. `Graphios`). +``` +# icinga2 feature enable graphite +``` A popular alternative frontend for Graphite is for example [Grafana](https://grafana.org). +Integration in Icinga Web 2 is possible by installing the official [graphite module](https://icinga.com/docs/graphite/latest/). + +![Icinga Web 2 Detail View with Graphite](images/addons/icingaweb2_graphite.png) + + ### InfluxDB [InfluxDB](https://influxdb.com) is a time series, metrics, and analytics database. @@ -62,35 +59,96 @@ It’s written in Go and has no external dependencies. Use the [InfluxdbWriter](14-features.md#influxdb-writer) feature for sending real-time metrics from Icinga 2 to InfluxDB. - # icinga2 feature enable influxdb +``` +# icinga2 feature enable influxdb +``` A popular frontend for InfluxDB is for example [Grafana](https://grafana.org). +Integration in Icinga Web 2 is possible by installing the community [Grafana module](https://github.com/Mikesch-mp/icingaweb2-module-grafana). + +![Icinga Web 2 Detail View with Grafana](images/addons/icingaweb2_grafana.png) + + +### PNP + +[PNP](https://www.pnp4nagios.org) is a graphing addon. + +[PNP](https://www.pnp4nagios.org) is an addon which adds a graphical representation of the performance data collected +by the monitoring plugins. The data is stored as rrd (round robin database) files. + +Use your distribution's package manager to install the `pnp4nagios` package. + +If you're planning to use it, configure it to use the +[bulk mode with npcd and npcdmod](https://docs.pnp4nagios.org/pnp-0.6/modes#bulk_mode_with_npcd_and_npcdmod) +in combination with Icinga 2's [PerfdataWriter](14-features.md#writing-performance-data-files). NPCD collects the performance +data files which Icinga 2 generates. + +Enable performance data writer in icinga 2 + +``` +# icinga2 feature enable perfdata +``` + +Configure npcd to use the performance data created by Icinga 2: + +``` +vim /etc/pnp4nagios/npcd.cfg +``` + +Set `perfdata_spool_dir = /var/spool/icinga2/perfdata` and restart the `npcd` daemon. + +There's also an Icinga Web 2 module for direct PNP graph integration +available at [Icinga Exchange](https://exchange.icinga.com/icinga/PNP). + ## Visualization -### Icinga Reporting +### Maps -By enabling the [DB IDO](14-features.md#db-ido) feature you can use the -[Icinga Reporting package](https://docs.icinga.com/latest/en/reporting.html). +This community module displays host objects as markers on openstreetmap in Icinga Web 2. +It uses the data provided by the monitoring module and as such the [DB IDO](14-features.md#db-ido) +from Icinga 2. -### NagVis +If you configure multiple hosts with the same coordinates, i.e. servers in a datacenter, a clustered view is rendered. -By using either [Livestatus](14-features.md#setting-up-livestatus) or -[DB IDO](14-features.md#db-ido) as a backend you can create your own network maps -based on your monitoring configuration and status data using [NagVis](https://www.nagvis.org). +Check the [Map module docs](https://github.com/nbuchwitz/icingaweb2-module-map) for more details on +installation, configuration and integration. -The configuration in nagvis.ini.php should look like this for Livestatus for example: +![Icinga Web 2 Maps](images/addons/icingaweb2_maps.png) - [backend_live_1] - backendtype="mklivestatus" - socket="unix:/var/run/icinga2/cmd/livestatus" +### Business Process -If you are planning an integration into Icinga Web 2, look at [this module](https://github.com/Icinga/icingaweb2-module-nagvis). +Create top-level views of your applications in a graphical editor. +Rules express dependencies between existing hosts and services and +let you alert on application level. Business processes are displayed +in a tree or list overview and can be added to any dashboard. -### Thruk +![Icinga Web 2 Business Process](images/addons/icingaweb2_businessprocess.png) + +Read more [here](https://icinga.com/products/icinga-business-process-modelling/). + +### Certificate Monitoring + +Monitor your certificates in an efficient and comfortable way. Be aware of required +actions and view all details at a glance. + +![Icinga Certificate Monitoring](images/addons/icinga_certificate_monitoring.png) + +Read more [here](https://icinga.com/products/icinga-certificate-monitoring/) +and [here](https://icinga.com/2019/06/03/monitoring-automation-with-icinga-certificate-monitoring/). + +### Dashing Dashboard + +The [Icinga 2 dashboard](https://github.com/dnsmichi/dashing-icinga2) is built +on top of Dashing and uses the [REST API](12-icinga2-api.md#icinga2-api) to visualize what's going +on with your monitoring. It combines several popular widgets and provides development +instructions for your own implementation. + +The dashboard also allows to embed the [Icinga Web 2](https://icinga.com/products/icinga-web-2/) +host and service problem lists as Iframe. + +![Dashing dashboard](images/addons/dashing_icinga2.png) -[Thruk](https://www.thruk.org) is an alternative web interface which can be used with Icinga 2 -and the [Livestatus](14-features.md#setting-up-livestatus) feature. ## Log Monitoring @@ -102,7 +160,7 @@ is even simpler these days. * Configure the logstash `nagios` output to send passive traps to Icinga 2 using the external command pipe. * Execute a plugin to check Graylog alert streams. -More details can be found in [this blog post](https://www.icinga.com/2014/12/02/team-icinga-at-osmc-2014/). +More details can be found in [this blog post](https://icinga.com/2014/12/02/team-icinga-at-osmc-2014/). ## Notification Scripts and Interfaces @@ -116,79 +174,27 @@ There's a variety of resources available, for example different notification scr * Ticket systems * etc. -Additionally external services can be [integrated with Icinga 2](https://www.icinga.com/products/integrations/): +Blog posts and howtos: -* [Pagerduty](https://www.icinga.com/partners/pagerduty/) -* [VictorOps](https://www.icinga.com/partners/victorops/) -* [StackStorm](https://www.icinga.com/partners/stackstorm/) +* [Environmental Monitoring and Alerting](https://icinga.com/2019/09/02/environmental-monitoring-and-alerting-via-text-message/) -More information can be found on the [Icinga Website](https://www.icinga.com/). +Additionally external services can be [integrated with Icinga 2](https://icinga.com/products/integrations/): + +* [Pagerduty](https://icinga.com/products/integrations/pagerduty/) +* [VictorOps](https://icinga.com/products/integrations/victorops/) +* [StackStorm](https://icinga.com/products/integrations/stackstorm/) + +More information can be found on the [Icinga Website](https://icinga.com/). ## Configuration Management Tools -If you require your favourite configuration tool to export the Icinga 2 configuration, please get in -touch with their developers. The Icinga project does not provide a configuration web interface -yet. Follow the [Icinga Blog](https://www.icinga.com/blog/) for updates on this topic. +Checkout these specific integrations: -If you're looking for puppet manifests, chef cookbooks, ansible recipes, etc. -- we're happy -to integrate them upstream, so please get in touch with the [Icinga team](https://www.icinga.com/community/get-involved/). +* [Ansible Roles](https://icinga.com/products/integrations/) +* [Puppet Module](https://icinga.com/products/integrations/puppet/) +* [Chef Cookbook](https://icinga.com/products/integrations/chef/) -These tools are currently in development and require feedback and tests: +If you're looking for different config management integrations -- we're happy +to add them upstream, so please get in touch with the [Icinga team](https://icinga.com/community/). -* [Ansible Roles](https://github.com/Icinga/icinga2-ansible) -* [Puppet Module](https://github.com/Icinga/puppet-icinga2) -* [Chef Cookbook](https://github.com/Icinga/chef-icinga2) -## More Addon Integration Hints - -### PNP Action Url - -They work in a similar fashion for Icinga 2 and are used for 1.x web interfaces (Icinga Web 2 doesn't require -the action url attribute in its own module). - - template Host "pnp-hst" { - action_url = "/pnp4nagios/graph?host=$HOSTNAME$" - } - - template Service "pnp-svc" { - action_url = "/pnp4nagios/graph?host=$HOSTNAME$&srv=$SERVICEDESC$" - } - -### PNP Custom Templates with Icinga 2 - -PNP automatically determines the graph template from the check command name (or the argument's name). -This behavior changed in Icinga 2 compared to Icinga 1.x. Though there are certain possibilities to -fix this: - -* Create a symlink for example from the `templates.dist/check_ping.php` template to the actual check name in Icinga 2 (`templates/ping4.php`) -* Pass the check command name inside the [format template configuration](14-features.md#writing-performance-data-files) - -The latter becomes difficult with agent based checks like NRPE or SSH where the first command argument acts as -graph template identifier. There is the possibility to define the pnp template name as custom attribute -and use that inside the formatting templates as `SERVICECHECKCOMMAND` for instance. - -Example for services: - - # vim /etc/icinga2/features-enabled/perfdata.conf - - service_format_template = "DATATYPE::SERVICEPERFDATA\tTIMET::$icinga.timet$\tHOSTNAME::$host.name$\tSERVICEDESC::$service.name$\tSERVICEPERFDATA::$service.perfdata$\tSERVICECHECKCOMMAND::$service.check_command$$pnp_check_arg1$\tHOSTSTATE::$host.state$\tHOSTSTATETYPE::$host.state_type$\tSERVICESTATE::$service.state$\tSERVICESTATETYPE::$service.state_type$" - - # vim /etc/icinga2/conf.d/services.conf - - template Service "pnp-svc" { - action_url = "/pnp4nagios/graph?host=$HOSTNAME$&srv=$SERVICEDESC$" - vars.pnp_check_arg1 = "" - } - - apply Service "nrpe-check" { - import "pnp-svc" - check_command = nrpe - vars.nrpe_command = "check_disk" - - vars.pnp_check_arg1 = "!$nrpe_command$" - } - -If there are warnings about unresolved macros, make sure to specify a default value for `vars.pnp_check_arg1` inside the - -In PNP, the custom template for nrpe is then defined in `/etc/pnp4nagios/custom/nrpe.cfg` -and the additional command arg string will be seen in the xml too for other templates. diff --git a/doc/14-features.md b/doc/14-features.md index d7a41a289..c9fa71b95 100644 --- a/doc/14-features.md +++ b/doc/14-features.md @@ -21,25 +21,42 @@ By default file the `mainlog` feature is enabled. When running Icinga 2 on a terminal log messages with severity `information` or higher are written to the console. -Packages will install a configuration file for logrotate on supported -platforms. This configuration ensures that the `icinga2.log`, `error.log` and -`debug.log` files are rotated on a daily basis. +### Log Rotation -## DB IDO +Packages provide a configuration file for [logrotate](https://linux.die.net/man/8/logrotate) +on Linux/Unix. Typically this is installed into `/etc/logrotate.d/icinga2` +and modifications won't be overridden on upgrade. + +Instead of sending the reload HUP signal, logrotate +sends the USR1 signal to notify the Icinga daemon +that it has rotate the log file. Icinga reopens the log +files then: + +* `/var/log/icinga2/icinga2.log` (requires `mainlog` enabled) +* `/var/log/icinga2/debug.log` (requires `debuglog` enabled) +* `/var/log/icinga2/erorr.log` + +By default, log files will be rotated daily. + +## Core Backends + +### REST API + +The REST API is documented [here](12-icinga2-api.md#icinga2-api) as a core feature. + +### IDO Database (DB IDO) The IDO (Icinga Data Output) feature for Icinga 2 takes care of exporting all configuration and status information into a database. The IDO database is used by Icinga Web 2 as data backend. -Details on the installation can be found in the [Configuring DB IDO](02-getting-started.md#configuring-db-ido-mysql) +Details on the installation can be found in the [Configuring DB IDO](02-installation.md#configuring-db-ido-mysql) chapter. Details on the configuration can be found in the [IdoMysqlConnection](09-object-types.md#objecttype-idomysqlconnection) and [IdoPgsqlConnection](09-object-types.md#objecttype-idopgsqlconnection) object configuration documentation. -The DB IDO feature supports [High Availability](06-distributed-monitoring.md#distributed-monitoring-high-availability-db-ido) in -the Icinga 2 cluster. -### DB IDO Health +#### DB IDO Health If the monitoring health indicator is critical in Icinga Web 2, you can use the following queries to manually check whether Icinga 2 @@ -87,8 +104,46 @@ status_update_time A detailed list on the available table attributes can be found in the [DB IDO Schema documentation](24-appendix.md#schema-db-ido). +#### DB IDO in Cluster HA Zones -### DB IDO Tuning +The DB IDO feature supports [High Availability](06-distributed-monitoring.md#distributed-monitoring-high-availability-db-ido) in +the Icinga 2 cluster. + +By default, both endpoints in a zone calculate the +endpoint which activates the feature, the other endpoint +automatically pauses it. If the cluster connection +breaks at some point, the paused IDO feature automatically +does a failover. + +You can disable this behaviour by setting `enable_ha = false` +in both feature configuration files. + +#### DB IDO Cleanup + +Objects get deactivated when they are deleted from the configuration. +This is visible with the `is_active` column in the `icinga_objects` table. +Therefore all queries need to join this table and add `WHERE is_active=1` as +condition. Deleted objects preserve their history table entries for later SLA +reporting. + +Historical data isn't purged by default. You can enable the least +kept data age inside the `cleanup` configuration attribute for the +IDO features [IdoMysqlConnection](09-object-types.md#objecttype-idomysqlconnection) +and [IdoPgsqlConnection](09-object-types.md#objecttype-idopgsqlconnection). + +Example if you prefer to keep notification history for 30 days: + +``` + cleanup = { + notifications_age = 30d + contactnotifications_age = 30d + } +``` + +The historical tables are populated depending on the data `categories` specified. +Some tables are empty by default. + +#### DB IDO Tuning As with any application database, there are ways to optimize and tune the database performance. @@ -134,103 +189,52 @@ VACUUM > Don't use `VACUUM FULL` as this has a severe impact on performance. -## External Commands +## Metrics -> **Note** -> -> Please use the [REST API](12-icinga2-api.md#icinga2-api) as modern and secure alternative -> for external actions. +Whenever a host or service check is executed, or received via the REST API, +best practice is to provide performance data. -Icinga 2 provides an external command pipe for processing commands -triggering specific actions (for example rescheduling a service check -through the web interface). +This data is parsed by features sending metrics to time series databases (TSDB): -In order to enable the `ExternalCommandListener` configuration use the -following command and restart Icinga 2 afterwards: +* [Graphite](14-features.md#graphite-carbon-cache-writer) +* [InfluxDB](14-features.md#influxdb-writer) +* [OpenTSDB](14-features.md#opentsdb-writer) - # icinga2 feature enable command +Metrics, state changes and notifications can be managed with the following integrations: -Icinga 2 creates the command pipe file as `/var/run/icinga2/cmd/icinga2.cmd` -using the default configuration. +* [Elastic Stack](14-features.md#elastic-stack-integration) +* [Graylog](14-features.md#graylog-integration) -Web interfaces and other Icinga addons are able to send commands to -Icinga 2 through the external command pipe, for example for rescheduling -a forced service check: - # /bin/echo "[`date +%s`] SCHEDULE_FORCED_SVC_CHECK;localhost;ping4;`date +%s`" >> /var/run/icinga2/cmd/icinga2.cmd +### Graphite Writer - # tail -f /var/log/messages +[Graphite](13-addons.md#addons-graphing-graphite) is a tool stack for storing +metrics and needs to be running prior to enabling the `graphite` feature. - Oct 17 15:01:25 icinga-server icinga2: Executing external command: [1382014885] SCHEDULE_FORCED_SVC_CHECK;localhost;ping4;1382014885 - Oct 17 15:01:25 icinga-server icinga2: Rescheduling next check for service 'ping4' - -A list of currently supported external commands can be found [here](24-appendix.md#external-commands-list-detail). - -Detailed information on the commands and their required parameters can be found -on the [Icinga 1.x documentation](https://docs.icinga.com/latest/en/extcommands2.html). - -## Performance Data - -When a host or service check is executed plugins should provide so-called -`performance data`. Next to that additional check performance data -can be fetched using Icinga 2 runtime macros such as the check latency -or the current service state (or additional custom attributes). - -The performance data can be passed to external applications which aggregate and -store them in their backends. These tools usually generate graphs for historical -reporting and trending. - -Well-known addons processing Icinga performance data are [PNP4Nagios](13-addons.md#addons-graphing-pnp), -[Graphite](13-addons.md#addons-graphing-graphite) or [OpenTSDB](14-features.md#opentsdb-writer). - -### Writing Performance Data Files - -PNP4Nagios and Graphios use performance data collector daemons to fetch -the current performance files for their backend updates. - -Therefore the Icinga 2 [PerfdataWriter](09-object-types.md#objecttype-perfdatawriter) -feature allows you to define the output template format for host and services helped -with Icinga 2 runtime vars. - - host_format_template = "DATATYPE::HOSTPERFDATA\tTIMET::$icinga.timet$\tHOSTNAME::$host.name$\tHOSTPERFDATA::$host.perfdata$\tHOSTCHECKCOMMAND::$host.check_command$\tHOSTSTATE::$host.state$\tHOSTSTATETYPE::$host.state_type$" - service_format_template = "DATATYPE::SERVICEPERFDATA\tTIMET::$icinga.timet$\tHOSTNAME::$host.name$\tSERVICEDESC::$service.name$\tSERVICEPERFDATA::$service.perfdata$\tSERVICECHECKCOMMAND::$service.check_command$\tHOSTSTATE::$host.state$\tHOSTSTATETYPE::$host.state_type$\tSERVICESTATE::$service.state$\tSERVICESTATETYPE::$service.state_type$" - -The default templates are already provided with the Icinga 2 feature configuration -which can be enabled using - - # icinga2 feature enable perfdata - -By default all performance data files are rotated in a 15 seconds interval into -the `/var/spool/icinga2/perfdata/` directory as `host-perfdata.` and -`service-perfdata.`. -External collectors need to parse the rotated performance data files and then -remove the processed files. - -### Graphite Carbon Cache Writer - -While there are some [Graphite](13-addons.md#addons-graphing-graphite) -collector scripts and daemons like Graphios available for Icinga 1.x it's more -reasonable to directly process the check and plugin performance -in memory in Icinga 2. Once there are new metrics available, Icinga 2 will directly -write them to the defined Graphite Carbon daemon tcp socket. +Icinga 2 writes parsed metrics directly to Graphite's Carbon Cache +TCP port, defaulting to `2003`. You can enable the feature using - # icinga2 feature enable graphite +``` +# icinga2 feature enable graphite +``` By default the [GraphiteWriter](09-object-types.md#objecttype-graphitewriter) feature expects the Graphite Carbon Cache to listen at `127.0.0.1` on TCP port `2003`. -#### Current Graphite Schema +#### Graphite Schema -The current naming schema is defined as follows. The [Icinga Web 2 Graphite module](https://github.com/icinga/icingaweb2-module-graphite) +The current naming schema is defined as follows. The [Icinga Web 2 Graphite module](https://icinga.com/products/integrations/graphite/) depends on this schema. The default prefix for hosts and services is configured using [runtime macros](03-monitoring-basics.md#runtime-macros)like this: - icinga2.$host.name$.host.$host.check_command$ - icinga2.$host.name$.services.$service.name$.$service.check_command$ +``` +icinga2.$host.name$.host.$host.check_command$ +icinga2.$host.name$.services.$service.name$.$service.check_command$ +``` You can customize the prefix name by using the `host_name_template` and `service_name_template` configuration attributes. @@ -250,9 +254,12 @@ The following characters are escaped in prefix labels: Metric values are stored like this: - .perfdata..value +``` +.perfdata..value +``` -The following characters are escaped in perfdata labels: +The following characters are escaped in performance labels +parsed from plugin output: Character | Escaped character --------------|-------------------------- @@ -261,29 +268,33 @@ The following characters are escaped in perfdata labels: / | _ :: | . -Note that perfdata labels may contain dots (`.`) allowing to +Note that labels may contain dots (`.`) allowing to add more subsequent levels inside the Graphite tree. `::` adds support for [multi performance labels](http://my-plugin.de/wiki/projects/check_multi/configuration/performance) and is therefore replaced by `.`. By enabling `enable_send_thresholds` Icinga 2 automatically adds the following threshold metrics: - .perfdata..min - .perfdata..max - .perfdata..warn - .perfdata..crit +``` +.perfdata..min +.perfdata..max +.perfdata..warn +.perfdata..crit +``` By enabling `enable_send_metadata` Icinga 2 automatically adds the following metadata metrics: - .metadata.current_attempt - .metadata.downtime_depth - .metadata.acknowledgement - .metadata.execution_time - .metadata.latency - .metadata.max_check_attempts - .metadata.reachable - .metadata.state - .metadata.state_type +``` +.metadata.current_attempt +.metadata.downtime_depth +.metadata.acknowledgement +.metadata.execution_time +.metadata.latency +.metadata.max_check_attempts +.metadata.reachable +.metadata.state +.metadata.state_type +``` Metadata metric overview: @@ -302,10 +313,31 @@ Metadata metric overview: The following example illustrates how to configure the storage schemas for Graphite Carbon Cache. - [icinga2_default] - # intervals like PNP4Nagios uses them per default - pattern = ^icinga2\. - retentions = 1m:2d,5m:10d,30m:90d,360m:4y +``` +[icinga2_default] +# intervals like PNP4Nagios uses them per default +pattern = ^icinga2\. +retentions = 1m:2d,5m:10d,30m:90d,360m:4y +``` + +#### Graphite in Cluster HA Zones + +The Graphite feature supports [high availability](06-distributed-monitoring.md#distributed-monitoring-high-availability-features) +in cluster zones since 2.11. + +By default, all endpoints in a zone will activate the feature and start +writing metrics to a Carbon Cache socket. In HA enabled scenarios, +it is possible to set `enable_ha = true` in all feature configuration +files. This allows each endpoint to calculate the feature authority, +and only one endpoint actively writes metrics, the other endpoints +pause the feature. + +When the cluster connection breaks at some point, the remaining endpoint(s) +in that zone will automatically resume the feature. This built-in failover +mechanism ensures that metrics are written even if the cluster fails. + +The recommended way of running Graphite in this scenario is a dedicated server +where Carbon Cache/Relay is running as receiver. ### InfluxDB Writer @@ -315,7 +347,9 @@ defined InfluxDB HTTP API. You can enable the feature using - # icinga2 feature enable influxdb +``` +# icinga2 feature enable influxdb +``` By default the [InfluxdbWriter](09-object-types.md#objecttype-influxdbwriter) feature expects the InfluxDB daemon to listen at `127.0.0.1` on port `8086`. @@ -352,7 +386,7 @@ apply Service "disk" for (disk => attributes in host.vars.disks) { } ``` -This is a typical pattern for checking individual disks, NICs, SSL certificates etc associated +This is a typical pattern for checking individual disks, NICs, TLS certificates etc associated with a host. What would be useful is to have the data points tagged with the specific instance for that check. This would allow you to query time series data for a check on a host and for a specific instance e.g. /dev/sda. To do this quite simply add the instance to the service variables: @@ -383,16 +417,35 @@ object InfluxdbWriter "influxdb" { } ``` +#### InfluxDB in Cluster HA Zones + +The InfluxDB feature supports [high availability](06-distributed-monitoring.md#distributed-monitoring-high-availability-features) +in cluster zones since 2.11. + +By default, all endpoints in a zone will activate the feature and start +writing metrics to the InfluxDB HTTP API. In HA enabled scenarios, +it is possible to set `enable_ha = true` in all feature configuration +files. This allows each endpoint to calculate the feature authority, +and only one endpoint actively writes metrics, the other endpoints +pause the feature. + +When the cluster connection breaks at some point, the remaining endpoint(s) +in that zone will automatically resume the feature. This built-in failover +mechanism ensures that metrics are written even if the cluster fails. + +The recommended way of running InfluxDB in this scenario is a dedicated server +where the InfluxDB HTTP API or Telegraf as Proxy are running. + ### Elastic Stack Integration -[Icingabeat](https://github.com/icinga/icingabeat) is an Elastic Beat that fetches data +[Icingabeat](https://icinga.com/products/integrations/elastic/) is an Elastic Beat that fetches data from the Icinga 2 API and sends it either directly to [Elasticsearch](https://www.elastic.co/products/elasticsearch) or [Logstash](https://www.elastic.co/products/logstash). More integrations: -* [Logstash output](https://github.com/Icinga/logstash-output-icinga) for the Icinga 2 API. -* [Logstash Grok Pattern](https://github.com/Icinga/logstash-grok-pattern) for Icinga 2 logs. +* [Logstash output](https://icinga.com/products/integrations/elastic/) for the Icinga 2 API. +* [Logstash Grok Pattern](https://icinga.com/products/integrations/elastic/) for Icinga 2 logs. #### Elasticsearch Writer @@ -403,7 +456,10 @@ The check results include parsed performance data metrics if enabled. > **Note** > -> Elasticsearch 5.x or 6.x are required. This feature has been successfully tested with Elasticsearch 5.6.7 and 6.2.3. +> Elasticsearch 5.x or 6.x are required. This feature has been successfully tested with +> Elasticsearch 5.6.7 and 6.3.1. + + Enable the feature and restart Icinga 2. @@ -429,7 +485,9 @@ attribute. Metric values are stored like this: - check_result.perfdata..value +``` +check_result.perfdata..value +``` The following characters are escaped in perfdata labels: @@ -448,10 +506,32 @@ and is therefore replaced by `.`. Icinga 2 automatically adds the following threshold metrics if existing: - check_result.perfdata..min - check_result.perfdata..max - check_result.perfdata..warn - check_result.perfdata..crit +``` +check_result.perfdata..min +check_result.perfdata..max +check_result.perfdata..warn +check_result.perfdata..crit +``` + +#### Elasticsearch in Cluster HA Zones + +The Elasticsearch feature supports [high availability](06-distributed-monitoring.md#distributed-monitoring-high-availability-features) +in cluster zones since 2.11. + +By default, all endpoints in a zone will activate the feature and start +writing events to the Elasticsearch HTTP API. In HA enabled scenarios, +it is possible to set `enable_ha = true` in all feature configuration +files. This allows each endpoint to calculate the feature authority, +and only one endpoint actively writes events, the other endpoints +pause the feature. + +When the cluster connection breaks at some point, the remaining endpoint(s) +in that zone will automatically resume the feature. This built-in failover +mechanism ensures that events are written even if the cluster fails. + +The recommended way of running Elasticsearch in this scenario is a dedicated server +where you either have the Elasticsearch HTTP API, or a TLS secured HTTP proxy, +or Logstash for additional filtering. ### Graylog Integration @@ -467,7 +547,9 @@ While it has been specified by the [Graylog](https://www.graylog.org) project as You can enable the feature using - # icinga2 feature enable gelf +``` +# icinga2 feature enable gelf +``` By default the `GelfWriter` object expects the GELF receiver to listen at `127.0.0.1` on TCP port `12201`. The default `source` attribute is set to `icinga2`. You can customize that for your needs if required. @@ -477,6 +559,24 @@ Currently these events are processed: * State changes * Notifications +#### Graylog/GELF in Cluster HA Zones + +The Gelf feature supports [high availability](06-distributed-monitoring.md#distributed-monitoring-high-availability-features) +in cluster zones since 2.11. + +By default, all endpoints in a zone will activate the feature and start +writing events to the Graylog HTTP API. In HA enabled scenarios, +it is possible to set `enable_ha = true` in all feature configuration +files. This allows each endpoint to calculate the feature authority, +and only one endpoint actively writes events, the other endpoints +pause the feature. + +When the cluster connection breaks at some point, the remaining endpoint(s) +in that zone will automatically resume the feature. This built-in failover +mechanism ensures that events are written even if the cluster fails. + +The recommended way of running Graylog in this scenario is a dedicated server +where you have the Graylog HTTP API listening. ### OpenTSDB Writer @@ -487,27 +587,35 @@ write them to the defined TSDB TCP socket. You can enable the feature using - # icinga2 feature enable opentsdb +``` +# icinga2 feature enable opentsdb +``` By default the `OpenTsdbWriter` object expects the TSD to listen at `127.0.0.1` on port `4242`. The current naming schema is - icinga.host. - icinga.service.. +``` +icinga.host. +icinga.service.. +``` for host and service checks. The tag host is always applied. To make sure Icinga 2 writes a valid metric into OpenTSDB some characters are replaced with `_` in the target name: - \ (and space) +``` +\ (and space) +``` The resulting name in OpenTSDB might look like: - www-01 / http-cert / response time - icinga.http_cert.response_time +``` +www-01 / http-cert / response time +icinga.http_cert.response_time +``` In addition to the performance data retrieved from the check plugin, Icinga 2 sends internal check statistic data to OpenTSDB: @@ -527,7 +635,9 @@ internal check statistic data to OpenTSDB: While reachable, state and state_type are metrics for the host or service the other metrics follow the current naming schema - icinga.check. +``` +icinga.check. +``` with the following tags @@ -542,6 +652,75 @@ with the following tags > You might want to set the tsd.core.auto_create_metrics setting to `true` > in your opentsdb.conf configuration file. +#### OpenTSDB in Cluster HA Zones + +The OpenTSDB feature supports [high availability](06-distributed-monitoring.md#distributed-monitoring-high-availability-features) +in cluster zones since 2.11. + +By default, all endpoints in a zone will activate the feature and start +writing events to the OpenTSDB listener. In HA enabled scenarios, +it is possible to set `enable_ha = true` in all feature configuration +files. This allows each endpoint to calculate the feature authority, +and only one endpoint actively writes metrics, the other endpoints +pause the feature. + +When the cluster connection breaks at some point, the remaining endpoint(s) +in that zone will automatically resume the feature. This built-in failover +mechanism ensures that metrics are written even if the cluster fails. + +The recommended way of running OpenTSDB in this scenario is a dedicated server +where you have OpenTSDB running. + + +### Writing Performance Data Files + +PNP and Graphios use performance data collector daemons to fetch +the current performance files for their backend updates. + +Therefore the Icinga 2 [PerfdataWriter](09-object-types.md#objecttype-perfdatawriter) +feature allows you to define the output template format for host and services helped +with Icinga 2 runtime vars. + +``` +host_format_template = "DATATYPE::HOSTPERFDATA\tTIMET::$icinga.timet$\tHOSTNAME::$host.name$\tHOSTPERFDATA::$host.perfdata$\tHOSTCHECKCOMMAND::$host.check_command$\tHOSTSTATE::$host.state$\tHOSTSTATETYPE::$host.state_type$" +service_format_template = "DATATYPE::SERVICEPERFDATA\tTIMET::$icinga.timet$\tHOSTNAME::$host.name$\tSERVICEDESC::$service.name$\tSERVICEPERFDATA::$service.perfdata$\tSERVICECHECKCOMMAND::$service.check_command$\tHOSTSTATE::$host.state$\tHOSTSTATETYPE::$host.state_type$\tSERVICESTATE::$service.state$\tSERVICESTATETYPE::$service.state_type$" +``` + +The default templates are already provided with the Icinga 2 feature configuration +which can be enabled using + +``` +# icinga2 feature enable perfdata +``` + +By default all performance data files are rotated in a 15 seconds interval into +the `/var/spool/icinga2/perfdata/` directory as `host-perfdata.` and +`service-perfdata.`. +External collectors need to parse the rotated performance data files and then +remove the processed files. + +#### Perfdata Files in Cluster HA Zones + +The Perfdata feature supports [high availability](06-distributed-monitoring.md#distributed-monitoring-high-availability-features) +in cluster zones since 2.11. + +By default, all endpoints in a zone will activate the feature and start +writing metrics to the local spool directory. In HA enabled scenarios, +it is possible to set `enable_ha = true` in all feature configuration +files. This allows each endpoint to calculate the feature authority, +and only one endpoint actively writes metrics, the other endpoints +pause the feature. + +When the cluster connection breaks at some point, the remaining endpoint(s) +in that zone will automatically resume the feature. This built-in failover +mechanism ensures that metrics are written even if the cluster fails. + +The recommended way of running Perfdata is to mount the perfdata spool +directory via NFS on a central server where PNP with the NPCD collector +is running on. + + + ## Livestatus @@ -557,7 +736,7 @@ Livestatus. > > Only install the Livestatus feature if your web interface or addon requires > you to do so. -> [Icinga Web 2](02-getting-started.md#setting-up-icingaweb2) does not need +> [Icinga Web 2](02-installation.md#setting-up-icingaweb2) does not need > Livestatus. Details on the available tables and attributes with Icinga 2 can be found @@ -565,18 +744,24 @@ in the [Livestatus Schema](24-appendix.md#schema-livestatus) section. You can enable Livestatus using icinga2 feature enable: - # icinga2 feature enable livestatus +``` +# icinga2 feature enable livestatus +``` After that you will have to restart Icinga 2: - # systemctl restart icinga2 +``` +# systemctl restart icinga2 +``` By default the Livestatus socket is available in `/var/run/icinga2/cmd/livestatus`. In order for queries and commands to work you will need to add your query user (e.g. your web server) to the `icingacmd` group: - # usermod -a -G icingacmd www-data +``` +# usermod -a -G icingacmd www-data +``` The Debian packages use `nagios` as the user and group name. Make sure to change `icingacmd` to `nagios` if you're using Debian. @@ -588,8 +773,9 @@ In order to use the historical tables provided by the livestatus feature (for ex are expected to be in `/var/log/icinga2/compat`. A different path can be set using the `compat_log_path` configuration attribute. - # icinga2 feature enable compatlog - +``` +# icinga2 feature enable compatlog +``` ### Livestatus Sockets @@ -615,33 +801,35 @@ programmatically: [Monitoring::Livestatus](http://search.cpan.org/~nierlein/Moni Example using the unix socket: - # echo -e "GET services\n" | /usr/bin/nc -U /var/run/icinga2/cmd/livestatus +``` +# echo -e "GET services\n" | /usr/bin/nc -U /var/run/icinga2/cmd/livestatus Example using the tcp socket listening on port `6558`: - # echo -e 'GET services\n' | netcat 127.0.0.1 6558 +# echo -e 'GET services\n' | netcat 127.0.0.1 6558 - # cat servicegroups < A list of available external commands and their parameters can be found [here](24-appendix.md#external-commands-list-detail) - $ echo -e 'COMMAND ' | netcat 127.0.0.1 6558 - +``` +$ echo -e 'COMMAND ' | netcat 127.0.0.1 6558 +``` ### Livestatus Filters and, or, negate Operator | Negate | Description - ----------|------------------------ + ----------|----------|------------- = | != | Equality ~ | !~ | Regex match =~ | !=~ | Equality ignoring case @@ -669,20 +857,22 @@ Schema: "Stats: aggregatefunction aggregateattribute" Example: - GET hosts - Filter: has_been_checked = 1 - Filter: check_type = 0 - Stats: sum execution_time - Stats: sum latency - Stats: sum percent_state_change - Stats: min execution_time - Stats: min latency - Stats: min percent_state_change - Stats: max execution_time - Stats: max latency - Stats: max percent_state_change - OutputFormat: json - ResponseHeader: fixed16 +``` +GET hosts +Filter: has_been_checked = 1 +Filter: check_type = 0 +Stats: sum execution_time +Stats: sum latency +Stats: sum percent_state_change +Stats: min execution_time +Stats: min latency +Stats: min percent_state_change +Stats: max execution_time +Stats: max latency +Stats: max percent_state_change +OutputFormat: json +ResponseHeader: fixed16 +``` ### Livestatus Output @@ -694,7 +884,9 @@ is a pipe (2nd level). Separators can be set using ASCII codes like: - Separators: 10 59 44 124 +``` +Separators: 10 59 44 124 +``` * JSON @@ -735,27 +927,33 @@ The `commands` table is populated with `CheckCommand`, `EventCommand` and `Notif A detailed list on the available table attributes can be found in the [Livestatus Schema documentation](24-appendix.md#schema-livestatus). -## Status Data Files +## Deprecated Features + +### Status Data Files > **Note** > -> This feature is DEPRECATED and will be removed in Icinga 2 v2.11. +> This feature is DEPRECATED and will be removed in future releases. +> Check the [roadmap](https://github.com/Icinga/icinga2/milestones). Icinga 1.x writes object configuration data and status data in a cyclic interval to its `objects.cache` and `status.dat` files. Icinga 2 provides the `StatusDataWriter` object which dumps all configuration objects and status updates in a regular interval. - # icinga2 feature enable statusdata +``` +# icinga2 feature enable statusdata +``` If you are not using any web interface or addon which uses these files, you can safely disable this feature. -## Compat Log Files +### Compat Log Files > **Note** > -> This feature is DEPRECATED and will be removed in Icinga 2 v2.11. +> This feature is DEPRECATED and will be removed in future releases. +> Check the [roadmap](https://github.com/Icinga/icinga2/milestones). The Icinga 1.x log format is considered being the `Compat Log` in Icinga 2 provided with the `CompatLogger` object. @@ -768,17 +966,65 @@ for answering queries to historical tables. The `CompatLogger` object can be enabled with - # icinga2 feature enable compatlog +``` +# icinga2 feature enable compatlog +``` By default, the Icinga 1.x log file called `icinga.log` is located in `/var/log/icinga2/compat`. Rotated log files are moved into `var/log/icinga2/compat/archives`. -## Check Result Files +### External Command Pipe > **Note** > -> This feature is DEPRECATED and will be removed in Icinga 2 v2.11. +> Please use the [REST API](12-icinga2-api.md#icinga2-api) as modern and secure alternative +> for external actions. + +> **Note** +> +> This feature is DEPRECATED and will be removed in future releases. +> Check the [roadmap](https://github.com/Icinga/icinga2/milestones). + +Icinga 2 provides an external command pipe for processing commands +triggering specific actions (for example rescheduling a service check +through the web interface). + +In order to enable the `ExternalCommandListener` configuration use the +following command and restart Icinga 2 afterwards: + +``` +# icinga2 feature enable command +``` + +Icinga 2 creates the command pipe file as `/var/run/icinga2/cmd/icinga2.cmd` +using the default configuration. + +Web interfaces and other Icinga addons are able to send commands to +Icinga 2 through the external command pipe, for example for rescheduling +a forced service check: + +``` +# /bin/echo "[`date +%s`] SCHEDULE_FORCED_SVC_CHECK;localhost;ping4;`date +%s`" >> /var/run/icinga2/cmd/icinga2.cmd + +# tail -f /var/log/messages + +Oct 17 15:01:25 icinga-server icinga2: Executing external command: [1382014885] SCHEDULE_FORCED_SVC_CHECK;localhost;ping4;1382014885 +Oct 17 15:01:25 icinga-server icinga2: Rescheduling next check for service 'ping4' +``` + +A list of currently supported external commands can be found [here](24-appendix.md#external-commands-list-detail). + +Detailed information on the commands and their required parameters can be found +on the [Icinga 1.x documentation](https://docs.icinga.com/latest/en/extcommands2.html). + + +### Check Result Files + +> **Note** +> +> This feature is DEPRECATED and will be removed in future releases. +> Check the [roadmap](https://github.com/Icinga/icinga2/milestones). Icinga 1.x writes its check result files to a temporary spool directory where they are processed in a regular interval. @@ -793,7 +1039,8 @@ environments, Icinga 2 supports the `CheckResultReader` object. There is no feature configuration available, but it must be defined on-demand in your Icinga 2 objects configuration. - object CheckResultReader "reader" { - spool_dir = "/data/check-results" - } - +``` +object CheckResultReader "reader" { + spool_dir = "/data/check-results" +} +``` diff --git a/doc/15-troubleshooting.md b/doc/15-troubleshooting.md index d78bc9aa0..5422f2c49 100644 --- a/doc/15-troubleshooting.md +++ b/doc/15-troubleshooting.md @@ -3,7 +3,8 @@ ## Required Information Please ensure to provide any detail which may help reproduce and understand your issue. -Whether you ask on the community channels or you create an issue at [GitHub](https://github.com/Icinga), make sure +Whether you ask on the [community channels](https://community.icinga.com) or you +create an issue at [GitHub](https://github.com/Icinga), make sure that others can follow your explanations. If necessary, draw a picture and attach it for better illustration. This is especially helpful if you are troubleshooting a distributed setup. @@ -18,12 +19,12 @@ findings and details please. * `icinga2 --version` * `icinga2 feature list` * `icinga2 daemon -C` - * [Icinga Web 2](https://www.icinga.com/products/icinga-web-2/) version (screenshot from System - About) - * [Icinga Web 2 modules](https://www.icinga.com/products/icinga-web-2-modules/) e.g. the Icinga Director (optional) + * [Icinga Web 2](https://icinga.com/products/icinga-web-2/) version (screenshot from System - About) + * [Icinga Web 2 modules](https://icinga.com/products/icinga-web-2-modules/) e.g. the Icinga Director (optional) * Configuration insights: * Provide complete configuration snippets explaining your problem in detail - * Your [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) file - * If you run multiple Icinga 2 instances, the [zones.conf](04-configuring-icinga-2.md#zones-conf) file (or `icinga2 object list --type Endpoint` and `icinga2 object list --type Zone`) from all affected nodes. + * Your [icinga2.conf](04-configuration.md#icinga2-conf) file + * If you run multiple Icinga 2 instances, the [zones.conf](04-configuration.md#zones-conf) file (or `icinga2 object list --type Endpoint` and `icinga2 object list --type Zone`) from all affected nodes. * Logs * Relevant output from your main and [debug log](15-troubleshooting.md#troubleshooting-enable-debug-output) in `/var/log/icinga2`. Please add step-by-step explanations with timestamps if required. * The newest Icinga 2 crash log if relevant, located in `/var/log/icinga2/crash` @@ -44,7 +45,7 @@ is also key to identify bottlenecks and issues. * Analyze the system's performance and dentify bottlenecks and issues. * Collect details about all applications (e.g. Icinga 2, MySQL, Apache, Graphite, Elastic, etc.). * If data is exchanged via network (e.g. central MySQL cluster) ensure to monitor the bandwidth capabilities too. -* Add graphs and screenshots to your issue description +* Add graphs from Grafana or Graphite as screenshots to your issue description Install tools which help you to do so. Opinions differ, let us know if you have any additions here! @@ -107,6 +108,16 @@ You can also start `perfmon` and analyze specific performance counters. Keep notes which could be important for your monitoring, and add service checks later on. +> **Tip** +> +> Use an administrative Powershell to gain more insights. + +``` +cd C:\ProgramData\icinga2\var\log\icinga2 + +Get-Content .\icinga2.log -tail 10 -wait +``` + ## Enable Debug Output ### Enable Debug Output on Linux/Unix @@ -120,6 +131,13 @@ Enable the `debuglog` feature: The debug log file can be found in `/var/log/icinga2/debug.log`. +You can tail the log files with an administrative shell: + +``` +cd /var/log/icinga2 +tail -f debug.log +``` + Alternatively you may run Icinga 2 in the foreground with debugging enabled. Specify the console log severity as an additional parameter argument to `-x`. @@ -132,18 +150,29 @@ and `debug`. ### Enable Debug Output on Windows -Open a command prompt with administrative privileges and enable the debug log feature. +Open a Powershell with administrative privileges and enable the debug log feature. ``` -C:> icinga2.exe feature enable debuglog +C:\> cd C:\Program Files\ICINGA2\sbin + +C:\Program Files\ICINGA2\sbin> .\icinga2.exe feature enable debuglog ``` Ensure that the Icinga 2 service already writes the main log into `C:\ProgramData\icinga2\var\log\icinga2`. -Restart the Icinga 2 service and open the newly created `debug.log` file. +Restart the Icinga 2 service in an administrative Powershell and open the newly created `debug.log` file. ``` -C:> net stop icinga2 -C:> net start icinga2 +C:\> Restart-Service icinga2 + +C:\> Get-Service icinga2 +``` + +You can tail the log files with an administrative Powershell: + +``` +C:\> cd C:\ProgramData\icinga2\var\log\icinga2 + +C:\ProgramData\icinga2\var\log\icinga2> Get-Content .\debug.log -tail 10 -wait ``` ## Configuration Troubleshooting @@ -187,6 +216,14 @@ Object 'localhost!ssh' of type 'Service': [...] ``` +On Windows, use an administrative Powershell: + +``` +C:\> cd C:\Program Files\ICINGA2\sbin + +C:\Program Files\ICINGA2\sbin> .\icinga2.exe object list +``` + You can also filter by name and type: ``` @@ -237,9 +274,9 @@ include include ``` -in the [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) configuration file. These files are not considered configuration files and will be overridden -on upgrade, so please send modifications as proposed patches upstream. The default include path is set to -`LocalStateDir + "/share/icinga2/includes"`. +in the [icinga2.conf](04-configuration.md#icinga2-conf) configuration file. These files are not considered +configuration files and will be overridden on upgrade, so please send modifications as proposed patches upstream. +The default include path is set to `/usr/share/icinga2/includes` with the constant `IncludeConfDir`. You should add your own command definitions to a new file in `conf.d/` called `commands.conf` or similar. @@ -248,7 +285,7 @@ or similar. * Make sure that the line(s) are not [commented out](17-language-reference.md#comments) (starting with `//` or `#`, or encapsulated by `/* ... */`). -* Is the configuration file included in [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf)? +* Is the configuration file included in [icinga2.conf](04-configuration.md#icinga2-conf)? Run the [configuration validation](11-cli-commands.md#config-validation) and add `notice` as log severity. Search for the file which should be included i.e. using the `grep` CLI command. @@ -274,7 +311,7 @@ did not properly escape the single dollar sign preventing its usage as [runtime critical/config: Error: Validation failed for Object 'ping4' (Type: 'Service') at /etc/icinga2/zones.d/global-templates/windows.conf:24: Closing $ not found in macro format string 'top-syntax=${list}'. ``` -Correct the custom attribute value to +Correct the custom variable value to ``` "top-syntax=$${list}" @@ -326,6 +363,9 @@ $ curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Over } ``` +Alternatively when using the Director, navigate into the Service Detail View +in Icinga Web and pick `Inspect` to query the details. + Example for using the `icinga2 console` CLI command evaluation functionality: ``` @@ -351,17 +391,34 @@ Example for searching the debug log: ### Checks are not executed +* First off, decide whether the checks are executed locally, or remote in a distributed setup. + +If the master does not receive check results from the satellite, move your analysis to the satellite +and verify why the checks are not executed there. + * Check the [debug log](15-troubleshooting.md#troubleshooting-enable-debug-output) to see if the check command gets executed. -* Verify that failed depedencies do not prevent command execution. +* Verify that failed dependencies do not prevent command execution. * Make sure that the plugin is executable by the Icinga 2 user (run a manual test). * Make sure the [checker](11-cli-commands.md#enable-features) feature is enabled. * Use the Icinga 2 API [event streams](12-icinga2-api.md#icinga2-api-event-streams) to receive live check result streams. -Examples: +Test a plugin as icinga user. ``` # sudo -u icinga /usr/lib/nagios/plugins/check_ping -4 -H 127.0.0.1 -c 5000,100% -w 3000,80% +``` +> **Note** +> +> **Never test plugins as root, but the icinga daemon user.** The environment and permissions differ. +> +> Also, the daemon user **does not** spawn a terminal shell (Bash, etc.) so it won't read anything from .bashrc +> and variants. The Icinga daemon only relies on sysconfig environment variables being set. + + +Enable the checker feature. + +``` # icinga2 feature enable checker The feature 'checker' is already enabled. ``` @@ -369,7 +426,8 @@ The feature 'checker' is already enabled. Fetch all check result events matching the `event.service` name `random`: ``` -$ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/events?queue=debugchecks&types=CheckResult&filter=match%28%22random*%22,event.service%29' +$ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST \ + 'https://localhost:5665/v1/events?queue=debugchecks&types=CheckResult&filter=match%28%22random*%22,event.service%29' ``` @@ -393,10 +451,10 @@ $ curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Over "results": [ { "attrs": { - "__name": "icinga2-client1.localdomain!disk", + "__name": "icinga2-agent1.localdomain!disk", "last_check_result": { "active": true, - "check_source": "icinga2-client1.localdomain", + "check_source": "icinga2-agent1.localdomain", ... @@ -404,20 +462,23 @@ $ curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Over }, "joins": {}, "meta": {}, - "name": "icinga2-client1.localdomain!disk", + "name": "icinga2-agent1.localdomain!disk", "type": "Service" } ] } ``` -Example for using the `icinga2 console` CLI command evaluation functionality: +Alternatively when using the Director, navigate into the Service Detail View +in Icinga Web and pick `Inspect` to query the details. + +Example with the debug console: ``` $ ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://root@localhost:5665/' \ ---eval 'get_service("icinga2-client1.localdomain", "disk").last_check_result.check_source' | python -m json.tool +--eval 'get_service("icinga2-agent1.localdomain", "disk").last_check_result.check_source' | python -m json.tool -"icinga2-client1.localdomain" +"icinga2-agent1.localdomain" ``` @@ -475,13 +536,13 @@ in mind when using a different package. This could happen with [clients as command endpoint execution](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint). -If you have for example a client host `icinga2-client1.localdomain` +If you have for example a client host `icinga2-agent1.localdomain` and a service `disk` check defined on the master, the warning and critical thresholds are sometimes to applied and unwanted notification alerts are raised. This happens because the client itself includes a host object with -its `NodeName` and a basic set of checks in the [conf.d](04-configuring-icinga-2.md#conf-d) +its `NodeName` and a basic set of checks in the [conf.d](04-configuration.md#conf-d) directory, i.e. `disk` with the default thresholds. Clients which have the `checker` feature enabled will attempt @@ -494,11 +555,11 @@ master you will receive wrong check results from the client. Solution: * Disable the `checker` feature on clients: `icinga2 feature disable checker`. -* Remove the inclusion of [conf.d](04-configuring-icinga-2.md#conf-d) as suggested in the [client setup docs](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint). +* Remove the inclusion of [conf.d](04-configuration.md#conf-d) as suggested in the [client setup docs](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint). ### Check Fork Errors -Newer versions of Systemd on Linux limit spawned processes for +Newer versions of systemd on Linux limit spawned processes for services. * v227 introduces the `TasksMax` setting to units which allows to specify the spawned process limit. @@ -506,7 +567,7 @@ services. * v231 changes the default value to 15% This can cause problems with Icinga 2 in large environments with many -commands executed in parallel starting with Systemd v228. Some distributions +commands executed in parallel starting with systemd v228. Some distributions also may have changed the defaults. The error message could look like this: @@ -534,41 +595,45 @@ An example is available inside the GitHub repository in [etc/initsystem](https:/ External Resources: * [Fork limit for cgroups](https://lwn.net/Articles/663873/) -* [Systemd changelog](https://github.com/systemd/systemd/blob/master/NEWS) +* [systemd changelog](https://github.com/systemd/systemd/blob/master/NEWS) * [Icinga 2 upstream issue](https://github.com/Icinga/icinga2/issues/5611) -* [Systemd upstream discussion](https://github.com/systemd/systemd/issues/3211) +* [systemd upstream discussion](https://github.com/systemd/systemd/issues/3211) ### Systemd Watchdog Usually Icinga 2 is a mission critical part of infrastructure and should be online at all times. In case of a recoverable crash (e.g. OOM) you may want to -restart Icinga 2 automatically. With Systemd it is as easy as overriding some -settings of the Icinga 2 Systemd service by creating +restart Icinga 2 automatically. With systemd it is as easy as overriding some +settings of the Icinga 2 systemd service by creating `/etc/systemd/system/icinga2.service.d/override.conf` with the following content: - [Service] - Restart=always - RestartSec=1 - StartLimitInterval=10 - StartLimitBurst=3 +``` +[Service] +Restart=always +RestartSec=1 +StartLimitInterval=10 +StartLimitBurst=3 +``` Using the watchdog can also help with monitoring Icinga 2, to activate and use it add the following to the override: - WatchdogSec=30s +``` +WatchdogSec=30s +``` -This way Systemd will kill Icinga 2 if does not notify for over 30 seconds, a timout of less than 10 seconds is not +This way systemd will kill Icinga 2 if does not notify for over 30 seconds, a timout of less than 10 seconds is not recommended. When the watchdog is activated, `Restart=` can be set to `watchdog` to restart Icinga 2 in the case of a watchdog timeout. Run `systemctl daemon-reload && systemctl restart icinga2` to apply the changes. -Now Systemd will always try to restart Icinga 2 (except if you run +Now systemd will always try to restart Icinga 2 (except if you run `systemctl stop icinga2`). After three failures in ten seconds it will stop trying because you probably have a problem that requires manual intervention. ### Late Check Results -[Icinga Web 2](https://www.icinga.com/products/icinga-web-2/) provides +[Icinga Web 2](https://icinga.com/products/icinga-web-2/) provides a dashboard overview for `overdue checks`. The REST API provides the [status](12-icinga2-api.md#icinga2-api-status) URL endpoint with some generic metrics @@ -584,7 +649,7 @@ You can also calculate late check results via the REST API: * Compare the timestamp with the current time and add `check_interval` multiple times (change it to see which results are really late, like five times check_interval) You can use the [icinga2 console](11-cli-commands.md#cli-command-console) to connect to the instance, fetch all data -and calculate the differences. More infos can be found in [this blogpost](https://www.icinga.com/2016/08/11/analyse-icinga-2-problems-using-the-console-api/). +and calculate the differences. More infos can be found in [this blogpost](https://icinga.com/2016/08/11/analyse-icinga-2-problems-using-the-console-api/). ``` # ICINGA2_API_USERNAME=root ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://localhost:5665/' @@ -659,7 +724,7 @@ but you can adjust this by omitting the `len()` call inside the for loop. ## Notifications Troubleshooting -### Notifications are not sent +### Notifications are not sent * Check the [debug log](15-troubleshooting.md#troubleshooting-enable-debug-output) to see if a notification is triggered. * If yes, verify that all conditions are satisfied. @@ -703,12 +768,102 @@ You can use the Icinga 2 API [event streams](12-icinga2-api.md#icinga2-api-event $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/events?queue=debugnotifications&types=Notification' ``` + +### Analyze Notification Result + +> **Note** +> +> This feature is available since v2.11 and requires all endpoints +> being updated. + +Notifications inside a HA enabled zone are balanced between the endpoints, +just like checks. + +Sometimes notifications may fail, and with looking into the (debug) logs +for both masters, you cannot correlate this correctly. + +The `last_notification_result` runtime attribute is stored and synced for Notification +objects and can be queried via REST API. + +Example for retrieving the notification object and result from all `disk` services using a +[regex match](18-library-reference.md#global-functions-regex) on the name: + +``` +$ curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Override: GET' -X POST 'https://localhost:5665/v1/objects/notifications' \ +-d '{ "filter": "regex(pattern, service.name)", "filter_vars": { "pattern": "^disk" }, "attrs": [ "__name", "last_notification_result" ], "pretty": true }' +{ + "results": [ + + { + "attrs": { + "last_notification_result": { + "active": true, + "command": [ + "/etc/icinga2/scripts/mail-service-notification.sh", + "-4", + "", + "-6", + "", + "-b", + "", + "-c", + "", + "-d", + "2019-08-02 10:54:16 +0200", + "-e", + "disk", + "-l", + "icinga2-agent1.localdomain", + "-n", + "icinga2-agent1.localdomain", + "-o", + "DISK OK - free space: / 38108 MB (90.84% inode=100%);", + "-r", + "user@localdomain", + "-s", + "OK", + "-t", + "RECOVERY", + "-u", + "disk" + ], + "execution_end": 1564736056.186217, + "execution_endpoint": "icinga2-master1.localdomain", + "execution_start": 1564736056.132323, + "exit_status": 0.0, + "output": "", + "type": "NotificationResult" + } + }, + "joins": {}, + "meta": {}, + "name": "icinga2-agent1.localdomain!disk!mail-service-notification", + "type": "Notification" + } + +... + + ] +} +``` + +Example with the debug console: + +``` +$ ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://root@localhost:5665/' --eval 'get_object(Notification, "icinga2-agent1.localdomain!disk!mail-service-notification").last_notification_result.execution_endpoint' | jq + +"icinga2-agent1.localdomain" +``` + +Whenever a notification command failed to execute, you can fetch the output as well. + + ## Feature Troubleshooting ### Feature is not working * Make sure that the feature configuration is enabled by symlinking from `features-available/` -to `features-enabled` and that the latter is included in [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf). +to `features-enabled` and that the latter is included in [icinga2.conf](04-configuration.md#icinga2-conf). * Are the feature attributes set correctly according to the documentation? * Any errors on the logs? @@ -743,7 +898,7 @@ $ curl -k -s -u root:icinga -H 'Accept: application/json' -X DELETE 'https://loc } ``` -## REST API Troubleshooting: No Objects Found +### REST API Troubleshooting: No Objects Found Please note that the `404` status with no objects being found can also originate from missing or too strict object permissions for the authenticated user. @@ -757,33 +912,137 @@ In order to analyse and fix the problem, please check the following: - use an administrative account with full permissions to check whether the objects are actually there. - verify the permissions on the affected ApiUser object and fix them. +### Missing Runtime Objects (Hosts, Downtimes, etc.) + +Runtime objects consume the internal config packages shared with +the REST API config packages. Each host, downtime, comment, service, etc. created +via the REST API is stored in the `_api` package. + +This includes downtimes and comments, which where sometimes stored in the wrong +directory path, because the active-stage file was empty/truncated/unreadable at +this point. + +Wrong: + +``` +/var/lib/icinga2/api/packages/_api//conf.d/downtimes/1234-5678-9012-3456.conf +``` + +Correct: + +``` +/var/lib/icinga2/api/packages/_api/dbe0bef8-c72c-4cc9-9779-da7c4527c5b2/conf.d/downtimes/1234-5678-9012-3456.conf +``` + +At creation time, the object lives in memory but its storage is broken. Upon restart, +it is missing and e.g. a missing downtime will re-enable unwanted notifications. + +`abcd-ef12-3456-7890` is the active stage name which wasn't correctly +read by the Icinga daemon. This information is stored in `/var/lib/icinga2/api/packages/_api/active-stage`. + +2.11 now limits the direct active-stage file access (this is hidden from the user), +and caches active stages for packages in-memory. + +It also tries to repair the broken package, and logs a new message: + +``` +systemctl restart icinga2 + +tail -f /var/log/icinga2/icinga2.log + +[2019-05-10 12:27:15 +0200] information/ConfigObjectUtility: Repairing config package '_api' with stage 'dbe0bef8-c72c-4cc9-9779-da7c4527c5b2'. +``` + +If this does not happen, you can manually fix the broken config package, and mark a deployed stage as active +again, carefully do the following steps with creating a backup before: + +Navigate into the API package prefix. + +``` +cd /var/lib/icinga2/api/packages +``` + +Change into the broken package directory and list all directories and files +ordered by latest changes. + +``` +cd _api +ls -lahtr + +drwx------ 4 michi wheel 128B Mar 27 14:39 .. +-rw-r--r-- 1 michi wheel 25B Mar 27 14:39 include.conf +-rw-r--r-- 1 michi wheel 405B Mar 27 14:39 active.conf +drwx------ 7 michi wheel 224B Mar 27 15:01 dbe0bef8-c72c-4cc9-9779-da7c4527c5b2 +drwx------ 5 michi wheel 160B Apr 26 12:47 . +``` + +As you can see, the `active-stage` file is missing. When it is there, verify that its content +is set to the stage directory as follows. + +If you have more than one stage directory here, pick the latest modified +directory. Copy the directory name `abcd-ef12-3456-7890` and +add it into a new file `active-stage`. This can be done like this: + +``` +echo "dbe0bef8-c72c-4cc9-9779-da7c4527c5b2" > active-stage +``` + +`active.conf` needs to have the correct active stage too, add it again +like this. Note: This is deep down in the code, use with care! + +``` +sed -i 's/ActiveStages\["_api"\] = .*/ActiveStages\["_api"\] = "dbe0bef8-c72c-4cc9-9779-da7c4527c5b2"/g' /var/lib/icinga2/api/packages/_api/active.conf +``` + +Restart Icinga 2. + +``` +systemctl restart icinga2 +``` + + +> **Note** +> +> The internal `_api` config package structure may change in the future. Do not modify +> things in there manually or with scripts unless guided here or asked by a developer. + ## Certificate Troubleshooting +Tools for analysing certificates and TLS connections: + +- `openssl` binary on Linux/Unix, `openssl.exe` on Windows ([download](https://slproweb.com/products/Win32OpenSSL.html)) +- `sslscan` tool, available [here](https://github.com/rbsec/sslscan) (Linux/Windows) + +Note: You can also execute sslscan on Windows using Powershell. + + ### Certificate Verification -If the TLS handshake fails when a client connects to the cluster or the REST API, +Whenever the TLS handshake fails when a client connects to the cluster or the REST API, ensure to verify the used certificates. Print the CA and client certificate and ensure that the following attributes are set: * Version must be 3. * Serial number is a hex-encoded string. -* Issuer should be your certificate authority (defaults to `Icinga CA` for all CLI commands). -* Validity, meaning to say the certificate is not expired. +* Issuer should be your certificate authority (defaults to `Icinga CA` for all certificates generated by CLI commands and automated signing requests). +* Validity: The certificate must not be expired. * Subject with the common name (CN) matches the client endpoint name and its FQDN. * v3 extensions must set the basic constraint for `CA:TRUE` (ca.crt) or `CA:FALSE` (client certificate). -* Subject Alternative Name is set to a proper DNS name (required for REST API and browsers). +* Subject Alternative Name is set to the resolvable DNS name (required for REST API and browsers). +Navigate into the local certificate store: + ``` -# cd /var/lib/icinga2/certs/ +$ cd /var/lib/icinga2/certs/ ``` -CA certificate: +Print the CA certificate: ``` -# openssl x509 -in ca.crt -text +$ openssl x509 -in ca.crt -text Certificate: Data: @@ -808,10 +1067,10 @@ Certificate: ... ``` -Client public certificate: +Print the client public certificate: ``` -# openssl x509 -in icinga2-client1.localdomain.crt -text +$ openssl x509 -in icinga2-agent1.localdomain.crt -text Certificate: Data: @@ -823,7 +1082,7 @@ Certificate: Validity Not Before: Aug 20 16:20:05 2016 GMT Not After : Aug 17 16:20:05 2031 GMT - Subject: CN=icinga2-client1.localdomain + Subject: CN=icinga2-agent1.localdomain Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (4096 bit) @@ -834,7 +1093,7 @@ Certificate: X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Alternative Name: - DNS:icinga2-client1.localdomain + DNS:icinga2-agent1.localdomain Signature Algorithm: sha256WithRSAEncryption ... ``` @@ -843,46 +1102,182 @@ Make sure to verify the client's certificate and its received `ca.crt` in `/var/ both instances are signed by the **same CA**. ``` -# openssl verify -verbose -CAfile /var/lib/icinga2/certs/ca.crt /var/lib/icinga2/certs/icinga2-master1.localdomain.crt -icinga2-master1.localdomain.crt: OK +$ openssl verify -verbose -CAfile /var/lib/icinga2/certs/ca.crt /var/lib/icinga2/certs/icinga2-master1.localdomain.crt -# openssl verify -verbose -CAfile /var/lib/icinga2/certs/ca.crt /var/lib/icinga2/certs/icinga2-client1.localdomain.crt -icinga2-client1.localdomain.crt: OK +icinga2-master1.localdomain.crt: OK +``` + +``` +$ openssl verify -verbose -CAfile /var/lib/icinga2/certs/ca.crt /var/lib/icinga2/certs/icinga2-agent1.localdomain.crt + +icinga2-agent1.localdomain.crt: OK ``` Fetch the `ca.crt` file from the client node and compare it to your master's `ca.crt` file: ``` -# scp icinga2-client1:/var/lib/icinga2/certs/ca.crt test-client-ca.crt -# diff -ur /var/lib/icinga2/certs/ca.crt test-client-ca.crt +$ scp icinga2-agent1:/var/lib/icinga2/certs/ca.crt test-client-ca.crt +$ diff -ur /var/lib/icinga2/certs/ca.crt test-client-ca.crt ``` -On SLES11 you'll need to use the `openssl1` command instead of `openssl`. - - + +Icinga offers two methods: + +* [CSR Auto-Signing](06-distributed-monitoring.md#distributed-monitoring-setup-csr-auto-signing) which uses a client (an agent or a satellite) ticket generated on the master as trust identifier. +* [On-Demand CSR Signing](06-distributed-monitoring.md#distributed-monitoring-setup-on-demand-csr-signing) which allows to sign pending certificate requests on the master. + +Whenever a signed certificate is not received on the requesting clients, ensure to check the following: + +* The ticket was valid and the master's log shows nothing different (CSR Auto-Signing only) +* If the agent/satellite is directly connected to the CA master, check whether the master actually has performance problems to process the request. If the connection is closed without certificate response, analyse the master's health. It is also advised to upgrade to v2.11 where network stack problems have been fixed. +* If you're using a 3+ level cluster, check whether the satellite really forwarded the CSR signing request and the master processed it. + +Other common errors: + +* The generated ticket is invalid. The client receives this error message, as well as the master logs a warning message. +* The [api](09-object-types.md#objecttype-apilistener) feature does not have the `ticket_salt` attribute set to the generated `TicketSalt` constant by the CLI wizards. + +In case you are using On-Demand CSR Signing, `icinga2 ca list` on the master only lists +pending requests since v2.11. Add `--all` to also see signed requests. Keep in mind that +old requests are purged after 1 week automatically. -### Certificate Problems with OpenSSL 1.1.0 +### TLS Handshake: Ciphers -Users have reported problems with SSL certificates inside a distributed monitoring setup when they +Starting with v2.11, the default configured ciphers have been hardened to modern +standards. This includes TLS v1.2 as minimum protocol version too. -* updated their Icinga 2 package to 2.7.0 on Windows or -* upgraded their distribution which included an update to OpenSSL 1.1.0. +In case the TLS handshake fails with `no shared cipher`, first analyse whether both +instances support the same ciphers. -Example during startup on a Windows client: +#### Client connects to Server + +Connect using `openssl s_client` and try to reproduce the connection problem. + +> **Important** +> +> The endpoint with the server role **accepting** the connection picks the preferred +> cipher. E.g. when a satellite connects to the master, the master chooses the cipher. +> +> Keep this in mind where to simulate the client role connecting to a server with +> CLI tools such as `openssl s_client`. + + +`openssl s_client` tells you about the supported and shared cipher suites +on the remote server. `openssl ciphers` lists locally available ciphers. ``` -critical/SSL: Error loading and verifying locations in ca key file 'C:\ProgramData\icinga2\etc/icinga2/pki/ca.crt': 219029726, "error:0D0E20DE:asn1 encoding routines:c2i_ibuf:illegal zero content" -critical/config: Error: Cannot make SSL context for cert path: 'C:\ProgramData\icinga2\etc/icinga2/pki/client.crt' key path: 'C:\ProgramData\icinga2\etc/icinga2/pki/client.key' ca path: 'C:\ProgramData\icinga2\etc/icinga2/pki/ca.crt'. +$ openssl s_client -connect 192.168.33.5:5665 +... + +--- +SSL handshake has read 2899 bytes and written 786 bytes +--- +New, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384 +Server public key is 4096 bit +Secure Renegotiation IS supported +Compression: NONE +Expansion: NONE +No ALPN negotiated +SSL-Session: + Protocol : TLSv1.2 + Cipher : AES256-GCM-SHA384 + +... ``` -A technical analysis and solution for re-creating the public CA certificate is -available in [this advisory](https://www.icinga.com/2017/08/30/advisory-for-ssl-problems-with-leading-zeros-on-openssl-1-1-0/). +You can specifically use one cipher or a list with the `-cipher` parameter: + +``` +openssl s_client -connect 192.168.33.5:5665 -cipher 'ECDHE-RSA-AES256-GCM-SHA384' +``` + +In order to fully simulate a connecting client, provide the certificates too: + +``` +CERTPATH='/var/lib/icinga2/certs' +HOSTNAME='icinga2.vagrant.demo.icinga.com' +openssl s_client -connect 192.168.33.5:5665 -cert "${CERTPATH}/${HOSTNAME}.crt" -key "${CERTPATH}/${HOSTNAME}.key" -CAfile "${CERTPATH}/ca.crt" -cipher 'ECDHE-RSA-AES256-GCM-SHA384' +``` + +In case to need to change the default cipher list, +set the [cipher_list](09-object-types.md#objecttype-apilistener) attribute +in the `api` feature configuration accordingly. + +Beware of using insecure ciphers, this may become a +security risk in your organisation. + +#### Server Accepts Client + +If the master node does not actively connect to the satellite/agent node(s), but instead +the child node actively connectsm, you can still simulate a TLS handshake. + +Use `openssl s_server` instead of `openssl s_client` on the master during the connection +attempt. + +``` +$ openssl s_server -connect 192.168.56.101:5665 +``` + +Since the server role chooses the preferred cipher suite in Icinga, +you can test-drive the "agent connects to master" mode here, granted that +the TCP connection is not blocked by the firewall. -## Cluster and Clients Troubleshooting +#### Cipher Scan Tools + +You can also use different tools to test the available cipher suites, this is what SSL Labs, etc. +provide for TLS enabled websites as well. [This post](https://superuser.com/questions/109213/how-do-i-list-the-ssl-tls-cipher-suites-a-particular-website-offers) +highlights some tools and scripts such as [sslscan](https://github.com/rbsec/sslscan) or [testssl.sh](https://github.com/drwetter/testssl.sh/) + +Example for sslscan on macOS against a Debian 10 Buster instance +running v2.11: + +``` +$ brew install sslscan + +$ sslscan 192.168.33.22:5665 +Version: 1.11.13-static +OpenSSL 1.0.2f 28 Jan 2016 + +Connected to 192.168.33.22 + +Testing SSL server 192.168.33.22 on port 5665 using SNI name 192.168.33.22 + + TLS Fallback SCSV: +Server supports TLS Fallback SCSV + + TLS renegotiation: +Session renegotiation not supported + + TLS Compression: +Compression disabled + + Heartbleed: +TLS 1.2 not vulnerable to heartbleed +TLS 1.1 not vulnerable to heartbleed +TLS 1.0 not vulnerable to heartbleed + + Supported Server Cipher(s): +Preferred TLSv1.2 256 bits ECDHE-RSA-AES256-GCM-SHA384 Curve P-256 DHE 256 +Accepted TLSv1.2 128 bits ECDHE-RSA-AES128-GCM-SHA256 Curve P-256 DHE 256 +Accepted TLSv1.2 256 bits ECDHE-RSA-AES256-SHA384 Curve P-256 DHE 256 +Accepted TLSv1.2 128 bits ECDHE-RSA-AES128-SHA256 Curve P-256 DHE 256 + + SSL Certificate: +Signature Algorithm: sha256WithRSAEncryption +RSA Key Strength: 4096 + +Subject: icinga2-debian10.vagrant.demo.icinga.com +Altnames: DNS:icinga2-debian10.vagrant.demo.icinga.com +Issuer: Icinga CA + +Not valid before: Jul 12 07:39:55 2019 GMT +Not valid after: Jul 8 07:39:55 2034 GMT +``` + +## Distributed Troubleshooting This applies to any Icinga 2 node in a [distributed monitoring setup](06-distributed-monitoring.md#distributed-monitoring-scenarios). @@ -910,24 +1305,24 @@ works (default port is `5665`). # netstat -tulpen | grep icinga -# nmap icinga2-client1.localdomain +# nmap icinga2-agent1.localdomain ``` -### Cluster Troubleshooting SSL Errors +### Cluster Troubleshooting TLS Errors -If the cluster communication fails with SSL error messages, make sure to check +If the cluster communication fails with TLS/SSL error messages, make sure to check the following -* File permissions on the SSL certificate files +* File permissions on the TLS certificate files * Does the used CA match for all cluster endpoints? * Verify the `Issuer` being your trusted CA * Verify the `Subject` containing your endpoint's common name (CN) * Check the validity of the certificate itself -Try to manually connect from `icinga2-client1.localdomain` to the master node `icinga2-master1.localdomain`: +Try to manually connect from `icinga2-agent1.localdomain` to the master node `icinga2-master1.localdomain`: ``` -# openssl s_client -CAfile /var/lib/icinga2/certs/ca.crt -cert /var/lib/icinga2/certs/icinga2-client1.localdomain.crt -key /var/lib/icinga2/certs/icinga2-client1.localdomain.key -connect icinga2-master1.localdomain:5665 +$ openssl s_client -CAfile /var/lib/icinga2/certs/ca.crt -cert /var/lib/icinga2/certs/icinga2-agent1.localdomain.crt -key /var/lib/icinga2/certs/icinga2-agent1.localdomain.key -connect icinga2-master1.localdomain:5665 CONNECTED(00000003) --- @@ -939,21 +1334,22 @@ If the connection attempt fails or your CA does not match, [verify the certifica #### Cluster Troubleshooting Unauthenticated Clients -Unauthenticated nodes are able to connect. This is required for client setups. +Unauthenticated nodes are able to connect. This is required for agent/satellite setups. Master: ``` -[2015-07-13 18:29:25 +0200] information/ApiListener: New client connection for identity 'icinga2-client1.localdomain' (unauthenticated) +[2015-07-13 18:29:25 +0200] information/ApiListener: New client connection for identity 'icinga2-agent1.localdomain' (unauthenticated) ``` -Client as command execution bridge: +Agent as command execution bridge: ``` [2015-07-13 18:29:26 +1000] notice/ClusterEvents: Discarding 'execute command' message from 'icinga2-master1.localdomain': Invalid endpoint origin (client not allowed). ``` -If these messages do not go away, make sure to [verify the master and client certificates](15-troubleshooting.md#troubleshooting-certificate-verification). +If these messages do not go away, make sure to [verify the master and agent certificates](15-troubleshooting.md#troubleshooting-certificate-verification). + ### Cluster Troubleshooting Message Errors @@ -967,21 +1363,25 @@ for later synchronisation, you should make sure to check why the network connect Ensure to setup [cluster health checks](06-distributed-monitoring.md#distributed-monitoring-health-checks) to monitor all endpoints and zones connectivity. + ### Cluster Troubleshooting Command Endpoint Errors -Command endpoints can be used [for clients](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint) +Command endpoints can be used [for agents](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint) as well as inside an [High-Availability cluster](06-distributed-monitoring.md#distributed-monitoring-scenarios). -There is no cli command for manually executing the check, but you can verify +There is no CLI command for manually executing the check, but you can verify the following (e.g. by invoking a forced check from the web interface): -* `/var/log/icinga2/icinga2.log` contains connection and execution errors. - * The ApiListener is not enabled to [accept commands](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint). - * `CheckCommand` definition not found on the remote client. - * Referenced check plugin not found on the remote client. +* `/var/log/icinga2/icinga2.log` shows connection and execution errors. + * The ApiListener is not enabled to [accept commands](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint). This is visible as `UNKNOWN` check result output. + * `CheckCommand` definition not found on the remote client. This is visible as `UNKNWON` check result output. + * Referenced check plugin not found on the remote agent. * Runtime warnings and errors, e.g. unresolved runtime macros or configuration problems. * Specific error messages are also populated into `UNKNOWN` check results including a detailed error message in their output. -* Verify the `check_source` object attribute. This is populated by the node executing the check. +* Verify the [check source](15-troubleshooting.md#checks-check-source). This is populated by the node executing the check. You can see that in Icinga Web's detail view or by querying the REST API for this checkable object. + +Additional tasks: + * More verbose logs are found inside the [debug log](15-troubleshooting.md#troubleshooting-enable-debug-output). * Use the Icinga 2 API [event streams](12-icinga2-api.md#icinga2-api-event-streams) to receive live check result streams. @@ -996,16 +1396,93 @@ $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://local ### Cluster Troubleshooting Config Sync -If the cluster zones do not sync their configuration, make sure to check the following: +In order to troubleshoot this, remember the key things with the config sync: * Within a config master zone, only one configuration master is allowed to have its config in `/etc/icinga2/zones.d`. -** The master syncs the configuration to `/var/lib/icinga2/api/zones/` during startup and only syncs valid configuration to the other nodes. -** The other nodes receive the configuration into `/var/lib/icinga2/api/zones/`. -* The `icinga2.log` log file in `/var/log/icinga2` will indicate whether this ApiListener -[accepts config](06-distributed-monitoring.md#distributed-monitoring-top-down-config-sync), or not. + * The config master copies the zone configuration from `/etc/icinga2/zones.d` to `/var/lib/icinga2/api/zones`. This storage is the same for all cluster endpoints, and the source for all config syncs. + * The config master puts the `.authoritative` marker on these zone files locally. This is to ensure that it doesn't receive config updates from other endpoints. If you have copied the content from `/var/lib/icinga2/api/zones` to another node, ensure to remove them. +* During startup, the master validates the entire configuration and only syncs valid configuration to other zone endpoints. -Verify the object's [version](09-object-types.md#object-types) attribute on all nodes to -check whether the config update and reload was successful or not. +Satellites/Agents < 2.11 store the received configuration directly in `/var/lib/icinga2/api/zones`, validating it and reloading the daemon. +Satellites/Agents >= 2.11 put the received configuration into the staging directory `/var/lib/icinga2/api/zones-stage` first, and will only copy this to the production directory `/var/lib/icinga2/api/zones` once the validation was successful. + +The configuration sync logs the operations during startup with the `information` severity level. Received zone configuration is also logged. + +Typical errors are: + +* The api feature doesn't [accept config](06-distributed-monitoring.md#distributed-monitoring-top-down-config-sync). This is logged into `/var/lib/icinga2/icinga2.log`. +* The received configuration zone is not configured in [zones.conf](04-configuration.md#zones-conf) and Icinga denies it. This is logged into `/var/lib/icinga2/icinga2.log`. +* The satellite/agent has local configuration in `/etc/icinga2/zones.d` and thinks it is authoritive for this zone. It then denies the received update. Purge the content from `/etc/icinga2/zones.d`, `/var/lib/icinga2/api/zones/*` and restart Icinga to fix this. + +#### New configuration does not trigger a reload + +The debug/notice log dumps the calculated checksums for all files and the comparison. Analyse this to troubleshoot further. + +A complete sync for the `director-global` global zone can look like this: + +``` +[2019-08-01 09:20:25 +0200] notice/JsonRpcConnection: Received 'config::Update' message from 'icinga2-master1.localdomain' +[2019-08-01 09:20:25 +0200] information/ApiListener: Applying config update from endpoint 'icinga2-master1.localdomain' of zone 'master'. +[2019-08-01 09:20:25 +0200] notice/ApiListener: Creating config update for file '/var/lib/icinga2/api/zones/director-global/.checksums'. +[2019-08-01 09:20:25 +0200] notice/ApiListener: Creating config update for file '/var/lib/icinga2/api/zones/director-global/.timestamp'. +[2019-08-01 09:20:25 +0200] notice/ApiListener: Creating config update for file '/var/lib/icinga2/api/zones/director-global/director/001-director-basics.conf'. +[2019-08-01 09:20:25 +0200] notice/ApiListener: Creating config update for file '/var/lib/icinga2/api/zones/director-global/director/host_templates.conf'. +[2019-08-01 09:20:25 +0200] information/ApiListener: Received configuration for zone 'director-global' from endpoint 'icinga2-master1.localdomain'. Comparing the checksums. +[2019-08-01 09:20:25 +0200] debug/ApiListener: Checking for config change between stage and production. Old (4): '{"/.checksums":"c4dd1237e36dcad9142f4d9a81324a7cae7d01543a672299 +b8c1bb08b629b7d1","/.timestamp":"f21c0e6551328812d9f5176e5e31f390de0d431d09800a85385630727b404d83","/director/001-director-basics.conf":"f86583eec81c9bf3a1823a761991fb53d640bd0dc +6cd12bf8c5e6a275359970f","/director/host_templates.conf":"831e9b7e3ec1e33288e56a51e63c688da1d6316155349382a101f7fce6229ecc"}' vs. new (4): '{"/.checksums":"c4dd1237e36dcad9142f4d +9a81324a7cae7d01543a672299b8c1bb08b629b7d1","/.timestamp":"f21c0e6551328812d9f5176e5e31f390de0d431d09800a85385630727b404d83","/director/001-director-basics.conf":"f86583eec81c9bf +3a1823a761991fb53d640bd0dc6cd12bf8c5e6a275359970f","/director/host_templates.conf":"831e9b7e3ec1e33288e56a51e63c688da1d6316155349382a101f7fce6229ecc"}'. +[2019-08-01 09:20:25 +0200] debug/ApiListener: Ignoring old internal file '/.checksums'. +[2019-08-01 09:20:25 +0200] debug/ApiListener: Ignoring old internal file '/.timestamp'. +[2019-08-01 09:20:25 +0200] debug/ApiListener: Checking /director/001-director-basics.conf for old checksum: f86583eec81c9bf3a1823a761991fb53d640bd0dc6cd12bf8c5e6a275359970f. +[2019-08-01 09:20:25 +0200] debug/ApiListener: Checking /director/host_templates.conf for old checksum: 831e9b7e3ec1e33288e56a51e63c688da1d6316155349382a101f7fce6229ecc. +[2019-08-01 09:20:25 +0200] debug/ApiListener: Ignoring new internal file '/.checksums'. +[2019-08-01 09:20:25 +0200] debug/ApiListener: Ignoring new internal file '/.timestamp'. +[2019-08-01 09:20:25 +0200] debug/ApiListener: Checking /director/001-director-basics.conf for new checksum: f86583eec81c9bf3a1823a761991fb53d640bd0dc6cd12bf8c5e6a275359970f. +[2019-08-01 09:20:25 +0200] debug/ApiListener: Checking /director/host_templates.conf for new checksum: 831e9b7e3ec1e33288e56a51e63c688da1d6316155349382a101f7fce6229ecc. +[2019-08-01 09:20:25 +0200] information/ApiListener: Stage: Updating received configuration file '/var/lib/icinga2/api/zones-stage/director-global//director/001-director-basics.c +onf' for zone 'director-global'. +[2019-08-01 09:20:25 +0200] information/ApiListener: Stage: Updating received configuration file '/var/lib/icinga2/api/zones-stage/director-global//director/host_templates.conf' +for zone 'director-global'. +[2019-08-01 09:20:25 +0200] information/ApiListener: Applying configuration file update for path '/var/lib/icinga2/api/zones-stage/director-global' (2209 Bytes). + +... + +[2019-08-01 09:20:25 +0200] information/ApiListener: Received configuration updates (4) from endpoint 'icinga2-master1.localdomain' are different to production, triggering validation and reload. +[2019-08-01 09:20:25 +0200] notice/Process: Running command '/usr/lib/x86_64-linux-gnu/icinga2/sbin/icinga2' '--no-stack-rlimit' 'daemon' '--close-stdio' '-e' '/var/log/icinga2/e +rror.log' '--validate' '--define' 'System.ZonesStageVarDir=/var/lib/icinga2/api/zones-stage/': PID 4532 +[2019-08-01 09:20:25 +0200] notice/Process: PID 4532 ('/usr/lib/x86_64-linux-gnu/icinga2/sbin/icinga2' '--no-stack-rlimit' 'daemon' '--close-stdio' '-e' '/var/log/icinga2/error.l +og' '--validate' '--define' 'System.ZonesStageVarDir=/var/lib/icinga2/api/zones-stage/') terminated with exit code 0 +[2019-08-01 09:20:25 +0200] information/ApiListener: Config validation for stage '/var/lib/icinga2/api/zones-stage/' was OK, replacing into '/var/lib/icinga2/api/zones/' and trig +gering reload. +[2019-08-01 09:20:26 +0200] information/ApiListener: Copying file 'director-global//.checksums' from config sync staging to production zones directory. +[2019-08-01 09:20:26 +0200] information/ApiListener: Copying file 'director-global//.timestamp' from config sync staging to production zones directory. +[2019-08-01 09:20:26 +0200] information/ApiListener: Copying file 'director-global//director/001-director-basics.conf' from config sync staging to production zones directory. +[2019-08-01 09:20:26 +0200] information/ApiListener: Copying file 'director-global//director/host_templates.conf' from config sync staging to production zones directory. + +... + +[2019-08-01 09:20:26 +0200] notice/Application: Got reload command, forwarding to umbrella process (PID 4236) +``` + +#### Syncing Binary Files is Denied + +The config sync is built for syncing text configuration files, wrapped into JSON-RPC messages. +Some users have started to use this as binary file sync instead of using tools built for this: +rsync, git, Puppet, Ansible, etc. + +Starting with 2.11, this attempt is now prohibited and logged. + +``` +[2019-08-02 16:03:19 +0200] critical/ApiListener: Ignoring file '/etc/icinga2/zones.d/global-templates/forbidden.exe' for cluster config sync: Does not contain valid UTF8. Binary files are not supported. +Context: + (0) Creating config update for file '/etc/icinga2/zones.d/global-templates/forbidden.exe' + (1) Activating object 'api' of type 'ApiListener' +``` + +In order to solve this problem, remove the mentioned files from `zones.d` and use an alternate way +of syncing plugin binaries to your satellites and agents. ### Cluster Troubleshooting Overdue Check Results @@ -1024,7 +1501,7 @@ certificate's CN, the master will deny all events. > **Tip** > -> [Icinga Web 2](02-getting-started.md#setting-up-icingaweb2) provides a dashboard view +> [Icinga Web 2](02-installation.md#setting-up-icingaweb2) provides a dashboard view > for overdue check results. Enable the [debug log](15-troubleshooting.md#troubleshooting-enable-debug-output) on the master @@ -1035,14 +1512,14 @@ If the client cannot authenticate, it's a more general [problem](15-troubleshoot The client's endpoint is not configured on nor trusted by the master node: ``` -Discarding 'check result' message from 'icinga2-client1.localdomain': Invalid endpoint origin (client not allowed). +Discarding 'check result' message from 'icinga2-agent1.localdomain': Invalid endpoint origin (client not allowed). ``` The check result message sent by the client does not belong to the zone the checkable object is in on the master: ``` -Discarding 'check result' message from 'icinga2-client1.localdomain': Unauthorized access. +Discarding 'check result' message from 'icinga2-agent1.localdomain': Unauthorized access. ``` @@ -1058,3 +1535,93 @@ Check the following: * Check your [connection](15-troubleshooting.md#troubleshooting-cluster-connection-errors) in general. * Does the log replay work, e.g. are all events processed and the directory gets cleared up over time? * Decrease the `log_duration` attribute value for that specific [endpoint](09-object-types.md#objecttype-endpoint). + +The cluster health checks also measure the `slave_lag` metric. Use this data to correlate +graphs with other events (e.g. disk I/O, network problems, etc). + + +### Cluster Troubleshooting: Windows Agents + + +#### Windows Service Exe Path + +Icinga agents can be installed either as x86 or x64 package. If you enable features, or wonder why +logs are not written, the first step is to analyse which path the Windows service `icinga2` is using. + +Start a new administrative Powershell and ensure that the `icinga2` service is running. + +``` +C:\Program Files\ICINGA2\sbin> net start icinga2 +``` + +Use the `Get-WmiObject` function to extract the windows service and its path name. + +``` +C:\Program Files\ICINGA2\sbin> Get-WmiObject win32_service | ?{$_.Name -like '*icinga*'} | select Name, DisplayName, State, PathName + +Name DisplayName State PathName +---- ----------- ----- -------- +icinga2 Icinga 2 Running "C:\Program Files\ICINGA2\sbin\icinga2.exe" --scm "daemon" +``` + +If you have used the `icinga2.exe` from a different path to enable e.g. the `debuglog` feature, +navigate into `C:\Program Files\ICINGA2\sbin\` and use the correct exe to control the feature set. + + +#### Windows Agents consuming 100% CPU + +> **Note** +> +> The network stack was rewritten in 2.11. This fixes several hanging connections and threads +> on older Windows agents and master/satellite nodes. Prior to testing the below, plan an upgrade. + +Icinga 2 requires the `NodeName` [constant](17-language-reference.md#constants) in various places to run. +This includes loading the TLS certificates, setting the proper check source, +and so on. + +Typically the Windows setup wizard and also the CLI commands populate the [constants.conf](04-configuration.md#constants-conf) +file with the auto-detected or user-provided FQDN/Common Name. + +If this constant is not set during startup, Icinga will try to resolve the +FQDN, if that fails, fetch the hostname. If everything fails, it logs +an error and sets this to `localhost`. This results in undefined behaviour +if ignored by the admin. + +Querying the DNS when not reachable is CPU consuming, and may look like Icinga +is doing lots of checks, etc. but actually really is just starting up. + +In order to fix this, edit the `constants.conf` file and populate +the `NodeName` constant with the FQDN. Ensure this is the same value +as the local endpoint object name. + +``` +const NodeName = "windows-agent1.domain.com" +``` + + + +#### Windows blocking Icinga 2 with ephemeral port range + +When you see a message like this in your Windows agent logs: + +``` +critical/TcpSocket: Invalid socket: 10055, "An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full." +``` + +Windows is blocking Icinga 2 and as such, no more TCP connection handling is possible. + +Depending on the version, patch level and installed applications, Windows is changing its +range of [ephemeral ports](https://en.wikipedia.org/wiki/Ephemeral_port#Range). + +In order to solve this, raise the the `MaxUserPort` value in the registry. + +``` +HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters + +Value Name: MaxUserPort Value +Type: DWORD +Value data: 65534 +``` + +More details in [this blogpost](https://www.netways.de/blog/2019/01/24/windows-blocking-icinga-2-with-ephemeral-port-range/) +and this [MS help entry](https://support.microsoft.com/en-us/help/196271/when-you-try-to-connect-from-tcp-ports-greater-than-5000-you-receive-t). diff --git a/doc/16-upgrading-icinga-2.md b/doc/16-upgrading-icinga-2.md index 2b8d486bf..88c7d8284 100644 --- a/doc/16-upgrading-icinga-2.md +++ b/doc/16-upgrading-icinga-2.md @@ -1,22 +1,541 @@ # Upgrading Icinga 2 -Upgrading Icinga 2 is usually quite straightforward. Ordinarily the only manual steps involved +Upgrading Icinga 2 is usually quite straightforward. +Ordinarily the only manual steps involved are scheme updates for the IDO database. Specific version upgrades are described below. Please note that version updates are incremental. An upgrade from v2.6 to v2.8 requires to follow the instructions for v2.7 too. +## Upgrading to v2.11 + +### Packages + +EOL distributions where no packages are available with this release: + +* SLES 11 +* Ubuntu 14 LTS +* RHEL/CentOS 6 x86 + +Raspbian Packages are available inside the `icinga-buster` repository +on [https://packages.icinga.com](http://packages.icinga.com/raspbian/). +Please note that Stretch is not supported suffering from compiler +regressions. Upgrade to Raspbian Buster is highly recommended. + +#### Added: Boost 1.66+ + +The rewrite of our core network stack for cluster and REST API +requires newer Boost versions, specifically >= 1.66. For technical +details, please continue reading in [this issue](https://github.com/Icinga/icinga2/issues/7041). + +The package dependencies have been updated for RPM/DEB already. +On platforms where EPEL or Backports cannot satisfy this dependency, +we provide Boost as package on our [package repository](https://packages.icinga.com): + +* SLES 12 (this replaces the SDK requirement) +* CentOS 6 x64 +* Debian Jessie +* Ubuntu Xenial/Bionic + +After upgrade, you may remove the old Boost packages (1.53 or anything above) +if you don't need them anymore. + +#### Added: .NET Framework 4.6 + +We modernized the graphical Windows wizard to use the more recent .NET Framework 4.6. This requires that Windows versions +older than Windows 10/Windows Server 2016 installs at least [.NET Framework 4.6](https://www.microsoft.com/en-US/download/details.aspx?id=53344). Starting with Windows 10/Windows Server 2016 a .NET Framework 4.6 or higher is installed by default. + +The MSI-Installer package checks if the .NET Framework 4.6 or higher is present, if not the installation wizard will abort with an error message telling you to install at least .NET Framework 4.6. + +#### Removed: YAJL + +Our JSON library, namely [YAJL](https://github.com/lloyd/yajl), isn't maintained anymore +and may cause [crashes](https://github.com/Icinga/icinga2/issues/6684). + +It is replaced by [JSON for Modern C++](https://github.com/nlohmann/json) by Niels Lohmann +and compiled into the binary as header only include. It helps our way to C++11 and allows +to fix additional UTF8 issues more easily. Read more about its [design goals](https://github.com/nlohmann/json#design-goals) +and [benchmarks](https://github.com/miloyip/nativejson-benchmark#parsing-time). + +### Core + +#### Reload Handling + +2.11 provides fixes for unwanted notifications during restarts. +The updated systemd service file now uses the `KillMode=mixed` setting. + +The reload handling was improved with an umbrella process, which means +that normal runtime operations include **3 processes**. You may need to +adjust the local instance monitoring of the [procs](08-advanced-topics.md#monitoring-icinga) check. + +More details can be found in the [technical concepts](19-technical-concepts.md#technical-concepts-core-reload) chapter. + +#### Downtime Notifications + +Imagine that a host/service changes to a HARD NOT-OK state, +and its check interval is set to a high interval e.g. 1 hour. + +A maintenance downtime prevents the notification being sent, +but once it ends and the host/service is still in a downtime, +no immediate notification is re-sent but you'll have to wait +for the next check. + +Another scenario is with one-shot notifications (interval=0) +which would never notify again after the downtime ends and +the problem state being intact. The state change logic requires +to recover and become HARD NOT-OK to notify again. + +In order to solve these problems with filtered/suppressed notifications +in downtimes, v2.11 changes the behaviour like this: + +- If there was a notification suppressed in a downtime, the core stores that information +- Once the downtime ends and the problem state is still intact, Icinga checks whether a re-notification should be sent immediately + +A new cluster message was added to keep this in sync amongst HA masters. + +> **Important** +> +> In order to properly use this new feature, all involved endpoints +> must be upgraded to v2.11. + +### Network Stack + +The core network stack has been rewritten in 2.11 (some say this could be Icinga 3). + +You can read the full story [here](https://github.com/Icinga/icinga2/issues/7041). + +The only visible changes for users are: + +- No more dead-locks with hanging TLS connections (Cluster, REST API) +- Better log messages in error cases +- More robust and stable with using external libraries instead of self-written socket I/O + +Coming with this release, we've also updated TLS specific requirements +explained below. + +#### TLS 1.2 + +v2.11 raises the minimum required TLS version to 1.2. +This is available since OpenSSL 1.0.1 (EL6 & Debian Jessie). + +Older Icinga satellites/agents need to support TLS 1.2 during the TLS +handshake. + +The `api` feature attribute `tls_protocolmin` now only supports the +value `TLSv1.2` being the default. + +#### Hardened Cipher List + +The previous default cipher list allowed weak ciphers. There's no sane way +other than explicitly setting the allowed ciphers. + +The new default sets this to: + +``` +ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384:AES128-GCM-SHA256 +``` + +You can override this setting in the [api](09-object-types.md#objecttype-apilistener) +feature with the `cipher_list` attribute. + +In case that one of these ciphers is marked as insecure in the future, +please let us know with an issue on GitHub. + +### Cluster + +#### Config Sync + +2.11 overhauls the cluster config sync in many ways. This includes the +following under the hood: + +- Synced configuration files are not immediately put into production, but left inside a stage. +- Unsuccessful config validation never puts the config into production, additional logging and API states are available. +- Zone directories which are not configured in zones.conf, are not included anymore on secondary master/satellites/clients. +- Synced config change calculation use checksums instead of timestamps to trigger validation/reload. This is more safe, and the usage of timestamps is now deprecated. +- Don't allow parallel cluster syncs to avoid race conditions with overridden files. +- Deleted directories and files are now purged, previous versions had a bug. + +Whenever a newer child endpoint receives a configuration update without +checksums, it will log a warning. + +``` +Received configuration update without checksums from parent endpoint satellite1. This behaviour is deprecated. Please upgrade the parent endpoint to 2.11+ +``` + +This is a gentle reminder to upgrade the master and satellites first, +prior to installing new clients/agents. + +Technical details are available in the [technical concepts](19-technical-concepts.md#technical-concepts-cluster-config-sync) chapter. + +Since the config sync change detection now uses checksums, this may fail +with anything else than syncing configuration text files. Syncing binary +files were never supported, but rumors say that some users do so. + +This is now prohibited and logged. + +``` +[2019-08-02 16:03:19 +0200] critical/ApiListener: Ignoring file '/etc/icinga2/zones.d/global-templates/forbidden.exe' for cluster config sync: Does not contain valid UTF8. Binary files are not supported. +Context: + (0) Creating config update for file '/etc/icinga2/zones.d/global-templates/forbidden.exe' + (1) Activating object 'api' of type 'ApiListener' +``` + +Such binaries wrapped into JSON-RPC cluster messages may always cause changes +and trigger reload loops. In order to prevent such harm in production, +use infrastructure tools such as Foreman, Puppet, Ansible, etc. to install +plugins on the masters, satellites and agents. + + +#### HA-aware Features + +v2.11 introduces additional HA functionality similar to the DB IDO feature. +This enables the feature being active only on one endpoint while the other +endpoint is paused. When one endpoint is shut down, automatic failover happens. + +This feature is turned off by default keeping the current behaviour. If you need +it active on just one endpoint, set `enable_ha = true` on both endpoints in the +feature configuration. + +This affects the following features: + +* [Elasticsearch](09-object-types.md#objecttype-elasticsearchwriter) +* [Gelf](09-object-types.md#objecttype-gelfwriter) +* [Graphite](09-object-types.md#objecttype-graphitewriter) +* [InfluxDB](09-object-types.md#objecttype-influxdbwriter) +* [OpenTsdb](09-object-types.md#objecttype-opentsdbwriter) +* [Perfdata](09-object-types.md#objecttype-perfdatawriter) (for PNP) + +### HA Failover + +The reconnect failover has been improved, and the default `failover_timeout` +for the DB IDO features has been lowered from 60 to 30 seconds. +Object authority updates (required for balancing in the cluster) happen +more frequenty (was 30, is 10 seconds). +Also the cold startup without object authority updates has been reduced +from 60 to 30 seconds. This is to allow cluster reconnects (lowered from 60s to 10s in 2.10) +before actually considering a failover/split brain scenario. + +The [IdoMysqlConnection](09-object-types.md#objecttype-idomysqlconnection) and [IdoPgsqlConnection](09-object-types.md#objecttype-idopgsqlconnection) +objects provide a new attribute named `last_failover` which shows the last failover timestamp. +This value also is available in the [ido](10-icinga-template-library.md#itl-icinga-ido) CheckCommand output. + + +### CLI Commands + +The `troubleshoot` CLI command has been removed. It was never completed, +and turned out not to provide required details for GitHub issues anyways. + +We didn't ask nor endorse users on GitHub/Discourse in the past 2 years, so +we're removing it without deprecation. + +Issue templates, the troubleshooting docs and support knowledge has +proven to be better. + +#### Permissions + +CLI commands such as `api setup`, `node wizard/setup`, `feature enable/disable/list` +required root permissions previously. Since the file permissions allow +the Icinga user to change things already, and users kept asking to +run Icinga on their own webspace without root permissions, this is now possible +with 2.11. + +If you are running the commands with a different user than the +compiled `ICINGA_USER` and `ICINGA_GROUP` CMake settings (`icinga` everywhere, +except Debian with `nagios` for historical reasons), ensure that this +user has the capabilities to change to a different user. + +If you still encounter problems, run the aforementioned CLI commands as root, +or with sudo. + +#### CA List Behaviour Change + +`ca list` only shows the pending certificate signing requests by default. + +You can use the new `--all` parameter to show all signing requests. +Note that Icinga automatically purges signed requests older than 1 week. + +#### New: CA Remove/Restore + +`ca remove` allows you to remove pending signing requests. Once the +master receives a CSR, and it is marked as removed, the request is +denied. + +`ca restore` allows you to restore a removed signing request. You +can list removed signing requests with the new `--removed` parameter +for `ca list`. + +### Configuration + +The deprecated `concurrent_checks` attribute in the [checker feature](09-object-types.md#objecttype-checkercomponent) +has no effect anymore if set. Please use the [MaxConcurrentChecks](17-language-reference.md#icinga-constants-global-config) +constant in [constants.conf](04-configuration.md#constants-conf) instead. + +### REST API + +#### Actions + +The [schedule-downtime](12-icinga2-api.md#icinga2-api-actions-schedule-downtime-host-all-services) +action supports the `all_services` parameter for Host types. Defaults to false. + +#### Config Packages + +Deployed configuration packages require an active stage, with many previous +allowed. This mechanism is used by the Icinga Director as external consumer, +and Icinga itself for storing runtime created objects inside the `_api` +package. + +This includes downtimes and comments, which where sometimes stored in the wrong +directory path, because the active-stage file was empty/truncated/unreadable at +this point. + +2.11 makes this mechanism more stable and detects broken config packages. +It will also attempt to fix them, the following log entry is perfectly fine. + +``` +[2019-05-10 12:12:09 +0200] information/ConfigObjectUtility: Repairing config package '_api' with stage 'dbe0bef8-c72c-4cc9-9779-da7c4527c5b2'. +``` + +If you still encounter problems, please follow [this troubleshooting entry](15-troubleshooting.md#troubleshooting-api-missing-runtime-objects). + +### DB IDO MySQL Schema + +The schema for MySQL contains an optional update which +drops unneeded indexes. You don't necessarily need to apply +this update. + +### Documentation + +* `Custom attributes` have been renamed to `Custom variables` following the name `vars` and their usage in backends and web interfaces. +The term `custom attribute` still applies, but referring from the web to the core docs is easier. +* The distributed environment term `client` has been refined into `agent`. Wordings and images have been adjusted, and a `client` only is used as +general term when requesting something from a parent server role. +* The images for basics, modes and scenarios in the distributed monitoring chapter have been re-created from scratch. +* `02-getting-started.md` was renamed to `02-installation.md`, `04-configuring-icinga-2.md` into `04-configuration.md`. Apache redirects will be in place. + +## Upgrading to v2.10 + +### Path Constant Changes + +During package upgrades you may see a notice that the configuration +content of features has changed. This is due to a more general approach +with path constants in v2.10. + +The known constants `SysconfDir` and `LocalStateDir` stay intact and won't +break on upgrade. +If you are using these constants in your own custom command definitions +or other objects, you are advised to revise them and update them according +to the [documentation](17-language-reference.md#icinga-constants). + +Example diff: + +``` +object NotificationCommand "mail-service-notification" { +- command = [ SysconfDir + "/icinga2/scripts/mail-service-notification.sh" ] ++ command = [ ConfigDir + "/scripts/mail-service-notification.sh" ] +``` + +If you have the `ICINGA2_RUN_DIR` environment variable configured in the +sysconfig file, you need to rename it to `ICINGA2_INIT_RUN_DIR`. `ICINGA2_STATE_DIR` +has been removed and this setting has no effect. + +> **Note** +> +> This is important if you rely on the sysconfig configuration in your own scripts. + +### New Constants + +New [Icinga constants](17-language-reference.md#icinga-constants) have been added in this release. + +* `Environment` for specifying the Icinga environment. Defaults to not set. +* `ApiBindHost` and `ApiBindPort` to allow overriding the default ApiListener values. This will be used for an Icinga addon only. + +### Configuration: Namespaces + +The keywords `namespace` and `using` are now [reserved](17-language-reference.md#reserved-keywords) for the namespace functionality provided +with v2.10. Read more about how it works [here](17-language-reference.md#namespaces). + +### Configuration: ApiListener + +Anonymous JSON-RPC connections in the cluster can now be configured with `max_anonymous_clients` +attribute. +The corresponding REST API results from `/v1/status/ApiListener` in `json_rpc` have been renamed +from `clients` to `anonymous_clients` to better reflect their purpose. Authenticated clients +are counted as connected endpoints. A similar change is there for the performance data metrics. + +The TLS handshake timeout defaults to 10 seconds since v2.8.2. This can now be configured +with the configuration attribute `tls_handshake_timeout`. Beware of performance issues +with setting a too high value. + +### API: schedule-downtime Action + +The attribute `child_options` was previously accepting 0,1,2 for specific child downtime settings. +This behaviour stays intact, but the new proposed way are specific constants as values (`DowntimeNoChildren`, `DowntimeTriggeredChildren`, `DowntimeNonTriggeredChildren`). + +### Notifications: Recovery and Acknowledgement + +When a user should be notified on `Problem` and `Acknowledgement`, v2.10 now checks during +the `Acknowledgement` notification event whether this user has been notified about a problem before. + +``` + types = [ Problem, Acknowledgement, Recovery ] +``` + +If **no** `Problem` notification was sent, and the types filter includes problems for this user, +the `Acknowledgement` notification is **not** sent. + +In contrast to that, the following configuration always sends `Acknowledgement` notifications. + +``` + types = [ Acknowledgement, Recovery ] +``` + +This change also restores the old behaviour for `Recovery` notifications. The above configuration +leaving out the `Problem` type can be used to only receive recovery notifications. If `Problem` +is added to the types again, Icinga 2 checks whether it has notified a user of a problem when +sending the recovery notification. + +More details can be found in [this PR](https://github.com/Icinga/icinga2/pull/6527). + +### Stricter configuration validation + +Some config errors are now fatal. While it never worked before, icinga2 refuses to start now! + +For example the following started to give a fatal error in 2.10: + +``` + object Zone "XXX" { + endpoints = [ "master-server" ] + parent = "global-templates" + } +``` + +```critical/config: Error: Zone 'XXX' can not have a global zone as parent.``` + +### Package Changes + +Debian/Ubuntu drops the `libicinga2` package. `apt-get upgrade icinga2` +won't remove such packages leaving the upgrade in an unsatisfied state. + +Please use `apt-get full-upgrade` or `apt-get dist-upgrade` instead, as explained [here](https://github.com/Icinga/icinga2/issues/6695#issuecomment-430585915). + +On RHEL/CentOS/Fedora, `icinga2-libs` has been obsoleted. Unfortunately yum's dependency +resolver doesn't allow to install older versions than 2.10 then. Please +read [here](https://github.com/Icinga/icinga-packaging/issues/114#issuecomment-429264827) +for details. + +RPM packages dropped the [Classic UI](16-upgrading-icinga-2.md#upgrading-to-2-8-removed-classicui-config-package) +package in v2.8, Debian/Ubuntu packages were forgotten. This is now the case with this +release. Icinga 1.x is EOL by the end of 2018, plan your migration to [Icinga Web 2](https://icinga.com/docs/icingaweb2/latest/). + ## Upgrading to v2.9 +### Deprecation and Removal Notes + +- Deprecation of 1.x compatibility features: `StatusDataWriter`, `CompatLogger`, `CheckResultReader`. Their removal is scheduled for 2.11. +Icinga 1.x is EOL and will be out of support by the end of 2018. +- Removal of Icinga Studio. It always has been experimental and did not satisfy our high quality standards. We've therefore removed it. + +### Sysconfig Changes + +The security fixes in v2.8.2 required moving specific runtime settings +into the Sysconfig file and environment. This included that Icinga 2 +would itself parse this file and read the required variables. This has generated +numerous false-positive log messages and led to many support questions. v2.9.0 +changes this in the standard way to read these variables from the environment, and use +sane compile-time defaults. + +> **Note** +> +> In order to upgrade, remove everything in the sysconfig file and re-apply +> your changes. + +There is a bug with existing sysconfig files where path variables are not expanded +because systemd [does not support](https://github.com/systemd/systemd/issues/2123) +shell variable expansion. This worked with SysVInit though. + +Edit the sysconfig file and either remove everything, or edit this line +on RHEL 7. Modify the path for other distributions. + +``` +vim /etc/sysconfig/icinga2 + +-ICINGA2_PID_FILE=$ICINGA2_RUN_DIR/icinga2/icinga2.pid ++ICINGA2_PID_FILE=/run/icinga2/icinga2.pid +``` + +If you want to adjust the number of open files for the Icinga application +for example, you would just add this setting like this on RHEL 7: + +``` +vim /etc/sysconfig/icinga2 + +ICINGA2_RLIMIT_FILES=50000 +``` + +Restart Icinga 2 afterwards, the systemd service file automatically puts the +value into the application's environment where this is read on startup. + +### Setup Wizard Changes + +Client and satellite setups previously had the example configuration in `conf.d` included +by default. This caused trouble on config sync, or with locally executed checks generating +wrong check results for command endpoint clients. + +In v2.9.0 `node wizard`, `node setup` and the graphical Windows wizard will disable +the inclusion by default. You can opt-out and explicitly enable it again if needed. + +In addition to the default global zones `global-templates` and `director-global`, +the setup wizards also offer to specify your own custom global zones and generate +the required configuration automatically. + +The setup wizards also use full qualified names for Zone and Endpoint object generation, +either the default values (FQDN for clients) or the user supplied input. This removes +the dependency on the `NodeName` and `ZoneName` constant and helps to immediately see +the parent-child relationship. Those doing support will also see the benefit in production. + +### CLI Command Changes + +The [node setup](06-distributed-monitoring.md#distributed-monitoring-automation-cli-node-setup) +parameter `--master_host` was deprecated and replaced with `--parent_host`. +This parameter is now optional to allow connection-less client setups similar to the `node wizard` +CLI command. The `parent_zone` parameter has been added to modify the parent zone name e.g. +for client-to-satellite setups. + +The `api user` command which was released in v2.8.2 turned out to cause huge problems with +configuration validation, windows restarts and OpenSSL versions. It is therefore removed in 2.9, +the `password_hash` attribute for the ApiUser object stays intact but has no effect. This is to ensure +that clients don't break on upgrade. We will revise this feature in future development iterations. + ### Configuration Changes The CORS attributes `access_control_allow_credentials`, `access_control_allow_headers` and `access_control_allow_methods` are now controlled by Icinga 2 and cannot be changed anymore. -### CLI Command Changes +### Unique Generated Names + +With the removal of RHEL 5 as supported platform, we can finally use real unique IDs. +This is reflected in generating names for e.g. API stage names. Previously it was a handcrafted +mix of local FQDN, timestamps and random numbers. + +### Custom Vars not updating + +A rare issue preventing the custom vars of objects created prior to 2.9.0 being updated when changed may occur. To +remedy this, truncate the customvar tables and restart Icinga 2. The following is an example of how to do this with mysql: + +``` +$ mysql -uroot -picinga icinga +MariaDB [icinga]> truncate icinga_customvariables; +Query OK, 0 rows affected (0.05 sec) +MariaDB [icinga]> truncate icinga_customvariablestatus; +Query OK, 0 rows affected (0.03 sec) +MariaDB [icinga]> exit +Bye +$ sudo systemctl restart icinga2 +``` + +Custom vars should now stay up to date. -The `node setup` parameter `--master_host` was deprecated and replaced with `--parent_host`. This parameter is now optional to allow connection-less client setups similar to the `node wizard` CLI command. The `parent_zone` parameter has been added to modify the parent zone name e.g. for client-to-satellite setups. ## Upgrading to v2.8.2 @@ -53,8 +572,8 @@ The default certificate path was changed from `/etc/icinga2/pki` to Old Path | New Path ---------------------------------------------------|--------------------------------------------------- - `/etc/icinga2/pki/icinga2-client1.localdomain.crt` | `/var/lib/icinga2/certs/icinga2-client1.localdomain.crt` - `/etc/icinga2/pki/icinga2-client1.localdomain.key` | `/var/lib/icinga2/certs/icinga2-client1.localdomain.key` + `/etc/icinga2/pki/icinga2-agent1.localdomain.crt` | `/var/lib/icinga2/certs/icinga2-agent1.localdomain.crt` + `/etc/icinga2/pki/icinga2-agent1.localdomain.key` | `/var/lib/icinga2/certs/icinga2-agent1.localdomain.key` `/etc/icinga2/pki/ca.crt` | `/var/lib/icinga2/certs/ca.crt` This applies to Windows clients in the same way: `%ProgramData%\etc\icinga2\pki` @@ -62,8 +581,8 @@ was moved to `%ProgramData%\var\lib\icinga2\certs`. Old Path | New Path ----------------------------------------------------------------|---------------------------------------------------------------- - `%ProgramData%\etc\icinga2\pki\icinga2-client1.localdomain.crt` | `%ProgramData%\var\lib\icinga2\certs\icinga2-client1.localdomain.crt` - `%ProgramData%\etc\icinga2\pki\icinga2-client1.localdomain.key` | `%ProgramData%\var\lib\icinga2\certs\icinga2-client1.localdomain.key` + `%ProgramData%\etc\icinga2\pki\icinga2-agent1.localdomain.crt` | `%ProgramData%\var\lib\icinga2\certs\icinga2-agent1.localdomain.crt` + `%ProgramData%\etc\icinga2\pki\icinga2-agent1.localdomain.key` | `%ProgramData%\var\lib\icinga2\certs\icinga2-agent1.localdomain.key` `%ProgramData%\etc\icinga2\pki\ca.crt` | `%ProgramData%\var\lib\icinga2\certs\ca.crt` @@ -205,10 +724,10 @@ attributes. More details can be found [here](08-advanced-topics.md#check-flappin ## Upgrading to v2.7 v2.7.0 provided new notification scripts and commands. Please ensure to -update your configuration accordingly. An advisory has been published [here](https://www.icinga.com/2017/08/23/advisory-for-icinga-2-v2-7-update-and-mail-notification-scripts/). +update your configuration accordingly. An advisory has been published [here](https://icinga.com/2017/08/23/advisory-for-icinga-2-v2-7-update-and-mail-notification-scripts/). In case are having troubles with OpenSSL 1.1.0 and the -public CA certificates, please read [this advisory](https://www.icinga.com/2017/08/30/advisory-for-ssl-problems-with-leading-zeros-on-openssl-1-1-0/) +public CA certificates, please read [this advisory](https://icinga.com/2017/08/30/advisory-for-ssl-problems-with-leading-zeros-on-openssl-1-1-0/) and check the [troubleshooting chapter](15-troubleshooting.md#troubleshooting). If Icinga 2 fails to start with an empty reference to `$ICINGA2_CACHE_DIR` diff --git a/doc/17-language-reference.md b/doc/17-language-reference.md index d2a865811..ddf048614 100644 --- a/doc/17-language-reference.md +++ b/doc/17-language-reference.md @@ -5,12 +5,14 @@ Icinga 2 features an object-based configuration format. You can define new objects using the `object` keyword: - object Host "host1.example.org" { - display_name = "host1" +``` +object Host "host1.example.org" { + display_name = "host1" - address = "192.168.0.1" - address6 = "::1" - } + address = "192.168.0.1" + address6 = "2001:db8:1234::42" +} +``` In general you need to write each statement on a new line. Expressions started with `{`, `(` and `[` extend until the matching closing character and can be broken @@ -19,11 +21,13 @@ up into multiple lines. Alternatively you can write multiple statements on a single line by separating them with a semicolon: - object Host "host1.example.org" { - display_name = "host1" +``` +object Host "host1.example.org" { + display_name = "host1" - address = "192.168.0.1"; address6 = "::1" - } + address = "192.168.0.1"; address6 = "2001:db8:1234::42" +} +``` Each object is uniquely identified by its type (`Host`) and name (`host1.example.org`). Some types have composite names, e.g. the @@ -53,7 +57,9 @@ A floating-point number. Example: - 27.3 +``` +27.3 +``` ### Duration Literals @@ -62,7 +68,9 @@ suffixes to help with specifying time durations. Example: - 2.5m +``` +2.5m +``` Supported suffixes include ms (milliseconds), s (seconds), m (minutes), h (hours) and d (days). @@ -76,7 +84,9 @@ A string. Example: - "Hello World!" +``` +"Hello World!" +``` #### String Literals Escape Sequences @@ -104,10 +114,12 @@ Strings spanning multiple lines can be specified by enclosing them in Example: - {{{This - is - a multi-line - string.}}} +``` +{{{This +is +a multi-line +string.}}} +``` Unlike in ordinary strings special characters do not have to be escaped in multi-line string literals. @@ -130,10 +142,12 @@ The comma after the last key-value pair is optional. Example: - { - address = "192.168.0.1" - port = 443 - } +``` +{ + address = "192.168.0.1" + port = 443 +} +``` Identifiers may not contain certain characters (e.g. space) or start with certain characters (e.g. digits). If you want to use a dictionary @@ -149,7 +163,9 @@ The comma after the last element is optional. Example: - [ "hello", 42 ] +``` +[ "hello", 42 ] +``` An array may simultaneously contain values of different types, such as strings and numbers. @@ -160,46 +176,103 @@ The following operators are supported in expressions. The operators are sorted b Operator | Precedence | Examples (Result) | Description ---------|------------|-----------------------------------------------|-------------------------------- -() | 1 | (3 + 3) * 5 | Groups sub-expressions -() | 1 | Math.random() | Calls a function -[] | 1 | a[3] | Array subscript -. | 1 | a.b | Element access -! | 2 | !"Hello" (false), !false (true) | Logical negation of the operand -~ | 2 | ~true (false) | Bitwise negation of the operand -+ | 2 | +3 | Unary plus -- | 2 | -3 | Unary minus -* | 3 | 5m * 10 (3000) | Multiplies two numbers -/ | 3 | 5m / 5 (60) | Divides two numbers -% | 3 | 17 % 12 (5) | Remainder after division -+ | 4 | 1 + 3 (4), "hello " + "world" ("hello world") | Adds two numbers; concatenates strings -- | 4 | 3 - 1 (2) | Subtracts two numbers -<< | 5 | 4 << 8 (1024) | Left shift ->> | 5 | 1024 >> 4 (64) | Right shift -< | 6 | 3 < 5 (true) | Less than -> | 6 | 3 > 5 (false) | Greater than -<= | 6 | 3 <= 3 (true) | Less than or equal ->= | 6 | 3 >= 3 (true) | Greater than or equal -in | 7 | "foo" in [ "foo", "bar" ] (true) | Element contained in array -!in | 7 | "foo" !in [ "bar", "baz" ] (true) | Element not contained in array -== | 8 | "hello" == "hello" (true), 3 == 5 (false) | Equal to -!= | 8 | "hello" != "world" (true), 3 != 3 (false) | Not equal to -& | 9 | 7 & 3 (3) | Binary AND -^ | 10 | 17 ^ 12 (29) | Bitwise XOR -| | 11 | 2 | 3 (3) | Binary OR -&& | 13 | true && false (false), 3 && 7 (7), 0 && 7 (0) | Logical AND -|| | 14 | true || false (true), 0 || 7 (7)| Logical OR -= | 12 | a = 3 | Assignment -=> | 15 | x => x * x (function with arg x) | Lambda, for loop +`()` | 1 | (3 + 3) * 5 | Groups sub-expressions +`()` | 1 | Math.random() | Calls a function +`[]` | 1 | a[3] | Array subscript +`.` | 1 | a.b | Element access +`!` | 2 | !"Hello" (false), !false (true) | Logical negation of the operand +`~` | 2 | ~true (false) | Bitwise negation of the operand +`+` | 2 | +3 | Unary plus +`-` | 2 | -3 | Unary minus +`&` | 2 | &var (reference to 'var') | Reference operator +`*` | 2 | *var | Indirection operator +`*` | 3 | 5m * 10 (3000) | Multiplies two numbers +`/` | 3 | 5m / 5 (60) | Divides two numbers +`%` | 3 | 17 % 12 (5) | Remainder after division +`+` | 4 | 1 + 3 (4), "hello " + "world" ("hello world") | Adds two numbers; concatenates strings +`-` | 4 | 3 - 1 (2) | Subtracts two numbers +`<<` | 5 | 4 << 8 (1024) | Left shift +`>>` | 5 | 1024 >> 4 (64) | Right shift +`<` | 6 | 3 < 5 (true) | Less than +`>` | 6 | 3 > 5 (false) | Greater than +`<=` | 6 | 3 <= 3 (true) | Less than or equal +`>=` | 6 | 3 >= 3 (true) | Greater than or equal +`in` | 7 | "foo" in [ "foo", "bar" ] (true) | Element contained in array +`!in` | 7 | "foo" !in [ "bar", "baz" ] (true) | Element not contained in array +`==` | 8 | "hello" == "hello" (true), 3 == 5 (false) | Equal to +`!=` | 8 | "hello" != "world" (true), 3 != 3 (false) | Not equal to +`&` | 9 | 7 & 3 (3) | Binary AND +`^` | 10 | 17 ^ 12 (29) | Bitwise XOR +| | 11 | 2 | 3 (3) | Binary OR +|| | 12 | true || false (true), 0 || 7 (7)| Logical OR +`&&` | 13 | true && false (false), 3 && 7 (7), 0 && 7 (0) | Logical AND +`=` | 14 | a = 3 | Assignment +`=>` | 15 | x => x * x (function with arg x) | Lambda, for loop + +### References + +A reference to a value can be obtained using the `&` operator. The `*` operator can be used +to dereference a reference: + +``` +var value = "Hello!" +var p = &value /* p refers to value */ +*p = "Hi!" +log(value) // Prints "Hi!" because the variable was changed +``` + +### Namespaces + +Namespaces can be used to organize variables and functions. They are used to avoid name conflicts. The `namespace` +keyword is used to create a new namespace: + +``` +namespace Utils { + function calculate() { + return 2 + 2 + } +} +``` + +The namespace is made available as a global variable which has the namespace's name (e.g. `Utils`): + +``` +Utils.calculate() +``` + +The `using` keyword can be used to make all attributes in a namespace available to a script without having to +explicitly specify the namespace's name for each access: + +``` +using Utils +calculate() +``` + +The `using` keyword only has an effect for the current file and only for code that follows the keyword: + +``` +calculate() // This will not work. +using Utils +``` + +The following namespaces are automatically imported as if by using the `using` keyword: + +* System +* System.Configuration +* Types +* Icinga ### Function Calls Functions can be called using the `()` operator: - const MyGroups = [ "test1", "test" ] +``` +const MyGroups = [ "test1", "test" ] - { - check_interval = len(MyGroups) * 1m - } +{ + check_interval = len(MyGroups) * 1m +} +``` A list of available functions is available in the [Library Reference](18-library-reference.md#library-reference) chapter. @@ -207,7 +280,7 @@ A list of available functions is available in the [Library Reference](18-library In addition to the `=` operator shown above a number of other operators to manipulate attributes are supported. Here's a list of all -available operators: +available operators (the outermost `{` `}` stand for a local variable scope): ### Operator = @@ -215,10 +288,12 @@ Sets an attribute to the specified value. Example: - { - a = 5 - a = 7 - } +``` +{ + a = 5 + a = 7 +} +``` In this example `a` has the value `7` after both instructions are executed. @@ -226,65 +301,81 @@ In this example `a` has the value `7` after both instructions are executed. The += operator is a shortcut. The following expression: - { - a = [ "hello" ] - a += [ "world" ] - } +``` +{ + a = [ "hello" ] + a += [ "world" ] +} +``` is equivalent to: - { - a = [ "hello" ] - a = a + [ "world" ] - } +``` +{ + a = [ "hello" ] + a = a + [ "world" ] +} +``` ### Operator -= The -= operator is a shortcut. The following expression: - { - a = 10 - a -= 5 - } +``` +{ + a = 10 + a -= 5 +} +``` is equivalent to: - { - a = 10 - a = a - 5 - } +``` +{ + a = 10 + a = a - 5 +} +``` ### Operator \*= The *= operator is a shortcut. The following expression: - { - a = 60 - a *= 5 - } +``` +{ + a = 60 + a *= 5 +} +``` is equivalent to: - { - a = 60 - a = a * 5 - } +``` +{ + a = 60 + a = a * 5 +} +``` ### Operator /= The /= operator is a shortcut. The following expression: - { - a = 300 - a /= 5 - } +``` +{ + a = 300 + a /= 5 +} +``` is equivalent to: - { - a = 300 - a = a / 5 - } +``` +{ + a = 300 + a = a / 5 +} +``` ## Indexer @@ -292,23 +383,29 @@ The indexer syntax provides a convenient way to set dictionary elements. Example: - { - hello.key = "world" - } +``` +{ + hello.key = "world" +} +``` Example (alternative syntax): - { - hello["key"] = "world" - } +``` +{ + hello["key"] = "world" +} +``` This is equivalent to writing: - { - hello += { - key = "world" - } - } +``` +{ + hello += { + key = "world" + } +} +``` If the `hello` attribute does not already have a value, it is automatically initialized to an empty dictionary. @@ -318,22 +415,24 @@ Objects can import attributes from other objects. Example: - template Host "default-host" { - vars.colour = "red" - } +``` +template Host "default-host" { + vars.colour = "red" +} - template Host "test-host" { - import "default-host" +template Host "test-host" { + import "default-host" - vars.colour = "blue" - } + vars.colour = "blue" +} - object Host "localhost" { - import "test-host" +object Host "localhost" { + import "test-host" - address = "127.0.0.1" - address6 = "::1" - } + address = "127.0.0.1" + address6 = "::1" +} +``` The `default-host` and `test-host` objects are marked as templates using the `template` keyword. Unlike ordinary objects templates are not @@ -341,7 +440,7 @@ instantiated at run-time. Parent objects do not necessarily have to be templates, however in general they are. The `vars` dictionary for the `localhost` object contains all three -custom attributes and the custom attribute `colour` has the value `"blue"`. +custom variables and the custom variable `colour` has the value `"blue"`. Parent objects are resolved in the order they're specified using the `import` keyword. @@ -349,9 +448,11 @@ Parent objects are resolved in the order they're specified using the Default templates which are automatically imported into all object definitions can be specified using the `default` keyword: - template CheckCommand "plugin-check-command" default { - // ... - } +``` +template CheckCommand "plugin-check-command" default { + // ... +} +``` Default templates are imported before any other user-specified statement in an object definition is evaluated. @@ -363,45 +464,91 @@ is unspecified. Global constants can be set using the `const` keyword: - const VarName = "some value" +``` +const VarName = "some value" +``` Once defined a constant can be accessed from any file. Constants cannot be changed once they are set. > **Tip** > -> Best practice is to manage constants in the [constants.conf](04-configuring-icinga-2.md#constants-conf) file. +> Best practice is to manage constants in the [constants.conf](04-configuration.md#constants-conf) file. ### Icinga 2 Specific Constants -Icinga 2 provides a number of special global constants. Some of them can be overridden using the `--define` command line parameter: +Icinga 2 provides a number of special global constants. These include directory paths, global configuration +and runtime parameters for the application version and (build) platform. + +#### Directory Path Constants + +Constant | Description +--------------------|------------------- +ConfigDir |**Read-only.** Main configuration directory. Usually set to `/etc/icinga2`. +DataDir |**Read-only.** Runtime data for the Icinga daemon. Usually set to `/var/lib/icinga2`. +LogDir |**Read-only.** Logfiles from the daemon. Usually set to `/var/log/icinga2`. +CacheDir |**Read-only.** Cached status information of the daemon. Usually set to `/var/cache/icinga2`. +SpoolDir |**Read-only.** Spool directory for certain data outputs. Usually set to `/var/spool/icinga2`. +InitRunDir |**Read-only.** Directory for PID files and sockets in daemon mode. Usually set to `/run/icinga2`. +ZonesDir |**Read-only.** Contains the path of the zones.d directory. Defaults to `ConfigDir + "/zones.d"`. + +#### Global Configuration Constants + +Constant | Description +--------------------|------------------- +Vars |**Read-write.** Contains a dictionary with global custom variables. Not set by default. +NodeName |**Read-write.** Contains the cluster node name. Set to the local hostname by default. +ReloadTimeout |**Read-write.** Defines the reload timeout for child processes. Defaults to `300s`. +Environment |**Read-write.** The name of the Icinga environment. Included in the SNI host name for outbound connections. Not set by default. +RunAsUser |**Read-write.** Defines the user the Icinga 2 daemon is running as. Set in the Icinga 2 sysconfig. +RunAsGroup |**Read-write.** Defines the group the Icinga 2 daemon is running as. Set in the Icinga 2 sysconfig. +MaxConcurrentChecks |**Read-write.** The number of max checks run simultaneously. Defaults to `512`. +ApiBindHost |**Read-write.** Overrides the default value for the ApiListener `bind_host` attribute. Not set by default. +ApiBindPort |**Read-write.** Overrides the default value for the ApiListener `bind_port` attribute. Not set by default. + +#### Application Runtime Constants + +Constant | Description +--------------------|------------------- +PlatformName |**Read-only.** The name of the operating system, e.g. `Ubuntu`. +PlatformVersion |**Read-only.** The version of the operating system, e.g. `14.04.3 LTS`. +PlatformKernel |**Read-only.** The name of the operating system kernel, e.g. `Linux`. +PlatformKernelVersion|**Read-only.** The version of the operating system kernel, e.g. `3.13.0-63-generic`. +BuildCompilerName |**Read-only.** The name of the compiler Icinga was built with, e.g. `Clang`. +BuildCompilerVersion|**Read-only.** The version of the compiler Icinga was built with, e.g. `7.3.0.7030031`. +BuildHostName |**Read-only.** The name of the host Icinga was built on, e.g. `acheron`. +ApplicationVersion |**Read-only.** The application version, e.g. `2.9.0`. + +#### Additional Constants + +Writable constants can be specified on the CLI using the `--define/-D` parameter. + +> **Note for v2.10+** +> +> Default paths which include `/etc` and `/var` as base directory continue to work +> based on the `SysconfDir` and `LocalStateDir` constants respectively. + +In addition to that, the constants below are used to define specific file paths. You should never need +to change them, as they are pre-compiled based on the constants above. Variable |Description --------------------|------------------- -PrefixDir |**Read-only.** Contains the installation prefix that was specified with cmake -DCMAKE_INSTALL_PREFIX. Defaults to "/usr/local". -SysconfDir |**Read-only.** Contains the path of the sysconf directory. Defaults to PrefixDir + "/etc". -ZonesDir |**Read-only.** Contains the path of the zones.d directory. Defaults to SysconfDir + "/zones.d". -LocalStateDir |**Read-only.** Contains the path of the local state directory. Defaults to PrefixDir + "/var". -RunDir |**Read-only.** Contains the path of the run directory. Defaults to LocalStateDir + "/run". -PkgDataDir |**Read-only.** Contains the path of the package data directory. Defaults to PrefixDir + "/share/icinga2". -StatePath |**Read-write.** Contains the path of the Icinga 2 state file. Defaults to LocalStateDir + "/lib/icinga2/icinga2.state". -ObjectsPath |**Read-write.** Contains the path of the Icinga 2 objects file. Defaults to LocalStateDir + "/cache/icinga2/icinga2.debug". -PidPath |**Read-write.** Contains the path of the Icinga 2 PID file. Defaults to RunDir + "/icinga2/icinga2.pid". -Vars |**Read-write.** Contains a dictionary with global custom attributes. Not set by default. -NodeName |**Read-write.** Contains the cluster node name. Set to the local hostname by default. -RunAsUser |**Read-write.** Defines the user the Icinga 2 daemon is running as. Set in the Icinga 2 sysconfig. -RunAsGroup |**Read-write.** Defines the group the Icinga 2 daemon is running as. Set in the Icinga 2 sysconfig. -PlatformName |**Read-only.** The name of the operating system, e.g. "Ubuntu". -PlatformVersion |**Read-only.** The version of the operating system, e.g. "14.04.3 LTS". -PlatformKernel |**Read-only.** The name of the operating system kernel, e.g. "Linux". -PlatformKernelVersion|**Read-only.** The version of the operating system kernel, e.g. "3.13.0-63-generic". -BuildCompilerName |**Read-only.** The name of the compiler Icinga was built with, e.g. "Clang". -BuildCompilerVersion|**Read-only.** The version of the compiler Icinga was built with, e.g. "7.3.0.7030031". -BuildHostName |**Read-only.** The name of the host Icinga was built on, e.g. "acheron". -ApplicationVersion |**Read-only.** The application version, e.g. "2.9.0". -MaxConcurrentChecks |**Read-write**. The number of max checks run simultaneously. Defaults to 512. -Environment |**Read-write**. The name of the Icinga environment. Included in the SNI host name when making outbound connections. Defaults to "production". +StatePath |**Read-write.** Contains the path of the Icinga 2 state file. Defaults to `DataDir + "/icinga2.state"`. +ObjectsPath |**Read-write.** Contains the path of the Icinga 2 objects file. Defaults to `CacheDir + "/icinga2.debug"`. +PidPath |**Read-write.** Contains the path of the Icinga 2 PID file. Defaults to `InitRunDir + "/icinga2.pid"`. +PkgDataDir |**Read-only.** Contains the path of the package data directory. Defaults to `PrefixDir + "/share/icinga2"`. +The constants below have been used until Icinga v2.10, and are still intact. You don't need them +for future builds and configuration based on the newly available constants above. + +Variable |Description +--------------------|------------------- +PrefixDir |**Read-only.** Contains the installation prefix that was specified with `cmake -DCMAKE_INSTALL_PREFIX`. `Defaults to "/usr/local"`. +SysconfDir |**Read-only.** Contains the path of the sysconf directory. Defaults to `PrefixDir + "/etc"`. +LocalStateDir |**Read-only.** Contains the path of the local state directory. Defaults to `PrefixDir + "/var"`. +RunDir |**Read-only.** Contains the path of the run directory. Defaults to `LocalStateDir + "/run"`. + +#### Advanced Constants and Variables Advanced runtime constants. Please only use them if advised by support or developers. @@ -409,22 +556,30 @@ Variable | Description ---------------------------|------------------- EventEngine |**Read-write.** The name of the socket event engine, can be `poll` or `epoll`. The epoll interface is only supported on Linux. AttachDebugger |**Read-write.** Whether to attach a debugger when Icinga 2 crashes. Defaults to `false`. -ICINGA2\_RLIMIT\_FILES |**Read-write.** Defines the resource limit for RLIMIT_NOFILE that should be set at start-up. Value cannot be set lower than the default `16 * 1024`. 0 disables the setting. Set in Icinga 2 sysconfig. -ICINGA2\_RLIMIT\_PROCESSES |**Read-write.** Defines the resource limit for RLIMIT_NPROC that should be set at start-up. Value cannot be set lower than the default `16 * 1024`. 0 disables the setting. Set in Icinga 2 sysconfig. -ICINGA2\_RLIMIT\_STACK |**Read-write.** Defines the resource limit for RLIMIT_STACK that should be set at start-up. Value cannot be set lower than the default `256 * 1024`. 0 disables the setting. Set in Icinga 2 sysconfig. + +Advanced sysconfig environment variables, defined in `/etc/sysconfig/icinga2` (RHEL/SLES) or `/etc/default/icinga2` (Debian/Ubuntu). + +Variable | Description +---------------------------|------------------- +ICINGA2\_RLIMIT\_FILES |**Read-write.** Defines the resource limit for `RLIMIT_NOFILE` that should be set at start-up. Value cannot be set lower than the default `16 * 1024`. 0 disables the setting. Set in Icinga 2 sysconfig. +ICINGA2\_RLIMIT\_PROCESSES |**Read-write.** Defines the resource limit for `RLIMIT_NPROC` that should be set at start-up. Value cannot be set lower than the default `16 * 1024`. 0 disables the setting. Set in Icinga 2 sysconfig. +ICINGA2\_RLIMIT\_STACK |**Read-write.** Defines the resource limit for `RLIMIT_STACK` that should be set at start-up. Value cannot be set lower than the default `256 * 1024`. 0 disables the setting. Set in Icinga 2 sysconfig. + ## Apply The `apply` keyword can be used to create new objects which are associated with another group of objects. - apply Service "ping" to Host { - import "generic-service" +``` +apply Service "ping" to Host { + import "generic-service" - check_command = "ping4" + check_command = "ping4" - assign where host.name == "localhost" - } + assign where host.name == "localhost" +} +``` In this example the `assign where` condition is a boolean expression which is evaluated for all objects of type `Host` and a new service with name "ping" @@ -459,13 +614,14 @@ chapter. [Apply](17-language-reference.md#apply) rules can be extended with the [for loop](17-language-reference.md#for-loops) keyword. - apply Service "prefix-" for (key => value in host.vars.dictionary) to Host { - import "generic-service" - - check_command = "ping4" - vars.host_value = value - } +``` +apply Service "prefix-" for (key => value in host.vars.dictionary) to Host { + import "generic-service" + check_command = "ping4" + vars.host_value = value +} +``` Any valid config attribute can be accessed using the `host` and `service` variables. The attribute must be of the Array or Dictionary type. In this example @@ -476,7 +632,7 @@ In this example all generated service object names consist of `prefix-` and the value of the `key` iterator. The prefix string can be omitted if not required. The `key` and `value` variables can be used for object attribute assignment, e.g. for -setting the `check_command` attribute or custom attributes as command parameters. +setting the `check_command` attribute or custom variables as command parameters. `apply for` rules are first evaluated against all objects matching the `for loop` list and afterwards the `assign where` and `ignore where` conditions are evaluated. @@ -492,11 +648,13 @@ chapter. Group objects can be assigned to specific member objects using the `assign where` and `ignore where` conditions. - object HostGroup "linux-servers" { - display_name = "Linux Servers" +``` +object HostGroup "linux-servers" { + display_name = "Linux Servers" - assign where host.vars.os == "Linux" - } + assign where host.vars.os == "Linux" +} +``` In this example the `assign where` condition is a boolean expression which is evaluated for all objects of the type `Host`. Each matching host is added as member to the host group @@ -538,13 +696,15 @@ The Icinga 2 configuration format supports C/C++-style and shell-style comments. Example: - /* - This is a comment. - */ - object Host "localhost" { - check_interval = 30 // this is also a comment. - retry_interval = 15 # yet another comment - } +``` +/* + This is a comment. + */ +object Host "localhost" { + check_interval = 30 // this is also a comment. + retry_interval = 15 # yet another comment +} +``` ## Includes @@ -554,15 +714,19 @@ Paths must be relative to the configuration file that contains the Example: - include "some/other/file.conf" - include "conf.d/*.conf" +``` +include "some/other/file.conf" +include "conf.d/*.conf" +``` Wildcard includes are not recursive. Icinga also supports include search paths similar to how they work in a C/C++ compiler: - include +``` +include +``` Note the use of angle brackets instead of double quotes. This causes the config compiler to search the include search paths for the specified @@ -579,8 +743,10 @@ files in a directory which match a certain pattern. Example: - include_recursive "conf.d", "*.conf" - include_recursive "templates" +``` +include_recursive "conf.d", "*.conf" +include_recursive "templates" +``` The first parameter specifies the directory from which files should be recursively included. @@ -590,6 +756,14 @@ When no pattern is specified the default pattern "*.conf" is used. ## Zone Includes +> **Note** +> +> This is an internal functionality consumed by Icinga itself. +> +> The preferred way for users managing configuration files in +> zones is to use the [cluster config sync](06-distributed-monitoring.md#distributed-monitoring-top-down-config-sync) +> or [REST API config packages](12-icinga2-api.md#icinga2-api-config-management). + The `include_zones` recursively includes all subdirectories for the given path. @@ -598,8 +772,10 @@ in these subdirectories to the name of the subdirectory. Example: - include_zones "etc", "zones.d", "*.conf" - include_zones "puppet", "puppet-zones" +``` +include_zones "etc", "zones.d", "*.conf" +include_zones "puppet", "puppet-zones" +``` The first parameter specifies a tag name for this directive. Each `include_zones` invocation should use a unique tag name. When copying the zones' configuration @@ -623,14 +799,18 @@ Functions can be defined using the `function` keyword. Example: - function multiply(a, b) { - return a * b - } +``` +function multiply(a, b) { + return a * b +} +``` When encountering the `return` keyword further execution of the function is terminated and the specified value is supplied to the caller of the function: - log(multiply(3, 5)) +``` +log(multiply(3, 5)) +``` In this example the `multiply` function we declared earlier is invoked with two arguments (3 and 5). The function computes the product of those arguments and makes the result available to the @@ -642,16 +822,20 @@ Functions which do not have a `return` statement have their return value set to last expression which was performed by the function. For example, we could have also written our `multiply` function like this: - function multiply(a, b) { - a * b - } +``` +function multiply(a, b) { + a * b +} +``` Anonymous functions can be created by omitting the name in the function definition. The resulting function object can be used like any other value: - var fn = function() { 3 } +``` +var fn = function() { 3 } - fn() /* Returns 3 */ +fn() /* Returns 3 */ +``` ## Lambda Expressions @@ -659,20 +843,26 @@ Functions can also be declared using the alternative lambda syntax. Example: - f = (x) => x * x +``` +f = (x) => x * x +``` Multiple statements can be used by putting the function body into braces: - f = (x) => { - log("Lambda called") - x * x - } +``` +f = (x) => { + log("Lambda called") + x * x +} +``` Just like with ordinary functions the return value is the value of the last statement. For lambdas which take exactly one argument the braces around the arguments can be omitted: - f = x => x * x +``` +f = x => x * x +``` ## Abbreviated Lambda Syntax @@ -680,7 +870,9 @@ Lambdas which take no arguments can also be written using the abbreviated lambda Example: - f = {{ 3 }} +``` +f = {{ 3 }} +``` This creates a new function which returns the value 3. @@ -696,10 +888,12 @@ already exists there: The local scope contains variables which only exist during the invocation of the current function, object or apply statement. Local variables can be declared using the `var` keyword: - function multiply(a, b) { - var temp = a * b - return temp - } +``` +function multiply(a, b) { + var temp = a * b + return temp +} +``` Each time the `multiply` function is invoked a new `temp` variable is used which is in no way related to previous invocations of the function. @@ -710,40 +904,45 @@ the `this` scope is used. The `this` scope refers to the current object which the function or object/apply statement operates on. - object Host "localhost" { - check_interval = 5m - } +``` +object Host "localhost" { + check_interval = 5m +} +``` In this example the `this` scope refers to the "localhost" object. The `check_interval` attribute is set for this particular host. You can explicitly access the `this` scope using the `this` keyword: - object Host "localhost" { - var check_interval = 5m - - /* This explicitly specifies that the attribute should be set - * for the host, if we had omitted `this.` the (poorly named) - * local variable `check_interval` would have been modified instead. - */ - this.check_interval = 1m - } +``` +object Host "localhost" { + var check_interval = 5m + /* This explicitly specifies that the attribute should be set + * for the host, if we had omitted `this.` the (poorly named) + * local variable `check_interval` would have been modified instead. + */ + this.check_interval = 1m +} +``` Similarly the keywords `locals` and `globals` are available to access the local and global scope. Functions also have a `this` scope. However unlike for object/apply statements the `this` scope for a function is set to whichever object was used to invoke the function. Here's an example: - hm = { - h_word = null - - function init(word) { - h_word = word - } - } +``` + hm = { + h_word = null - /* Let's invoke the init() function */ - hm.init("hello") + function init(word) { + h_word = word + } + } + + /* Let's invoke the init() function */ + hm.init("hello") +``` We're using `hm.init` to invoke the function which causes the value of `hm` to become the `this` scope for this function call. @@ -755,22 +954,26 @@ outside of their scope (except for global variables). In order to access variables which are defined in the outer scope the `use` keyword can be used: - function MakeHelloFunction(name) { - return function() use(name) { - log("Hello, " + name) - } - } +``` +function MakeHelloFunction(name) { + return function() use(name) { + log("Hello, " + name) + } +} +``` In this case a new variable `name` is created inside the inner function's scope which has the value of the `name` function argument. Alternatively a different value for the inner variable can be specified: - function MakeHelloFunction(name) { - return function() use (greeting = "Hello, " + name) { - log(greeting) - } - } +``` +function MakeHelloFunction(name) { + return function() use (greeting = "Hello, " + name) { + log(greeting) + } +} +``` ## Conditional Statements @@ -779,26 +982,30 @@ construct can be used to accomplish this. Example: - a = 3 +``` +a = 3 - if (a < 5) { - a *= 7 - } else if (a > 10) { - a *= 5 - } else { - a *= 2 - } +if (a < 5) { + a *= 7 +} else if (a > 10) { + a *= 5 +} else { + a *= 2 +} +``` An if/else construct can also be used in place of any other value. The value of an if/else statement is the value of the last statement which was evaluated for the branch which was taken: - a = if (true) { - log("Taking the 'true' branch") - 7 * 3 - } else { - log("Taking the 'false' branch") - 9 - } +``` +a = if (true) { + log("Taking the 'true' branch") + 7 * 3 +} else { + log("Taking the 'false' branch") + 9 +} +``` This example prints the log message "Taking the 'true' branch" and the `a` variable is set to 21 (7 * 3). @@ -811,12 +1018,14 @@ This is repeated until the condition is no longer true. Example: - var num = 5 +``` +var num = 5 - while (num > 5) { - log("Test") - num -= 1 - } +while (num > 5) { + log("Test") + num -= 1 +} +``` The `continue` and `break` keywords can be used to control how the loop is executed: The `continue` keyword skips over the remaining expressions for the loop body and begins the next loop evaluation. The `break` keyword @@ -828,42 +1037,53 @@ The `for` statement can be used to iterate over arrays and dictionaries. Example: - var list = [ "a", "b", "c" ] +``` +var list = [ "a", "b", "c" ] - for (item in list) { - log("Item: " + item) - } +for (var item in list) { + log("Item: " + item) +} +``` The loop body is evaluated once for each item in the array. The variable `item` is declared as a local variable just as if the `var` keyword had been used. Iterating over dictionaries can be accomplished in a similar manner: - var dict = { a = 3, b = 7 } +``` +var dict = { a = 3, b = 7 } - for (key => value in dict) { - log("Key: " + key + ", Value: " + value) - } +for (var key => var value in dict) { + log("Key: " + key + ", Value: " + value) +} +``` The `continue` and `break` keywords can be used to control how the loop is executed: The `continue` keyword skips over the remaining expressions for the loop body and begins the next loop evaluation. The `break` keyword breaks out of the loop. +The `var` keyword is optional when declaring variables in the loop's header. Variables declared without the `var` +keyword are nonetheless local to the function. + ## Constructors In order to create a new value of a specific type constructor calls may be used. Example: - var pd = PerfdataValue() - pd.label = "test" - pd.value = 10 +``` +var pd = PerfdataValue() +pd.label = "test" +pd.value = 10 +``` You can also try to convert an existing value to another type by specifying it as an argument for the constructor call. Example: - var s = String(3) /* Sets s to "3". */ +``` +var s = String(3) /* Sets s to "3". */ +``` ## Throwing Exceptions @@ -872,7 +1092,9 @@ using the `throw` keyword. Example: - throw "An error occurred." +``` +throw "An error occurred." +``` ## Handling Exceptions @@ -881,13 +1103,15 @@ Exceptions can be handled using the `try` and `except` keywords. When an excepti Example: - try { - throw "Test" +``` +try { + throw "Test" - log("This statement won't get executed.") - } except { - log("An error occurred in the try clause.") - } + log("This statement won't get executed.") +} except { + log("An error occurred in the try clause.") +} +``` ## Breakpoints @@ -899,7 +1123,9 @@ By default breakpoints have no effect unless Icinga is started with the `--scrip All values have a static type. The `typeof` function can be used to determine the type of a value: - typeof(3) /* Returns an object which represents the type for numbers */ +``` +typeof(3) /* Returns an object which represents the type for numbers */ +``` The following built-in types are available: @@ -918,14 +1144,18 @@ e.g. Host, Service, CheckCommand, etc. Each type has an associated type object which describes the type's semantics. These type objects are made available using global variables which match the type's name: - /* This logs 'true' */ - log(typeof(3) == Number) +``` +/* This logs 'true' */ +log(typeof(3) == Number) +``` The type object's `prototype` property can be used to find out which methods a certain type supports: - /* This returns: ["contains","find","len","lower","replace","reverse","split","substr","to_string","trim","upper"] */ - keys(String.prototype) +``` +/* This returns: ["contains","find","len","lower","replace","reverse","split","substr","to_string","trim","upper"] */ +keys(String.prototype) +``` Additional documentation on type methods is available in the [library reference](18-library-reference.md#library-reference). @@ -937,76 +1167,84 @@ The location of the currently executing script can be obtained using the Example: - log("Hello from '" + current_filename + "' in line " + current_line) +``` +log("Hello from '" + current_filename + "' in line " + current_line) +``` ## Reserved Keywords -These keywords are reserved and must not be used as constants or custom attributes. - - object - template - include - include_recursive - include_zones - library - null - true - false - const - var - this - globals - locals - use - default - ignore_on_error - current_filename - current_line - apply - to - where - import - assign - ignore - function - return - break - continue - for - if - else - while - throw - try - except - in +These keywords are reserved and must not be used as constants or custom variables. +``` +object +template +include +include_recursive +include_zones +library +null +true +false +const +var +this +globals +locals +use +default +ignore_on_error +current_filename +current_line +apply +to +where +import +assign +ignore +function +return +break +continue +for +if +else +while +throw +try +except +in +using +namespace +``` You can escape reserved keywords using the `@` character. The following example tries to set `vars.include` which references a reserved keyword and generates an error: - [2014-09-15 17:24:00 +0200] critical/config: Location: - /etc/icinga2/conf.d/hosts/localhost.conf(13): vars.sla = "24x7" - /etc/icinga2/conf.d/hosts/localhost.conf(14): - /etc/icinga2/conf.d/hosts/localhost.conf(15): vars.include = "some cmdb export field" - ^^^^^^^ - /etc/icinga2/conf.d/hosts/localhost.conf(16): } - /etc/icinga2/conf.d/hosts/localhost.conf(17): +``` +[2014-09-15 17:24:00 +0200] critical/config: Location: +/etc/icinga2/conf.d/hosts/localhost.conf(13): vars.sla = "24x7" +/etc/icinga2/conf.d/hosts/localhost.conf(14): +/etc/icinga2/conf.d/hosts/localhost.conf(15): vars.include = "some cmdb export field" + ^^^^^^^ +/etc/icinga2/conf.d/hosts/localhost.conf(16): } +/etc/icinga2/conf.d/hosts/localhost.conf(17): - Config error: in /etc/icinga2/conf.d/hosts/localhost.conf: 15:8-15:14: syntax error, unexpected include (T_INCLUDE), expecting T_IDENTIFIER - [2014-09-15 17:24:00 +0200] critical/config: 1 errors, 0 warnings. +Config error: in /etc/icinga2/conf.d/hosts/localhost.conf: 15:8-15:14: syntax error, unexpected include (T_INCLUDE), expecting T_IDENTIFIER +[2014-09-15 17:24:00 +0200] critical/config: 1 errors, 0 warnings. +``` You can escape the `include` keyword by prefixing it with an additional `@` character: - object Host "localhost" { - import "generic-host" +``` +object Host "localhost" { + import "generic-host" - address = "127.0.0.1" - address6 = "::1" + address = "127.0.0.1" + address6 = "::1" - vars.os = "Linux" - vars.sla = "24x7" - - vars.@include = "some cmdb export field" - } + vars.os = "Linux" + vars.sla = "24x7" + vars.@include = "some cmdb export field" +} +``` diff --git a/doc/18-library-reference.md b/doc/18-library-reference.md index 5a3209dfe..9ec4acd31 100644 --- a/doc/18-library-reference.md +++ b/doc/18-library-reference.md @@ -14,7 +14,9 @@ them in your scenarios. Signature: - function regex(pattern, value, mode) +``` +function regex(pattern, value, mode) +``` Returns true if the regular expression `pattern` matches the `value`, false otherwise. The `value` can be of the type [String](18-library-reference.md#string-type) or [Array](18-library-reference.md#array-type) (which @@ -28,32 +30,37 @@ The default mode is `MatchAll`. Example for string values: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => host.vars.os_type = "Linux/Unix" - null - <2> => regex("^Linux", host.vars.os_type) - true - <3> => regex("^Linux$", host.vars.os_type) - false +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => host.vars.os_type = "Linux/Unix" +null +<2> => regex("^Linux", host.vars.os_type) +true +<3> => regex("^Linux$", host.vars.os_type) +false +``` Example for an array of string values: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => host.vars.databases = [ "db-prod1", "db-prod2", "db-dev" ] - null - <2> => regex("^db-prod\\d+", host.vars.databases, MatchAny) - true - <3> => regex("^db-prod\\d+", host.vars.databases, MatchAll) - false - +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => host.vars.databases = [ "db-prod1", "db-prod2", "db-dev" ] +null +<2> => regex("^db-prod\\d+", host.vars.databases, MatchAny) +true +<3> => regex("^db-prod\\d+", host.vars.databases, MatchAll) +false +``` ### match Signature: - function match(pattern, text, mode) +``` +function match(pattern, text, mode) +``` Returns true if the wildcard (`?*`) `pattern` matches the `value`, false otherwise. The `value` can be of the type [String](18-library-reference.md#string-type) or [Array](18-library-reference.md#array-type) (which @@ -65,32 +72,37 @@ The default mode is `MatchAll`. Example for string values: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => var name = "db-prod-sfo-657" - null - <2> => match("*prod-sfo*", name) - true - <3> => match("*-dev-*", name) - false +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => var name = "db-prod-sfo-657" +null +<2> => match("*prod-sfo*", name) +true +<3> => match("*-dev-*", name) +false +``` Example for an array of string values: - $ icinga2 console - Icinga 2 (version: v2.7.0-28) - <1> => host.vars.application_types = [ "web-wp", "web-rt", "db-local" ] - null - <2> => match("web-*", host.vars.application_types, MatchAll) - false - <3> => match("web-*", host.vars.application_types, MatchAny) - true - +``` +$ icinga2 console +Icinga 2 (version: v2.11.0-28) +<1> => host.vars.application_types = [ "web-wp", "web-rt", "db-local" ] +null +<2> => match("web-*", host.vars.application_types, MatchAll) +false +<3> => match("web-*", host.vars.application_types, MatchAny) +true +``` ### cidr_match Signature: - function cidr_match(pattern, ip, mode) +``` +function cidr_match(pattern, ip, mode) +``` Returns true if the CIDR pattern matches the IP address, false otherwise. @@ -102,33 +114,39 @@ either `MatchAll` (in which case all elements for an array have to match) or `Ma Example for a single IP address: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => host.address = "192.168.56.101" - null - <2> => cidr_match("192.168.56.0/24", host.address) - true - <3> => cidr_match("192.168.56.0/26", host.address) - false +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => host.address = "192.168.56.101" +null +<2> => cidr_match("192.168.56.0/24", host.address) +true +<3> => cidr_match("192.168.56.0/26", host.address) +false +``` Example for an array of IP addresses: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => host.vars.vhost_ips = [ "192.168.56.101", "192.168.56.102", "10.0.10.99" ] - null - <2> => cidr_match("192.168.56.0/24", host.vars.vhost_ips, MatchAll) - false - <3> => cidr_match("192.168.56.0/24", host.vars.vhost_ips, MatchAny) - true +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => host.vars.vhost_ips = [ "192.168.56.101", "192.168.56.102", "10.0.10.99" ] +null +<2> => cidr_match("192.168.56.0/24", host.vars.vhost_ips, MatchAll) +false +<3> => cidr_match("192.168.56.0/24", host.vars.vhost_ips, MatchAny) +true +``` ### range Signature: - function range(end) - function range(start, end) - function range(start, end, increment) +``` +function range(end) +function range(start, end) +function range(start, end, increment) +``` Returns an array of numbers in the specified range. If you specify one parameter, the first element starts at `0`. @@ -142,20 +160,24 @@ as third parameter. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => range(5) - [ 0.000000, 1.000000, 2.000000, 3.000000, 4.000000 ] - <2> => range(2,4) - [ 2.000000, 3.000000 ] - <3> => range(2,10,2) - [ 2.000000, 4.000000, 6.000000, 8.000000 ] +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => range(5) +[ 0.000000, 1.000000, 2.000000, 3.000000, 4.000000 ] +<2> => range(2,4) +[ 2.000000, 3.000000 ] +<3> => range(2,10,2) +[ 2.000000, 4.000000, 6.000000, 8.000000 ] +``` ### len Signature: - function len(value) +``` +function len(value) +``` Returns the length of the value, i.e. the number of elements for an array or dictionary, or the length of the string in bytes. @@ -166,68 +188,78 @@ prototype method: [Array#len](18-library-reference.md#array-len), [Dictionary#le Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => host.groups = [ "linux-servers", "db-servers" ] - null - <2> => host.groups.len() - 2.000000 - <3> => host.vars.disks["/"] = {} - null - <4> => host.vars.disks["/var"] = {} - null - <5> => host.vars.disks.len() - 2.000000 - <6> => host.vars.os_type = "Linux/Unix" - null - <7> => host.vars.os_type.len() - 10.000000 - +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => host.groups = [ "linux-servers", "db-servers" ] +null +<2> => host.groups.len() +2.000000 +<3> => host.vars.disks["/"] = {} +null +<4> => host.vars.disks["/var"] = {} +null +<5> => host.vars.disks.len() +2.000000 +<6> => host.vars.os_type = "Linux/Unix" +null +<7> => host.vars.os_type.len() +10.000000 +``` ### union Signature: - function union(array, array, ...) +``` +function union(array, array, ...) +``` Returns an array containing all unique elements from the specified arrays. Example: - - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => var dev_notification_groups = [ "devs", "slack" ] - null - <2> => var host_notification_groups = [ "slack", "noc" ] - null - <3> => union(dev_notification_groups, host_notification_groups) - [ "devs", "noc", "slack" ] +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => var dev_notification_groups = [ "devs", "slack" ] +null +<2> => var host_notification_groups = [ "slack", "noc" ] +null +<3> => union(dev_notification_groups, host_notification_groups) +[ "devs", "noc", "slack" ] +``` ### intersection Signature: - function intersection(array, array, ...) +``` +function intersection(array, array, ...) +``` Returns an array containing all unique elements which are common to all specified arrays. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => var dev_notification_groups = [ "devs", "slack" ] - null - <2> => var host_notification_groups = [ "slack", "noc" ] - null - <3> => intersection(dev_notification_groups, host_notification_groups) - [ "slack" ] +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => var dev_notification_groups = [ "devs", "slack" ] +null +<2> => var host_notification_groups = [ "slack", "noc" ] +null +<3> => intersection(dev_notification_groups, host_notification_groups) +[ "slack" ] +``` ### keys Signature: - function keys(dict) +``` +function keys(dict) +``` Returns an array containing the dictionary's keys. @@ -236,20 +268,24 @@ prototype method: [Dictionary#keys](18-library-reference.md#dictionary-keys). Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => host.vars.disks["/"] = {} - null - <2> => host.vars.disks["/var"] = {} - null - <3> => host.vars.disks.keys() - [ "/", "/var" ] +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => host.vars.disks["/"] = {} +null +<2> => host.vars.disks["/var"] = {} +null +<3> => host.vars.disks.keys() +[ "/", "/var" ] +``` ### string Signature: - function string(value) +``` +function string(value) +``` Converts the value to a string. @@ -264,81 +300,99 @@ prototype method: Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => 5.to_string() - "5" - <2> => false.to_string() - "false" - <3> => "abc".to_string() - "abc" - <4> => [ "dev", "slack" ].to_string() - "[ \"dev\", \"slack\" ]" - <5> => { "/" = {}, "/var" = {} }.to_string() - "{\n\t\"/\" = {\n\t}\n\t\"/var\" = {\n\t}\n}" - <6> => DateTime(2016, 11, 25).to_string() - "2016-11-25 00:00:00 +0100" +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => 5.to_string() +"5" +<2> => false.to_string() +"false" +<3> => "abc".to_string() +"abc" +<4> => [ "dev", "slack" ].to_string() +"[ \"dev\", \"slack\" ]" +<5> => { "/" = {}, "/var" = {} }.to_string() +"{\n\t\"/\" = {\n\t}\n\t\"/var\" = {\n\t}\n}" +<6> => DateTime(2016, 11, 25).to_string() +"2016-11-25 00:00:00 +0100" +``` ### number Signature: - function number(value) +``` +function number(value) +``` Converts the value to a number. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => number(false) - 0.000000 - <2> => number("78") - 78.000000 +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => number(false) +0.000000 +<2> => number("78") +78.000000 +``` ### bool Signature: - function bool(value) +``` +function bool(value) +``` Converts the value to a bool. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => bool(1) - true - <2> => bool(0) - false +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => bool(1) +true +<2> => bool(0) +false +``` ### random Signature: - function random() +``` +function random() +``` Returns a random value between 0 and RAND\_MAX (as defined in stdlib.h). - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => random() - 1263171996.000000 - <2> => random() - 108402530.000000 +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => random() +1263171996.000000 +<2> => random() +108402530.000000 +``` ### log Signature: - function log(value) +``` +function log(value) +``` Writes a message to the log. Non-string values are converted to a JSON string. Signature: - function log(severity, facility, value) +``` +function log(severity, facility, value) +``` Writes a message to the log. `severity` can be one of `LogDebug`, `LogNotice`, `LogInformation`, `LogWarning`, and `LogCritical`. @@ -347,140 +401,188 @@ Non-string values are converted to a JSON string. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => log(LogCritical, "Console", "First line") - critical/Console: First line - null - <2> => var groups = [ "devs", "slack" ] - null - <3> => log(LogCritical, "Console", groups) - critical/Console: ["devs","slack"] - null +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => log(LogCritical, "Console", "First line") +critical/Console: First line +null +<2> => var groups = [ "devs", "slack" ] +null +<3> => log(LogCritical, "Console", groups) +critical/Console: ["devs","slack"] +null +``` ### typeof Signature: - function typeof(value) +``` +function typeof(value) +``` Returns the [Type](18-library-reference.md#type-type) object for a value. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => typeof(3) == Number - true - <2> => typeof("str") == String - true - <3> => typeof(true) == Boolean - true - <4> => typeof([ 1, 2, 3]) == Array - true - <5> => typeof({ a = 2, b = 3 }) == Dictionary - true +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => typeof(3) == Number +true +<2> => typeof("str") == String +true +<3> => typeof(true) == Boolean +true +<4> => typeof([ 1, 2, 3]) == Array +true +<5> => typeof({ a = 2, b = 3 }) == Dictionary +true +``` ### get_time Signature: - function get_time() +``` +function get_time() +``` Returns the current UNIX timestamp as floating point number. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => get_time() - 1480072135.633008 - <2> => get_time() - 1480072140.401207 +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => get_time() +1480072135.633008 +<2> => get_time() +1480072140.401207 +``` ### parse_performance_data Signature: - function parse_performance_data(pd) +``` +function parse_performance_data(pd) +``` Parses a performance data string and returns an array describing the values. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => var pd = "'time'=1480074205.197363;;;" - null - <2> => parse_performance_data(pd) - { - counter = false - crit = null - label = "time" - max = null - min = null - type = "PerfdataValue" - unit = "" - value = 1480074205.197363 - warn = null - } +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => var pd = "'time'=1480074205.197363;;;" +null +<2> => parse_performance_data(pd) +{ + counter = false + crit = null + label = "time" + max = null + min = null + type = "PerfdataValue" + unit = "" + value = 1480074205.197363 + warn = null +} +``` + +### getenv + +Signature: + +``` +function getenv(key) +``` + +Returns the value from the specified environment variable key. + +Example: + +``` +$ MY_ENV_VAR=icinga2 icinga2 console +Icinga 2 (version: v2.11.0) +Type $help to view available commands. +<1> => getenv("MY_ENV_VAR") +"icinga2" +``` ### dirname Signature: - function dirname(path) +``` +function dirname(path) +``` Returns the directory portion of the specified path. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => var path = "/etc/icinga2/scripts/xmpp-notification.pl" - null - <2> => dirname(path) - "/etc/icinga2/scripts" +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => var path = "/etc/icinga2/scripts/xmpp-notification.pl" +null +<2> => dirname(path) +"/etc/icinga2/scripts" +``` ### basename Signature: - function basename(path) +``` +function basename(path) +``` Returns the filename portion of the specified path. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => var path = "/etc/icinga2/scripts/xmpp-notification.pl" - null - <2> => basename(path) - "xmpp-notification.pl" +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => var path = "/etc/icinga2/scripts/xmpp-notification.pl" +null +<2> => basename(path) +"xmpp-notification.pl" +``` ### path\_exists Signature: - function path_exists(path) +``` +function path_exists(path) +``` Returns true if the specified path exists, false otherwise. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => var path = "/etc/icinga2/scripts/xmpp-notification.pl" - null - <2> => path_exists(path) - true +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => var path = "/etc/icinga2/scripts/xmpp-notification.pl" +null +<2> => path_exists(path) +true +``` ### glob Signature: - function glob(pathSpec, type) +``` +function glob(pathSpec, type) +``` Returns an array containing all paths which match the `pathSpec` argument. @@ -489,18 +591,22 @@ The `type` argument is optional and specifies which types of paths are matched. This can be a combination of the `GlobFile` and `GlobDirectory` constants. The default value is `GlobFile | GlobDirectory`. - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => var pathSpec = "/etc/icinga2/conf.d/*.conf" - null - <2> => glob(pathSpec) - [ "/etc/icinga2/conf.d/app.conf", "/etc/icinga2/conf.d/commands.conf", ... ] +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => var pathSpec = "/etc/icinga2/conf.d/*.conf" +null +<2> => glob(pathSpec) +[ "/etc/icinga2/conf.d/app.conf", "/etc/icinga2/conf.d/commands.conf", ... ] +``` ### glob\_recursive Signature: - function glob_recursive(path, pattern, type) +``` +function glob_recursive(path, pattern, type) +``` Recursively descends into the specified directory and returns an array containing all paths which match the `pattern` argument. @@ -509,50 +615,62 @@ The `type` argument is optional and specifies which types of paths are matched. This can be a combination of the `GlobFile` and `GlobDirectory` constants. The default value is `GlobFile | GlobDirectory`. - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => var path = "/etc/icinga2/zones.d/" - null - <2> => var pattern = "*.conf" - null - <3> => glob_recursive(path, pattern) - [ "/etc/icinga2/zones.d/global-templates/templates.conf", "/etc/icinga2/zones.d/master/hosts.conf", ... ] +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => var path = "/etc/icinga2/zones.d/" +null +<2> => var pattern = "*.conf" +null +<3> => glob_recursive(path, pattern) +[ "/etc/icinga2/zones.d/global-templates/templates.conf", "/etc/icinga2/zones.d/master/hosts.conf", ... ] +``` ### escape_shell_arg Signature: - function escape_shell_arg(text) +``` +function escape_shell_arg(text) +``` Escapes a string for use as a single shell argument. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => escape_shell_arg("'$host.name$' '$service.name$'") - "''\\''$host.name$'\\'' '\\''$service.name$'\\'''" +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => escape_shell_arg("'$host.name$' '$service.name$'") +"''\\''$host.name$'\\'' '\\''$service.name$'\\'''" +``` ### escape_shell_cmd Signature: - function escape_shell_cmd(text) +``` +function escape_shell_cmd(text) +``` Escapes shell meta characters in a string. Example: - $ icinga2 console - Icinga 2 (version: v2.7.0) - <1> => escape_shell_cmd("/bin/echo 'shell test' $ENV") - "/bin/echo 'shell test' \\$ENV" +``` +$ icinga2 console +Icinga 2 (version: v2.11.0) +<1> => escape_shell_cmd("/bin/echo 'shell test' $ENV") +"/bin/echo 'shell test' \\$ENV" +``` ### escape_create_process_arg Signature: - function escape_create_process_arg(text) +``` +function escape_create_process_arg(text) +``` Escapes a string for use as an argument for CreateProcess(). Windows only. @@ -560,7 +678,9 @@ Escapes a string for use as an argument for CreateProcess(). Windows only. Signature: - function sleep(interval) +``` +function sleep(interval) +``` Sleeps for the specified amount of time (in seconds). @@ -584,10 +704,10 @@ The returned value depends on the attribute value which is resolved from the specified runtime macro. This function is only available in runtime evaluated functions, e.g. -for [custom attributes](03-monitoring-basics.md#custom-attributes-functions) which +for [custom variables](03-monitoring-basics.md#custom-variables-functions) which use the [abbreviated lambda syntax](17-language-reference.md#nullary-lambdas). -This example sets the `snmp_address` custom attribute +This example sets the `snmp_address` custom variable based on `$address$` and `$address6$`. ``` @@ -614,7 +734,9 @@ These functions can be used to retrieve a reference to another object by name. Signature: - function get_check_command(name); +``` +function get_check_command(name); +``` Returns the CheckCommand object with the specified name, or `null` if no such CheckCommand object exists. @@ -622,7 +744,9 @@ Returns the CheckCommand object with the specified name, or `null` if no such Ch Signature: - function get_event_command(name); +``` +function get_event_command(name); +``` Returns the EventCommand object with the specified name, or `null` if no such EventCommand object exists. @@ -630,7 +754,9 @@ Returns the EventCommand object with the specified name, or `null` if no such Ev Signature: - function get_notification_command(name); +``` +function get_notification_command(name); +``` Returns the NotificationCommand object with the specified name, or `null` if no such NotificationCommand object exists. @@ -638,7 +764,9 @@ Returns the NotificationCommand object with the specified name, or `null` if no Signature: - function get_host(host_name); +``` +function get_host(host_name); +``` Returns the Host object with the specified name, or `null` if no such Host object exists. @@ -647,8 +775,10 @@ Returns the Host object with the specified name, or `null` if no such Host objec Signature: - function get_service(host_name, service_name); - function get_service(host, service_name); +``` +function get_service(host_name, service_name); +function get_service(host, service_name); +``` Returns the Service object with the specified host name or object and service name pair, or `null` if no such Service object exists. @@ -658,7 +788,7 @@ which fetches the `disk` service object from the current Icinga 2 node: ``` $ ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://root@localhost:5665/' -Icinga 2 (version: v2.7.0) +Icinga 2 (version: v2.11.0) <1> => get_service(NodeName, "disk") <2> => get_service(NodeName, "disk").__name @@ -672,8 +802,10 @@ Icinga 2 (version: v2.7.0) Signature: - function get_services(host_name); - function get_services(host); +``` +function get_services(host_name); +function get_services(host); +``` Returns an [array](17-language-reference.md#array) of service objects for the specified host name or object, or `null` if no such host object exists. @@ -683,7 +815,7 @@ which fetches all service objects from the current Icinga 2 node: ``` $ ICINGA2_API_PASSWORD=icinga icinga2 console --connect 'https://root@localhost:5665/' -Icinga 2 (version: v2.7.0) +Icinga 2 (version: v2.11.0) <1> => get_services(NodeName).map(s => s.name) [ "disk", "disk /", "http", "icinga", "load", "ping4", "ping6", "procs", "ssh", "users" ] @@ -704,7 +836,9 @@ in using the [map](18-library-reference.md#array-map) functionality: Signature: - function get_user(name); +``` +function get_user(name); +``` Returns the User object with the specified name, or `null` if no such User object exists. @@ -712,7 +846,9 @@ Returns the User object with the specified name, or `null` if no such User objec Signature: - function get_host_group(name); +``` +function get_host_group(name); +``` Returns the HostGroup object with the specified name, or `null` if no such HostGroup object exists. @@ -721,7 +857,9 @@ Returns the HostGroup object with the specified name, or `null` if no such HostG Signature: - function get_service_group(name); +``` +function get_service_group(name); +``` Returns the ServiceGroup object with the specified name, or `null` if no such ServiceGroup object exists. @@ -729,7 +867,9 @@ Returns the ServiceGroup object with the specified name, or `null` if no such Se Signature: - function get_user_group(name); +``` +function get_user_group(name); +``` Returns the UserGroup object with the specified name, or `null` if no such UserGroup object exists. @@ -738,7 +878,9 @@ Returns the UserGroup object with the specified name, or `null` if no such UserG Signature: - function get_time_period(name); +``` +function get_time_period(name); +``` Returns the TimePeriod object with the specified name, or `null` if no such TimePeriod object exists. @@ -747,7 +889,9 @@ Returns the TimePeriod object with the specified name, or `null` if no such Time Signature: - function get_object(type, name); +``` +function get_object(type, name); +``` Returns the object with the specified type and name, or `null` if no such object exists. `type` must refer to a type object. @@ -757,7 +901,9 @@ to a type object. Signature: - function get_objects(type); +``` +function get_objects(type); +``` Returns an array of objects whose type matches the specified type. `type` must refer to a type object. @@ -800,7 +946,9 @@ Square root of 2. Signature: - function abs(x); +``` +function abs(x); +``` Returns the absolute value of `x`. @@ -808,7 +956,9 @@ Returns the absolute value of `x`. Signature: - function acos(x); +``` +function acos(x); +``` Returns the arccosine of `x`. @@ -816,7 +966,9 @@ Returns the arccosine of `x`. Signature: - function asin(x); +``` +function asin(x); +``` Returns the arcsine of `x`. @@ -824,7 +976,9 @@ Returns the arcsine of `x`. Signature: - function atan(x); +``` +function atan(x); +``` Returns the arctangent of `x`. @@ -832,15 +986,18 @@ Returns the arctangent of `x`. Signature: - function atan2(y, x); - +``` +function atan2(y, x); +``` Returns the arctangent of the quotient of `y` and `x`. ### Math.ceil Signature: - function ceil(x); +``` +function ceil(x); +``` Returns the smallest integer value not less than `x`. @@ -848,7 +1005,9 @@ Returns the smallest integer value not less than `x`. Signature: - function cos(x); +``` +function cos(x); +``` Returns the cosine of `x`. @@ -856,7 +1015,9 @@ Returns the cosine of `x`. Signature: - function exp(x); +``` +function exp(x); +``` Returns E raised to the `x`th power. @@ -864,7 +1025,9 @@ Returns E raised to the `x`th power. Signature: - function floor(x); +``` +function floor(x); +``` Returns the largest integer value not greater than `x`. @@ -872,7 +1035,9 @@ Returns the largest integer value not greater than `x`. Signature: - function isinf(x); +``` +function isinf(x); +``` Returns whether `x` is infinite. @@ -880,7 +1045,9 @@ Returns whether `x` is infinite. Signature: - function isnan(x); +``` +function isnan(x); +``` Returns whether `x` is NaN (not-a-number). @@ -888,7 +1055,9 @@ Returns whether `x` is NaN (not-a-number). Signature: - function log(x); +``` +function log(x); +``` Returns the natural logarithm of `x`. @@ -896,7 +1065,9 @@ Returns the natural logarithm of `x`. Signature: - function max(...); +``` +function max(...); +``` Returns the largest argument. A variable number of arguments can be specified. If no arguments are given, -Infinity is returned. @@ -905,7 +1076,9 @@ If no arguments are given, -Infinity is returned. Signature: - function min(...); +``` +function min(...); +``` Returns the smallest argument. A variable number of arguments can be specified. If no arguments are given, +Infinity is returned. @@ -914,7 +1087,9 @@ If no arguments are given, +Infinity is returned. Signature: - function pow(x, y); +``` +function pow(x, y); +``` Returns `x` raised to the `y`th power. @@ -922,7 +1097,9 @@ Returns `x` raised to the `y`th power. Signature: - function random(); +``` +function random(); +``` Returns a pseudo-random number between 0 and 1. @@ -930,7 +1107,9 @@ Returns a pseudo-random number between 0 and 1. Signature: - function round(x); +``` +function round(x); +``` Returns `x` rounded to the nearest integer value. @@ -938,7 +1117,9 @@ Returns `x` rounded to the nearest integer value. Signature: - function sign(x); +``` +function sign(x); +``` Returns -1 if `x` is negative, 1 if `x` is positive and 0 if `x` is 0. @@ -947,7 +1128,9 @@ and 0 if `x` is 0. Signature: - function sin(x); +``` +function sin(x); +``` Returns the sine of `x`. @@ -955,7 +1138,9 @@ Returns the sine of `x`. Signature: - function sqrt(x); +``` +function sqrt(x); +``` Returns the square root of `x`. @@ -963,7 +1148,9 @@ Returns the square root of `x`. Signature: - function tan(x); +``` +function tan(x); +``` Returns the tangent of `x`. @@ -975,7 +1162,9 @@ The global `Json` object can be used to encode and decode JSON. Signature: - function encode(x); +``` +function encode(x); +``` Encodes an arbitrary value into JSON. @@ -983,7 +1172,9 @@ Encodes an arbitrary value into JSON. Signature: - function decode(x); +``` +function decode(x); +``` Decodes a JSON string. @@ -993,14 +1184,18 @@ Decodes a JSON string. Signature: - function to_string(); +``` +function to_string(); +``` The `to_string` method returns a string representation of the number. Example: - var example = 7 +``` +var example = 7 example.to_string() /* Returns "7" */ +``` ## Boolean type @@ -1008,14 +1203,18 @@ Example: Signature: - function to_string(); +``` +function to_string(); +``` The `to_string` method returns a string representation of the boolean value. Example: - var example = true +``` +var example = true example.to_string() /* Returns "true" */ +``` ## String type @@ -1023,7 +1222,9 @@ Example: Signature: - function find(str, start); +``` +function find(str, start); +``` Returns the zero-based index at which the string `str` was found in the string. If the string was not found, -1 is returned. `start` specifies the zero-based index at which `find` should @@ -1031,13 +1232,17 @@ start looking for the string (defaults to 0 when not specified). Example: - "Hello World".find("World") /* Returns 6 */ +``` +"Hello World".find("World") /* Returns 6 */ +``` ### String#contains Signature: - function contains(str); +``` +function contains(str); +``` Returns `true` if the string `str` was found in the string. If the string was not found, `false` is returned. Use [find](18-library-reference.md#string-find) @@ -1045,50 +1250,66 @@ for getting the index instead. Example: - "Hello World".contains("World") /* Returns true */ +``` +"Hello World".contains("World") /* Returns true */ +``` ### String#len Signature - function len(); +``` +function len(); +``` Returns the length of the string in bytes. Note that depending on the encoding type of the string this is not necessarily the number of characters. Example: - "Hello World".len() /* Returns 11 */ +``` +"Hello World".len() /* Returns 11 */ +``` ### String#lower Signature: - function lower(); +``` +function lower(); +``` Returns a copy of the string with all of its characters converted to lower-case. Example: - "Hello World".lower() /* Returns "hello world" */ +``` +"Hello World".lower() /* Returns "hello world" */ +``` ### String#upper Signature: - function upper(); +``` +function upper(); +``` Returns a copy of the string with all of its characters converted to upper-case. Example: - "Hello World".upper() /* Returns "HELLO WORLD" */ +``` +"Hello World".upper() /* Returns "HELLO WORLD" */ +``` ### String#replace Signature: - function replace(search, replacement); +``` +function replace(search, replacement); +``` Returns a copy of the string with all occurences of the string specified in `search` replaced with the string specified in `replacement`. @@ -1097,33 +1318,43 @@ with the string specified in `replacement`. Signature: - function split(delimiters); +``` +function split(delimiters); +``` Splits a string into individual parts and returns them as an array. The `delimiters` argument specifies the characters which should be used as delimiters between parts. Example: - "x-7,y".split("-,") /* Returns [ "x", "7", "y" ] */ +``` +"x-7,y".split("-,") /* Returns [ "x", "7", "y" ] */ +``` ### String#substr Signature: - function substr(start, len); +``` +function substr(start, len); +``` Returns a part of a string. The `start` argument specifies the zero-based index at which the part begins. The optional `len` argument specifies the length of the part ("until the end of the string" if omitted). Example: - "Hello World".substr(6) /* Returns "World" */ +``` +"Hello World".substr(6) /* Returns "World" */ +``` ### String#to_string Signature: - function to_string(); +``` +function to_string(); +``` Returns a copy of the string. @@ -1131,7 +1362,9 @@ Returns a copy of the string. Signature: - function reverse(); +``` +function reverse(); +``` Returns a copy of the string in reverse order. @@ -1139,7 +1372,9 @@ Returns a copy of the string in reverse order. Signature: - function trim(); +``` +function trim(); +``` Removes trailing whitespaces and returns the string. @@ -1151,7 +1386,9 @@ This is the base type for all types in the Icinga application. Signature: - function clone(); +``` + function clone(); +``` 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 @@ -1161,7 +1398,9 @@ object is recursively copied. Signature: - function to_string(); +``` +function to_string(); +``` Returns a string representation for the object. Unless overridden this returns a string of the format "Object of type ''" where is the name of the @@ -1169,19 +1408,23 @@ object's type. Example: - [ 3, true ].to_string() /* Returns "[ 3.000000, true ]" */ +``` +[ 3, true ].to_string() /* Returns "[ 3.000000, true ]" */ +``` ### Object#type Signature: - String type; +String type; Returns the object's type name. This attribute is read-only. Example: - get_host("localhost").type /* Returns "Host" */ +``` +get_host("localhost").type /* Returns "Host" */ +``` ## Type type @@ -1195,19 +1438,25 @@ All types are registered as global variables. For example, in order to obtain a Signature: - Type base; +``` +Type base; +``` Returns a reference to the type's base type. This attribute is read-only. Example: - Dictionary.base == Object /* Returns true, because the Dictionary type inherits directly from the Object type. */ +``` +Dictionary.base == Object /* Returns true, because the Dictionary type inherits directly from the Object type. */ +``` ### Type#name Signature: - String name; +``` +String name; +``` Returns the name of the type. @@ -1215,7 +1464,9 @@ Returns the name of the type. Signature: - Object prototype; +``` +Object prototype; +``` Returns the prototype object for the type. When an attribute is accessed on an object that doesn't exist the prototype object is checked to see if an attribute with the requested name exists. If it does, the attribute's value is returned. @@ -1223,7 +1474,9 @@ The prototype functionality is used to implement methods. Example: - 3.to_string() /* Even though '3' does not have a to_string property the Number type's prototype object does. */ +``` +3.to_string() /* Even though '3' does not have a to_string property the Number type's prototype object does. */ +``` ## Array type @@ -1233,7 +1486,9 @@ Inherits methods from the [Object type](18-library-reference.md#object-type). Signature: - function add(value); +``` +function add(value); +``` Adds a new value after the last element in the array. @@ -1241,13 +1496,17 @@ Adds a new value after the last element in the array. Signature: - function clear(); +``` +function clear(); +``` Removes all elements from the array. ### Array#shallow_clone - function shallow_clone(); +``` +function shallow_clone(); +``` Returns a copy of the array. Note that for elements which are reference values (e.g. objects such as arrays and dictionaries) only the references are copied. @@ -1256,7 +1515,9 @@ as arrays and dictionaries) only the references are copied. Signature: - function contains(value); +``` +function contains(value); +``` Returns true if the array contains the specified value, false otherwise. @@ -1264,7 +1525,9 @@ Returns true if the array contains the specified value, false otherwise. Signature: - function freeze() +``` +function freeze() +``` Disallows further modifications to this array. Trying to modify the array will result in an exception. @@ -1272,7 +1535,9 @@ Disallows further modifications to this array. Trying to modify the array will r Signature: - function len(); +``` +function len(); +``` Returns the number of elements contained in the array. @@ -1280,7 +1545,9 @@ Returns the number of elements contained in the array. Signature: - function remove(index); +``` +function remove(index); +``` Removes the element at the specified zero-based index. @@ -1288,7 +1555,9 @@ Removes the element at the specified zero-based index. Signature: - function set(index, value); +``` +function set(index, value); +``` Sets the element at the zero-based index to the specified value. The `index` must refer to an element which already exists in the array. @@ -1297,7 +1566,9 @@ which already exists in the array. Signature: - function get(index); +``` +function get(index); +``` Retrieves the element at the specified zero-based index. @@ -1305,7 +1576,9 @@ Retrieves the element at the specified zero-based index. Signature: - function sort(less_cmp); +``` +function sort(less_cmp); +``` Returns a copy of the array where all items are sorted. The items are compared using the `<` (less-than) operator. A custom comparator function @@ -1315,7 +1588,9 @@ can be specified with the `less_cmp` argument. Signature: - function join(separator); +``` +function join(separator); +``` Joins all elements of the array using the specified separator. @@ -1323,7 +1598,9 @@ Joins all elements of the array using the specified separator. Signature: - function reverse(); +``` +function reverse(); +``` Returns a new array with all elements of the current array in reverse order. @@ -1331,7 +1608,9 @@ Returns a new array with all elements of the current array in reverse order. Signature: - function map(func); +``` +function map(func); +``` Calls `func(element)` for each of the elements in the array and returns a new array containing the return values of these function calls. @@ -1340,7 +1619,9 @@ a new array containing the return values of these function calls. Signature: - function reduce(func); +``` +function reduce(func); +``` Reduces the elements of the array into a single value by calling the provided function `func` as `func(a, b)` repeatedly where `a` and `b` are elements of the array @@ -1350,7 +1631,9 @@ or results from previous function calls. Signature: - function filter(func); +``` +function filter(func); +``` Returns a copy of the array containing only the elements for which `func(element)` is true. @@ -1359,7 +1642,9 @@ is true. Signature: - function any(func); +``` +function any(func); +``` Returns true if the array contains at least one element for which `func(element)` is true, false otherwise. @@ -1368,7 +1653,9 @@ is true, false otherwise. Signature: - function all(func); +``` +function all(func); +``` Returns true if the array contains only elements for which `func(element)` is true, false otherwise. @@ -1377,7 +1664,9 @@ is true, false otherwise. Signature: - function unique(); +``` +function unique(); +``` Returns a copy of the array with all duplicate elements removed. The original order of the array is not preserved. @@ -1390,7 +1679,9 @@ Inherits methods from the [Object type](18-library-reference.md#object-type). Signature: - function shallow_clone(); +``` +function shallow_clone(); +``` Returns a copy of the dictionary. Note that for elements which are reference values (e.g. objects such as arrays and dictionaries) only the references are copied. @@ -1399,7 +1690,9 @@ as arrays and dictionaries) only the references are copied. Signature: - function contains(key); +``` +function contains(key); +``` Returns true if a dictionary item with the specified `key` exists, false otherwise. @@ -1407,7 +1700,9 @@ Returns true if a dictionary item with the specified `key` exists, false otherwi Signature: - function freeze() +``` +function freeze() +``` Disallows further modifications to this dictionary. Trying to modify the dictionary will result in an exception. @@ -1415,7 +1710,9 @@ Disallows further modifications to this dictionary. Trying to modify the diction Signature: - function len(); +``` +function len(); +``` Returns the number of items contained in the dictionary. @@ -1423,16 +1720,30 @@ Returns the number of items contained in the dictionary. Signature: - function remove(key); +``` +function remove(key); +``` Removes the item with the specified `key`. Trying to remove an item which does not exist is a no-op. +### Dictionary#clear + +Signature: + +``` +function clear(); +``` + +Removes all items from the dictionary. + ### Dictionary#set Signature: - function set(key, value); +``` +function set(key, value); +``` Creates or updates an item with the specified `key` and `value`. @@ -1440,7 +1751,9 @@ Creates or updates an item with the specified `key` and `value`. Signature: - function get(key); +``` +function get(key); +``` Retrieves the value for the specified `key`. Returns `null` if they `key` does not exist in the dictionary. @@ -1449,7 +1762,9 @@ in the dictionary. Signature: - function keys(); +``` +function keys(); +``` Returns a list of keys for all items that are currently in the dictionary. @@ -1457,7 +1772,9 @@ Returns a list of keys for all items that are currently in the dictionary. Signature: - function values(); +``` +function values(); +``` Returns a list of values for all items that are currently in the dictionary. @@ -1469,41 +1786,49 @@ Inherits methods from the [Object type](18-library-reference.md#object-type). Signature: - function call(thisArg, ...); +``` +function call(thisArg, ...); +``` Invokes the function using an alternative `this` scope. The `thisArg` argument specifies the `this` scope for the function. All other arguments are passed directly to the function. Example: - function set_x(val) { - this.x = val - } +``` +function set_x(val) { + this.x = val +} - dict = {} +dict = {} - set_x.call(dict, 7) /* Invokes set_x using `dict` as `this` */ +set_x.call(dict, 7) /* Invokes set_x using `dict` as `this` */ +``` ### Function#callv Signature: - function callv(thisArg, args); +``` +function callv(thisArg, args); +``` Invokes the function using an alternative `this` scope. The `thisArg` argument specifies the `this` scope for the function. The items in the `args` array are passed to the function as individual arguments. Example: - function set_x(val) { - this.x = val - } +``` +function set_x(val) { + this.x = val +} - var dict = {} +var dict = {} - var args = [ 7 ] +var args = [ 7 ] - set_x.callv(dict, args) /* Invokes set_x using `dict` as `this` */ +set_x.callv(dict, args) /* Invokes set_x using `dict` as `this` */ +``` ## DateTime type @@ -1513,18 +1838,22 @@ Inherits methods from the [Object type](18-library-reference.md#object-type). Signature: - function DateTime() - function DateTime(unixTimestamp) - function DateTime(year, month, day) - function DateTime(year, month, day, hours, minutes, seconds) +``` +function DateTime() +function DateTime(unixTimestamp) +function DateTime(year, month, day) +function DateTime(year, month, day, hours, minutes, seconds) +``` Constructs a new DateTime object. When no arguments are specified for the constructor a new DateTime object representing the current time is created. Example: - var d1 = DateTime() /* current time */ - var d2 = DateTime(2016, 5, 21) /* midnight April 21st, 2016 (local time) */ +``` +var d1 = DateTime() /* current time */ +var d2 = DateTime(2016, 5, 21) /* midnight April 21st, 2016 (local time) */ +``` ### DateTime arithmetic @@ -1532,41 +1861,55 @@ Subtracting two DateTime objects yields the interval between them, in seconds. Example: - var delta = DateTime() - DateTime(2016, 5, 21) /* seconds since midnight April 21st, 2016 */ +``` +var delta = DateTime() - DateTime(2016, 5, 21) /* seconds since midnight April 21st, 2016 */ +``` Subtracting a number from a DateTime object yields a new DateTime object that is further in the past: Example: - var dt = DateTime() - 2 * 60 * 60 /* Current time minus 2 hours */ +``` +var dt = DateTime() - 2 * 60 * 60 /* Current time minus 2 hours */ +``` Adding a number to a DateTime object yields a new DateTime object that is in the future: Example: - var dt = DateTime() + 24 * 60 60 /* Current time plus 24 hours */ +``` +var dt = DateTime() + 24 * 60 * 60 /* Current time plus 24 hours */ +``` ### DateTime#format Signature: - function format(fmt) +``` +function format(fmt) +``` Returns a string representation for the DateTime object using the specified format string. The format string may contain format conversion placeholders as specified in strftime(3). Example: - var s = DateTime(2016, 4, 21).format("%A") /* Sets s to "Thursday". */ +``` +var s = DateTime(2016, 4, 21).format("%A") /* Sets s to "Thursday". */ +``` ### DateTime#to_string Signature: - function to_string() +``` +function to_string() +``` Returns a string representation for the DateTime object. Uses a suitable default format. Example: - var s = DateTime(2016, 4, 21).to_string() /* Sets s to "2016-04-21 00:00:00 +0200". */ +``` +var s = DateTime(2016, 4, 21).to_string() /* Sets s to "2016-04-21 00:00:00 +0200". */ +``` diff --git a/doc/19-technical-concepts.md b/doc/19-technical-concepts.md index 1932a6097..6b1718773 100644 --- a/doc/19-technical-concepts.md +++ b/doc/19-technical-concepts.md @@ -1,19 +1,223 @@ # Technical Concepts -This chapter provides insights into specific Icinga 2 -components, libraries, features and any other technical concept -and design. +This chapter provides technical concepts and design insights +into specific Icinga 2 components such as: + +* [Application](19-technical-concepts.md#technical-concepts-application) +* [Configuration](19-technical-concepts.md#technical-concepts-configuration) +* [Features](19-technical-concepts.md#technical-concepts-features) +* [Check Scheduler](19-technical-concepts.md#technical-concepts-check-scheduler) +* [Checks](19-technical-concepts.md#technical-concepts-checks) +* [Cluster](19-technical-concepts.md#technical-concepts-cluster) +* [TLS Network IO](19-technical-concepts.md#technical-concepts-tls-network-io) - + +The config compiler initializes the scanner inside the [lexer](19-technical-concepts.md#technical-concepts-configuration-lexer) +stage. + +The configuration files are parsed into memory from inside the [daemon CLI command](19-technical-concepts.md#technical-concepts-application-cli-commands-daemon) +which invokes the config validation in `ValidateConfigFiles()`. This compiles the +files into an AST expression which is executed. + +At this stage, the expressions generate so-called "config items" which +are a pre-stage of the later compiled object. + +`ConfigItem::CommitItems` takes care of committing the items, and doing a +rollback on failure. It also checks against matching apply rules from the previous run +and generates statistics about the objects which can be seen by the config validation. + +`ConfigItem::CommitNewItems` collects the registered types and items, +and checks for a specific required order, e.g. a service object needs +a host object first. + +The following stages happen then: + +- **Commit**: A workqueue then commits the items in a parallel fashion for this specific type. The object gets its name, and the AST expression is executed. It is then registered into the item into `m_Object` as reference. +- **OnAllConfigLoaded**: Special signal for each object to pre-load required object attributes, resolve group membership, initialize functions and timers. +- **CreateChildObjects**: Run apply rules for this specific type. +- **CommitNewItems**: Apply rules may generate new config items, this is to ensure that they again run through the stages. + +Note that the items are now committed and the configuration is validated and loaded +into memory. The final config objects are not yet activated though. + +This only happens after the validation, when the application is about to be run +with `ConfigItem::ActivateItems`. + +Each item has an object created in `m_Object` which is checked in a loop. +Again, the dependency order of activated objects is important here, e.g. logger features come first, then +config objects and last the checker, api, etc. features. This is done by sorting the objects +based on their type specific activation priority. + +The following signals are triggered in the stages: + +- **PreActivate**: Setting the `active` flag for the config object. +- **Activate**: Calls `Start()` on the object, sets the local HA authority and notifies subscribers that this object is now activated (e.g. for config updates in the DB backend). + + +### References + +* [The Icinga Config Compiler: An Overview](https://www.netways.de/blog/2018/07/12/the-icinga-config-compiler-an-overview/) +* [A parser/lexer/compiler for the Leonardo language](https://github.com/EmilGedda/Leonardo) +* [I wrote a programming language. Here’s how you can, too.](https://medium.freecodecamp.org/the-programming-language-pipeline-91d3f449c919) +* [http://onoffswitch.net/building-a-custom-lexer/](http://onoffswitch.net/building-a-custom-lexer/) +* [Writing an Interpreter with Lex, Yacc, and Memphis](http://memphis.compilertools.net/interpreter.html) +* [Flex](https://github.com/westes/flex) +* [GNU Bison](https://www.gnu.org/software/bison/) + +## Core + +### Core: Reload Handling + +The initial design of the reload state machine looks like this: + +* receive reload signal SIGHUP +* fork a child process, start configuration validation in parallel work queues +* parent process continues with old configuration objects and the event scheduling +(doing checks, replicating cluster events, triggering alert notifications, etc.) +* validation NOT ok: child process terminates, parent process continues with old configuration state +* validation ok: child process signals parent process to terminate and save its current state (all events until now) into the icinga2 state file +* parent process shuts down writing icinga2.state file +* child process waits for parent process gone, reads the icinga2 state file and synchronizes all historical and status data +* child becomes the new session leader + +Since Icinga 2.6, there are two processes when checked with `ps aux | grep icinga2` or `pidof icinga2`. +This was to ensure that feature file descriptors don't leak into the plugin process (e.g. DB IDO MySQL sockets). + +Icinga 2.9 changed the reload handling a bit with SIGUSR2 signals +and systemd notifies. + +With systemd, it could occur that the tree was broken thus resulting +in killing all remaining processes on stop, instead of a clean exit. +You can read the full story [here](https://github.com/Icinga/icinga2/issues/7309). + +With 2.11 you'll now see 3 processes: + +- The umbrella process which takes care about signal handling and process spawning/stopping +- The main process with the check scheduler, notifications, etc. +- The execution helper process + +During reload, the umbrella process spawns a new reload process which validates the configuration. +Once successful, the new reload process signals the umbrella process that it is finished. +The umbrella process forwards the signal and tells the old main process to shutdown. +The old main process writes the icinga2.state file. The umbrella process signals +the reload process that the main process terminated. + +The reload process was in idle wait before, and now continues to read the written +state file and run the event loop (checks, notifications, "events", ...). The reload +process itself also spawns the execution helper process again. + ## Features @@ -40,14 +244,292 @@ The GraphiteWriter feature calls the registered function and processes the received data. Features which connect Icinga 2 to external interfaces normally parse and reformat the received data into an applicable format. +Since this check result signal is blocking, many of the features include a work queue +with asynchronous task handling. + The GraphiteWriter uses a TCP socket to communicate with the carbon cache daemon of Graphite. The InfluxDBWriter is instead writing bulk metric messages -to InfluxDB's HTTP API. +to InfluxDB's HTTP API, similar to Elasticsearch. + + +## Check Scheduler + +The check scheduler starts a thread which loops forever. It waits for +check events being inserted into `m_IdleCheckables`. + +If the current pending check event number is larger than the configured +max concurrent checks, the thread waits up until it there's slots again. + +In addition, further checks on enabled checks, check periods, etc. are +performed. Once all conditions have passed, the next check timestamp is +calculated and updated. This also is the timestamp where Icinga expects +a new check result ("freshness check"). + +The object is removed from idle checkables, and inserted into the +pending checkables list. This can be seen via REST API metrics for the +checker component feature as well. + +The actual check execution happens asynchronously using the application's +thread pool. + +Once the check returns, it is removed from pending checkables and again +inserted into idle checkables. This ensures that the scheduler takes this +checkable event into account in the next iteration. + +### Start + +When checkable objects get activated during the startup phase, +the checker feature registers a handler for this event. This is due +to the fact that the `checker` feature is fully optional, and e.g. not +used on command endpoint clients. + +Whenever such an object activation signal is triggered, Icinga 2 checks +whether it is [authoritative for this object](19-technical-concepts.md#technical-concepts-cluster-ha-object-authority). +This means that inside an HA enabled zone with two endpoints, only non-paused checkable objects are +actively inserted into the idle checkable list for the check scheduler. + +### Initial Check + +When a new checkable object (host or service) is initially added to the +configuration, Icinga 2 performs the following during startup: + +* `Checkable::Start()` is called and calculates the first check time +* With a spread delta, the next check time is actually set. + +If the next check should happen within a time frame of 60 seconds, +Icinga 2 calculates a delta from a random value. The minimum of `check_interval` +and 60 seconds is used as basis, multiplied with a random value between 0 and 1. + +In the best case, this check gets immediately executed after application start. +The worst case scenario is that the check is scheduled 60 seconds after start +the latest. + +The reasons for delaying and spreading checks during startup is that +the application typically needs more resources at this time (cluster connections, +feature warmup, initial syncs, etc.). Immediate check execution with +thousands of checks could lead into performance problems, and additional +events for each received check results. + +Therefore the initial check window is 60 seconds on application startup, +random seed for all checkables. This is not predictable over multiple restarts +for specific checkable objects, the delta changes every time. + +### Scheduling Offset + +There's a high chance that many checkable objects get executed at the same time +and interval after startup. The initial scheduling spreads that a little, but +Icinga 2 also attempts to ensure to keep fixed intervals, even with high check latency. + +During startup, Icinga 2 calculates the scheduling offset from a random number: + +* `Checkable::Checkable()` calls `SetSchedulingOffset()` with `Utility::Random()` +* The offset is a pseudo-random integral value between `0` and `RAND_MAX`. + +Whenever the next check time is updated with `Checkable::UpdateNextCheck()`, +the scheduling offset is taken into account. + +Depending on the state type (SOFT or HARD), either the `retry_interval` or `check_interval` +is used. If the interval is greater than 1 second, the time adjustment is calculated in the +following way: + +`now * 100 + offset` divided by `interval * 100`, using the remainder (that's what `fmod()` is for) +and dividing this again onto base 100. + +Example: offset is 6500, interval 300, now is 1542190472. + +``` +1542190472 * 100 + 6500 = 154219053714 +300 * 100 = 30000 +154219053714 / 30000 = 5140635.1238 + +(5140635.1238 - 5140635.0) * 30000 = 3714 +3714 / 100 = 37.14 +``` + +37.15 seconds as an offset would be far too much, so this is again used as a calculation divider for the +real offset with the base of 5 times the actual interval. + +Again, the remainder is calculated from the offset and `interval * 5`. This is divided onto base 100 again, +with an additional 0.5 seconds delay. + +Example: offset is 6500, interval 300. + +``` +6500 / 300 = 21.666666666666667 +(21.666666666666667 - 21.0) * 300 = 200 +200 / 100 = 2 +2 + 0.5 = 2.5 +``` + +The minimum value between the first adjustment and the second offset calculation based on the interval is +taken, in the above example `2.5` wins. + +The actual next check time substracts the adjusted time from the future interval addition to provide +a more widespread scheduling time among all checkable objects. + +`nextCheck = now - adj + interval` + +You may ask, what other values can happen with this offset calculation. Consider calculating more examples +with different interval settings. + +Example: offset is 34567, interval 60, now is 1542190472. + +``` +1542190472 * 100 + 34567 = 154219081767 +60 * 100 = 6000 +154219081767 / 6000 = 25703180.2945 +(25703180.2945 - 25703180.0) * 6000 / 100 = 17.67 + +34567 / 60 = 576.116666666666667 +(576.116666666666667 - 576.0) * 60 / 100 + 0.5 = 1.2 +``` + +`1m` interval starts at `now + 1.2s`. + +Example: offset is 12345, interval 86400, now is 1542190472. + +``` +1542190472 * 100 + 12345 = 154219059545 +86400 * 100 = 8640000 +154219059545 / 8640000 = 17849.428188078703704 +(17849.428188078703704 - 17849) * 8640000 = 3699545 +3699545 / 100 = 36995.45 + +12345 / 86400 = 0.142881944444444 +0.142881944444444 * 86400 / 100 + 0.5 = 123.95 +``` + +`1d` interval starts at `now + 2m4s`. + +> **Note** +> +> In case you have a better algorithm at hand, feel free to discuss this in a PR on GitHub. +> It needs to fulfill two things: 1) spread and shuffle execution times on each `next_check` update +> 2) not too narrowed window for both long and short intervals +> Application startup and initial checks need to be handled with care in a slightly different +> fashion. + +When `SetNextCheck()` is called, there are signals registered. One of them sits +inside the `CheckerComponent` class whose handler `CheckerComponent::NextCheckChangedHandler()` +deletes/inserts the next check event from the scheduling queue. This basically +is a list with multiple indexes with the keys for scheduling info and the object. + + +## Checks + +### Check Latency and Execution Time + +Each check command execution logs the start and end time where +Icinga 2 (and the end user) is able to calculate the plugin execution time from it. + +``` +GetExecutionEnd() - GetExecutionStart() +``` + +The higher the execution time, the higher the command timeout must be set. Furthermore +users and developers are encouraged to look into plugin optimizations to minimize the +execution time. Sometimes it is better to let an external daemon/script do the checks +and feed them back via REST API. + +Icinga 2 stores the scheduled start and end time for a check. If the actual +check execution time differs from the scheduled time, e.g. due to performance +problems or limited execution slots (concurrent checks), this value is stored +and computed from inside the check result. + +The difference between the two deltas is called `check latency`. + +``` +(GetScheduleEnd() - GetScheduleStart()) - CalculateExecutionTime() +``` + +### Severity + +The severity attribute is introduced with Icinga v2.11 and provides +a bit mask calculated value from specific checkable object states. + +The severity value is pre-calculated for visualization interfaces +such as Icinga Web which sorts the problem dashboard by severity by default. + +The higher the severity number is, the more important the problem is. + +Flags: + +``` +/** + * Severity Flags + * + * @ingroup icinga + */ +enum SeverityFlag +{ + SeverityFlagDowntime = 1, + SeverityFlagAcknowledgement = 2, + SeverityFlagHostDown = 4, + SeverityFlagUnhandled = 8, + SeverityFlagPending = 16, + SeverityFlagWarning = 32, + SeverityFlagUnknown = 64, + SeverityFlagCritical = 128, +}; +``` + + +Host: + +``` + /* OK/Warning = Up, Critical/Unknown = Down */ + if (!HasBeenChecked()) + severity |= SeverityFlagPending; + else if (state == ServiceUnknown) + severity |= SeverityFlagCritical; + else if (state == ServiceCritical) + severity |= SeverityFlagCritical; + + if (IsInDowntime()) + severity |= SeverityFlagDowntime; + else if (IsAcknowledged()) + severity |= SeverityFlagAcknowledgement; + else + severity |= SeverityFlagUnhandled; +``` + + +Service: + +``` + if (!HasBeenChecked()) + severity |= SeverityFlagPending; + else if (state == ServiceWarning) + severity |= SeverityFlagWarning; + else if (state == ServiceUnknown) + severity |= SeverityFlagUnknown; + else if (state == ServiceCritical) + severity |= SeverityFlagCritical; + + if (IsInDowntime()) + severity |= SeverityFlagDowntime; + else if (IsAcknowledged()) + severity |= SeverityFlagAcknowledgement; + else if (m_Host->GetProblem()) + severity |= SeverityFlagHostDown; + else + severity |= SeverityFlagUnhandled; +``` ## Cluster +This documentation refers to technical roles between cluster +endpoints. + +- The `server` or `parent` role accepts incoming connection attempts and handles requests +- The `client` role actively connects to remote endpoints receiving config/commands, requesting certificates, etc. + +A client role is not necessarily bound to the Icinga agent. +It may also be a satellite which actively connects to the +master. + ### Communication Icinga 2 uses its own certificate authority (CA) by default. The @@ -58,11 +540,11 @@ Each node certificate must be signed by the private CA key. Note: The following description uses `parent node` and `child node`. This also applies to nodes in the same cluster zone. -During the connection attempt, an SSL handshake is performed. +During the connection attempt, a TLS handshake is performed. If the public certificate of a child node is not signed by the same CA, the child node is not trusted and the connection will be closed. -If the SSL handshake succeeds, the parent node reads the +If the TLS handshake succeeds, the parent node reads the certificate's common name (CN) of the child node and looks for a local Endpoint object name configuration. @@ -93,7 +575,7 @@ signing master. Icinga 2 v2.8 introduces the possibility to request certificates from indirectly connected nodes. This is required for multi level -cluster environments with masters, satellites and clients. +cluster environments with masters, satellites and agents. CSR Signing in general starts with the master setup. This step ensures that the master is in a working CSR signing state with: @@ -141,7 +623,7 @@ cluster message. If the child node was not the certificate request origin, it only updates the cached request for the child node and send another cluster message -down to its child node (e.g. from a satellite to a client). +down to its child node (e.g. from a satellite to an agent). If no ticket was specified, the signing master waits until the @@ -164,6 +646,10 @@ This mode leaves the node in a semi-configured state. You need to manually copy the master's public CA key into `/var/lib/icinga2/certs/ca.crt` on the client before starting Icinga 2. +> **Note** +> +> The `client` in this case can be either a satellite or an agent. + The parent node needs to actively connect to the child node. Once this connections succeeds, the child node will actively request a signed certificate. @@ -172,10 +658,10 @@ The update procedure works the same way as above. ### High Availability -High availability is automatically enabled between two nodes in the same +General high availability is automatically enabled between two endpoints in the same cluster zone. -This requires the same configuration and enabled features on both nodes. +**This requires the same configuration and enabled features on both nodes.** HA zone members trust each other and share event updates as cluster messages. This includes for example check results, next check timestamp updates, acknowledgements @@ -184,18 +670,52 @@ or notifications. This ensures that both nodes are synchronized. If one node goes away, the remaining node takes over and continues as normal. +#### High Availability: Object Authority Cluster nodes automatically determine the authority for configuration -objects. This results in activated but paused objects. You can verify +objects. By default, all config objects are set to `HARunEverywhere` and +as such the object authority is true for any config object on any instance. + +Specific objects can override and influence this setting, e.g. with `HARunOnce` +instead prior to config object activation. + +This is done when the daemon starts and in a regular interval inside +the ApiListener class, specifically calling `ApiListener::UpdateObjectAuthority()`. + +The algorithm works like this: + +* Determine whether this instance is assigned to a local zone and endpoint. +* Collects all endpoints in this zone if they are connected. +* If there's two endpoints, but only us seeing ourselves and the application start is less than 60 seconds in the past, do nothing (wait for cluster reconnect to take place, grace period). +* Sort the collected endpoints by name. +* Iterate over all config types and their respective objects + * Ignore !active objects + * Ignore objects which are !HARunOnce. This means, they can run multiple times in a zone and don't need an authority update. + * If this instance doesn't have a local zone, set authority to true. This is for non-clustered standalone environments where everything belongs to this instance. + * Calculate the object authority based on the connected endpoint names. + * Set the authority (true or false) + +The object authority calculation works "offline" without any message exchange. +Each instance alculates the SDBM hash of the config object name, puts that in contrast +modulo the connected endpoints size. +This index is used to lookup the corresponding endpoint in the connected endpoints array, +including the local endpoint. Whether the local endpoint is equal to the selected endpoint, +or not, this sets the authority to `true` or `false`. + +``` +authority = endpoints[Utility::SDBM(object->GetName()) % endpoints.size()] == my_endpoint; +``` + +`ConfigObject::SetAuthority(bool authority)` triggers the following events: + +* Authority is true and object now paused: Resume the object and set `paused` to `false`. +* Authority is false, object not paused: Pause the object and set `paused` to true. + +**This results in activated but paused objects on one endpoint.** You can verify that by querying the `paused` attribute for all objects via REST API -or debug console. - -Nodes inside a HA zone calculate the object authority independent from each other. - -The number of endpoints in a zone is defined through the configuration. This number -is used inside a local modulo calculation to determine whether the node feels -responsible for this object or not. +or debug console on both endpoints. +Endpoints inside a HA zone calculate the object authority independent from each other. This object authority is important for selected features explained below. Since features are configuration objects too, you must ensure that all nodes @@ -204,7 +724,37 @@ one might have a checker feature on the left node, nothing on the right node. This leads to late check results because one half is not executed by the right node which holds half of the object authorities. -### High Availability: Checker +By default, features are enabled to "Run-Everywhere". Specific features which +support HA awareness, provide the `enable_ha` configuration attribute. When `enable_ha` +is set to `true` (usually the default), "Run-Once" is set and the feature pauses on one side. + +``` +vim /etc/icinga2/features-enabled/graphite.conf + +object GraphiteWriter "graphite" { + ... + enable_ha = true +} +``` + +Once such a feature is paused, there won't be any more event handling, e.g. the Elasticsearch +feature won't process any checkresults nor write to the Elasticsearch REST API. + +When the cluster connection drops, the feature configuration object is updated with +the new object authority by the ApiListener timer and resumes its operation. You can see +that by grepping the log file for `resumed` and `paused`. + +``` +[2018-10-24 13:28:28 +0200] information/GraphiteWriter: 'g-ha' paused. +``` + +``` +[2018-10-24 13:28:28 +0200] information/GraphiteWriter: 'g-ha' resumed. +``` + +Specific features with HA capabilities are explained below. + +#### High Availability: Checker The `checker` feature only executes checks for `Checkable` objects (Host, Service) where it is authoritative. @@ -215,7 +765,7 @@ The cluster message routing ensures that all check results are synchronized to nodes which are not authoritative for this configuration object. -### High Availability: Notifications +#### High Availability: Notifications The `notification` feature only sends notifications for `Notification` objects where it is authoritative. @@ -225,7 +775,7 @@ That way each node only executes notifications for a segment of all notification Notified users and other event details are synchronized throughout the cluster. This is required if for example the DB IDO feature is active on the other node. -### High Availability: DB IDO +#### High Availability: DB IDO If you don't have HA enabled for the IDO feature, both nodes will write their status and historical data to their own separate database @@ -320,9 +870,1188 @@ Icinga 2 v2.9+ adds more performance metrics for these values: * `sum_bytes_sent_per_second` and `sum_bytes_received_per_second` +### Config Sync + +The visible feature for the user is to put configuration files in `/etc/icinga2/zones.d/` +and have them synced automatically to all involved zones and endpoints. + +This not only includes host and service objects being checked +in a satellite zone, but also additional config objects such as +commands, groups, timeperiods and also templates. + +Additional thoughts and complexity added: + +- Putting files into zone directory names removes the burden to set the `zone` attribute on each object in this directory. This is done automatically by the config compiler. +- Inclusion of `zones.d` happens automatically, the user shouldn't be bothered about this. +- Before the REST API was created, only static configuration files in `/etc/icinga2/zones.d` existed. With the addition of config packages, additional `zones.d` targets must be registered (e.g. used by the Director) +- Only one config master is allowed. This one identifies itself with configuration files in `/etc/icinga2/zones.d`. This is not necessarily the zone master seen in the debug logs, that one is important for message routing internally. +- Objects and templates which cannot be bound into a specific zone (e.g. hosts in the satellite zone) must be made available "globally". +- Users must be able to deny the synchronisation of specific zones, e.g. for security reasons. + +#### Config Sync: Config Master + +All zones must be configured and included in the `zones.conf` config file beforehand. +The zone names are the identifier for the directories underneath the `/etc/icinga2/zones.d` +directory. If a zone is not configured, it will not be included in the config sync - keep this +in mind for troubleshooting. + +When the config master starts, the content of `/etc/icinga2/zones.d` is automatically +included. There's no need for an additional entry in `icinga2.conf` like `conf.d`. +You can verify this by running the config validation on debug level: + +``` +icinga2 daemon -C -x debug | grep 'zones.d' + +[2019-06-19 15:16:19 +0200] notice/ConfigCompiler: Compiling config file: /etc/icinga2/zones.d/global-templates/commands.conf +``` + +Once the config validation succeeds, the startup routine for the daemon +copies the files into the "production" directory in `/var/lib/icinga2/api/zones`. +This directory is used for all endpoints where Icinga stores the received configuration. +With the exception of the config master retrieving this from `/etc/icinga2/zones.d` instead. + +These operations are logged for better visibility. + +``` +[2019-06-19 15:26:38 +0200] information/ApiListener: Copying 1 zone configuration files for zone 'global-templates' to '/var/lib/icinga2/api/zones/global-templates'. +[2019-06-19 15:26:38 +0200] information/ApiListener: Updating configuration file: /var/lib/icinga2/api/zones/global-templates//_etc/commands.conf +``` + +The master is finished at this point. Depending on the cluster configuration, +the next iteration is a connected endpoint after successful TLS handshake and certificate +authentication. + +It calls `SendConfigUpdate(client)` which sends the [config::Update](19-technical-concepts.md#technical-concepts-json-rpc-messages-config-update) +JSON-RPC message including all required zones and their configuration file content. + + +#### Config Sync: Receive Config + +The secondary master endpoint and endpoints in a child zone will be connected to the config +master. The endpoint receives the [config::Update](19-technical-concepts.md#technical-concepts-json-rpc-messages-config-update) +JSON-RPC message and processes the content in `ConfigUpdateHandler()`. This method checks +whether config should be accepted. In addition to that, it locks a local mutex to avoid race conditions +with multiple syncs in parallel. + +After that, the received configuration content is analysed. + +> **Note** +> +> The cluster design allows that satellite endpoints may connect to the secondary master first. +> There is no immediate need to always connect to the config master first, especially since +> the satellite endpoints don't know that. +> +> The secondary master not only stores the master zone config files, but also all child zones. +> This is also the case for any HA enabled zone with more than one endpoint. + + +2.11 puts the received configuration files into a staging directory in +`/var/lib/icinga2/api/zones-stage`. Previous versions directly wrote the +files into production which could have led to broken configuration on the +next manual restart. + +``` +[2019-06-19 16:08:29 +0200] information/ApiListener: New client connection for identity 'master1' to [127.0.0.1]:5665 +[2019-06-19 16:08:30 +0200] information/ApiListener: Applying config update from endpoint 'master1' of zone 'master'. +[2019-06-19 16:08:30 +0200] information/ApiListener: Received configuration for zone 'agent' from endpoint 'master1'. Comparing the checksums. +[2019-06-19 16:08:30 +0200] information/ApiListener: Stage: Updating received configuration file '/var/lib/icinga2/api/zones-stage/agent//_etc/host.conf' for zone 'agent'. +[2019-06-19 16:08:30 +0200] information/ApiListener: Applying configuration file update for path '/var/lib/icinga2/api/zones-stage/agent' (176 Bytes). +[2019-06-19 16:08:30 +0200] information/ApiListener: Received configuration for zone 'master' from endpoint 'master1'. Comparing the checksums. +[2019-06-19 16:08:30 +0200] information/ApiListener: Applying configuration file update for path '/var/lib/icinga2/api/zones-stage/master' (17 Bytes). +[2019-06-19 16:08:30 +0200] information/ApiListener: Received configuration from endpoint 'master1' is different to production, triggering validation and reload. +``` + +It then validates the received configuration in its own config stage. There is +an parameter override in place which disables the automatic inclusion of the production +config in `/var/lib/icinga2/api/zones`. + +Once completed, the reload is triggered. This follows the same configurable timeout +as with the global reload. + +``` +[2019-06-19 16:52:26 +0200] information/ApiListener: Config validation for stage '/var/lib/icinga2/api/zones-stage/' was OK, replacing into '/var/lib/icinga2/api/zones/' and triggering reload. +[2019-06-19 16:52:27 +0200] information/Application: Got reload command: Started new instance with PID '19945' (timeout is 300s). +[2019-06-19 16:52:28 +0200] information/Application: Reload requested, letting new process take over. +``` + +Whenever the staged configuration validation fails, Icinga logs this including a reference +to the startup log file which includes additional errors. + +``` +[2019-06-19 15:45:27 +0200] critical/ApiListener: Config validation failed for staged cluster config sync in '/var/lib/icinga2/api/zones-stage/'. Aborting. Logs: '/var/lib/icinga2/api/zones-stage//startup.log' +``` + + +#### Config Sync: Changes and Reload + +Whenever a new configuration is received, it is validated and upon success, the +daemon automatically reloads. While the daemon continues with checks, the reload +cannot hand over open TCP connections. That being said, reloading the daemon everytime +a configuration is synchronized would lead into many not connected endpoints. + +Therefore the cluster config sync checks whether the configuration files actually +changed, and will only trigger a reload when such a change happened. + +2.11 calculates a checksum from each file content and compares this to the +production configuration. Previous versions used additional metadata with timestamps from +files which sometimes led to problems with asynchronous dates. + +> **Note** +> +> For compatibility reasons, the timestamp metadata algorithm is still intact, e.g. +> when the client is 2.11 already, but the parent endpoint is still on 2.10. + +Icinga logs a warning when this happens. + +``` +Received configuration update without checksums from parent endpoint satellite1. This behaviour is deprecated. Please upgrade the parent endpoint to 2.11+ +``` + + +The debug log provides more details on the actual checksums and checks. Future output +may change, use this solely for troubleshooting and debugging whenever the cluster +config sync fails. + +``` +[2019-06-19 16:13:16 +0200] information/ApiListener: Received configuration for zone 'agent' from endpoint 'master1'. Comparing the checksums. +[2019-06-19 16:13:16 +0200] debug/ApiListener: Checking for config change between stage and production. Old (3): '{"/.checksums":"7ede1276a9a32019c1412a52779804a976e163943e268ec4066e6b6ec4d15d73","/.timestamp":"ec4354b0eca455f7c2ca386fddf5b9ea810d826d402b3b6ac56ba63b55c2892c","/_etc/host.conf":"35d4823684d83a5ab0ca853c9a3aa8e592adfca66210762cdf2e54339ccf0a44"}' vs. new (3): '{"/.checksums":"84a586435d732327e2152e7c9b6d85a340cc917b89ae30972042f3dc344ea7cf","/.timestamp":"0fd6facf35e49ab1b2a161872fa7ad794564eba08624373d99d31c32a7a4c7d3","/_etc/host.conf":"0d62075e89be14088de1979644b40f33a8f185fcb4bb6ff1f7da2f63c7723fcb"}'. +[2019-06-19 16:13:16 +0200] debug/ApiListener: Checking /_etc/host.conf for checksum: 35d4823684d83a5ab0ca853c9a3aa8e592adfca66210762cdf2e54339ccf0a44 +[2019-06-19 16:13:16 +0200] debug/ApiListener: Path '/_etc/host.conf' doesn't match old checksum '0d62075e89be14088de1979644b40f33a8f185fcb4bb6ff1f7da2f63c7723fcb' with new checksum '35d4823684d83a5ab0ca853c9a3aa8e592adfca66210762cdf2e54339ccf0a44'. +``` + + +#### Config Sync: Trust + +The config sync follows the "top down" approach, where the master endpoint in the master +zone is allowed to synchronize configuration to the child zone, e.g. the satellite zone. + +Endpoints in the same zone, e.g. a secondary master, receive configuration for the same +zone and all child zones. + +Endpoints in the satellite zone trust the parent zone, and will accept the pushed +configuration via JSON-RPC cluster messages. By default, this is disabled and must +be enabled with the `accept_config` attribute in the ApiListener feature (manually or with CLI +helpers). + +The satellite zone will not only accept zone configuration for its own zone, but also +all configured child zones. That is why it is important to configure the zone hierarchy +on the satellite as well. + +Child zones are not allowed to sync configuration up to the parent zone. Each Icinga instance +evaluates this in startup and knows on endpoint connect which config zones need to be synced. + + +Global zones have a special trust relationship: They are synced to all child zones, be it +a satellite zone or agent zone. Since checkable objects such as a Host or a Service object +must have only one endpoint as authority, they cannot be put into a global zone (denied by +the config compiler). + +Apply rules and templates are allowed, since they are evaluated in the endpoint which received +the synced configuration. Keep in mind that there may be differences on the master and the satellite +when e.g. hostgroup membership is used for assign where expressions, but the groups are only +available on the master. + + +### Cluster: Message Routing + +One fundamental part of the cluster message routing is the MessageOrigin object. +This is created when a new JSON-RPC message is received in `JsonRpcConnection::MessageHandler()`. + +It contains + +- FromZone being extracted from the endpoint object which owns the JsonRpcConnection +- FromClient being the JsonRpcConnection bound to the endpoint object + +These attributes are checked in message receive api handlers for security access. E.g. whether a +message origin is from a child zone which is not allowed, etc. +This is explained in the [JSON-RPC messages](19-technical-concepts.md#technical-concepts-json-rpc-messages) chapter. + +Whenever such a message is processed on the client, it may trigger additional cluster events +which are sent back to other endpoints. Therefore it is key to always pass the MessageOrigin +`origin` when processing these messages locally. + +Example: + +- Client receives a CheckResult from another endpoint in the same zone, call it `sender` for now +- Calls ProcessCheckResult() to store the CR and calculcate states, notifications, etc. +- Calls the OnNewCheckResult() signal to trigger IDO updates + +OnNewCheckResult() also calls a registered cluster handler which forwards the CheckResult to other cluster members. + +Without any origin details, this CheckResult would be relayed to the `sender` endpoint again. +Which processes the message, ProcessCheckResult(), OnNewCheckResult(), sends back and so on. + +That creates a loop which our cluster protocol needs to prevent at all cost. + +RelayMessageOne() takes care of the routing. This involves fetching the targetZone for this message and its endpoints. + +- Don't relay messages to ourselves. +- Don't relay messages to disconnected endpoints. +- Don't relay the message to the zone through more than one endpoint unless this is our own zone. +- Don't relay messages back to the endpoint which we got the message from. **THIS** +- Don't relay messages back to the zone which we got the message from. +- Only relay message to the zone master if we're not currently the zone master. + +``` + e1 is zone master, e2 and e3 are zone members. + + Message is sent from e2 or e3: + !isMaster == true + targetEndpoint e1 is zone master -> send the message + targetEndpoint e3 is not zone master -> skip it, avoid routing loops + + Message is sent from e1: + !isMaster == false -> send the messages to e2 and e3 being the zone routing master. +``` + +With passing the `origin` the following condition prevents sending a message back to sender: + +``` +if (origin && origin->FromClient && targetEndpoint == origin->FromClient->GetEndpoint()) { +``` + +This message then simply gets skipped for this specific Endpoint and is never sent. + +This analysis originates from a long-lasting [downtime loop bug](https://github.com/Icinga/icinga2/issues/7198). + +## TLS Network IO + +### TLS Connection Handling + +Icinga supports two connection directions, controlled via the `host` attribute +inside the Endpoint objects: + +* Outgoing connection attempts +* Incoming connection handling + +Once the connection is established, higher layers can exchange JSON-RPC and +HTTP messages. It doesn't matter which direction these message go. + +This offers a big advantage over single direction connections, just like +polling via HTTP only. Also, connections are kept alive as long as data +is transmitted. + +When the master connects to the child zone member(s), this requires more +resources there. Keep this in mind when endpoints are not reachable, the +TCP timeout blocks other resources. Moving a satellite zone in the middle +between masters and agents helps to split the tasks - the master +processes and stores data, deploys configuration and serves the API. The +satellites schedule the checks, connect to the agents and receive +check results. + +Agents/Clients can also connect to the parent endpoints - be it a master or +a satellite. This is the preferred way out of a DMZ, and also reduces the +overhead with connecting to e.g. 2000 agents on the master. You can +benchmark this when TCP connections are broken and timeouts are encountered. + +#### Master Processes Incoming Connection + +* The node starts a new ApiListener, this invokes `AddListener()` + * Setup TLS Context (SslContext) + * Initialize global I/O engine and create a TCP acceptor + * Resolve bind host/port (optional) + * Listen on IPv4 and IPv6 + * Re-use socket address and port + * Listen on port 5665 with `INT_MAX` possible sockets +* Spawn a new Coroutine which listens for new incoming connections as 'TCP server' pattern + * Accept new connections asynchronously + * Spawn a new Coroutine which handles the new client connection in a different context, Role: Server + +#### Master Connects Outgoing + +* The node starts a timer in a 10 seconds interval with `ApiReconnectTimerHandler()` as callback + * Loop over all configured zones, exclude global zones and not direct parent/child zones + * Get the endpoints configured in the zones, exclude: local endpoint, no 'host' attribute, already connected or in progress + * Call `AddConnection()` +* Spawn a new Coroutine after making the TLS context + * Use the global I/O engine for socket I/O + * Create TLS stream + * Connect to endpoint host/port details + * Handle the client connection, Role: Client + +#### TLS Handshake + +* Create a TLS connection in sslConn and perform an asynchronous TLS handshake +* Get the peer certificate +* Verify the presented certificate: `ssl::verify_peer` and `ssl::verify_client_once` +* Get the certificate CN and compare it against the endpoint name - if not matching, return and close the connection + +#### Data Exchange + +Everything runs through TLS, we don't use any "raw" connections nor plain message handling. + +HTTP and JSON-RPC messages share the same port and API, so additional handling is required. + +On a new connection and successful TLS handshake, the first byte is read. This either +is a JSON-RPC message in Netstring format starting with a number, or plain HTTP. + +``` +HTTP/1.1 + +2:{} +``` + +Depending on this, `ClientJsonRpc` or `ClientHttp` are assigned. + +JSON-RPC: + +* Create a new JsonRpcConnection object + * When the endpoint object is configured, spawn a Coroutine which takes care of syncing the client (file and runtime config, replay log, etc.) + * No endpoint treats this connection as anonymous client, with a configurable limit. This client may send a CSR signing request for example. + * Start the JsonRpcConnection - this spawns Coroutines to HandleIncomingMessages, WriteOutgoingMessages, HandleAndWriteHeartbeats and CheckLiveness + +HTTP: + +* Create a new HttpServerConnection + * Start the HttpServerConnection - this spawns Coroutines to ProcessMessages and CheckLiveness + + +All the mentioned Coroutines run asynchronously using the global I/O engine's context. +More details on this topic can be found in [this blogpost](https://www.netways.de/blog/2019/04/04/modern-c-programming-coroutines-with-boost/). + +The lower levels of context switching and sharing or event polling are +hidden in Boost ASIO, Beast, Coroutine and Context libraries. + +#### Data Exchange: Coroutines and I/O Engine + +Light-weight and fast operations such as connection handling or TLS handshakes +are performed in the default `IoBoundWorkSlot` pool inside the I/O engine. + +The I/O engine has another pool available: `CpuBoundWork`. + +This is used for processing CPU intensive tasks, such as handling a HTTP request. +Depending on the available CPU cores, this is limited to `std::thread::hardware_concurrency() * 3u / 2u`. + +``` +1 core * 3 / 2 = 1 +2 cores * 3 / 2 = 3 +8 cores * 3 / 2 = 12 +16 cores * 3 / 2 = 24 +``` + +The I/O engine itself is used with all network I/O in Icinga, not only the cluster +and the REST API. Features such as Graphite, InfluxDB, etc. also consume its functionality. + +There are 2 * CPU cores threads available which run the event loop +in the I/O engine. This polls the I/O service with `m_IoService.run();` +and triggers an asynchronous event progress for waiting coroutines. + + +## JSON-RPC Message API + +**The JSON-RPC message API is not a public API for end users.** In case you want +to interact with Icinga, use the [REST API](12-icinga2-api.md#icinga2-api). + +This section describes the internal cluster messages exchanged between endpoints. + +> **Tip** +> +> Debug builds with `icinga2 daemon -DInternal.DebugJsonRpc=1` unveils the JSON-RPC messages. + +### Registered Handler Functions + +Functions by example: + +Event Sender: `Checkable::OnNewCheckResult` + +``` +On.connect(&xyzHandler) +``` + +Event Receiver (Client): `CheckResultAPIHandler` in `REGISTER_APIFUNCTION` + +``` +APIHandler() +``` + +### Messages + +#### icinga::Hello + +> Location: `apilistener.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | icinga::Hello +params | Dictionary + +##### Params + +Currently empty. + +##### Functions + +Event Sender: When a new client connects in `NewClientHandlerInternal()`. +Event Receiver: `HelloAPIHandler` + +##### Permissions + +None, this is a required message. + +#### event::Heartbeat + +> Location: `jsonrpcconnection-heartbeat.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::Heartbeat +params | Dictionary + +##### Params + +Key | Type | Description +----------|---------------|------------------ +timeout | Number | Heartbeat timeout, sender sets 120s. + + +##### Functions + +Event Sender: `JsonRpcConnection::HeartbeatTimerHandler` +Event Receiver: `HeartbeatAPIHandler` + +Both sender and receiver exchange this heartbeat message. If the sender detects +that a client endpoint hasn't sent anything in the updated timeout span, it disconnects +the client. This is to avoid stale connections with no message processing. + +##### Permissions + +None, this is a required message. + +#### event::CheckResult + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::CheckResult +params | Dictionary + +##### Params + +Key | Type | Description +----------|---------------|------------------ +host | String | Host name +service | String | Service name +cr | Serialized CR | Check result + +##### Functions + +Event Sender: `Checkable::OnNewCheckResult` +Event Receiver: `CheckResultAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Hosts/services do not exist +* Origin is a remote command endpoint different to the configured, and whose zone is not allowed to access this checkable. + +#### event::SetNextCheck + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::SetNextCheck +params | Dictionary + +##### Params + +Key | Type | Description +------------|---------------|------------------ +host | String | Host name +service | String | Service name +next\_check | Timestamp | Next scheduled time as UNIX timestamp. + +##### Functions + +Event Sender: `Checkable::OnNextCheckChanged` +Event Receiver: `NextCheckChangedAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Checkable does not exist. +* Origin endpoint's zone is not allowed to access this checkable. + +#### event::SuppressedNotifications + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::SuppressedNotifications +params | Dictionary + +##### Params + +Key | Type | Description +-------------------------|---------------|------------------ +host | String | Host name +service | String | Service name +supressed\_notifications | Number | Bitmask for suppressed notifications. + +##### Functions + +Event Sender: `Checkable::OnSuppressedNotificationsChanged` +Event Receiver: `SuppressedNotificationsChangedAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Checkable does not exist. +* Origin endpoint's zone is not allowed to access this checkable. + + +#### event::SetNextNotification + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::SetNextNotification +params | Dictionary + +##### Params + +Key | Type | Description +-------------------|---------------|------------------ +host | String | Host name +service | String | Service name +notification | String | Notification name +next\_notification | Timestamp | Next scheduled notification time as UNIX timestamp. + +##### Functions + +Event Sender: `Notification::OnNextNotificationChanged` +Event Receiver: `NextNotificationChangedAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Notification does not exist. +* Origin endpoint's zone is not allowed to access this checkable. + +#### event::SetForceNextCheck + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::SetForceNextCheck +params | Dictionary + +##### Params + +Key | Type | Description +----------|---------------|------------------ +host | String | Host name +service | String | Service name +forced | Boolean | Forced next check (execute now) + +##### Functions + +Event Sender: `Checkable::OnForceNextCheckChanged` +Event Receiver: `ForceNextCheckChangedAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Checkable does not exist. +* Origin endpoint's zone is not allowed to access this checkable. + +#### event::SetForceNextNotification + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::SetForceNextNotification +params | Dictionary + +##### Params + +Key | Type | Description +----------|---------------|------------------ +host | String | Host name +service | String | Service name +forced | Boolean | Forced next check (execute now) + +##### Functions + +Event Sender: `Checkable::SetForceNextNotification` +Event Receiver: `ForceNextNotificationChangedAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Checkable does not exist. +* Origin endpoint's zone is not allowed to access this checkable. + +#### event::SetAcknowledgement + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::SetAcknowledgement +params | Dictionary + +##### Params + +Key | Type | Description +-----------|---------------|------------------ +host | String | Host name +service | String | Service name +author | String | Acknowledgement author name. +comment | String | Acknowledgement comment content. +acktype | Number | Acknowledgement type (0=None, 1=Normal, 2=Sticky) +notify | Boolean | Notification should be sent. +persistent | Boolean | Whether the comment is persistent. +expiry | Timestamp | Optional expire time as UNIX timestamp. + +##### Functions + +Event Sender: `Checkable::OnForceNextCheckChanged` +Event Receiver: `ForceNextCheckChangedAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Checkable does not exist. +* Origin endpoint's zone is not allowed to access this checkable. + +#### event::ClearAcknowledgement + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::ClearAcknowledgement +params | Dictionary + +##### Params + +Key | Type | Description +----------|---------------|------------------ +host | String | Host name +service | String | Service name + +##### Functions + +Event Sender: `Checkable::OnAcknowledgementCleared` +Event Receiver: `AcknowledgementClearedAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Checkable does not exist. +* Origin endpoint's zone is not allowed to access this checkable. + +#### event::SendNotifications + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::SendNotifications +params | Dictionary + +##### Params + +Key | Type | Description +----------|---------------|------------------ +host | String | Host name +service | String | Service name +cr | Serialized CR | Check result +type | Number | enum NotificationType, same as `types` for notification objects. +author | String | Author name +text | String | Notification text + +##### Functions + +Event Sender: `Checkable::OnNotificationsRequested` +Event Receiver: `SendNotificationsAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Checkable does not exist. +* Origin endpoint's zone the same as the receiver. This binds notification messages to the HA zone. + +#### event::NotificationSentUser + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::NotificationSentUser +params | Dictionary + +##### Params + +Key | Type | Description +--------------|-----------------|------------------ +host | String | Host name +service | String | Service name +notification | String | Notification name. +user | String | Notified user name. +type | Number | enum NotificationType, same as `types` in Notification objects. +cr | Serialized CR | Check result. +author | String | Notification author (for specific types) +text | String | Notification text (for specific types) +command | String | Notification command name. + +##### Functions + +Event Sender: `Checkable::OnNotificationSentToUser` +Event Receiver: `NotificationSentUserAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Checkable does not exist. +* Origin endpoint's zone the same as the receiver. This binds notification messages to the HA zone. + +#### event::NotificationSentToAllUsers + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::NotificationSentToAllUsers +params | Dictionary + +##### Params + +Key | Type | Description +----------------------------|-----------------|------------------ +host | String | Host name +service | String | Service name +notification | String | Notification name. +users | Array of String | Notified user names. +type | Number | enum NotificationType, same as `types` in Notification objects. +cr | Serialized CR | Check result. +author | String | Notification author (for specific types) +text | String | Notification text (for specific types) +last\_notification | Timestamp | Last notification time as UNIX timestamp. +next\_notification | Timestamp | Next scheduled notification time as UNIX timestamp. +notification\_number | Number | Current notification number in problem state. +last\_problem\_notification | Timestamp | Last problem notification time as UNIX timestamp. +no\_more\_notifications | Boolean | Whether to send future notifications when this notification becomes active on this HA node. + +##### Functions + +Event Sender: `Checkable::OnNotificationSentToAllUsers` +Event Receiver: `NotificationSentToAllUsersAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Checkable does not exist. +* Origin endpoint's zone the same as the receiver. This binds notification messages to the HA zone. + +#### event::ExecuteCommand + +> Location: `clusterevents-check.cpp` and `checkable-check.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::ExecuteCommand +params | Dictionary + +##### Params + +Key | Type | Description +--------------|---------------|------------------ +host | String | Host name. +service | String | Service name. +command\_type | String | `check_command` or `event_command`. +command | String | CheckCommand or EventCommand name. +macros | Dictionary | Command arguments as key/value pairs for remote execution. + + +##### Functions + +**Event Sender:** This gets constructed directly in `Checkable::ExecuteCheck()` or `Checkable::ExecuteEventHandler()` when a remote command endpoint is configured. + +* `Get{CheckCommand,EventCommand}()->Execute()` simulates an execution and extracts all command arguments into the `macro` dictionary (inside lib/methods tasks). +* When the endpoint is connected, the message is constructed and sent directly. +* When the endpoint is not connected and not syncing replay logs and 5m after application start, generate an UNKNOWN check result for the user ("not connected"). + +**Event Receiver:** `ExecuteCommandAPIHandler` + +Special handling, calls `ClusterEvents::EnqueueCheck()` for command endpoint checks. +This function enqueues check tasks into a queue which is controlled in `RemoteCheckThreadProc()`. + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Origin endpoint's zone is not a parent zone of the receiver endpoint. +* `accept_commands = false` in the `api` feature configuration sends back an UNKNOWN check result to the sender. + +The receiver constructs a virtual host object and looks for the local CheckCommand object. + +Returns UNKNOWN as check result to the sender + +* when the CheckCommand object does not exist. +* when there was an exception triggered from check execution, e.g. the plugin binary could not be executed or similar. + +The returned messages are synced directly to the sender's endpoint, no cluster broadcast. + +> **Note**: EventCommand errors are just logged on the remote endpoint. + +#### config::Update + +> Location: `apilistener-filesync.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | config::Update +params | Dictionary + +##### Params + +Key | Type | Description +-----------|---------------|------------------ +update | Dictionary | Config file paths and their content. +update\_v2 | Dictionary | Additional meta config files introduced in 2.4+ for compatibility reasons. + +##### Functions + +**Event Sender:** `SendConfigUpdate()` called in `ApiListener::SyncClient()` when a new client endpoint connects. +**Event Receiver:** `ConfigUpdateHandler` reads the config update content and stores them in `/var/lib/icinga2/api`. +When it detects a configuration change, the function requests and application restart. + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* The origin sender is not in a parent zone of the receiver. +* `api` feature does not accept config. + +Config updates will be ignored when: + +* The zone is not configured on the receiver endpoint. +* The zone is authoritative on this instance (this only happens on a master which has `/etc/icinga2/zones.d` populated, and prevents sync loops) + +#### config::UpdateObject + +> Location: `apilistener-configsync.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | config::UpdateObject +params | Dictionary + +##### Params + +Key | Type | Description +---------------------|-------------|------------------ +name | String | Object name. +type | String | Object type name. +version | Number | Object version. +config | String | Config file content for `_api` packages. +modified\_attributes | Dictionary | Modified attributes at runtime as key value pairs. +original\_attributes | Array | Original attributes as array of keys. + + +##### Functions + +**Event Sender:** Either on client connect (full sync), or runtime created/updated object + +`ApiListener::SendRuntimeConfigObjects()` gets called when a new endpoint is connected +and runtime created config objects need to be synced. This invokes a call to `UpdateConfigObject()` +to only sync this JsonRpcConnection client. + +`ConfigObject::OnActiveChanged` (created or deleted) or `ConfigObject::OnVersionChanged` (updated) +also call `UpdateConfigObject()`. + +**Event Receiver:** `ConfigUpdateObjectAPIHandler` calls `ConfigObjectUtility::CreateObject()` in order +to create the object if it is not already existing. Afterwards, all modified attributes are applied +and in case, original attributes are restored. The object version is set as well, keeping it in sync +with the sender. + +##### Permissions + +###### Sender + +Client receiver connects: + +The sender only syncs config object updates to a client which can access +the config object, in `ApiListener::SendRuntimeConfigObjects()`. + +In addition to that, the client endpoint's zone is checked whether this zone may access +the config object. + +Runtime updated object: + +Only if the config object belongs to the `_api` package. + + +###### Receiver + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Origin sender endpoint's zone is in a child zone. +* `api` feature does not accept config +* The received config object type does not exist (this is to prevent failures with older nodes and new object types). + +Error handling: + +* Log an error if `CreateObject` fails (only if the object does not already exist) +* Local object version is newer than the received version, object will not be updated. +* Compare modified and original attributes and restore any type of change here. + + +#### config::DeleteObject + +> Location: `apilistener-configsync.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | config::DeleteObject +params | Dictionary + +##### Params + +Key | Type | Description +--------------------|-------------|------------------ +name | String | Object name. +type | String | Object type name. +version | Number | Object version. + +##### Functions + +**Event Sender:** + +`ConfigObject::OnActiveChanged` (created or deleted) or `ConfigObject::OnVersionChanged` (updated) +call `DeleteConfigObject()`. + +**Event Receiver:** `ConfigDeleteObjectAPIHandler` + +##### Permissions + +###### Sender + +Runtime deleted object: + +Only if the config object belongs to the `_api` package. + +###### Receiver + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Origin sender endpoint's zone is in a child zone. +* `api` feature does not accept config +* The received config object type does not exist (this is to prevent failures with older nodes and new object types). +* The object in question was not created at runtime, it does not belong to the `_api` package. + +Error handling: + +* Log an error if `DeleteObject` fails (only if the object does not already exist) + +#### pki::RequestCertificate + +> Location: `jsonrpcconnection-pki.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | pki::RequestCertificate +params | Dictionary + +##### Params + +Key | Type | Description +--------------|---------------|------------------ +ticket | String | Own ticket, or as satellite in CA proxy from local store. +cert\_request | String | Certificate request content from local store, optional. + +##### Functions + +Event Sender: `RequestCertificateHandler` +Event Receiver: `RequestCertificateHandler` + +##### Permissions + +This is an anonymous request, and the number of anonymous clients can be configured +in the `api` feature. + +Only valid certificate request messages are processed, and valid signed certificates +won't be signed again. + +#### pki::UpdateCertificate + +> Location: `jsonrpcconnection-pki.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | pki::UpdateCertificate +params | Dictionary + +##### Params + +Key | Type | Description +---------------------|---------------|------------------ +status\_code | Number | Status code, 0=ok. +cert | String | Signed certificate content. +ca | String | Public CA certificate content. +fingerprint\_request | String | Certificate fingerprint from the CSR. + + +##### Functions + +**Event Sender:** + +* When a client requests a certificate in `RequestCertificateHandler` and the satellite +already has a signed certificate, the `pki::UpdateCertificate` message is constructed and sent back. +* When the endpoint holding the master's CA private key (and TicketSalt private key) is able to sign +the request, the `pki::UpdateCertificate` message is constructed and sent back. + +**Event Receiver:** `UpdateCertificateHandler` + +##### Permissions + +Message updates are dropped when + +* The origin sender is not in a parent zone of the receiver. +* The certificate fingerprint is in an invalid format. + +#### log::SetLogPosition + +> Location: `apilistener.cpp` and `jsonrpcconnection.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | log::SetLogPosition +params | Dictionary + +##### Params + +Key | Type | Description +--------------------|---------------|------------------ +log\_position | Timestamp | The endpoint's log position as UNIX timestamp. + + +##### Functions + +**Event Sender:** + +During log replay to a client endpoint in `ApiListener::ReplayLog()`, each processed +file generates a message which updates the log position timestamp. + +`ApiListener::ApiTimerHandler()` invokes a check to keep all connected endpoints and +their log position in sync during replay log. + +**Event Receiver:** `SetLogPositionHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. diff --git a/doc/20-script-debugger.md b/doc/20-script-debugger.md index 09a11e95f..733142d21 100644 --- a/doc/20-script-debugger.md +++ b/doc/20-script-debugger.md @@ -3,7 +3,9 @@ You can run the Icinga 2 daemon with the `-X` (`--script-debugger`) parameter to enable the script debugger: - # icinga2 daemon -X +``` +# icinga2 daemon -X +``` When an exception occurs or the [debugger](17-language-reference.md#breakpoints) keyword is encountered in a user script, Icinga 2 launches a console that @@ -11,7 +13,9 @@ allows the user to debug the script. You can also attach the script debugger to the [configuration validation](11-cli-commands.md#config-validation): - # icinga2 daemon -C -X +``` +# icinga2 daemon -C -X +``` Here is a list of common errors which can be diagnosed with the script debugger: @@ -21,51 +25,57 @@ Here is a list of common errors which can be diagnosed with the script debugger: ## Debugging Configuration Errors The following example illustrates the problem of a service [apply rule](03-monitoring-basics.md#using-apply-for) -which expects a dictionary value for `config`, but the host custom attribute only +which expects a dictionary value for `config`, but the host custom variable only provides a string value: - object Host "script-debugger-host" { - check_command = "icinga" +``` +object Host "script-debugger-host" { + check_command = "icinga" - vars.http_vhosts["example.org"] = "192.168.1.100" // a string value - } + vars.http_vhosts["example.org"] = "192.168.1.100" // a string value +} - apply Service for (http_vhost => config in host.vars.http_vhosts) { - import "generic-service" +apply Service for (http_vhost => config in host.vars.http_vhosts) { + import "generic-service" - vars += config // expects a dictionary + vars += config // expects a dictionary - check_command = "http" - } + check_command = "http" +} +``` The error message on config validation will warn about the wrong value type, but does not provide any context which objects are affected. Enable the script debugger and run the config validation: - # icinga2 daemon -C -X +``` +# icinga2 daemon -C -X - Breakpoint encountered in /etc/icinga2/conf.d/services.conf: 59:67-65:1 - Exception: Error: Error while evaluating expression: Cannot convert value of type 'String' to an object. - Location: - /etc/icinga2/conf.d/services.conf(62): check_command = "http" - /etc/icinga2/conf.d/services.conf(63): - /etc/icinga2/conf.d/services.conf(64): vars += config - ^^^^^^^^^^^^^^ - /etc/icinga2/conf.d/services.conf(65): } - /etc/icinga2/conf.d/services.conf(66): - You can inspect expressions (such as variables) by entering them at the prompt. - To leave the debugger and continue the program use "$continue". - <1> => +Breakpoint encountered in /etc/icinga2/conf.d/services.conf: 59:67-65:1 +Exception: Error: Error while evaluating expression: Cannot convert value of type 'String' to an object. +Location: +/etc/icinga2/conf.d/services.conf(62): check_command = "http" +/etc/icinga2/conf.d/services.conf(63): +/etc/icinga2/conf.d/services.conf(64): vars += config + ^^^^^^^^^^^^^^ +/etc/icinga2/conf.d/services.conf(65): } +/etc/icinga2/conf.d/services.conf(66): +You can inspect expressions (such as variables) by entering them at the prompt. +To leave the debugger and continue the program use "$continue". +<1> => +``` You can print the variables `vars` and `config` to get an idea about their values: - <1> => vars - null - <2> => config - "192.168.1.100" - <3> => +``` +<1> => vars +null +<2> => config +"192.168.1.100" +<3> => +``` The `vars` attribute has to be a dictionary. Trying to set this attribute to a string caused the error in our configuration example. @@ -73,10 +83,12 @@ the error in our configuration example. In order to determine the name of the host where the value of the `config` variable came from you can inspect attributes of the service object: - <3> => host_name - "script-debugger-host-01" - <4> => name - "http" +``` +<3> => host_name +"script-debugger-host-01" +<4> => name +"http" +``` Additionally you can view the service object attributes by printing the value of `this`. @@ -84,28 +96,31 @@ Additionally you can view the service object attributes by printing the value of In order to halt execution in a script you can use the `debugger` keyword: - object Host "script-debugger-host-02" { - check_command = "dummy" - check_interval = 5s +``` +object Host "script-debugger-host-02" { + check_command = "dummy" + check_interval = 5s - vars.dummy_text = {{ - var text = "Hello from " + macro("$name$") - debugger - return text - }} - } + vars.dummy_text = {{ + var text = "Hello from " + macro("$name$") + debugger + return text + }} +} +``` Icinga 2 will spawn a debugger console every time the function is executed: - # icinga2 daemon -X - ... - Breakpoint encountered in /etc/icinga2/tests/script-debugger.conf: 7:5-7:12 - You can inspect expressions (such as variables) by entering them at the prompt. - To leave the debugger and continue the program use "$continue". - <1> => text - "Hello from script-debugger-host-02" - <2> => $continue - +``` +# icinga2 daemon -X +... +Breakpoint encountered in /etc/icinga2/tests/script-debugger.conf: 7:5-7:12 +You can inspect expressions (such as variables) by entering them at the prompt. +To leave the debugger and continue the program use "$continue". +<1> => text +"Hello from script-debugger-host-02" +<2> => $continue +``` ## Debugging API Filters @@ -115,7 +130,7 @@ an internal error, they return an empty result to the caller. In order to analyse these server-side errors, you can use the script debugger. -The following example tries filter for all host objects where the custom attribute +The following example tries filter for all host objects where the custom variable `os` is set. There are various possibilities to check that, one of them would be `host.vars.os != ""`. Another idea is to use the [contains](18-library-reference.md#dictionary-contains) method on the custom attribute dictionary like this: `host.vars.contains("os")`. @@ -126,7 +141,7 @@ $ curl -k -s -u root:icinga -H 'Accept: application/json' -H 'X-HTTP-Method-Over -d '{ "filter": "host.vars.contains(\"os\")", "attrs": [ "__name" ], "joins": [ "host.name", "host.vars" ], "pretty": true }' ``` -This will fail on all hosts which don't have any custom attribute specified. +This will fail on all hosts which don't have any custom variable specified. ``` # icinga2 daemon -X diff --git a/doc/21-development.md b/doc/21-development.md index cc591e506..ff24971a3 100644 --- a/doc/21-development.md +++ b/doc/21-development.md @@ -1,196 +1,193 @@ -# Develop Icinga 2 +# Development -This chapter provides hints on Icinga 2 development -especially for debugging purposes. +This chapter provides hints on Icinga 2 debugging, +development, package builds and tests. -> **Note** +* [Debug Icinga 2](21-development.md#development-debug) + * [GDB Backtrace](21-development.md#development-debug-gdb-backtrace) + * [Core Dump](21-development.md#development-debug-core-dump) +* [Test Icinga 2](21-development.md#development-tests) + * [Snapshot Packages (Nightly Builds)](21-development.md#development-tests-snapshot-packages) +* [Develop Icinga 2](21-development.md#development-develop) + * [Preparations](21-development.md#development-develop-prepare) + * [Design Patterns](21-development.md#development-develop-design-patterns) + * [Build Tools](21-development.md#development-develop-builds-tools) + * [Unit Tests](21-development.md#development-develop-tests) + * [Style Guide](21-development.md#development-develop-styleguide) +* [Development Environment](21-development.md#development-environment) + * [Linux Dev Environment](21-development.md#development-linux-dev-env) + * [macOS Dev Environment](21-development.md#development-macos-dev-env) + * [Windows Dev Environment](21-development.md#development-windows-dev-env) +* [Package Builds](21-development.md#development-package-builds) + * [RPM](21-development.md#development-package-builds-rpms) + * [DEB](21-development.md#development-package-builds-deb) + * [Windows](21-development.md#development-package-builds-windows) +* [Continuous Integration](21-development.md#development-ci) +* [Advanced Tips](21-development.md#development-advanced) + + + +## Debug Icinga 2 + +This chapter targets all users who have been asked by developers to provide +a stack trace or coredump if the application crashed. It is also useful +for developers working with different debuggers. + +> **Note:** > -> If you are planning to build your own development environment, -> please consult the `INSTALL.md` file from the source tree. +> This is intentionally mentioned before any development insights +> as debugging is a more frequent and commonly asked question. -## Debug Requirements +### Debug Requirements Make sure that the debug symbols are available for Icinga 2. The Icinga 2 packages provide a debug package which must be installed separately for all involved binaries, like `icinga2-bin` or `icinga2-ido-mysql`. -Debian/Ubuntu: +Distribution | Command +-------------------|------------------------------------------ +Debian/Ubuntu | `apt-get install icinga2-dbg` +RHEL/CentOS | `yum install icinga2-debuginfo` +Fedora | `dnf install icinga2-debuginfo icinga2-bin-debuginfo icinga2-ido-mysql-debuginfo` +SLES/openSUSE | `zypper install icinga2-bin-debuginfo icinga2-ido-mysql-debuginfo` - # apt-get install icinga2-dbg - -RHEL/CentOS: - - # yum install icinga2-debuginfo - -SLES/openSUSE: - - # zypper install icinga2-bin-debuginfo icinga2-ido-mysql-debuginfo - - -Furthermore, you may also have to install debug symbols for Boost and your C library. +Furthermore, you may also have to install debug symbols for Boost and your C++ library. If you're building your own binaries, you should use the `-DCMAKE_BUILD_TYPE=Debug` cmake build flag for debug builds. -## GDB +### GDB as Debugger -Install gdb: +Install GDB in your development environment. -Debian/Ubuntu: +Distribution | Command +-------------------|------------------------------------------ +Debian/Ubuntu | `apt-get install gdb` +RHEL/CentOS | `yum install gdb` +Fedora | `dnf install gdb` +SLES/openSUSE | `zypper install gdb` - # apt-get install gdb +#### GDB Run -RHEL/CentOS/Fedora: +Since v2.11 we would attach to the umbrella process spawned with `/usr/lib/icinga2/sbin/icinga2`, +therefore rather attach to a running process. - # yum install gdb +``` +# Typically the order of PIDs is: 1) umbrella 2) spawn helper 3) main process +pidof icinga2 -SLES/openSUSE: - - # zypper install gdb - - -Install the `boost`, `python` and `icinga2` pretty printers. Absolute paths are required, -so please make sure to update the installation paths accordingly (`pwd`). - - $ mkdir -p ~/.gdb_printers && cd ~/.gdb_printers - -Boost Pretty Printers compatible with Python 3: - - $ git clone https://github.com/mateidavid/Boost-Pretty-Printer.git && cd Boost-Pretty-Printer - $ git checkout python-3 - $ pwd - /home/michi/.gdb_printers/Boost-Pretty-Printer - -Python Pretty Printers: - - $ cd ~/.gdb_printers - $ svn co svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python - -Icinga 2 Pretty Printers: - - $ mkdir -p ~/.gdb_printers/icinga2 && cd ~/.gdb_printers/icinga2 - $ wget https://raw.githubusercontent.com/Icinga/icinga2/master/tools/debug/gdb/icingadbg.py - -Now you'll need to modify/setup your `~/.gdbinit` configuration file. -You can download the one from Icinga 2 and modify all paths. - -Example on Fedora 22: - - $ wget https://raw.githubusercontent.com/Icinga/icinga2/master/tools/debug/gdb/gdbinit -O ~/.gdbinit - $ vim ~/.gdbinit - - set print pretty on - - python - import sys - sys.path.insert(0, '/home/michi/.gdb_printers/icinga2') - from icingadbg import register_icinga_printers - register_icinga_printers() - end - - python - import sys - sys.path.insert(0, '/home/michi/.gdb_printers/python') - from libstdcxx.v6.printers import register_libstdcxx_printers - try: - register_libstdcxx_printers(None) - except: - pass - end - - python - import sys - sys.path.insert(0, '/home/michi/.gdb_printers/Boost-Pretty-Printer') - import boost_print - boost_print.register_printers() - end - - -If you are getting the following error when running gdb, the `libstdcxx` -printers are already preloaded in your environment and you can remove -the duplicate import in your `~/.gdbinit` file. - - RuntimeError: pretty-printer already registered: libstdc++-v6 - -### GDB Run - -Call GDB with the binary (`/usr/sbin/icinga2` is a wrapper script calling -`/usr/lib64/icinga2/sbin/icinga2` since 2.4) and all arguments and run it in foreground. - - # gdb --args /usr/lib64/icinga2/sbin/icinga2 daemon -x debug --no-stack-rlimit - -The exact path to the Icinga 2 binary differs on each distribution. On Ubuntu -it is installed into `/usr/lib/x86_64-linux-gnu/icinga2/sbin/icinga2` on 64-bit systems -for example. +gdb -p $(pidof icinga2 | cut -d ' ' -f3) +``` > **Note** > > If gdb tells you it's missing debug symbols, quit gdb and install > them: `Missing separate debuginfos, use: debuginfo-install ...` -Run the application. +Run/restart the application. - (gdb) r +``` +(gdb) r +``` Kill the running application. - (gdb) k +``` +(gdb) k +``` Continue after breakpoint. - (gdb) c +``` +(gdb) c +``` -### GDB Core Dump +#### GDB Core Dump Either attach to the running process using `gdb -p PID` or start a new gdb run. - (gdb) r - (gdb) generate-core-file +``` +(gdb) r +(gdb) generate-core-file +``` -### GDB Backtrace +#### GDB Backtrace If Icinga 2 aborted its operation abnormally, generate a backtrace. - (gdb) bt - (gdb) thread apply all bt full +> **Note** +> +> Please install the [required debug symbols](21-development.md#debug-requirements) +> prior to generating a backtrace. + +`thread apply all` is important here since this includes all running threads. +We need this information when e.g. debugging dead locks and hanging features. + +``` +(gdb) bt +(gdb) thread apply all bt full +``` If gdb stops at a SIGPIPE signal please disable the signal before -running Icinga 2. +running Icinga 2. This isn't an error, but we need to workaround it. - (gdb) handle SIGPIPE nostop noprint pass - (gdb) r +``` +(gdb) handle SIGPIPE nostop noprint pass +(gdb) r +``` -If you create a [bug report](https://www.icinga.com/community/get-involved/), +If you create a [new issue](https://github.com/Icinga/icinga2/issues), make sure to attach as much detail as possible. -### GDB Backtrace from Running Process +#### GDB Backtrace from Running Process If Icinga 2 is still running, generate a full backtrace from the running -process and store it into a new file (e.g. for debugging dead locks): +process and store it into a new file (e.g. for debugging dead locks). - # gdb -p $(pidof icinga2) -batch -ex "thread apply all bt full" -ex "detach" -ex "q" > gdb_bt.log +> **Note** +> +> Please install the [required debug symbols](21-development.md#debug-requirements) +> prior to generating a backtrace. -### GDB Backtrace Stepping +Icinga 2 runs with 2 processes: main and command executor, therefore generate two backtrace logs +and add them to the GitHub issue. + +``` +for pid in $(pidof icinga2); do gdb -p $pid -batch -ex "thread apply all bt full" -ex "detach" -ex "q" > gdb_bt_${pid}_`date +%s`.log; done +``` + +#### GDB Thread List from Running Process + +Instead of a full backtrace, you sometimes just need a list of running threads. + +``` +for pid in $(pidof icinga2); do gdb -p $pid -batch -ex "info threads" -ex "detach" -ex "q" > gdb_threads_${pid}_`date +%s`.log; done +``` + +#### GDB Backtrace Stepping Identifying the problem may require stepping into the backtrace, analysing the current scope, attributes, and possible unmet requirements. `p` prints the value of the selected variable or function call result. - (gdb) up - (gdb) down - (gdb) p checkable - (gdb) p checkable.px->m_Name +``` +(gdb) up +(gdb) down +(gdb) p checkable +(gdb) p checkable.px->m_Name +``` - -### GDB Breakpoints +#### GDB Breakpoints To set a breakpoint to a specific function call, or file specific line. - (gdb) b checkable.cpp:125 - (gdb) b icinga::Checkable::SetEnablePerfdata +``` +(gdb) b checkable.cpp:125 +(gdb) b icinga::Checkable::SetEnablePerfdata +``` GDB will ask about loading the required symbols later, select `yes` instead of `no`. @@ -198,12 +195,25 @@ of `no`. Then run Icinga 2 until it reaches the first breakpoint. Continue with `c` afterwards. - (gdb) run - (gdb) c +``` +(gdb) run +(gdb) c +``` + +In case you want to step into the next line of code, use `n`. If there is a +function call where you want to step into, use `s`. + +``` +(gdb) n + +(gdb) s +``` If you want to delete all breakpoints, use `d` and select `yes`. - (gdb) d +``` +(gdb) d +``` > **Tip** > @@ -211,107 +221,2405 @@ If you want to delete all breakpoints, use `d` and select `yes`. Breakpoint Example: - (gdb) b __cxa_throw - (gdb) r - (gdb) up - .... - (gdb) up - #11 0x00007ffff7cbf9ff in icinga::Utility::GlobRecursive(icinga::String const&, icinga::String const&, boost::function const&, int) (path=..., pattern=..., callback=..., type=1) - at /home/michi/coding/icinga/icinga2/lib/base/utility.cpp:609 - 609 callback(cpath); - (gdb) l - 604 - 605 #endif /* _WIN32 */ - 606 - 607 std::sort(files.begin(), files.end()); - 608 BOOST_FOREACH(const String& cpath, files) { - 609 callback(cpath); - 610 } - 611 - 612 std::sort(dirs.begin(), dirs.end()); - 613 BOOST_FOREACH(const String& cpath, dirs) { - (gdb) p files - $3 = std::vector of length 11, capacity 16 = {{static NPos = 18446744073709551615, m_Data = "/etc/icinga2/conf.d/agent.conf"}, {static NPos = 18446744073709551615, - m_Data = "/etc/icinga2/conf.d/commands.conf"}, {static NPos = 18446744073709551615, m_Data = "/etc/icinga2/conf.d/downtimes.conf"}, {static NPos = 18446744073709551615, - m_Data = "/etc/icinga2/conf.d/groups.conf"}, {static NPos = 18446744073709551615, m_Data = "/etc/icinga2/conf.d/notifications.conf"}, {static NPos = 18446744073709551615, - m_Data = "/etc/icinga2/conf.d/satellite.conf"}, {static NPos = 18446744073709551615, m_Data = "/etc/icinga2/conf.d/services.conf"}, {static NPos = 18446744073709551615, - m_Data = "/etc/icinga2/conf.d/templates.conf"}, {static NPos = 18446744073709551615, m_Data = "/etc/icinga2/conf.d/test.conf"}, {static NPos = 18446744073709551615, - m_Data = "/etc/icinga2/conf.d/timeperiods.conf"}, {static NPos = 18446744073709551615, m_Data = "/etc/icinga2/conf.d/users.conf"}} +``` +(gdb) b __cxa_throw +(gdb) r +(gdb) up +.... +(gdb) up +#11 0x00007ffff7cbf9ff in icinga::Utility::GlobRecursive(icinga::String const&, icinga::String const&, boost::function const&, int) (path=..., pattern=..., callback=..., type=1) + at /home/michi/coding/icinga/icinga2/lib/base/utility.cpp:609 +609 callback(cpath); +(gdb) l +604 +605 #endif /* _WIN32 */ +606 +607 std::sort(files.begin(), files.end()); +608 BOOST_FOREACH(const String& cpath, files) { +609 callback(cpath); +610 } +611 +612 std::sort(dirs.begin(), dirs.end()); +613 BOOST_FOREACH(const String& cpath, dirs) { +(gdb) p files +$3 = std::vector of length 11, capacity 16 = {{static NPos = 18446744073709551615, m_Data = "/etc/icinga2/conf.d/agent.conf"}, {static NPos = 18446744073709551615, + m_Data = "/etc/icinga2/conf.d/commands.conf"}, {static NPos = 18446744073709551615, m_Data = "/etc/icinga2/conf.d/downtimes.conf"}, {static NPos = 18446744073709551615, + m_Data = "/etc/icinga2/conf.d/groups.conf"}, {static NPos = 18446744073709551615, m_Data = "/etc/icinga2/conf.d/notifications.conf"}, {static NPos = 18446744073709551615, + m_Data = "/etc/icinga2/conf.d/satellite.conf"}, {static NPos = 18446744073709551615, m_Data = "/etc/icinga2/conf.d/services.conf"}, {static NPos = 18446744073709551615, + m_Data = "/etc/icinga2/conf.d/templates.conf"}, {static NPos = 18446744073709551615, m_Data = "/etc/icinga2/conf.d/test.conf"}, {static NPos = 18446744073709551615, + m_Data = "/etc/icinga2/conf.d/timeperiods.conf"}, {static NPos = 18446744073709551615, m_Data = "/etc/icinga2/conf.d/users.conf"}} +``` -## Core Dump +### Core Dump When the Icinga 2 daemon crashes with a `SIGSEGV` signal a core dump file should be written. This will help developers to analyze and fix the problem. -### Core Dump File Size Limit +#### Core Dump File Size Limit This requires setting the core dump file size to `unlimited`. -Example for Systemd: - vim /usr/lib/systemd/system/icinga2.service +##### Systemd - [Service] - ... - LimitCORE=infinity +``` +systemctl edit icinga2.service - systemctl daemon-reload +[Service] +... +LimitCORE=infinity - systemctl restart icinga2 +systemctl daemon-reload -Example for init script: +systemctl restart icinga2 +``` - vim /etc/init.d/icinga2 - ... - ulimit -c unlimited +##### Init Script - service icinga2 restart +``` +vim /etc/init.d/icinga2 +... +ulimit -c unlimited + +service icinga2 restart +``` + +##### Verify Verify that the Icinga 2 process core file size limit is set to `unlimited`. - cat /proc/`pidof icinga2`/limits - ... - Max core file size unlimited unlimited bytes +``` +for pid in $(pidof icinga2); do cat /proc/$pid/limits; done + +... +Max core file size unlimited unlimited bytes +``` -### Core Dump Kernel Format +#### Core Dump Kernel Format The Icinga 2 daemon runs with the SUID bit set. Therefore you need to explicitly enable core dumps for SUID on Linux. - sysctl -w fs.suid_dumpable=1 +``` +sysctl -w fs.suid_dumpable=2 +``` Adjust the coredump kernel format and file location on Linux: - sysctl -w kernel.core_pattern=/var/lib/cores/core.%e.%p +``` +sysctl -w kernel.core_pattern=/var/lib/cores/core.%e.%p - install -m 1777 -d /var/lib/cores +install -m 1777 -d /var/lib/cores +``` MacOS: - sysctl -w kern.corefile=/cores/core.%P +``` +sysctl -w kern.corefile=/cores/core.%P - chmod 777 /cores +chmod 777 /cores +``` -### Core Dump Analysis +#### Core Dump Analysis Once Icinga 2 crashes again a new coredump file will be written. Please attach this file to your bug report in addition to the general details. Simple test case for a `SIGSEGV` simulation with `sleep`: - ulimit -c unlimited - sleep 1800& - [1] - kill -SEGV - gdb `which sleep` /var/lib/cores/core.sleep. - (gdb) bt - rm /var/lib/cores/core.sleep.* +``` +ulimit -c unlimited +sleep 1800& +[1] +kill -SEGV +gdb `which sleep` /var/lib/cores/core.sleep. +(gdb) bt +rm /var/lib/cores/core.sleep.* +``` Analyzing Icinga 2: - gdb /usr/lib64/icinga2/sbin/icinga2 core.icinga2. - (gdb) bt +``` +gdb /usr/lib64/icinga2/sbin/icinga2 core.icinga2. +(gdb) bt +``` + +### LLDB as Debugger + +LLDB is available on macOS with the Xcode command line tools. + +``` +$ xcode-select --install +``` + +In order to run Icinga 2 with LLDB you need to pass the binary as argument. +Since v2.11 we would attach to the umbrella process, therefore rather +attach to a running process. + +``` +# Typically the order of PIDs is: 1) umbrella 2) spawn helper 3) main process +pidof icinga2 + +lldb -p $(pidof icinga2 | cut -d ' ' -f3) +``` + +Breakpoint: + +``` +> b checkable.cpp:57 +> b icinga::Checkable::ProcessCheckResult +``` + +Full backtrace: + +``` +> bt all +``` + +Select thread: + +``` +> thr sel 5 +``` + +Step into: + +``` +> s +``` + +Next step: + +``` +> n +``` + +Continue: + +``` +> c +``` + +Up/down in stacktrace: + +``` +> up +> down +``` + + +### Debug on Windows + + +Whenever the application crashes, the Windows error reporting (WER) can be [configured](https://docs.microsoft.com/en-gb/windows/win32/wer/collecting-user-mode-dumps) +to create user-mode dumps. + + +Tail the log file with Powershell: + +``` +Get-Content .\icinga2.log -tail 10 -wait +``` + + +#### Debug on Windows: Dependencies + +Similar to `ldd` or `nm` on Linux/Unix. + +Extract the dependent DLLs from a binary with Visual Studio's `dumpbin` tool +in Powershell: + +``` +C:> &'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.22.27905\bin\Hostx64\x64\dumpbin.exe' /dependents .\debug\Bin\Debug\Debug\boosttest-test-base.exe +DEBUG: 1+ >>>> &'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.22.27905\bin\Hostx64\x64\dumpbin.exe' /dependents .\debug\Bin\Debug\Debug\boosttest-test-base.exe +Microsoft (R) COFF/PE Dumper Version 14.22.27905.0 +Copyright (C) Microsoft Corporation. All rights reserved. + + +Dump of file .\debug\Bin\Debug\Debug\boosttest-test-base.exe + +File Type: EXECUTABLE IMAGE + + Image has the following dependencies: + + boost_coroutine-vc142-mt-gd-x64-1_71.dll + boost_date_time-vc142-mt-gd-x64-1_71.dll + boost_filesystem-vc142-mt-gd-x64-1_71.dll + boost_thread-vc142-mt-gd-x64-1_71.dll + boost_regex-vc142-mt-gd-x64-1_71.dll + libssl-1_1-x64.dll + libcrypto-1_1-x64.dll + WS2_32.dll + dbghelp.dll + SHLWAPI.dll + msi.dll + boost_unit_test_framework-vc142-mt-gd-x64-1_71.dll + KERNEL32.dll + SHELL32.dll + ADVAPI32.dll + MSVCP140D.dll + MSWSOCK.dll + bcrypt.dll + VCRUNTIME140D.dll + ucrtbased.dll + + Summary + + 1000 .00cfg + 68000 .data + B000 .idata + 148000 .pdata + 69C000 .rdata + 25000 .reloc + 1000 .rsrc + E7A000 .text + 1000 .tls +``` + + +## Test Icinga 2 + +### Snapshot Packages (Nightly Builds) + +Icinga provides snapshot packages as nightly builds from [Git master](https://github.com/icinga/icinga2). + +These packages contain development code which should be considered "work in progress". +While developers ensure that tests are running fine with CI actions on PRs, +things might break, or changes are not yet documented in the changelog. + +You can help the developers and test the snapshot packages, e.g. when larger +changes or rewrites are taking place for a new major version. Your feedback +is very much appreciated. + +Snapshot packages are available for all supported platforms including +Linux and Windows and can be obtained from [https://packages.icinga.com](https://packages.icinga.com). + +The [Vagrant boxes](https://github.com/Icinga/icinga-vagrant) also use +the Icinga snapshot packages to allow easier integration tests. It is also +possible to use Docker with base OS images and installing the snapshot +packages. + +If you encounter a problem, please [open a new issue](https://github.com/Icinga/icinga2/issues/new/choose) +on GitHub and mention that you're testing the snapshot packages. + +#### RHEL/CentOS + +2.11+ requires the [EPEL repository](02-installation.md#package-repositories-rhel-epel) for Boost 1.66+. + +In addition to that, the `icinga-rpm-release` package already provides the `icinga-snapshot-builds` +repository but it is disabled by default. + +``` +yum -y install https://packages.icinga.com/epel/icinga-rpm-release-7-latest.noarch.rpm +yum -y install epel-release +yum makecache + +yum install --enablerepo=icinga-snapshot-builds icinga2 +``` + +#### Debian + +2.11+ requires Boost 1.66+ which either is provided by the OS, backports or Icinga stable repositories. +It is advised to configure both Icinga repositories, stable and snapshot and selectively +choose the repository with the `-t` flag on `apt-get install`. + +``` +apt-get update +apt-get -y install apt-transport-https wget gnupg + +wget -O - https://packages.icinga.com/icinga.key | apt-key add - + +DIST=$(awk -F"[)(]+" '/VERSION=/ {print $2}' /etc/os-release); \ + echo "deb https://packages.icinga.com/debian icinga-${DIST} main" > \ + /etc/apt/sources.list.d/${DIST}-icinga.list + echo "deb-src https://packages.icinga.com/debian icinga-${DIST} main" >> \ + /etc/apt/sources.list.d/${DIST}-icinga.list + +DIST=$(awk -F"[)(]+" '/VERSION=/ {print $2}' /etc/os-release); \ + echo "deb http://packages.icinga.com/debian icinga-${DIST}-snapshots main" > \ + /etc/apt/sources.list.d/${DIST}-icinga-snapshots.list + echo "deb-src http://packages.icinga.com/debian icinga-${DIST}-snapshots main" >> \ + /etc/apt/sources.list.d/${DIST}-icinga-snapshots.list + +apt-get update +``` + +On Debian Stretch, you'll also need to add Debian Backports. + +``` +DIST=$(awk -F"[)(]+" '/VERSION=/ {print $2}' /etc/os-release); \ + echo "deb https://deb.debian.org/debian ${DIST}-backports main" > \ + /etc/apt/sources.list.d/${DIST}-backports.list + +apt-get update +``` + +Then install the snapshot packages. + +``` +DIST=$(awk -F"[)(]+" '/VERSION=/ {print $2}' /etc/os-release); \ +apt-get install -t icinga-${DIST}-snapshots icinga2 +``` + +#### Ubuntu + +``` +apt-get update +apt-get -y install apt-transport-https wget gnupg + +wget -O - https://packages.icinga.com/icinga.key | apt-key add - + +. /etc/os-release; if [ ! -z ${UBUNTU_CODENAME+x} ]; then DIST="${UBUNTU_CODENAME}"; else DIST="$(lsb_release -c| awk '{print $2}')"; fi; \ + echo "deb https://packages.icinga.com/ubuntu icinga-${DIST} main" > \ + /etc/apt/sources.list.d/${DIST}-icinga.list + echo "deb-src https://packages.icinga.com/ubuntu icinga-${DIST} main" >> \ + /etc/apt/sources.list.d/${DIST}-icinga.list + +. /etc/os-release; if [ ! -z ${UBUNTU_CODENAME+x} ]; then DIST="${UBUNTU_CODENAME}"; else DIST="$(lsb_release -c| awk '{print $2}')"; fi; \ + echo "deb https://packages.icinga.com/ubuntu icinga-${DIST}-snapshots main" > \ + /etc/apt/sources.list.d/${DIST}-icinga-snapshots.list + echo "deb-src https://packages.icinga.com/ubuntu icinga-${DIST}-snapshots main" >> \ + /etc/apt/sources.list.d/${DIST}-icinga-snapshots.list + +apt-get update +``` + +Then install the snapshot packages. + +``` +. /etc/os-release; if [ ! -z ${UBUNTU_CODENAME+x} ]; then DIST="${UBUNTU_CODENAME}"; else DIST="$(lsb_release -c| awk '{print $2}')"; fi; \ +apt-get install -t icinga-${DIST}-snapshots icinga2 +``` + +#### SLES + +The required Boost packages are provided with the stable release repository. + +``` +rpm --import https://packages.icinga.com/icinga.key + +zypper ar https://packages.icinga.com/SUSE/ICINGA-release.repo +zypper ref + +zypper ar https://packages.icinga.com/SUSE/ICINGA-snapshot.repo +zypper ref +``` + +Selectively install the snapshot packages using the `-r` parameter. + +``` +zypper in -r icinga-snapshot-builds icinga2 +``` + + +### Unit Tests + +Build the binaries and run the tests. + + +``` +make -j4 -C debug +make test -C debug +``` + +Run a specific boost test: + +``` +debug/Bin/Debug/boosttest-test-base --run_test=remote_url +``` + + + +## Develop Icinga 2 + +Icinga 2 can be built on many platforms such as Linux, Unix and Windows. +There are limitations in terms of support, e.g. Windows is only supported for agents, +not a full-featured master or satellite. + +Before you start with actual development, there is a couple of pre-requisites. + +### Preparations + +#### Choose your Editor + +Icinga 2 can be developed with your favorite editor. Icinga developers prefer +these tools: + +- vim +- CLion (macOS, Linux) +- MS Visual Studio (Windows) +- Atom + +Editors differ on the functionality. The more helpers you get for C++ development, +the faster your development workflow will be. + +#### Get to know the architecture + +Icinga 2 can run standalone or in distributed environments. It contains a whole lot +more than a simple check execution engine. + +Read more about it in the [Technical Concepts](19-technical-concepts.md#technical-concepts) chapter. + +#### Get to know the code + +First off, you really need to know C++ and portions of C++11 and the boost libraries. +Best is to start with a book or online tutorial to get into the basics. +Icinga developers gained their knowledge through studies, training and self-teaching +code by trying it out and asking senior developers for guidance. + +Here's a few books we can recommend: + +* [Accelerated C++: Practical Programming by Example](https://www.amazon.com/Accelerated-C-Practical-Programming-Example/dp/020170353X) (Andrew Koenig, Barbara E. Moo) +* [Effective C++](https://www.amazon.com/Effective-Specific-Improve-Programs-Designs/dp/0321334876) (Scott Meyers) +* [Boost C++ Application Development Cookbook - Second Edition: Recipes to simplify your application development](https://www.amazon.com/dp/1787282244/ref=cm_sw_em_r_mt_dp_U_dN1OCbERS00EQ) (Antony Polukhin) +* [Der C++ Programmierer](https://www.amazon.de/Programmierer-lernen-Professionell-anwenden-L%C3%B6sungen/dp/3446416447), German (Ulrich Breymann) +* [C++11 programmieren](https://www.amazon.de/gp/product/3836217325/), German (Torsten T. Will) + +In addition, it is a good bet to also know SQL when diving into backend development. + +* [SQL Performance Explained](https://www.amazon.de/gp/product/3950307826/) (Markus Winand) + +Last but not least, if you are developing on Windows, get to know the internals about services and the Win32 API. + +### Design Patterns + +Icinga 2 heavily relies on object-oriented programming and encapsulates common +functionality into classes and objects. It also uses modern programming techniques +to e.g. work with shared pointer memory management. + +Icinga 2 consists of libraries bundled into the main binary. Therefore you'll +find many code parts in the `lib/` directory wheras the actual application is +built from `icinga-app/`. Accompanied with Icinga 2, there's the Windows plugins +which are standalone and compiled from `plugins/`. + +Library | Description +---------------|------------------------------------ +base | Objects, values, types, streams, tockets, TLS, utilities, etc. +config | Configuration compiler, expressions, etc. +cli | CLI (sub) commands and helpers. +icinga | Icinga specific objects and event handling. +remote | Cluster and HTTP client/server and REST API related code. +checker | Checker feature, check scheduler. +notification | Notification feature, notification scheduler. +methods | Command execution methods, plugins and built-in checks. +perfdata | Performance data related, including Graphite, Elastic, etc. +db\_ido | IDO database abstraction layer. +db\_ido\_mysql | IDO database driver for MySQL. +db\_ido\_pgsql | IDO database driver for PgSQL. +mysql\_shin | Library stub for linking against the MySQL client libraries. +pgsql\_shim | Library stub for linking against the PgSQL client libraries. + +#### Class Compiler + +Another thing you will recognize are the `.ti` files which are compiled +by our own class compiler into actual source code. The meta language allows +developers to easily add object attributes and specify their behaviour. + +Some object attributes need to be stored over restarts in the state file +and therefore have the `state` attribute set. Others are treated as `config` +attribute and automatically get configuration validation functions created. +Hidden or read-only REST API attributes are marked with `no_user_view` and +`no_user_modify`. + +The most beneficial thing are getters and setters being generated. The actual object +inherits from `ObjectImpl` and therefore gets them "for free". + +Example: + +``` +vim lib/perfdata/gelfwriter.ti + + [config] enable_tls; + +vim lib/perfdata/gelfwriter.cpp + + if (GetEnableTls()) { +``` + +The logic is hidden in `tools/mkclass/` in case you want to learn more about it. +The first steps during CMake & make also tell you about code generation. + +### Build Tools + +#### CMake + +In its early development stages in 2012, Icinga 2 was built with autoconf/automake +and separate Windows project files. We've found this very fragile, and have changed +this into CMake as our build tool. + +The most common benefits: + +* Everything is described in CMakeLists.txt in each directory +* CMake only needs to know that a sub directory needs to be included. +* The global CMakeLists.txt acts as main entry point for requirement checks and library/header includes. +* Separate binary build directories, the actual source tree stays clean. +* CMake automatically generates a Visual Studio project file `icinga2.sln` on Windows. + +#### Unity Builds + +Another thing you should be aware of: Unity builds on and off. + +Typically, we already use caching mechanisms to reduce recompile time with ccache. +For release builds, there's always a new build needed as the difference is huge compared +to a previous (major) release. + +Therefore we've invented the Unity builds, which basically concatenates all source files +into one big library source code file. The compiler then doesn't need to load the many small +files but compiles and links this huge one. + +Unity builds require more memory which is why you should disable them for development +builds in small sized VMs (Linux, Windows) and also Docker containers. + +There's a couple of header files which are included everywhere. If you touch/edit them, +the cache is invalidated and you need to recompile a lot more files then. `base/utility.hpp` +and `remote/zone.hpp` are good candidates for this. + +### Unit Tests + +New functions and classes must implement new unit tests. Whenever +you decide to add new functions, ensure that you don't need a complex +mock or runtime attributes in order to test them. Better isolate +code into function interfaces which can be invoked in the Boost tests +framework. + +Look into the existing tests in the [test/](https://github.com/Icinga/icinga2/tree/master/test) directory +and adopt new test cases. + +Specific tests require special time windows, they are only +enabled in debug builds for developers. This is the case e.g. +for testing the flapping algorithm with expected state change +detection at a specific point from now. + + +### Style Guide + +Overview of project files: + +File Type | File Name/Extension | Description +---------------|---------------------|----------------------------- +Header | .hpp | Classes, enums, typedefs inside the icinga Namespace. +Source | .cpp | Method implementation for class functions, static/global variables. +CMake | CMakeLists.txt | Build configuration, source and header file references. +CMake Source | .cmake | Source/Header files generated from CMake placeholders. +ITL/conf.d | .conf | Template library and example files as configuration +Class Compiler | .ti | Object classes in our own language, generates source code as `-ti.{c,h}pp`. +Lexer/Parser | .ll, .yy | Flex/Bison code generated into source code from CMake builds. +Docs | .md | Markdown docs and READMEs. + +Anything else are additional tools and scripts for developers and build systems. + +All files must include the copyright header. We don't use the +current year as this implies yearly updates we don't want. + +Depending on the file type, this must be a comment. + +``` +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ +``` + +#### Code Formatting + +**Tabs instead of spaces.** Inside Visual Studio, choose to keep tabs instead of +spaces. Tabs should use 4 spaces indent by default, depending on your likings. + +We follow the clang format, with some exceptions. + +- Curly braces for functions and classes always start at a new line. + +``` +String ConfigObjectUtility::EscapeName(const String& name) +{ +//... +} + +String ConfigObjectUtility::CreateObjectConfig(const Type::Ptr& type, const String& fullName, + bool ignoreOnError, const Array::Ptr& templates, const Dictionary::Ptr& attrs) +{ +//... +} +``` + +- Too long lines break at a parameter, the new line needs a tab indent. + +``` + static String CreateObjectConfig(const Type::Ptr& type, const String& fullName, + bool ignoreOnError, const Array::Ptr& templates, const Dictionary::Ptr& attrs); +``` + +- Conditions require curly braces if it is not a single if with just one line. + + +``` + if (s == "OK") { + //... + } else { + //... + } + + if (!n) + return; +``` + +- There's a space between `if` and the opening brace `(`. Also after the closing brace `)` and opening curly brace `{`. +- Negation with `!` doesn't need an extra space. +- Else branches always start in the same line after the closing curly brace. + + +#### Code Comments + +Add comments wherever you think that another developer will have a hard +time to understand the complex algorithm. Or you might have forgotten +it in a year and struggle again. Also use comments to highlight specific +stages in a function. Generally speaking, make things easier for the +team and external contributors. + +Comments can also be used to mark additional references and TODOs. +If there is a specific GitHub issue or discussion going on, +use that information as a summary and link over to it on purpose. + +- Single line comments may use `//` or `/* ... */` +- Multi line comments must use this format: + +``` +/* Ensure to check for XY + * This relies on the fact that ABC has been set before. + */ +``` + +#### Function Docs + +Function header documentation must be added. The current code basis +needs rework, future functions must provide this. + +Editors like CLion or Visual Studio allow you to type `/**` followed +by Enter and generate the skeleton from the implemented function. + +Add a short summary in the first line about the function's purpose. +Edit the param section with short description on their intention. +The `return` value should describe the value type and additional details. + +Example: + +``` +/** + * Reads a message from the connected peer. + * + * @param stream ASIO TLS Stream + * @param yc Yield Context for ASIO + * @param maxMessageLength maximum size of bytes read. + * + * @return A JSON string + */ +String JsonRpc::ReadMessage(const std::shared_ptr& stream, boost::asio::yield_context yc, ssize_t maxMessageLength) +``` + +While we can generate code docs from it, the main idea behind it is +to provide on-point docs to fully understand all parameters and the +function's purpose in the same spot. + + +#### Header + +Only include other headers which are mandatory for the header definitions. +If the source file requires additional headers, add them there to avoid +include loops. + +The included header order is important. + +- First, include the library header `i2-.hpp`, e.g. `i2-base.hpp`. +- Second, include all headers from Icinga itself, e.g. `remote/apilistener.hpp`. `base` before `icinga` before `remote`, etc. +- Third, include third-party and external library headers, e.g. openssl and boost. +- Fourth, include STL headers. + +#### Source + +The included header order is important. + +- First, include the header whose methods are implemented. +- Second, include all headers from Icinga itself, e.g. `remote/apilistener.hpp`. `base` before `icinga` before `remote`, etc. +- Third, include third-party and external library headers, e.g. openssl and boost. +- Fourth, include STL headers. + +Always use an empty line after the header include parts. + +#### Namespace + +The icinga namespace is used globally, as otherwise we would need to write `icinga::Utility::FormatDateTime()`. + +``` +using namespace icinga; +``` + +Other namespaces must be declared in the scope they are used. Typically +this is inside the function where `boost::asio` and variants would +complicate the code. + +``` + namespace ssl = boost::asio::ssl; + + auto context (std::make_shared(ssl::context::sslv23)); +``` + +#### Functions + +Ensure to pass values and pointers as const reference. By default, all +values will be copied into the function scope, and we want to avoid this +wherever possible. + +``` +std::vector EventQueue::GetQueuesForType(const String& type) +``` + +C++ only allows to return a single value. This can be abstracted with +returning a specific class object, or with using a map/set. Array and +Dictionary objects increase the memory footprint, use them only where needed. + +A common use case for Icinga value types is where a function can return +different values - an object, an array, a boolean, etc. This happens in the +inner parts of the config compiler expressions, or config validation. + +The function caller is responsible to determine the correct value type +and handle possible errors. + +Specific algorithms may require to populate a list, which can be passed +by reference to the function. The inner function can then append values. +Do not use a global shared resource here, unless this is locked by the caller. + + +#### Conditions and Cases + +Prefer if-else-if-else branches. When integers are involved, +switch-case statements increase readability. Don't forget about `break` though! + +Avoid using ternary operators where possible. Putting a condition +after an assignment complicates reading the source. The compiler +optimizes this anyways. + +Wrong: + +``` + int res = s == "OK" ? 0 : s == "WARNING" ? 1; + + return res; +``` + +Better: + +``` + int res = 3; + + if (s == "OK") { + res = 0; + } else if (s == "WARNING") { + res = 1; + } +``` + +Even better: Create a lookup map instead of if branches. The complexity +is reduced to O(log(n)). + +``` + std::map stateMap = { + { "OK", 1 }, + { "WARNING", 2 } + } + + auto it = stateMap.find(s); + + if (it == stateMap.end()) { + return 3 + } + + return it.second; +``` + +The code is not as short as with a ternary operator, but one can re-use +this design pattern for other generic definitions with e.g. moving the +lookup into a utility class. + +Once a unit test is written, everything works as expected in the future. + +#### Locks and Guards + +Lock access to resources where multiple threads can read and write. +Icinga objects can be locked with the `ObjectLock` class. + +Object locks and guards must be limited to the scope where they are needed. Otherwise we could create dead locks. + +``` + { + ObjectLock olock(frame.Locals); + for (const Dictionary::Pair& kv : frame.Locals) { + AddSuggestion(matches, word, kv.first); + } + } +``` + +#### Objects and Pointers + +Use shared pointers for objects. Icinga objects implement the `Ptr` +typedef returning an `intrusive_ptr` for the class object (object.hpp). +This also ensures reference counting for the object's lifetime. + +Use raw pointers with care! + +Some methods and classes require specific shared pointers, especially +when interacting with the Boost library. + +#### Value Types + +Icinga has its own value types. These provide methods to allow +generic serialization into JSON for example, and other type methods +which are made available in the DSL too. + +- Always use `String` instead of `std::string`. If you need a C-string, use the `CStr()` method. +- Avoid casts and rather use the `Convert` class methods. + +``` + double s = static_cast(v); //Wrong + + double s = Convert::ToDouble(v); //Correct, ToDouble also provides overloads with different value types +``` + +- Prefer STL containers for internal non-user interfaces. Icinga value types add a small overhead which may decrease performance if e.g. the function is called 100k times. +- `Array::FromVector` and variants implement conversions, use them. + +#### Utilities + +Don't re-invent the wheel. The `Utility` class provides +many helper functions which allow you e.g. to format unix timestamps, +search in filesystem paths. + +Also inspect the Icinga objects, they also provide helper functions +for formatting, splitting strings, joining arrays into strings, etc. + +#### Libraries + +2.11 depends on [Boost 1.66](https://www.boost.org/doc/libs/1_66_0/). +Use the existing libraries and header-only includes +for this specific version. + +Note: Prefer C++11 features where possible, e.g. std::atomic and lambda functions. + +General: + +- [exception](https://www.boost.org/doc/libs/1_66_0/libs/exception/doc/boost-exception.html) (header only) +- [algorithm](https://www.boost.org/doc/libs/1_66_0/libs/algorithm/doc/html/index.html) (header only) +- [lexical_cast](https://www.boost.org/doc/libs/1_66_0/doc/html/boost_lexical_cast.html) (header only) +- [regex](https://www.boost.org/doc/libs/1_66_0/libs/regex/doc/html/index.html) +- [uuid](https://www.boost.org/doc/libs/1_66_0/libs/uuid/doc/uuid.html) (header only) +- [range](https://www.boost.org/doc/libs/1_66_0/libs/range/doc/html/index.html) (header only) +- [variant](https://www.boost.org/doc/libs/1_66_0/doc/html/variant.html) (header only) +- [multi_index](https://www.boost.org/doc/libs/1_66_0/libs/multi_index/doc/index.html) (header only) +- [function_types](https://www.boost.org/doc/libs/1_66_0/libs/function_types/doc/html/index.html) (header only) +- [circular_buffer](https://www.boost.org/doc/libs/1_66_0/doc/html/circular_buffer.html) (header only) +- [math](https://www.boost.org/doc/libs/1_66_0/libs/math/doc/html/index.html) (header only) + +Events and Runtime: + +- [system](https://www.boost.org/doc/libs/1_66_0/libs/system/doc/index.html) +- [thread](https://www.boost.org/doc/libs/1_66_0/doc/html/thread.html) +- [signals2](https://www.boost.org/doc/libs/1_66_0/doc/html/signals2.html) (header only) +- [program_options](https://www.boost.org/doc/libs/1_66_0/doc/html/program_options.html) +- [date_time](https://www.boost.org/doc/libs/1_66_0/doc/html/date_time.html) +- [filesystem](https://www.boost.org/doc/libs/1_66_0/libs/filesystem/doc/index.htm) + +Network I/O: + +- [asio](https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio.html) (header only) +- [beast](https://www.boost.org/doc/libs/1_66_0/libs/beast/doc/html/index.html) (header only) +- [coroutine](https://www.boost.org/doc/libs/1_66_0/libs/coroutine/doc/html/index.html) +- [context](https://www.boost.org/doc/libs/1_66_0/libs/context/doc/html/index.html) + +Consider abstracting their usage into `*utility.{c,h}pp` files with +wrapping existing Icinga types. That also allows later changes without +rewriting large code parts. + +> **Note** +> +> A new Boost library should be explained in a PR and discussed with the team. +> +> This requires package dependency changes. + +If you consider an external library or code to be included with Icinga, the following +requirements must be fulfilled: + +- License is compatible with GPLv2+. Boost license, MIT works, Apache is not. +- C++11 is supported, C++14 or later doesn't work +- Header only implementations are preferred, external libraries require packages on every distribution. +- No additional frameworks, Boost is the only allowed. +- The code is proven to be robust and the GitHub repository is alive, or has 1k+ stars. Good libraries also provide a user list, if e.g. Ceph is using it, this is a good candidate. + + +#### Log + +Icinga allows the user to configure logging backends, e.g. syslog or file. + +Any log message inside the code must use the `Log()` function. + +- The first parameter is the severity level, use them with care. +- The second parameter defines the location/scope where the log +happened. Typically we use the class name here, to better analyse +the logs the user provide in GitHub issues and on the community +channels. +- The third parameter takes a log message string + +If the message string needs to be computed from existing values, +everything must be converted to the String type beforehand. +This conversion for every value is very expensive which is why +we try to avoid it. + +Instead, use Log() with the shift operator where everything is written +on the stream and conversions are explicitly done with templates +in the background. + +The trick here is that the Log object is destroyed immediately +after being constructed once. The destructor actually +evaluates the values and sends it to registers loggers. + +Since flushing the stream every time a log entry occurs is +very expensive, a timer takes care of flushing the stream +every second. + +> **Tip** +> +> If logging stopped, the flush timer thread may be dead. +> Inspect that with gdb/lldb. + +Avoid log messages which could irritate the user. During +implementation, developers can change log levels to better +see what's going one, but remember to change this back to `debug` +or remove it entirely. + + +#### Goto + +Avoid using `goto` statements. There are rare occasions where +they are allowed: + +- The code would become overly complicated within nested loops and conditions. +- Event processing and C interfaces. +- Question/Answer loops within interactive CLI commands. + +#### Typedef and Auto Keywords + +Typedefs allow developers to use shorter names for specific types, +classes and structs. + +``` + typedef std::map >::iterator Iterator; +``` + +These typedefs should be part of the Class definition in the header, +or may be defined in the source scope where they are needed. + +Avoid declaring global typedefs, unless necessary. + +Using the `auto` keyword allows to ignore a specific value type. +This comes in handy with maps/sets where no specific access +is required. + +The following example iterates over a map returned from `GetTypes()`. + +``` + for (const auto& kv : GetTypes()) { + result.insert(kv.second); + } +``` + +The long example would require us to define a map iterator, and a slightly +different algorithm. + +``` + typedef std::map TypeMap; + typedef std::map::const_iterator TypeMapIterator; + + TypeMap types = GetTypes(); + + for (TypeMapIterator it = types.begin(); it != types.end(); it++) { + result.insert(it.second); + } +``` + +We could also use a pair here, but requiring to know +the specific types of the map keys and values. + +``` + typedef std::pair kv_pair; + + for (const kv_pair& kv : GetTypes()) { + result.insert(kv.second); + } +``` + +After all, `auto` shortens the code and one does not always need to know +about the specific types. Function documentation for `GetTypes()` is +required though. + + + +#### Whitespace Cleanup + +Patches must be cleaned up and follow the indent style (tabs instead of spaces). +You should also remove any trailing whitespaces. + +`git diff` allows to highlight such. + +``` +vim $HOME/.gitconfig + +[color "diff"] + whitespace = red reverse +[core] + whitespace=fix,-indent-with-non-tab,trailing-space,cr-at-eol +``` + +`vim` also can match these and visually alert you to remove them. + +``` +vim $HOME/.vimrc + +highlight ExtraWhitespace ctermbg=red guibg=red +match ExtraWhitespace /\s\+$/ +autocmd BufWinEnter * match ExtraWhitespace /\s\+$/ +autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@ + +### Linux Dev Environment + +Based on CentOS 7, we have an early draft available inside the Icinga Vagrant boxes: +[centos7-dev](https://github.com/Icinga/icinga-vagrant/tree/master/centos7-dev). + +If you're compiling Icinga 2 natively without any virtualization layer in between, +this usually is faster. This is also the reason why developers on macOS prefer native builds +over Linux or Windows VMs. Don't forget to test the actual code on Linux later! Socket specific +stuff like `epoll` is not available on Unix kernels. + +Depending on your workstation and environment, you may either develop and run locally, +use a container deployment pipeline or put everything in a high end resource remote VM. + +Fork https://github.com/Icinga/icinga2 into your own repository, e.g. `https://github.com/dnsmichi/icinga2`. + +Create two build directories for different binary builds. + +* `debug` contains the debug build binaries. They contain more debug information and run tremendously slower than release builds from packages. Don't use them for benchmarks. +* `release` contains the release build binaries, as you would install them on a live system. This helps comparing specific scenarios for race conditions and more. + +``` +mkdir -p release debug +``` + +Proceed with the specific distribution examples below. Keep in mind that these instructions +are best effort and sometimes out-of-date. Git Master may contain updates. + +* [CentOS 7](21-development.md#development-linux-dev-env-centos) +* [Debian 10 Buster](21-development.md#development-linux-dev-env-debian) +* [Ubuntu 18 Bionic](21-development.md#development-linux-dev-env-ubuntu) + + +#### CentOS 7 + +``` +yum -y install gdb vim git bash-completion htop + +yum -y install rpmdevtools ccache \ + cmake make gcc-c++ flex bison \ + openssl-devel boost169-devel systemd-devel \ + mysql-devel postgresql-devel libedit-devel \ + libstdc++-devel + +groupadd icinga +groupadd icingacmd +useradd -c "icinga" -s /sbin/nologin -G icingacmd -g icinga icinga + +ln -s /bin/ccache /usr/local/bin/gcc +ln -s /bin/ccache /usr/local/bin/g++ + +git clone https://github.com/icinga/icinga2.git && cd icinga2 +``` + +The debug build binaries contain specific code which runs +slower but allows for better debugging insights. + +For benchmarks, change `CMAKE_BUILD_TYPE` to `RelWithDebInfo` and +build inside the `release` directory. + +First, off export some generics for Boost. + +``` +export I2_BOOST="-DBoost_NO_BOOST_CMAKE=TRUE -DBoost_NO_SYSTEM_PATHS=TRUE -DBOOST_LIBRARYDIR=/usr/lib64/boost169 -DBOOST_INCLUDEDIR=/usr/include/boost169 -DBoost_ADDITIONAL_VERSIONS='1.69;1.69.0'" +``` + +Second, add the prefix path to it. + +``` +export I2_GENERIC="$I2_BOOST -DCMAKE_INSTALL_PREFIX=/usr/local/icinga2" +``` + +Third, define the two build types with their specific CMake variables. + +``` +export I2_DEBUG="-DCMAKE_BUILD_TYPE=Debug -DICINGA2_UNITY_BUILD=OFF $I2_GENERIC" +export I2_RELEASE="-DCMAKE_BUILD_TYPE=RelWithDebInfo -DICINGA2_WITH_TESTS=ON -DICINGA2_UNITY_BUILD=ON $I2_GENERIC" +``` + +Fourth, depending on your likings, you may add a bash alias for building, +or invoke the commands inside: + +``` +alias i2_debug="cd /root/icinga2; mkdir -p debug; cd debug; cmake $I2_DEBUG ..; make -j2; sudo make -j2 install; cd .." +alias i2_release="cd /root/icinga2; mkdir -p release; cd release; cmake $I2_RELEASE ..; make -j2; sudo make -j2 install; cd .." +``` + +This is taken from the [centos7-dev](https://github.com/Icinga/icinga-vagrant/tree/master/centos7-dev) Vagrant box. + + +The source installation doesn't set proper permissions, this is +handled in the package builds which are officially supported. + +``` +chown -R icinga:icinga /usr/local/icinga2/var/ + +/usr/local/icinga2/lib/icinga2/prepare-dirs /usr/local/icinga2/etc/sysconfig/icinga2 +/usr/local/icinga2/sbin/icinga2 api setup +vim /usr/local/icinga2/etc/icinga2/conf.d/api-users.conf + +/usr/local/icinga2/lib/icinga2/sbin/icinga2 daemon +``` + +#### Debian 10 + +Debian Buster doesn't need updated Boost packages from packages.icinga.com, +the distribution already provides 1.66+. For older versions such as Stretch, +include the release repository for packages.icinga.com as shown in the [setup instructions](02-installation.md#package-repositories-debian-ubuntu-raspbian). + +``` +$ docker run -ti ubuntu:bionic bash + +apt-get update +apt-get -y install apt-transport-https wget gnupg + +apt-get -y install gdb vim git cmake make ccache build-essential libssl-dev bison flex default-libmysqlclient-dev libpq-dev libedit-dev monitoring-plugins +apt-get -y install libboost-all-dev +``` + +``` +ln -s /usr/bin/ccache /usr/local/bin/gcc +ln -s /usr/bin/ccache /usr/local/bin/g++ + +groupadd icinga +groupadd icingacmd +useradd -c "icinga" -s /sbin/nologin -G icingacmd -g icinga icinga + +git clone https://github.com/icinga/icinga2.git && cd icinga2 + +mkdir debug release + +export I2_DEB="-DBoost_NO_BOOST_CMAKE=TRUE -DBoost_NO_SYSTEM_PATHS=TRUE -DBOOST_LIBRARYDIR=/usr/lib/x86_64-linux-gnu -DBOOST_INCLUDEDIR=/usr/include -DCMAKE_INSTALL_RPATH=/usr/lib/x86_64-linux-gnu" +export I2_GENERIC="-DCMAKE_INSTALL_PREFIX=/usr/local/icinga2 -DICINGA2_PLUGINDIR=/usr/local/sbin" +export I2_DEBUG="$I2_DEB $I2_GENERIC -DCMAKE_BUILD_TYPE=Debug -DICINGA2_UNITY_BUILD=OFF" + +cd debug +cmake .. $I2_DEBUG +cd .. + +make -j2 install -C debug +``` + + +The source installation doesn't set proper permissions, this is +handled in the package builds which are officially supported. + +``` +chown -R icinga:icinga /usr/local/icinga2/var/ + +/usr/local/icinga2/lib/icinga2/prepare-dirs /usr/local/icinga2/etc/sysconfig/icinga2 +/usr/local/icinga2/sbin/icinga2 api setup +vim /usr/local/icinga2/etc/icinga2/conf.d/api-users.conf + +/usr/local/icinga2/lib/icinga2/sbin/icinga2 daemon +``` + + +#### Ubuntu 18 Bionic + +Requires Boost packages from packages.icinga.com. + +``` +$ docker run -ti ubuntu:bionic bash + +apt-get update +apt-get -y install apt-transport-https wget gnupg + +wget -O - https://packages.icinga.com/icinga.key | apt-key add - + +. /etc/os-release; if [ ! -z ${UBUNTU_CODENAME+x} ]; then DIST="${UBUNTU_CODENAME}"; else DIST="$(lsb_release -c| awk '{print $2}')"; fi; \ + echo "deb https://packages.icinga.com/ubuntu icinga-${DIST} main" > \ + /etc/apt/sources.list.d/${DIST}-icinga.list + echo "deb-src https://packages.icinga.com/ubuntu icinga-${DIST} main" >> \ + /etc/apt/sources.list.d/${DIST}-icinga.list + +apt-get update +``` + +``` +apt-get -y install gdb vim git cmake make ccache build-essential libssl-dev bison flex default-libmysqlclient-dev libpq-dev libedit-dev monitoring-plugins + +apt-get install -y libboost1.67-icinga-all-dev + +ln -s /usr/bin/ccache /usr/local/bin/gcc +ln -s /usr/bin/ccache /usr/local/bin/g++ + +groupadd icinga +groupadd icingacmd +useradd -c "icinga" -s /sbin/nologin -G icingacmd -g icinga icinga + +git clone https://github.com/icinga/icinga2.git && cd icinga2 + +mkdir debug release + +export I2_DEB="-DBoost_NO_BOOST_CMAKE=TRUE -DBoost_NO_SYSTEM_PATHS=TRUE -DBOOST_LIBRARYDIR=/usr/lib/x86_64-linux-gnu/icinga-boost -DBOOST_INCLUDEDIR=/usr/include/icinga-boost -DCMAKE_INSTALL_RPATH=/usr/lib/x86_64-linux-gnu/icinga-boost" +export I2_GENERIC="-DCMAKE_INSTALL_PREFIX=/usr/local/icinga2 -DICINGA2_PLUGINDIR=/usr/local/sbin" +export I2_DEBUG="$I2_DEB $I2_GENERIC -DCMAKE_BUILD_TYPE=Debug -DICINGA2_UNITY_BUILD=OFF" + +cd debug +cmake .. $I2_DEBUG +cd .. +``` + +``` +make -j2 install -C debug +``` + +The source installation doesn't set proper permissions, this is +handled in the package builds which are officially supported. + +``` +chown -R icinga:icinga /usr/local/icinga2/var/ + +/usr/local/icinga2/lib/icinga2/prepare-dirs /usr/local/icinga2/etc/sysconfig/icinga2 +/usr/local/icinga2/sbin/icinga2 api setup +vim /usr/local/icinga2/etc/icinga2/conf.d/api-users.conf + +/usr/local/icinga2/lib/icinga2/sbin/icinga2 daemon +``` + +### macOS Dev Environment + +It is advised to use Homebrew to install required build dependencies. +Macports have been reported to work as well, typically you'll get more help +with Homebrew from Icinga developers. + +The idea is to run Icinga with the current user, avoiding root permissions. +This requires at least v2.11. + +> **Note** +> +> This is a pure development setup for Icinga developers reducing the compile +> time in contrast to VMs. There are no packages, startup scripts or dependency management involved. +> +> **macOS agents are not officially supported.** +> +> macOS uses its own TLS implementation, Icinga relies on extra OpenSSL packages +> requiring updates apart from vendor security updates. + +#### Requirements + +Explicitly use OpenSSL 1.1.x, older versions are out of support. + +``` +brew install ccache boost cmake bison flex openssl@1.1 mysql-connector-c++ postgresql libpq +``` + +##### ccache + +``` +sudo mkdir /opt/ccache + +sudo ln -s `which ccache` /opt/ccache/clang +sudo ln -s `which ccache` /opt/ccache/clang++ + +vim $HOME/.bash_profile + +# ccache is managed with symlinks to avoid collision with cgo +export PATH="/opt/ccache:$PATH" + +source $HOME/.bash_profile +``` + +#### Builds + +Icinga is built as release (optimized build for packages) and debug (more symbols and details for debugging). Debug builds +typically run slower than release builds and must not be used for performance benchmarks. + +The preferred installation prefix is `/usr/local/icinga/icinga2`. This allows to put e.g. Icinga Web 2 into the `/usr/local/icinga` directory as well. + +``` +mkdir -p release debug + +export I2_USER=$(id -u -n) +export I2_GROUP=$(id -g -n) +export I2_GENERIC="-DCMAKE_INSTALL_PREFIX=/usr/local/icinga/icinga2 -DICINGA2_USER=$I2_USER -DICINGA2_GROUP=$I2_GROUP -DOPENSSL_INCLUDE_DIR=/usr/local/opt/openssl@1.1/include -DOPENSSL_SSL_LIBRARY=/usr/local/opt/openssl@1.1/lib/libssl.dylib -DOPENSSL_CRYPTO_LIBRARY=/usr/local/opt/openssl@1.1/lib/libcrypto.dylib -DICINGA2_PLUGINDIR=/usr/local/sbin -DICINGA2_WITH_PGSQL=OFF -DCMAKE_EXPORT_COMPILE_COMMANDS=ON" +export I2_DEBUG="-DCMAKE_BUILD_TYPE=Debug -DICINGA2_UNITY_BUILD=OFF $I2_GENERIC" +export I2_RELEASE="-DCMAKE_BUILD_TYPE=RelWithDebInfo -DICINGA2_WITH_TESTS=ON -DICINGA2_UNITY_BUILD=ON $I2_GENERIC" + +cd debug +cmake $I2_DEBUG .. +cd .. + +make -j4 -C debug +make -j4 install -C debug +``` + +In order to run Icinga without any path prefix, and also use Bash completion it is advised to source additional +things into the local dev environment. + +``` +export PATH=/usr/local/icinga/icinga2/sbin/:$PATH + +test -f /usr/local/icinga/icinga2/etc/bash_completion.d/icinga2 && source /usr/local/icinga/icinga2/etc/bash_completion.d/icinga2 +``` + +##### Build Aliases + +This is derived from [dnsmichi's flavour](https://github.com/dnsmichi/dotfiles) and not generally best practice. + +``` +vim $HOME/.bash_profile + +export I2_USER=$(id -u -n) +export I2_GROUP=$(id -g -n) +export I2_GENERIC="-DCMAKE_INSTALL_PREFIX=/usr/local/icinga/icinga2 -DICINGA2_USER=$I2_USER -DICINGA2_GROUP=$I2_GROUP -DOPENSSL_INCLUDE_DIR=/usr/local/opt/openssl@1.1/include -DOPENSSL_SSL_LIBRARY=/usr/local/opt/openssl@1.1/lib/libssl.dylib -DOPENSSL_CRYPTO_LIBRARY=/usr/local/opt/openssl@1.1/lib/libcrypto.dylib -DICINGA2_PLUGINDIR=/usr/local/sbin -DICINGA2_WITH_PGSQL=OFF -DCMAKE_EXPORT_COMPILE_COMMANDS=ON" + +export I2_DEBUG="-DCMAKE_BUILD_TYPE=Debug -DICINGA2_UNITY_BUILD=OFF $I2_GENERIC" +export I2_RELEASE="-DCMAKE_BUILD_TYPE=RelWithDebInfo -DICINGA2_WITH_TESTS=ON -DICINGA2_UNITY_BUILD=ON $I2_GENERIC" + +alias i2_debug="mkdir -p debug; cd debug; cmake $I2_DEBUG ..; make -j4; make -j4 install; cd .." +alias i2_release="mkdir -p release; cd release; cmake $I2_RELEASE ..; make -j4; make -j4 install; cd .." + +export PATH=/usr/local/icinga/icinga2/sbin/:$PATH +test -f /usr/local/icinga/icinga2/etc/bash_completion.d/icinga2 && source /usr/local/icinga/icinga2/etc/bash_completion.d/icinga2 + + +source $HOME/.bash_profile +``` + +#### Permissions + +`make install` doesn't set all required permissions, override this. + +``` +chown -R $I2_USER:$I2_GROUP /usr/local/icinga/icinga2 +``` + +#### Run + +Start Icinga in foreground. + +``` +icinga2 daemon +``` + +Reloads triggered with HUP or cluster syncs just put the process into background. + +#### Plugins + +``` +brew install monitoring-plugins + +sudo vim /usr/local/icinga/icinga2/etc/icinga2/constants.conf +const PluginDir = "/usr/local/sbin" +``` + +#### Backends: Redis + +``` +brew install redis +brew services start redis +``` + +#### Databases: MariaDB + +``` +brew install mariadb +mkdir -p /usr/local/etc/my.cnf.d +brew services start mariadb + +mysql_secure_installation +``` + +``` +vim $HOME/.my.cnf + +[client] +user = root +password = supersecurerootpassword + +sudo -i +ln -s /Users/michi/.my.cnf $HOME/.my.cnf +exit +``` + +``` +mysql -e 'create database icinga;' +mysql -e "grant all on icinga.* to 'icinga'@'localhost' identified by 'icinga';" +mysql icinga < $HOME/dev/icinga/icinga2/lib/db_ido_mysql/schema/mysql.sql +``` + +#### API + +``` +icinga2 api setup +cd /usr/local/icinga/icinga2/var/lib/icinga2/certs +HOST_NAME=mbpmif.int.netways.de +icinga2 pki new-cert --cn ${HOST_NAME} --csr ${HOST_NAME}.csr --key ${HOST_NAME}.key +icinga2 pki sign-csr --csr ${HOST_NAME}.csr --cert ${HOST_NAME}.crt +echo "const NodeName = \"${HOST_NAME}\"" >> /usr/local/icinga/icinga2/etc/icinga2/constants.conf +``` + +#### Web + +While it is recommended to use Docker or the Icinga Web 2 development VM pointing to the shared IDO database resource/REST API, you can also install it locally on macOS. + +The required steps are described in [this script](https://github.com/dnsmichi/dotfiles/blob/master/icingaweb2.sh). + + + +### Windows Dev Environment + +The following sections explain how to setup the required build tools +and how to run and debug the code. + +#### Chocolatey + +Open an administrative command prompt (Win key, type “cmd”, right-click and “run as administrator”) and paste the following instructions: + +``` +@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin +``` + +#### Git, Posh and Vim + +In case you are used to `vim`, start a new administrative Powershell: + +``` +choco install -y vim +``` + +The same applies for Git integration in Powershell: + +``` +choco install -y poshgit +``` + +![Powershell Posh Git](images/development/windows_powershell_posh_git.png) + +In order to fix the colors for commands like `git status` or `git diff`, +edit `$HOME/.gitconfig` in your Powershell and add the following lines: + +``` +vim $HOME/.gitconfig + +[color "status"] + changed = cyan bold + untracked = yellow bold + added = green bold + branch = cyan bold + unmerged = red bold + +[color "diff"] + frag = cyan + new = green bold + commit = yellow + old = red white + +[color "branch"] + current = yellow reverse + local = yellow + remote = green bold + remote = red bold +``` + +#### Visual Studio + +Thanks to Microsoft they’ll now provide their Professional Edition of Visual Studio +as community version, free for use for open source projects such as Icinga. +The installation requires ~9GB disk space. [Download](https://www.visualstudio.com/downloads/) +the web installer and start the installation. + +Note: Both Visual Studio 2017 and 2019 are covered here. Older versions +are not supported. + +You need a free Microsoft account to download and also store your preferences. + +Install the following complete workloads: + +* C++ Desktop Development +* .NET Desktop Development + +In addition also choose these individual components on Visual Studio: + +* .NET + * .NET Framework 4.x targeting packs + * .NET Framework 4.x.y SDKs +* Code tools + * Git for Windows + * GitHub Extension for Visual Studio + * NuGet package manager +* Compilers, build tools and runtimes + * C# and Visual Basic Roslyn compilers + * C++ 2019 Redistributable Update + * C++ CMake tools for Windows + * C++/CLI Support for v142 build tools (14.22) + * MSBuild + * MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.22) +* Debugging and testing + * .NET profiling tools + * C++ profiling tools + * Just-in-Time debugger +* Development activities + * C# and Visual Basic + * C++ core features + * IntelliCode + * Live Share +* Games and Graphics + * Graphics debugger and GPU profiler for DirectX (required by C++ profiling tools) +* SDKs, libraries and frameworks + * Windows 10 SDK (10.0.18362.0 or later) + * Windows Universal C Runtime + +![Visual Studio Installer](images/development/windows_visual_studio_installer_01.png) +![Visual Studio Installer](images/development/windows_visual_studio_installer_02.png) +![Visual Studio Installer](images/development/windows_visual_studio_installer_03.png) + +After a while, Visual Studio will be ready. + +##### Style Guide for Visual Studio + +Navigate into `Tools > Options > Text Editor` and repeat the following for + +- C++ +- C# + +Navigate into `Tabs` and set: + +- Indenting: Smart (default) +- Tab size: 4 +- Indent size: 4 +- Keep tabs (instead of spaces) + +![Visual Studio Tabs](images/development/windows_visual_studio_tabs_c++.png) + + +#### Flex and Bison + +Install it using [chocolatey](https://www.wireshark.org/docs/wsdg_html_chunked/ChSetupWin32.html): + +``` +choco install -y winflexbison +``` + +Chocolatey installs these tools into the hidden directory `C:\ProgramData\chocolatey\lib\winflexbison\tools`. + +#### OpenSSL + +Icinga 2 requires the OpenSSL library. [Download](http://slproweb.com/products/Win32OpenSSL.html) the Win64 package +and install it into `c:\local\OpenSSL-Win64`. + +Once asked for `Copy OpenSSLs DLLs to` select `The Windows system directory`. That way CMake/Visual Studio +will automatically detect them for builds and packaging. + +> **Note** +> +> We cannot use the chocolatey package as this one does not provide any development headers. +> +> Choose 1.1.1 LTS from manual downloads for best compatibility. + +#### Boost + +Icinga needs the development header and library files from the Boost library. + +Visual Studio translates into the following compiler versions: + +- `msvc-14.1` = Visual Studio 2017 +- `msvc-14.2` = Visual Studio 2019 + +##### Pre-built Binaries + +Prefer the pre-built package over self-compiling, if the newest version already exists. + +Download the [boost-binaries](https://sourceforge.net/projects/boost/files/boost-binaries/) for + +- msvc-14.2 is Visual Studio 2019 +- 64 for 64 bit builds + +``` +https://sourceforge.net/projects/boost/files/boost-binaries/1.71.0/boost_1_71_0-msvc-14.2-64.exe/download +``` + +Run the installer and leave the default installation path in `C:\local\boost_1_71_0`. + + +##### Source & Compile + +In order to use the boost development header and library files you need to [download](http://www.boost.org/users/download/) +Boost and then extract it to e.g. `C:\local\boost_1_71_0`. + +> **Note** +> +> Just use `C:\local`, the zip file already contains the sub folder. Extraction takes a while, +> the archive contains more than 70k files. + +In order to integrate Boost into Visual Studio, open the `Developer Command Prompt` from the start menu, +and navigate to `C:\local\boost_1_71_0`. + +Execute `bootstrap.bat` first. + +``` +cd C:\local\boost_1_71_0 +bootstrap.bat +``` + +Once finished, specify the required `toolset` to compile boost against Visual Studio. +This takes quite some time in a Windows VM. Boost Context uses Assembler code, +which isn't treated as exception safe by the VS compiler. Therefore set the +additional compilation flag according to [this entry](https://lists.boost.org/Archives/boost/2015/08/224570.php). + +``` +b2 --toolset=msvc-14.2 link=static threading=multi runtime-link=static address-model=64 asmflags=\safeseh +``` + +![Windows Boost Build in VS Development Console](images/development/windows_boost_build_dev_cmd.png) + +#### TortoiseGit + +TortoiseGit provides a graphical integration into the Windows explorer. This makes it easier to checkout, commit +and whatnot. + +[Download](https://tortoisegit.org/download/) TortoiseGit on your system. + +In order to clone via Git SSH you also need to create a new directory called `.ssh` +inside your user's home directory. +Therefore open a command prompt (win key, type `cmd`, enter) and run `mkdir .ssh`. +Add your `id_rsa` private key and `id_rsa.pub` public key files into that directory. + +Start the setup routine and choose `OpenSSH` as default secure transport when asked. + +Open a Windows Explorer window and navigate into + +``` +cd %HOMEPATH%\source\repos +``` + +Right click and select `Git Clone` from the context menu. + +Use `ssh://git@github.com/icinga/icinga2.git` for SSH clones, `https://github.com/icinga/icinga2.git` otherwise. + +#### Packages + +CMake uses CPack and NSIS to create the setup executable including all binaries and libraries +in addition to setup dialogues and configuration. Therefore we’ll need to install [NSIS](http://nsis.sourceforge.net/Download) +first. + +We also need to install the Windows Installer XML (WIX) toolset. + +``` +choco install -y wixtoolset +``` + +#### CMake + +Icinga 2 uses CMake to manage the build environment. You can generate the Visual Studio project files +using CMake. [Download](https://cmake.org/download/) and install CMake. Select to add it to PATH for all users +when asked. + +> **Note** +> +> In order to properly detect the Boost libraries and VS 2019, install CMake 3.15.2+. +> +> **Tip** +> +> Cheatsheet: http://www.brianlheim.com/2018/04/09/cmake-cheat-sheet.html + +Once setup is completed, open a command prompt and navigate to + +``` +cd %HOMEPATH%\source\repos +``` + +Build Icinga with specific CMake variables. This generates a new Visual Studio project file called `icinga2.sln`. + +Visual Studio translates into the following: + +- `msvc-14.1` = Visual Studio 2017 +- `msvc-14.2` = Visual Studio 2019 + +You need to specify the previously installed component paths. + +Variable | Value | Description +----------------------|----------------------------------------------------------------------|------------------------------------------------------- +`BOOST_ROOT` | `C:\local\boost_1_71_0` | Root path where you've extracted and compiled Boost. +`BOOST_LIBRARYDIR` | Binary: `C:\local\boost_1_71_0\lib64-msvc-14.1`, Source: `C:\local\boost_1_71_0\stage` | Path to the static compiled Boost libraries, directory must contain `lib`. +`BISON_EXECUTABLE` | `C:\ProgramData\chocolatey\lib\winflexbison\tools\win_bison.exe` | Path to the Bison executable. +`FLEX_EXECUTABLE` | `C:\ProgramData\chocolatey\lib\winflexbison\tools\win_flex.exe` | Path to the Flex executable. +`ICINGA2_WITH_MYSQL` | OFF | Requires extra setup for MySQL if set to `ON`. Not supported for client setups. +`ICINGA2_WITH_PGSQL` | OFF | Requires extra setup for PgSQL if set to `ON`. Not supported for client setups. +`ICINGA2_UNITY_BUILD` | OFF | Disable unity builds for development environments. + +Tip: If you have previously opened a terminal, run `refreshenv` to re-read updated PATH variables. + +##### Build Scripts + +Icinga provides the build scripts inside the Git repository. + +Open a new Powershell and navigate into the cloned Git repository. Set +specific environment variables and run the build scripts. + +``` +cd %HOMEPATH%\source\repos\icinga2 + +.\tools\win32\configure-dev.ps1 +.\tools\win32\build.ps1 +.\tools\win32\test.ps1 +``` + +The debug MSI package is located in the `debug` directory. + +If you did not follow the above steps with Boost binaries +and OpenSSL paths, or using VS 2017, you can still modify +the environment variables. + +``` +$env:CMAKE_GENERATOR='Visual Studio 16 2019' +$env:CMAKE_GENERATOR_PLATFORM='x64' + +$env:ICINGA2_INSTALLPATH = 'C:\Program Files\Icinga2-debug' +$env:ICINGA2_BUILDPATH='debug' +$env:CMAKE_BUILD_TYPE='Debug' +$env:OPENSSL_ROOT_DIR='C:\OpenSSL-Win64' +$env:BOOST_ROOT='C:\local\boost_1_71_0' +$env:BOOST_LIBRARYDIR='C:\local\boost_1_71_0\lib64-msvc-14.2' +``` + +#### Icinga 2 in Visual Studio + +This requires running the configure script once. + +Navigate to + +``` +cd %HOMEPATH%\source\repos\icinga2\debug +``` + +Open `icinga2.sln`. Log into Visual Studio when asked. + +On the right panel, select to build the `Bin/icinga-app` solution. + +The executable binaries are located in `Bin\Release\Debug` in your `icinga2` +project directory. + +Navigate there and run `icinga2.exe --version`. + +``` +cd %HOMEPATH%\source\repos\icinga2\Bin\Release\Debug +icinga2.exe --version +``` + + +#### Release Package + +This is part of the build process script. Override the build type and pick a different +build directory. + +``` +cd %HOMEPATH%\source\repos\icinga2 + +$env:ICINGA2_BUILDPATH='release' +$env:CMAKE_BUILD_TYPE='RelWithDebInfo' + +.\tools\win32\configure-dev.ps1 +.\tools\win32\build.ps1 +.\tools\win32\test.ps1 +``` + +The release MSI package is located in the `release` directory. + + +### Embedded Dev Env: Pi + +> **Note** +> +> This isn't officially supported yet, just a few hints how you can do it yourself. + +The following examples source from armhf on Raspberry Pi. + +#### ccache + +``` +apt install -y ccache + +/usr/sbin/update-ccache-symlinks + +echo 'export PATH="/usr/lib/ccache:$PATH"' | tee -a ~/.bashrc + +source ~/.bashrc && echo $PATH +``` + +#### Build + +Copy the icinga2 source code into `$HOME/icinga2`. Clone the `deb-icinga2` repository into `debian/`. + +``` +git clone https://github.com/Icinga/icinga2 $HOME/icinga2 +git clone https://github.com/Icinga/deb-icinga2 $HOME/icinga2/debian +``` + +Then build a Debian package and install it like normal. +``` +dpkg-buildpackage -uc -us +``` + +## Package Builds + +This documentation is explicitly meant for packagers and the Icinga +build infrastructure. + +The following requirements need to be fulfilled in order to build the +Icinga application using a dist tarball (including notes for distributions): + +* cmake >= 2.6 +* GNU make (make) or ninja-build +* C++ compiler which supports C++11 + * RHEL/Fedora/SUSE: gcc-c++ >= 4.7 (extra Developer Tools on RHEL5/6 see below) + * Debian/Ubuntu: build-essential + * Alpine: build-base + * you can also use clang++ +* pkg-config +* OpenSSL library and header files >= 1.0.1 + * RHEL/Fedora: openssl-devel + * SUSE: libopenssl-devel + * Debian/Ubuntu: libssl-dev + * Alpine: libressl-dev +* Boost library and header files >= 1.66.0 + * RHEL/Fedora: boost166-devel + * Debian/Ubuntu: libboost-all-dev + * Alpine: boost-dev +* GNU bison (bison) +* GNU flex (flex) >= 2.5.35 +* systemd headers + * Only required when using systemd + * Debian/Ubuntu: libsystemd-dev + * RHEL/Fedora: systemd-devel + +### Optional features + +* MySQL (disable with CMake variable `ICINGA2_WITH_MYSQL` to `OFF`) + * RHEL/Fedora: mysql-devel + * SUSE: libmysqlclient-devel + * Debian/Ubuntu: default-libmysqlclient-dev | libmysqlclient-dev + * Alpine: mariadb-dev +* PostgreSQL (disable with CMake variable `ICINGA2_WITH_PGSQL` to `OFF`) + * RHEL/Fedora: postgresql-devel + * Debian/Ubuntu: libpq-dev + * postgresql-dev on Alpine +* libedit (CLI console) + * RHEL/Fedora: libedit-devel on CentOS (RHEL requires rhel-7-server-optional-rpms) + * Debian/Ubuntu/Alpine: libedit-dev +* Termcap (only required if libedit doesn't already link against termcap/ncurses) + * RHEL/Fedora: libtermcap-devel + * Debian/Ubuntu: (not necessary) + +### Special requirements + +**FreeBSD**: libexecinfo (automatically used when Icinga 2 is installed via port or package) + +**RHEL6**: Requires a newer boost version which is available on packages.icinga.com +with a version suffixed name. + +### Runtime user environment + +By default Icinga will run as user `icinga` and group `icinga`. Additionally the +external command pipe and livestatus features require a dedicated command group +`icingacmd`. You can choose your own user/group names and pass them to CMake +using the `ICINGA2_USER`, `ICINGA2_GROUP` and `ICINGA2_COMMAND_GROUP` variables. + +``` +# groupadd icinga +# groupadd icingacmd +# useradd -c "icinga" -s /sbin/nologin -G icingacmd -g icinga icinga +``` + +On Alpine (which uses ash busybox) you can run: + +``` +# addgroup -S icinga +# addgroup -S icingacmd +# adduser -S -D -H -h /var/spool/icinga2 -s /sbin/nologin -G icinga -g icinga icinga +# adduser icinga icingacmd +``` + +Add the web server user to the icingacmd group in order to grant it write +permissions to the external command pipe and livestatus socket: + +``` +# usermod -a -G icingacmd www-data +``` + +Make sure to replace "www-data" with the name of the user your web server +is running as. + +### Building Icinga 2: Example + +Once you have installed all the necessary build requirements you can build +Icinga 2 using the following commands: + +``` +$ mkdir release && cd release +$ cmake .. +$ cd .. +$ make -C release +$ make install -C release +``` + +You can specify an alternative installation prefix using `-DCMAKE_INSTALL_PREFIX`: + +``` +$ cmake .. -DCMAKE_INSTALL_PREFIX=/tmp/icinga2 +``` + +### CMake Variables + +In addition to `CMAKE_INSTALL_PREFIX` here are most of the supported Icinga-specific cmake variables. + +For all variables regarding defaults paths on in CMake, see +[GNUInstallDirs](https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html). + +Also see `CMakeLists.txt` for details. + +#### System Environment + +* `CMAKE_INSTALL_SYSCONFDIR`: The configuration directory; defaults to `CMAKE_INSTALL_PREFIX/etc` +* `CMAKE_INSTALL_LOCALSTATEDIR`: The state directory; defaults to `CMAKE_INSTALL_PREFIX/var` +* `ICINGA2_CONFIGDIR`: Main config directory; defaults to `CMAKE_INSTALL_SYSCONFDIR/icinga2` usually `/etc/icinga2` +* `ICINGA2_CACHEDIR`: Directory for cache files; defaults to `CMAKE_INSTALL_LOCALSTATEDIR/cache/icinga2` usually `/var/cache/icinga2` +* `ICINGA2_DATADIR`: Data directory for the daemon; defaults to `CMAKE_INSTALL_LOCALSTATEDIR/lib/icinga2` usually `/var/lib/icinga2` +* `ICINGA2_LOGDIR`: Logfiles of the daemon; defaults to `CMAKE_INSTALL_LOCALSTATEDIR/log/icinga2 usually `/var/log/icinga2` +* `ICINGA2_SPOOLDIR`: Spooling directory ; defaults to `CMAKE_INSTALL_LOCALSTATEDIR/spool/icinga2` usually `/var/spool/icinga2` +* `ICINGA2_INITRUNDIR`: Runtime data for the init system; defaults to `CMAKE_INSTALL_LOCALSTATEDIR/run/icinga2` usually `/run/icinga2` +* `ICINGA2_GIT_VERSION_INFO`: Whether to use Git to determine the version number; defaults to `ON` +* `ICINGA2_USER`: The user Icinga 2 should run as; defaults to `icinga` +* `ICINGA2_GROUP`: The group Icinga 2 should run as; defaults to `icinga` +* `ICINGA2_COMMAND_GROUP`: The command group Icinga 2 should use; defaults to `icingacmd` +* `ICINGA2_SYSCONFIGFILE`: Where to put the config file the initscript/systemd pulls it's dirs from; +* defaults to `CMAKE_INSTALL_PREFIX/etc/sysconfig/icinga2` +* `ICINGA2_PLUGINDIR`: The path for the Monitoring Plugins project binaries; defaults to `/usr/lib/nagios/plugins` + +#### Build Optimization + +* `ICINGA2_UNITY_BUILD`: Whether to perform a unity build; defaults to `ON`. Note: This requires additional memory and is not advised for building VMs, Docker for Mac and embedded hardware. +* `ICINGA2_LTO_BUILD`: Whether to use link time optimization (LTO); defaults to `OFF` + +#### Init System + +* `USE_SYSTEMD=ON|OFF`: Use systemd or a classic SysV initscript; defaults to `OFF` +* `INSTALL_SYSTEMD_SERVICE_AND_INITSCRIPT=ON|OFF` Force install both the systemd service definition file + and the SysV initscript in parallel, regardless of how `USE_SYSTEMD` is set. + Only use this for special packaging purposes and if you know what you are doing. + Defaults to `OFF`. + +#### Features + +* `ICINGA2_WITH_CHECKER`: Determines whether the checker module is built; defaults to `ON` +* `ICINGA2_WITH_COMPAT`: Determines whether the compat module is built; defaults to `ON` +* `ICINGA2_WITH_LIVESTATUS`: Determines whether the Livestatus module is built; defaults to `ON` +* `ICINGA2_WITH_NOTIFICATION`: Determines whether the notification module is built; defaults to `ON` +* `ICINGA2_WITH_PERFDATA`: Determines whether the perfdata module is built; defaults to `ON` +* `ICINGA2_WITH_TESTS`: Determines whether the unit tests are built; defaults to `ON` + +#### MySQL or MariaDB + +The following settings can be tuned for the MySQL / MariaDB IDO feature. + +* `ICINGA2_WITH_MYSQL`: Determines whether the MySQL IDO module is built; defaults to `ON` +* `MYSQL_CLIENT_LIBS`: Client implementation used (mysqlclient / mariadbclient); defaults searches for `mysqlclient` and `mariadbclient` +* `MYSQL_INCLUDE_DIR`: Directory containing include files for the mysqlclient; default empty - + checking multiple paths like `/usr/include/mysql` + +See [FindMySQL.cmake](https://github.com/Icinga/icinga2/blob/master/third-party/cmake/FindMySQL.cmake) +for implementation details. + +#### PostgreSQL + +The following settings can be tuned for the PostgreSQL IDO feature. + +* `ICINGA2_WITH_PGSQL`: Determines whether the PostgreSQL IDO module is built; defaults to `ON` +* `PostgreSQL_INCLUDE_DIR`: Top-level directory containing the PostgreSQL include directories +* `PostgreSQL_LIBRARY`: File path to PostgreSQL library : libpq.so (or libpq.so.[ver] file) + +See [FindPostgreSQL.cmake](https://github.com/Icinga/icinga2/blob/master/third-party/cmake/FindPostgreSQL.cmake) +for implementation details. + +#### Version detection + +CMake determines the Icinga 2 version number using `git describe` if the +source directory is contained in a Git repository. Otherwise the version number +is extracted from the [VERSION](VERSION) file. This behavior can be +overridden by creating a file called `icinga-version.h.force` in the source +directory. Alternatively the `-DICINGA2_GIT_VERSION_INFO=OFF` option for CMake +can be used to disable the usage of `git describe`. + + +### Building RPMs + +#### Build Environment on RHEL, CentOS, Fedora, Amazon Linux + +Setup your build environment: + +``` +yum -y install rpmdevtools +``` + +#### Build Environment on SuSE/SLES + +SLES: + +``` +zypper addrepo http://download.opensuse.org/repositories/devel:tools/SLE_12_SP4/devel:tools.repo +zypper refresh +zypper install rpmdevtools spectool +``` + +OpenSuSE: + +``` +zypper addrepo http://download.opensuse.org/repositories/devel:tools/openSUSE_Leap_15.0/devel:tools.repo +zypper refresh +zypper install rpmdevtools spectool +``` + +#### Package Builds + +Prepare the rpmbuild directory tree: + +``` +cd $HOME +rpmdev-setuptree +``` + +Snapshot builds: + +``` +curl https://raw.githubusercontent.com/Icinga/rpm-icinga2/master/icinga2.spec -o $HOME/rpmbuild/SPECS/icinga2.spec +``` + +> **Note** +> +> The above command builds snapshot packages. Change to the `release` branch +> for release package builds. + +Copy the tarball to `rpmbuild/SOURCES` e.g. by using the `spectool` binary +provided with `rpmdevtools`: + +``` +cd $HOME/rpmbuild/SOURCES +spectool -g ../SPECS/icinga2.spec + +cd $HOME/rpmbuild +``` + +Install the build dependencies. Example for CentOS 7: + +``` +yum -y install libedit-devel ncurses-devel gcc-c++ libstdc++-devel openssl-devel \ +cmake flex bison boost-devel systemd mysql-devel postgresql-devel httpd \ +selinux-policy-devel checkpolicy selinux-policy selinux-policy-doc +``` + +Note: If you are using Amazon Linux, systemd is not required. + +A shorter way is available using the `yum-builddep` command on RHEL based systems: + +``` +yum-builddep SPECS/icinga2.spec +``` + +Build the RPM: + +``` +rpmbuild -ba SPECS/icinga2.spec +``` + +#### Additional Hints + +##### SELinux policy module + +The following packages are required to build the SELinux policy module: + +* checkpolicy +* selinux-policy (selinux-policy on CentOS 6, selinux-policy-devel on CentOS 7) +* selinux-policy-doc + +##### RHEL/CentOS 6 + +The RedHat Developer Toolset is required for building Icinga 2 beforehand. +This contains a modern version of flex and a C++ compiler which supports +C++11 features. + +``` +cat >/etc/yum.repos.d/devtools-2.repo <$HOME/.rpmmacros < + +Setup your build environment on Debian/Ubuntu, copy the 'debian' directory from +the Debian packaging Git repository (https://github.com/Icinga/deb-icinga2) +into your source tree and run the following command: + +``` +dpkg-buildpackage -uc -us +``` + +### Build Alpine Linux packages + +A simple way to setup a build environment is installing Alpine in a chroot. +In this way, you can set up an Alpine build environment in a chroot under a +different Linux distro. +There is a script that simplifies these steps with just two commands, and +can be found [here](https://github.com/alpinelinux/alpine-chroot-install). + +Once the build environment is installed, you can setup the system to build +the packages by following [this document](https://wiki.alpinelinux.org/wiki/Creating_an_Alpine_package). + +### Build Post Install Tasks + +After building Icinga 2 yourself, your package build system should at least run the following post +install requirements: + +* enable the `checker`, `notification` and `mainlog` feature by default +* run 'icinga2 api setup' in order to enable the `api` feature and generate TLS certificates for the node + +### Run Icinga 2 + +Icinga 2 comes with a binary that takes care of loading all the relevant +components (e.g. for check execution, notifications, etc.): + +``` +icinga2 daemon + +[2016-12-08 16:44:24 +0100] information/cli: Icinga application loader (version: v2.5.4-231-gb10a6b7; debug) +[2016-12-08 16:44:24 +0100] information/cli: Loading configuration file(s). +[2016-12-08 16:44:25 +0100] information/ConfigItem: Committing config item(s). +... +``` + +#### Init Script + +Icinga 2 can be started as a daemon using the provided init script: + +``` +/etc/init.d/icinga2 +Usage: /etc/init.d/icinga2 {start|stop|restart|reload|checkconfig|status} +``` + +#### Systemd + +If your distribution uses systemd: + +``` +systemctl {start|stop|reload|status|enable|disable} icinga2 +``` + +In case the distribution is running systemd >227, you'll also +need to package and install the `etc/initsystem/icinga2.service.limits.conf` +file into `/etc/systemd/system/icinga2.service.d`. + +#### openrc + +Or if your distribution uses openrc (like Alpine): + +``` +rc-service icinga2 +Usage: /etc/init.d/icinga2 {start|stop|restart|reload|checkconfig|status} +``` + +Note: the openrc's init.d is not shipped by default. +A working init.d with openrc can be found here: (https://git.alpinelinux.org/cgit/aports/plain/community/icinga2/icinga2.initd). If you have customized some path, edit the file and adjust it according with your setup. +Those few steps can be followed: + +``` +wget https://git.alpinelinux.org/cgit/aports/plain/community/icinga2/icinga2.initd +mv icinga2.initd /etc/init.d/icinga2 +chmod +x /etc/init.d/icinga2 +``` + +Icinga 2 reads a single configuration file which is used to specify all +configuration settings (global settings, hosts, services, etc.). The +configuration format is explained in detail in the [doc/](doc/) directory. + +By default `make install` installs example configuration files in +`/usr/local/etc/icinga2` unless you have specified a different prefix or +sysconfdir. + + +### Windows Builds + +The Windows MSI packages are located at https://packages.icinga.com/windows/ + +The build infrastructure is based on GitLab CI and an Ansible provisioned +Windows VM running in OpenStack. + +The runner uses the scripts located in `tools/win32` to configure, build +and test the packages. Uploading them to the package repository is a +separate step. For manual package creation, please refer to [this chapter](21-development.md#development-windows-dev-env). + +![Windows build pipeline in GitLab](images/development/windows_builds_gitlab_pipeline.png) + + +## Continuous Integration + +Icinga uses the integrated CI capabilities on GitHub in the development workflow. +This ensures that incoming pull requests and branches are built on create/push events. +Contributors and developers can immediately see whether builds fail or succeed and +help the final reviews. + +* For Linux, we are currently using Travis CI. +* For Windows, AppVeyor has been integrated. + +Future plans involve making use of GitHub Actions. + +In addition to our development platform on GitHub, +we are using GitLab's CI platform to build binary packages for +all supported operating systems and distributions. +These CI pipelines provide even more detailed insights into +specific platform failures and developers can react faster. + +### CI: Travis CI + +[Travis CI](https://travis-ci.org/Icinga/icinga2) provides Ubuntu as base +distribution where Icinga is compiled from sources followed by running the +unit tests and a config validation check. + +For details, please refer to the [.travis.yml](https://github.com/Icinga/icinga2/blob/master/.travis.yml) +configuration file. + +### CI: AppVeyor + +[AppVeyor](https://ci.appveyor.com/project/icinga/icinga2) provides Windows +as platform where Visual Studio and Boost libraries come pre-installed. + +Icinga is built using the Powershell scripts located in `tools/win32`. +In addition to that, the unit tests are run. + +Please check the [appveyor.yml](https://github.com/Icinga/icinga2/blob/master/appveyor.yml) configuration +file for details. + + +## Advanced Development Tips + +### GDB Pretty Printers + +Install the `boost`, `python` and `icinga2` pretty printers. Absolute paths are required, +so please make sure to update the installation paths accordingly (`pwd`). + +``` +$ mkdir -p ~/.gdb_printers && cd ~/.gdb_printers +``` + +Boost Pretty Printers compatible with Python 3: + +``` +$ git clone https://github.com/mateidavid/Boost-Pretty-Printer.git && cd Boost-Pretty-Printer +$ git checkout python-3 +$ pwd +/home/michi/.gdb_printers/Boost-Pretty-Printer +``` + +Python Pretty Printers: + +``` +$ cd ~/.gdb_printers +$ svn co svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python +``` + +Icinga 2 Pretty Printers: + +``` +$ mkdir -p ~/.gdb_printers/icinga2 && cd ~/.gdb_printers/icinga2 +$ wget https://raw.githubusercontent.com/Icinga/icinga2/master/tools/debug/gdb/icingadbg.py +``` + +Now you'll need to modify/setup your `~/.gdbinit` configuration file. +You can download the one from Icinga 2 and modify all paths. + +Example on Fedora 22: + +``` +$ wget https://raw.githubusercontent.com/Icinga/icinga2/master/tools/debug/gdb/gdbinit -O ~/.gdbinit +$ vim ~/.gdbinit + +set print pretty on + +python +import sys +sys.path.insert(0, '/home/michi/.gdb_printers/icinga2') +from icingadbg import register_icinga_printers +register_icinga_printers() +end + +python +import sys +sys.path.insert(0, '/home/michi/.gdb_printers/python') +from libstdcxx.v6.printers import register_libstdcxx_printers +try: + register_libstdcxx_printers(None) +except: + pass +end + +python +import sys +sys.path.insert(0, '/home/michi/.gdb_printers/Boost-Pretty-Printer') +import boost_print +boost_print.register_printers() +end +``` + +If you are getting the following error when running gdb, the `libstdcxx` +printers are already preloaded in your environment and you can remove +the duplicate import in your `~/.gdbinit` file. + +``` +RuntimeError: pretty-printer already registered: libstdc++-v6 +``` diff --git a/doc/22-selinux.md b/doc/22-selinux.md index 0e442ecf3..20074d52a 100644 --- a/doc/22-selinux.md +++ b/doc/22-selinux.md @@ -18,16 +18,18 @@ There are two ways of installing the SELinux Policy for Icinga 2 on Enterprise L If the system runs in enforcing mode and you encounter problems you can set Icinga 2's domain to permissive mode. - # sestatus - SELinux status: enabled - SELinuxfs mount: /sys/fs/selinux - SELinux root directory: /etc/selinux - Loaded policy name: targeted - Current mode: enforcing - Mode from config file: enforcing - Policy MLS status: enabled - Policy deny_unknown status: allowed - Max kernel policy version: 28 +``` +# sestatus +SELinux status: enabled +SELinuxfs mount: /sys/fs/selinux +SELinux root directory: /etc/selinux +Loaded policy name: targeted +Current mode: enforcing +Mode from config file: enforcing +Policy MLS status: enabled +Policy deny_unknown status: allowed +Max kernel policy version: 28 +``` You can change the configured mode by editing `/etc/selinux/config` and the current mode by executing `setenforce 0`. @@ -35,13 +37,17 @@ You can change the configured mode by editing `/etc/selinux/config` and the curr Simply add the `icinga2-selinux` package to your installation. - # yum install icinga2-selinux +``` +# yum install icinga2-selinux +``` Ensure that the `icinga2` process is running in its own `icinga2_t` domain after installing the policy package: - # systemctl restart icinga2.service - # ps -eZ | grep icinga2 - system_u:system_r:icinga2_t:s0 2825 ? 00:00:00 icinga2 +``` +# systemctl restart icinga2.service +# ps -eZ | grep icinga2 +system_u:system_r:icinga2_t:s0 2825 ? 00:00:00 icinga2 +``` #### Manual installation @@ -49,24 +55,32 @@ This section describes the installation to support development and testing. It a As a prerequisite install the `git`, `selinux-policy-devel` and `audit` packages. Enable and start the audit daemon afterwards: - # yum install git selinux-policy-devel audit - # systemctl enable auditd.service - # systemctl start auditd.service +``` +# yum install git selinux-policy-devel audit +# systemctl enable auditd.service +# systemctl start auditd.service +``` After that clone the icinga2 git repository: - # git clone https://github.com/icinga/icinga2 +``` +# git clone https://github.com/icinga/icinga2 +``` To create and install the policy package run the installation script which also labels the resources. (The script assumes Icinga 2 was started once after system startup, the labeling of the port will only happen once and fail later on.) - # cd tools/selinux/ - # ./icinga.sh +``` +# cd tools/selinux/ +# ./icinga.sh +``` After that restart Icinga 2 and verify it running in its own domain `icinga2_t`. - # systemctl restart icinga2.service - # ps -eZ | grep icinga2 - system_u:system_r:icinga2_t:s0 2825 ? 00:00:00 icinga2 +``` +# systemctl restart icinga2.service +# ps -eZ | grep icinga2 +system_u:system_r:icinga2_t:s0 2825 ? 00:00:00 icinga2 +``` ### General @@ -106,6 +120,10 @@ SELinux is based on the least level of access required for a service to run. Usi Having this boolean enabled allows icinga2 to connect to all ports. This can be necessary if you use features which connect to unconfined services, for example the [influxdb writer](14-features.md#influxdb-writer). +**icinga2_run_sudo** + +To allow Icinga 2 executing plugins via sudo you can toogle this boolean. It is disabled by default, resulting in error messages like `execvpe(sudo) failed: Permission denied`. + **httpd_can_write_icinga2_command** To allow httpd to write to the command pipe of icinga2 this boolean has to be enabled. This is enabled by default, if not needed you can disable it for more security. @@ -126,23 +144,29 @@ Make sure to report the bugs in the policy afterwards. Download and install a plugin, for example check_mysql_health. - # wget https://labs.consol.de/download/shinken-nagios-plugins/check_mysql_health-2.1.9.2.tar.gz - # tar xvzf check_mysql_health-2.1.9.2.tar.gz - # cd check_mysql_health-2.1.9.2/ - # ./configure --libexecdir /usr/lib64/nagios/plugins - # make - # make install +``` +# wget https://labs.consol.de/download/shinken-nagios-plugins/check_mysql_health-2.1.9.2.tar.gz +# tar xvzf check_mysql_health-2.1.9.2.tar.gz +# cd check_mysql_health-2.1.9.2/ +# ./configure --libexecdir /usr/lib64/nagios/plugins +# make +# make install +``` It is labeled `nagios_unconfined_plugins_exec_t` by default, so it runs without restrictions. - # ls -lZ /usr/lib64/nagios/plugins/check_mysql_health - -rwxr-xr-x. root root system_u:object_r:nagios_unconfined_plugin_exec_t:s0 /usr/lib64/nagios/plugins/check_mysql_health +``` +# ls -lZ /usr/lib64/nagios/plugins/check_mysql_health +-rwxr-xr-x. root root system_u:object_r:nagios_unconfined_plugin_exec_t:s0 /usr/lib64/nagios/plugins/check_mysql_health +``` In this case the plugin is monitoring a service, so it should be labeled `nagios_services_plugin_exec_t` to restrict its permissions. - # chcon -t nagios_services_plugin_exec_t /usr/lib64/nagios/plugins/check_mysql_health - # ls -lZ /usr/lib64/nagios/plugins/check_mysql_health - -rwxr-xr-x. root root system_u:object_r:nagios_services_plugin_exec_t:s0 /usr/lib64/nagios/plugins/check_mysql_health +``` +# chcon -t nagios_services_plugin_exec_t /usr/lib64/nagios/plugins/check_mysql_health +# ls -lZ /usr/lib64/nagios/plugins/check_mysql_health +-rwxr-xr-x. root root system_u:object_r:nagios_services_plugin_exec_t:s0 /usr/lib64/nagios/plugins/check_mysql_health +``` The plugin still runs fine but if someone changes the script to do weird stuff it will fail to do so. @@ -152,28 +176,56 @@ You are running graphite on a different port than `2003` and want `icinga2` to c Change the port value for the graphite feature according to your graphite installation before enabling it. - # cat /etc/icinga2/features-enabled/graphite.conf - /** - * The GraphiteWriter type writes check result metrics and - * performance data to a graphite tcp socket. - */ +``` +# cat /etc/icinga2/features-enabled/graphite.conf +/** + * The GraphiteWriter type writes check result metrics and + * performance data to a graphite tcp socket. + */ - library "perfdata" +library "perfdata" - object GraphiteWriter "graphite" { - //host = "127.0.0.1" - //port = 2003 - port = 2004 - } - # icinga2 feature enable graphite +object GraphiteWriter "graphite" { + //host = "127.0.0.1" + //port = 2003 + port = 2004 +} +# icinga2 feature enable graphite +``` -Before you restart the icinga2 service allow it to connect to all ports by enabling the boolean ´icinga2_can_connect_all` (now and permanent). +Before you restart the icinga2 service allow it to connect to all ports by enabling the boolean `icinga2_can_connect_all` (now and permanent). - # setsebool icinga2_can_connect_all true - # setsebool -P icinga2_can_connect_all true +``` +# setsebool icinga2_can_connect_all true +# setsebool -P icinga2_can_connect_all true +``` If you restart the daemon now it will successfully connect to graphite. +#### Running plugins requiring sudo + +Some plugins require privileged access to the system and are designied to be executed via `sudo` to get these privileges. + +In this case it is the CheckCommand [running_kernel](10-icinga-template-library.md#plugin-contrib-command-running_kernel) which is set to use `sudo`. + + # cat /etc/icinga2/conf.d/services.conf + apply Service "kernel" { + import "generic-service" + + check_command = "running_kernel" + + vars.running_kernel_use_sudo = true + + assign where host.name == NodeName + } + +Having this Service defined will result in a UNKNOWN state and the error message `execvpe(sudo) failed: Permission denied` because SELinux dening the execution. + +Switching the boolean `icinga2_run_sudo` to allow the execution will result in the check executed successfully. + + # setsebool icinga2_run_sudo true + # setsebool -P icinga2_run_sudo true + #### Confining a user If you want to have an administrative account capable of only managing icinga2 and not the complete system, you can restrict the privileges by confining @@ -181,49 +233,63 @@ this user. This is completly optional! Start by adding the Icinga 2 administrator role `icinga2adm_r` to the administrative SELinux user `staff_u`. - # semanage user -m -R "staff_r sysadm_r system_r unconfined_r icinga2adm_r" staff_u +``` +# semanage user -m -R "staff_r sysadm_r system_r unconfined_r icinga2adm_r" staff_u +``` Confine your user login and create a sudo rule. - # semanage login -a dirk -s staff_u - # echo "dirk ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/dirk +``` +# semanage login -a dirk -s staff_u +# echo "dirk ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/dirk +``` Login to the system using ssh and verify your id. - $ id -Z - staff_u:staff_r:staff_t:s0-s0:c0.c1023 +``` +$ id -Z +staff_u:staff_r:staff_t:s0-s0:c0.c1023 +``` Try to execute some commands as root using sudo. - $ sudo id -Z - staff_u:staff_r:staff_t:s0-s0:c0.c1023 - $ sudo vi /etc/icinga2/icinga2.conf - "/etc/icinga2/icinga2.conf" [Permission Denied] - $ sudo cat /var/log/icinga2/icinga2.log - cat: /var/log/icinga2/icinga2.log: Keine Berechtigung - $ sudo systemctl reload icinga2.service - Failed to get D-Bus connection: No connection to service manager. +``` +$ sudo id -Z +staff_u:staff_r:staff_t:s0-s0:c0.c1023 +$ sudo vi /etc/icinga2/icinga2.conf +"/etc/icinga2/icinga2.conf" [Permission Denied] +$ sudo cat /var/log/icinga2/icinga2.log +cat: /var/log/icinga2/icinga2.log: Keine Berechtigung +$ sudo systemctl reload icinga2.service +Failed to get D-Bus connection: No connection to service manager. +``` Those commands fail because you only switch to root but do not change your SELinux role. Try again but tell sudo also to switch the SELinux role and type. - $ sudo -r icinga2adm_r -t icinga2adm_t id -Z - staff_u:icinga2adm_r:icinga2adm_t:s0-s0:c0.c1023 - $ sudo -r icinga2adm_r -t icinga2adm_t vi /etc/icinga2/icinga2.conf - "/etc/icinga2/icinga2.conf" - $ sudo -r icinga2adm_r -t icinga2adm_t cat /var/log/icinga2/icinga2.log - [2015-03-26 20:48:14 +0000] information/DynamicObject: Dumping program state to file '/var/lib/icinga2/icinga2.state' - $ sudo -r icinga2adm_r -t icinga2adm_t systemctl reload icinga2.service +``` +$ sudo -r icinga2adm_r -t icinga2adm_t id -Z +staff_u:icinga2adm_r:icinga2adm_t:s0-s0:c0.c1023 +$ sudo -r icinga2adm_r -t icinga2adm_t vi /etc/icinga2/icinga2.conf +"/etc/icinga2/icinga2.conf" +$ sudo -r icinga2adm_r -t icinga2adm_t cat /var/log/icinga2/icinga2.log +[2015-03-26 20:48:14 +0000] information/DynamicObject: Dumping program state to file '/var/lib/icinga2/icinga2.state' +$ sudo -r icinga2adm_r -t icinga2adm_t systemctl reload icinga2.service +``` Now the commands will work, but you have always to remember to add the arguments, so change the sudo rule to set it by default. - # echo "dirk ALL=(ALL) ROLE=icinga2adm_r TYPE=icinga2adm_t NOPASSWD: ALL" > /etc/sudoers.d/dirk +``` +# echo "dirk ALL=(ALL) ROLE=icinga2adm_r TYPE=icinga2adm_t NOPASSWD: ALL" > /etc/sudoers.d/dirk +``` Now try the commands again without providing the role and type and they will work, but if you try to read apache logs or restart apache for example it will still fail. - $ sudo cat /var/log/httpd/error_log - /bin/cat: /var/log/httpd/error_log: Keine Berechtigung - $ sudo systemctl reload httpd.service - Failed to issue method call: Access denied +``` +$ sudo cat /var/log/httpd/error_log +/bin/cat: /var/log/httpd/error_log: Keine Berechtigung +$ sudo systemctl reload httpd.service +Failed to issue method call: Access denied +``` ## Bugreports @@ -231,7 +297,7 @@ If you experience any problems while running in enforcing mode try to reproduce After some feedback Icinga 2 is now running in a enforced domain, but still adds also some rules for other necessary services so no problems should occure at all. But you can help to enhance the policy by testing Icinga 2 running confined by SELinux. -Please add the following information to [bug reports](https://www.icinga.com/community/get-involved/): +Please add the following information to [bug reports](https://icinga.com/community/): * Versions, configuration snippets, etc. * Output of `semodule -l | grep -e icinga2 -e nagios -e apache` diff --git a/doc/23-migrating-from-icinga-1x.md b/doc/23-migrating-from-icinga-1x.md index 4dd6e8397..c9cfa5085 100644 --- a/doc/23-migrating-from-icinga-1x.md +++ b/doc/23-migrating-from-icinga-1x.md @@ -6,6 +6,33 @@ The Icinga 2 configuration format introduces plenty of behavioural changes. In order to ease migration from Icinga 1.x, this section provides hints and tips on your migration requirements. + +### Automated Config Migration + +Depending on your previous setup, you may have already used different sources +for generating the 1.x configuration files. If this is the case, +we strongly recommend to use these sources in combination with +the [Icinga Director](https://icinga.com/docs/director/latest/doc/01-Introduction/). + +This can be for example: + +* A CMDB or RDBMS which provides host details and facts +* PuppetDB +* CSV/XSL/JSON files +* Cloud resources (AWS, etc.) + +In case you have been using Icinga Web 1.x or an addon requiring +the underlying IDO database, you can use this as database resource +to import the host details. + +Talks: + +* [This talk from OSMC 2016](https://www.youtube.com/watch?v=T6GBsfeXIZI) shares more insights (German). +* [Automated Monitoring in heterogeneous environments](https://www.youtube.com/watch?v=bkUlS5rlHzM&list=PLeoxx10paaAn_xHJ5wBhnBJyW_d5G7-Bl&index=8) + +Continue reading more about [Import Sources](https://icinga.com/docs/director/latest/doc/70-Import-and-Sync/) +for the Icinga Director. + ### Manual Config Migration For a long-term migration of your configuration you should consider re-creating @@ -33,24 +60,28 @@ all existing Icinga 1.x `*_interval` attributes require an additional `m` durati Icinga 1.x: - define service { - service_description service1 - host_name localhost1 - check_command test_customvar - use generic-service - check_interval 5 - retry_interval 1 - } +``` +define service { + service_description service1 + host_name localhost1 + check_command test_customvar + use generic-service + check_interval 5 + retry_interval 1 +} +``` Icinga 2: - object Service "service1" { - import "generic-service" - host_name = "localhost1" - check_command = "test_customvar" - check_interval = 5m - retry_interval = 1m - } +``` +object Service "service1" { + import "generic-service" + host_name = "localhost1" + check_command = "test_customvar" + check_interval = 5m + retry_interval = 1m +} +``` #### Manual Config Migration Hints for Services @@ -59,77 +90,88 @@ belongs to, you can migrate this to the [apply rules](03-monitoring-basics.md#us Icinga 1.x: - define service { - service_description service1 - host_name localhost1,localhost2 - check_command test_check - use generic-service - } +``` +define service { + service_description service1 + host_name localhost1,localhost2 + check_command test_check + use generic-service +} +``` Icinga 2: - apply Service "service1" { - import "generic-service" - check_command = "test_check" +``` +apply Service "service1" { + import "generic-service" + check_command = "test_check" - assign where host.name in [ "localhost1", "localhost2" ] - } + assign where host.name in [ "localhost1", "localhost2" ] +} +``` In Icinga 1.x you would have organized your services with hostgroups using the `hostgroup_name` attribute like the following example: - define service { - service_description servicewithhostgroups - hostgroup_name hostgroup1,hostgroup3 - check_command test_check - use generic-service - } +``` +define service { + service_description servicewithhostgroups + hostgroup_name hostgroup1,hostgroup3 + check_command test_check + use generic-service +} +``` Using Icinga 2 you can migrate this to the [apply rules](03-monitoring-basics.md#using-apply) syntax: - apply Service "servicewithhostgroups" { - import "generic-service" - check_command = "test_check" +``` +apply Service "servicewithhostgroups" { + import "generic-service" + check_command = "test_check" - assign where "hostgroup1" in host.groups - assign where "hostgroup3" in host.groups - } + assign where "hostgroup1" in host.groups + assign where "hostgroup3" in host.groups +} +``` #### Manual Config Migration Hints for Group Members The Icinga 1.x hostgroup `hg1` has two members `host1` and `host2`. The hostgroup `hg2` has `host3` as a member and includes all members of the `hg1` hostgroup. - define hostgroup { - hostgroup_name hg1 - members host1,host2 - } +``` +define hostgroup { + hostgroup_name hg1 + members host1,host2 +} - define hostgroup { - hostgroup_name hg2 - members host3 - hostgroup_members hg1 - } +define hostgroup { + hostgroup_name hg2 + members host3 + hostgroup_members hg1 +} +``` This can be migrated to Icinga 2 and [using group assign](17-language-reference.md#group-assign). The additional nested hostgroup `hg1` is included into `hg2` with the `groups` attribute. +``` +object HostGroup "hg1" { + groups = [ "hg2" ] + assign where host.name in [ "host1", "host2" ] +} - object HostGroup "hg1" { - assign where host.name in [ "host1", "host2" ] - } - - object HostGroup "hg2" { - groups = [ "hg1" ] - assign where host.name == "host3" - } +object HostGroup "hg2" { + assign where host.name == "host3" +} +``` These assign rules can be applied for all groups: `HostGroup`, `ServiceGroup` and `UserGroup` (requires renaming from `contactgroup`). > **Tip** > -> Define custom attributes and assign/ignore members based on these attribute pattern matches. +> Define custom variables and assign/ignore members based on these attribute pattern matches. @@ -138,50 +180,54 @@ These assign rules can be applied for all groups: `HostGroup`, `ServiceGroup` an Host and service check command arguments are separated by a `!` in Icinga 1.x. Their order is important and they are referenced as `$ARGn$` where `n` is the argument counter. - define command { - command_name my-ping - command_line $USER1$/check_ping -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -p 5 - } +``` +define command { + command_name my-ping + command_line $USER1$/check_ping -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -p 5 +} - define service { - use generic-service - host_name my-server - service_description my-ping - check_command my-ping-check!100.0,20%!500.0,60% - } +define service { + use generic-service + host_name my-server + service_description my-ping + check_command my-ping-check!100.0,20%!500.0,60% +} +``` While you could manually migrate this like (please note the new generic command arguments and default argument values!): - object CheckCommand "my-ping-check" { - command = [ - PluginDir + "/check_ping", "-4" - ] +``` +object CheckCommand "my-ping-check" { + command = [ + PluginDir + "/check_ping", "-4" + ] - arguments = { - "-H" = "$ping_address$" - "-w" = "$ping_wrta$,$ping_wpl$%" - "-c" = "$ping_crta$,$ping_cpl$%" - "-p" = "$ping_packets$" - "-t" = "$ping_timeout$" - } + arguments = { + "-H" = "$ping_address$" + "-w" = "$ping_wrta$,$ping_wpl$%" + "-c" = "$ping_crta$,$ping_cpl$%" + "-p" = "$ping_packets$" + "-t" = "$ping_timeout$" + } - vars.ping_address = "$address$" - vars.ping_wrta = 100 - vars.ping_wpl = 5 - vars.ping_crta = 200 - vars.ping_cpl = 15 - } + vars.ping_address = "$address$" + vars.ping_wrta = 100 + vars.ping_wpl = 5 + vars.ping_crta = 200 + vars.ping_cpl = 15 +} - object Service "my-ping" { - import "generic-service" - host_name = "my-server" - check_command = "my-ping-check" +object Service "my-ping" { + import "generic-service" + host_name = "my-server" + check_command = "my-ping-check" - vars.ping_wrta = 100 - vars.ping_wpl = 20 - vars.ping_crta = 500 - vars.ping_cpl = 60 - } + vars.ping_wrta = 100 + vars.ping_wpl = 20 + vars.ping_crta = 500 + vars.ping_cpl = 60 +} +``` #### Manual Config Migration Hints for Runtime Macros @@ -189,62 +235,73 @@ Runtime macros have been renamed. A detailed comparison table can be found [here For example, accessing the service check output looks like the following in Icinga 1.x: - $SERVICEOUTPUT$ +``` +$SERVICEOUTPUT$ +``` In Icinga 2 you will need to write: - $service.output$ +``` +$service.output$ +``` Another example referencing the host's address attribute in Icinga 1.x: - $HOSTADDRESS$ +``` +$HOSTADDRESS$ +``` In Icinga 2 you'd just use the following macro to access all `address` attributes (even overridden from the service objects): - $address$ +``` +$address$ +``` +#### Manual Config Migration Hints for Runtime Custom Variables -#### Manual Config Migration Hints for Runtime Custom Attributes +Custom variables from Icinga 1.x are available as Icinga 2 custom variables. -Custom variables from Icinga 1.x are available as Icinga 2 custom attributes. +``` +define command { + command_name test_customvar + command_line echo "Host CV: $_HOSTCVTEST$ Service CV: $_SERVICECVTEST$\n" +} - define command { - command_name test_customvar - command_line echo "Host CV: $_HOSTCVTEST$ Service CV: $_SERVICECVTEST$\n" - } +define host { + host_name localhost1 + check_command test_customvar + use generic-host + _CVTEST host cv value +} - define host { - host_name localhost1 - check_command test_customvar - use generic-host - _CVTEST host cv value - } - - define service { - service_description service1 - host_name localhost1 - check_command test_customvar - use generic-service - _CVTEST service cv value - } +define service { + service_description service1 + host_name localhost1 + check_command test_customvar + use generic-service + _CVTEST service cv value +} +``` Can be written as the following in Icinga 2: - object CheckCommand "test_customvar" { - command = "echo "Host CV: $host.vars.CVTEST$ Service CV: $service.vars.CVTEST$\n"" - } +``` +object CheckCommand "test_customvar" { + command = "echo "Host CV: $host.vars.CVTEST$ Service CV: $service.vars.CVTEST$\n"" +} - object Host "localhost1" { - import "generic-host" - check_command = "test_customvar" - vars.CVTEST = "host cv value" - } +object Host "localhost1" { + import "generic-host" + check_command = "test_customvar" + vars.CVTEST = "host cv value" +} - object Service "service1" { - host_name = "localhost1" - check_command = "test_customvar" - vars.CVTEST = "service cv value" - } +object Service "service1" { + host_name = "localhost1" + check_command = "test_customvar" + vars.CVTEST = "service cv value" +} +``` If you are just defining `$CVTEST$` in your command definition, its value depends on the execution scope -- the host check command will fetch the host attribute value of `vars.CVTEST` @@ -252,30 +309,34 @@ while the service check command resolves its value to the service attribute attr > **Note** > -> Custom attributes in Icinga 2 are case-sensitive. `vars.CVTEST` is not the same as `vars.CvTest`. +> Custom variables in Icinga 2 are case-sensitive. `vars.CVTEST` is not the same as `vars.CvTest`. #### Manual Config Migration Hints for Contacts (Users) Contacts in Icinga 1.x act as users in Icinga 2, but do not have any notification commands specified. This migration part is explained in the [next chapter](23-migrating-from-icinga-1x.md#manual-config-migration-hints-notifications). - define contact{ - contact_name testconfig-user - use generic-user - alias Icinga Test User - service_notification_options c,f,s,u - email icinga@localhost - } +``` +define contact{ + contact_name testconfig-user + use generic-user + alias Icinga Test User + service_notification_options c,f,s,u + email icinga@localhost +} +``` The `service_notification_options` can be [mapped](23-migrating-from-icinga-1x.md#manual-config-migration-hints-notification-filters) into generic `state` and `type` filters, if additional notification filtering is required. `alias` gets renamed to `display_name`. - object User "testconfig-user" { - import "generic-user" - display_name = "Icinga Test User" - email = "icinga@localhost" - } +``` +object User "testconfig-user" { + import "generic-user" + display_name = "Icinga Test User" + email = "icinga@localhost" +} +``` This user can be put into usergroups (former contactgroups) or referenced in newly migration notification objects. @@ -312,23 +373,28 @@ the host and service notification commands involved. Generate a new notification object based on these values. Import the generic template based on the type (`host` or `service`). Assign it to the host or service and set the newly generated notification command name as `command` attribute. - object Notification "" { - import "mail-host-notification" - host_name = "" - command = "" - +``` +object Notification "" { + import "mail-host-notification" + host_name = "" + command = "" +``` Convert the `notification_options` attribute from Icinga 1.x to Icinga 2 `states` and `types`. Details [here](23-migrating-from-icinga-1x.md#manual-config-migration-hints-notification-filters). Add the notification period. - states = [ OK, Warning, Critical ] - types = [ Recovery, Problem, Custom ] - period = "24x7" +``` + states = [ OK, Warning, Critical ] + types = [ Recovery, Problem, Custom ] + period = "24x7" +``` The current contact acts as `users` attribute. - users = [ "" ] - } +``` + users = [ "" ] +} +``` Do this in a loop for all notification commands (depending if host or service contact). Once done, dump the collected notification commands. @@ -374,53 +440,57 @@ hostgroup. The default `notification_interval` is set to `10` minutes notifying After 20 minutes (`10*2`, notification_interval * first_notification) the notification is escalated to the `cg_ops` contactgroup until 60 minutes (`10*6`) have passed. - define service { - service_description dep_svc01 - host_name dep_hostsvc01,dep_hostsvc03 - check_command test2 - use generic-service - notification_interval 10 - contact_groups cg_admin - } +``` +define service { + service_description dep_svc01 + host_name dep_hostsvc01,dep_hostsvc03 + check_command test2 + use generic-service + notification_interval 10 + contact_groups cg_admin +} - define hostgroup { - hostgroup_name hg_svcdep2 - members dep_hostsvc03 - } +define hostgroup { + hostgroup_name hg_svcdep2 + members dep_hostsvc03 +} - # with hostgroup_name and service_description - define serviceescalation { - hostgroup_name hg_svcdep2 - service_description dep_svc01 - first_notification 2 - last_notification 6 - contact_groups cg_ops - } +# with hostgroup_name and service_description +define serviceescalation { + hostgroup_name hg_svcdep2 + service_description dep_svc01 + first_notification 2 + last_notification 6 + contact_groups cg_ops +} +``` In Icinga 2 the service and hostgroup definition will look quite the same. Save the `notification_interval` and `contact_groups` attribute for an additional notification. - apply Service "dep_svc01" { - import "generic-service" +``` +apply Service "dep_svc01" { + import "generic-service" - check_command = "test2" + check_command = "test2" - assign where host.name == "dep_hostsvc01" - assign where host.name == "dep_hostsvc03" - } + assign where host.name == "dep_hostsvc01" + assign where host.name == "dep_hostsvc03" +} - object HostGroup "hg_svcdep2" { - assign where host.name == "dep_hostsvc03" - } +object HostGroup "hg_svcdep2" { + assign where host.name == "dep_hostsvc03" +} - apply Notification "email" to Service { - import "service-mail-notification" +apply Notification "email" to Service { + import "service-mail-notification" - interval = 10m - user_groups = [ "cg_admin" ] + interval = 10m + user_groups = [ "cg_admin" ] - assign where service.name == "dep_svc01" && (host.name == "dep_hostsvc01" || host.name == "dep_hostsvc03") - } + assign where service.name == "dep_svc01" && (host.name == "dep_hostsvc01" || host.name == "dep_hostsvc03") +} +``` Calculate the begin and end time for the newly created escalation notification: @@ -429,19 +499,21 @@ Calculate the begin and end time for the newly created escalation notification: Assign the notification escalation to the service `dep_svc01` on all hosts in the hostgroup `hg_svcdep2`. - apply Notification "email-escalation" to Service { - import "service-mail-notification" +``` +apply Notification "email-escalation" to Service { + import "service-mail-notification" - interval = 10m - user_groups = [ "cg_ops" ] + interval = 10m + user_groups = [ "cg_ops" ] - times = { - begin = 20m - end = 1h - } + times = { + begin = 20m + end = 1h + } - assign where service.name == "dep_svc01" && "hg_svcdep2" in host.groups - } + assign where service.name == "dep_svc01" && "hg_svcdep2" in host.groups +} +``` The assign rule could be made more generic and the notification be applied to more than just this service belonging to hosts in the matched hostgroup. @@ -469,41 +541,43 @@ If the state filter matches, you can define whether to disable checks and notifi The following example describes service dependencies. If you migrate from Icinga 1.x, you will only want to use the classic `Host-to-Host` and `Service-to-Service` dependency relationships. - define service { - service_description dep_svc01 - hostgroup_name hg_svcdep1 - check_command test2 - use generic-service - } +``` +define service { + service_description dep_svc01 + hostgroup_name hg_svcdep1 + check_command test2 + use generic-service +} - define service { - service_description dep_svc02 - hostgroup_name hg_svcdep2 - check_command test2 - use generic-service - } +define service { + service_description dep_svc02 + hostgroup_name hg_svcdep2 + check_command test2 + use generic-service +} - define hostgroup { - hostgroup_name hg_svcdep2 - members host2 - } +define hostgroup { + hostgroup_name hg_svcdep2 + members host2 +} - define host{ - use linux-server-template - host_name host1 - address 192.168.1.10 - } +define host{ + use linux-server-template + host_name host1 + address 192.168.1.10 +} - # with hostgroup_name and service_description - define servicedependency { - host_name host1 - dependent_hostgroup_name hg_svcdep2 - service_description dep_svc01 - dependent_service_description * - execution_failure_criteria u,c - notification_failure_criteria w,u,c - inherits_parent 1 - } +# with hostgroup_name and service_description +define servicedependency { + host_name host1 + dependent_hostgroup_name hg_svcdep2 + service_description dep_svc01 + dependent_service_description * + execution_failure_criteria u,c + notification_failure_criteria w,u,c + inherits_parent 1 +} +``` Map the dependency attributes accordingly. @@ -517,44 +591,48 @@ Map the dependency attributes accordingly. And migrate the host and services. - object Host "host1" { - import "linux-server-template" - address = "192.168.1.10" - } +``` +object Host "host1" { + import "linux-server-template" + address = "192.168.1.10" +} - object HostGroup "hg_svcdep2" { - assign where host.name == "host2" - } +object HostGroup "hg_svcdep2" { + assign where host.name == "host2" +} - apply Service "dep_svc01" { - import "generic-service" - check_command = "test2" +apply Service "dep_svc01" { + import "generic-service" + check_command = "test2" - assign where "hp_svcdep1" in host.groups - } + assign where "hp_svcdep1" in host.groups +} - apply Service "dep_svc02" { - import "generic-service" - check_command = "test2" +apply Service "dep_svc02" { + import "generic-service" + check_command = "test2" - assign where "hp_svcdep2" in host.groups - } + assign where "hp_svcdep2" in host.groups +} +``` When it comes to the `execution_failure_criteria` and `notification_failure_criteria` attribute migration, you will need to map the most common values, in this example `u,c` (`Unknown` and `Critical` will cause the dependency to fail). Therefore the `Dependency` should be ok on Ok and Warning. `inherits_parents` is always enabled. - apply Dependency "all-svc-for-hg-hg_svcdep2-on-host1-dep_svc01" to Service { - parent_host_name = "host1" - parent_service_name = "dep_svc01" +``` +apply Dependency "all-svc-for-hg-hg_svcdep2-on-host1-dep_svc01" to Service { + parent_host_name = "host1" + parent_service_name = "dep_svc01" - states = [ Ok, Warning ] - disable_checks = true - disable_notifications = true + states = [ Ok, Warning ] + disable_checks = true + disable_notifications = true - assign where "hg_svcdep2" in host.groups - } + assign where "hg_svcdep2" in host.groups +} +``` Host dependencies are explained in the [next chapter](23-migrating-from-icinga-1x.md#manual-config-migration-hints-host-parents). @@ -570,42 +648,46 @@ virtual machines `vmware-vm1` and `vmware-vm2`. By default all hosts in the hostgroup `vmware` should get the parent assigned. This isn't really solvable with Icinga 1.x parents, but only with host dependencies. - define host{ - use linux-server-template - host_name vmware-master - hostgroups vmware - address 192.168.1.10 - } +``` +define host{ + use linux-server-template + host_name vmware-master + hostgroups vmware + address 192.168.1.10 +} - define host{ - use linux-server-template - host_name vmware-vm1 - hostgroups vmware - address 192.168.27.1 - parents vmware-master - } +define host{ + use linux-server-template + host_name vmware-vm1 + hostgroups vmware + address 192.168.27.1 + parents vmware-master +} - define host{ - use linux-server-template - host_name vmware-vm2 - hostgroups vmware - address 192.168.28.1 - parents vmware-master - } +define host{ + use linux-server-template + host_name vmware-vm2 + hostgroups vmware + address 192.168.28.1 + parents vmware-master +} +``` By default all hosts in the hostgroup `vmware` should get the parent assigned (but not the `vmware-master` host itself). This isn't really solvable with Icinga 1.x parents, but only with host dependencies as shown below: - define hostdependency { - dependent_hostgroup_name vmware - dependent_host_name !vmware-master - host_name vmware-master - inherits_parent 1 - notification_failure_criteria d,u - execution_failure_criteria d,u - dependency_period testconfig-24x7 - } +``` +define hostdependency { + dependent_hostgroup_name vmware + dependent_host_name !vmware-master + host_name vmware-master + inherits_parent 1 + notification_failure_criteria d,u + execution_failure_criteria d,u + dependency_period testconfig-24x7 +} +``` When migrating to Icinga 2, the parents must be changed to a newly created host dependency. @@ -620,33 +702,34 @@ Map the following attributes The Icinga 2 configuration looks like this: +``` +object Host "vmware-master" { + import "linux-server-template" + groups += [ "vmware" ] + address = "192.168.1.10" + vars.is_vmware_master = true +} - object Host "vmware-master" { - import "linux-server-template" - groups += [ "vmware" ] - address = "192.168.1.10" - vars.is_vmware_master = true - } +object Host "vmware-vm1" { + import "linux-server-template" + groups += [ "vmware" ] + address = "192.168.27.1" +} - object Host "vmware-vm1" { - import "linux-server-template" - groups += [ "vmware" ] - address = "192.168.27.1" - } +object Host "vmware-vm2" { + import "linux-server-template" + groups += [ "vmware" ] + address = "192.168.28.1" +} - object Host "vmware-vm2" { - import "linux-server-template" - groups += [ "vmware" ] - address = "192.168.28.1" - } +apply Dependency "vmware-master" to Host { + parent_host_name = "vmware-master" - apply Dependency "vmware-master" to Host { - parent_host_name = "vmware-master" - - assign where "vmware" in host.groups - ignore where host.vars.is_vmware_master - ignore where host.name == "vmware-master" - } + assign where "vmware" in host.groups + ignore where host.vars.is_vmware_master + ignore where host.name == "vmware-master" +} +``` For easier identification you could add the `vars.is_vmware_master` attribute to the `vmware-master` host and let the dependency ignore that instead of the hardcoded host name. That's different @@ -655,29 +738,31 @@ to the Icinga 1.x example and a best practice hint only. Another way to express the same configuration would be something like: - object Host "vmware-master" { - import "linux-server-template" - groups += [ "vmware" ] - address = "192.168.1.10" - } +``` +object Host "vmware-master" { + import "linux-server-template" + groups += [ "vmware" ] + address = "192.168.1.10" +} - object Host "vmware-vm1" { - import "linux-server-template" - groups += [ "vmware" ] - address = "192.168.27.1" - vars.parents = [ "vmware-master" ] - } +object Host "vmware-vm1" { + import "linux-server-template" + groups += [ "vmware" ] + address = "192.168.27.1" + vars.parents = [ "vmware-master" ] +} - object Host "vmware-vm2" { - import "linux-server-template" - groups += [ "vmware" ] - address = "192.168.28.1" - vars.parents = [ "vmware-master" ] - } +object Host "vmware-vm2" { + import "linux-server-template" + groups += [ "vmware" ] + address = "192.168.28.1" + vars.parents = [ "vmware-master" ] +} - apply Dependency "host-to-parent-" for (parent in host.vars.parents) to Host { - parent_host_name = parent - } +apply Dependency "host-to-parent-" for (parent in host.vars.parents) to Host { + parent_host_name = parent +} +``` This example allows finer grained host-to-host dependency, as well as multiple dependency support. @@ -707,24 +792,30 @@ the Icinga daemon at startup. icinga.cfg: - enable_notifications=1 +``` +enable_notifications=1 +``` objects.cfg: - define service { - notifications_enabled 0 - } +``` +define service { + notifications_enabled 0 +} +``` Icinga 2 supports objects and (global) variables, but does not make a difference between the main configuration file or any other included file. icinga2.conf: - const EnableNotifications = true +``` +const EnableNotifications = true - object Service "test" { - enable_notifications = false - } +object Service "test" { + enable_notifications = false +} +``` #### Sample Configuration and ITL @@ -746,7 +837,7 @@ included in `icinga2.conf` by default. > **Note** > > Add your own custom templates in the `conf.d/` directory as well, e.g. inside -> the [templates.conf](04-configuring-icinga-2.md#templates-conf) file. +> the [templates.conf](04-configuration.md#templates-conf) file. ### Main Config File @@ -754,7 +845,7 @@ In Icinga 1.x there are many global configuration settings available in `icinga. Icinga 2 only uses a small set of [global constants](17-language-reference.md#constants) allowing you to specify certain different setting such as the `NodeName` in a cluster scenario. -Aside from that, the [icinga2.conf](04-configuring-icinga-2.md#icinga2-conf) should take care of including +Aside from that, the [icinga2.conf](04-configuration.md#icinga2-conf) should take care of including global constants, enabled [features](11-cli-commands.md#enable-features) and the object configuration. ### Include Files and Directories @@ -765,25 +856,33 @@ suffix in the given directory. Only absolute paths may be used. The `cfg_file` and `cfg_dir` directives can include the same file twice which leads to configuration errors in Icinga 1.x. - cfg_file=/etc/icinga/objects/commands.cfg - cfg_dir=/etc/icinga/objects +``` +cfg_file=/etc/icinga/objects/commands.cfg +cfg_dir=/etc/icinga/objects +``` Icinga 2 supports wildcard includes and relative paths, e.g. for including `conf.d/*.conf` in the same directory. - include "conf.d/*.conf" +``` +include "conf.d/*.conf" +``` If you want to include files and directories recursively, you need to define a separate option and add the directory and an optional pattern. - include_recursive "conf.d" +``` +include_recursive "conf.d" +``` A global search path for includes is available for advanced features like the Icinga Template Library (ITL) or additional monitoring plugins check command configuration. - include - include +``` +include +include +``` By convention the `.conf` suffix is used for Icinga 2 configuration files. @@ -796,13 +895,15 @@ set in the `resource.cfg` configuration file in Icinga 1.x. By convention the Icinga 2 uses global constants instead. In the default config these are set in the `constants.conf` configuration file: - /** - * This file defines global constants which can be used in - * the other configuration files. At a minimum the - * PluginDir constant should be defined. - */ +``` +/** + * This file defines global constants which can be used in + * the other configuration files. At a minimum the + * PluginDir constant should be defined. + */ - const PluginDir = "/usr/lib/nagios/plugins" +const PluginDir = "/usr/lib/nagios/plugins" +``` [Global macros](17-language-reference.md#constants) can only be defined once. Trying to modify a global constant will result in an error. @@ -825,35 +926,41 @@ Icinga Web 2 for example). Object names are not specified using attributes (e.g. `service_description` for services) like in Icinga 1.x but directly after their type definition. - define service { - host_name localhost - service_description ping4 - } +``` +define service { + host_name localhost + service_description ping4 +} - object Service "ping4" { - host_name = "localhost" - } +object Service "ping4" { + host_name = "localhost" +} +``` ### Templates In Icinga 1.x templates are identified using the `register 0` setting. Icinga 2 uses the `template` identifier: - template Service "ping4-template" { } +``` +template Service "ping4-template" { } +``` Icinga 1.x objects inherit from templates using the `use` attribute. Icinga 2 uses the keyword `import` with template names in double quotes. - define service { - service_description testservice - use tmpl1,tmpl2,tmpl3 - } +``` +define service { + service_description testservice + use tmpl1,tmpl2,tmpl3 +} - object Service "testservice" { - import "tmpl1" - import "tmpl2" - import "tmpl3" - } +object Service "testservice" { + import "tmpl1" + import "tmpl2" + import "tmpl3" +} +``` The last template overrides previously set values. @@ -862,13 +969,15 @@ The last template overrides previously set values. Icinga 1.x separates attribute and value pairs with whitespaces/tabs. Icinga 2 requires an equal sign (=) between them. - define service { - check_interval 5 - } +``` +define service { + check_interval 5 +} - object Service "test" { - check_interval = 5m - } +object Service "test" { + check_interval = 5m +} +``` Please note that the default time value is seconds if no duration literal is given. `check_interval = 5` behaves the same as `check_interval = 5s`. @@ -886,27 +995,27 @@ The `alias` is used for group, timeperiod, etc. objects too. Icinga 2 only supports the `display_name` attribute which is also taken into account by Icinga web interfaces. -### Custom Attributes +### Custom Variables -Icinga 2 allows you to define custom attributes in the `vars` dictionary. +Icinga 2 allows you to define custom variables in the `vars` dictionary. The `notes`, `notes_url`, `action_url`, `icon_image`, `icon_image_alt` attributes for host and service objects are still available in Icinga 2. `2d_coords` and `statusmap_image` are not supported in Icinga 2. -#### Custom Variables - Icinga 1.x custom variable attributes must be prefixed using an underscore (`_`). -In Icinga 2 these attributes must be added to the `vars` dictionary as custom attributes. +In Icinga 2 these attributes must be added to the `vars` dictionary as custom variables. - vars.dn = "cn=icinga2-dev-host,ou=icinga,ou=main,ou=IcingaConfig,ou=LConf,dc=icinga,dc=org" - vars.cv = "my custom cmdb description" +``` +vars.dn = "cn=icinga2-dev-host,ou=icinga,ou=main,ou=IcingaConfig,ou=LConf,dc=icinga,dc=org" +vars.cv = "my custom cmdb description" +``` -These custom attributes are also used as [command parameters](03-monitoring-basics.md#command-passing-parameters). +These custom variables are also used as [command parameters](03-monitoring-basics.md#command-passing-parameters). -While Icinga 1.x only supports numbers and strings as custom attribute values, +While Icinga 1.x only supports numbers and strings as custom variable values, Icinga 2 extends that to arrays and (nested) dictionaries. For more details -look [here](03-monitoring-basics.md#custom-attributes). +look [here](03-monitoring-basics.md#custom-variables). ### Host Service Relation @@ -942,13 +1051,13 @@ and their users. ### Macros Various object attributes and runtime variables can be accessed as macros in -commands in Icinga 1.x -- Icinga 2 supports all required [custom attributes](03-monitoring-basics.md#custom-attributes). +commands in Icinga 1.x -- Icinga 2 supports all required [custom variables](03-monitoring-basics.md#custom-variables). #### Command Arguments If you have previously used Icinga 1.x, you may already be familiar with user and argument definitions (e.g., `USER1` or `ARG1`). Unlike in Icinga 1.x -the Icinga 2 custom attributes may have arbitrary names and arguments are no +the Icinga 2 custom variables may have arbitrary names and arguments are no longer specified in the `check_command` setting. In Icinga 1.x arguments are specified in the `check_command` attribute and @@ -972,7 +1081,7 @@ can be set using the `env` attribute in command objects. #### Runtime Macros Icinga 2 requires an object specific namespace when accessing configuration -and stateful runtime macros. Custom attributes can be accessed directly. +and stateful runtime macros. Custom variables can be accessed directly. If a runtime macro from Icinga 1.x is not listed here, it is not supported by Icinga 2. @@ -1110,47 +1219,49 @@ Changes to global statistic macros: The following external commands are not supported: - CHANGE_*MODATTR - CHANGE_CONTACT_HOST_NOTIFICATION_TIMEPERIOD - CHANGE_HOST_NOTIFICATION_TIMEPERIOD - CHANGE_SVC_NOTIFICATION_TIMEPERIOD - DEL_DOWNTIME_BY_HOSTGROUP_NAME - DEL_DOWNTIME_BY_START_TIME_COMMENT - DISABLE_ALL_NOTIFICATIONS_BEYOND_HOST - DISABLE_CONTACT_HOST_NOTIFICATIONS - DISABLE_CONTACT_SVC_NOTIFICATIONS - DISABLE_CONTACTGROUP_HOST_NOTIFICATIONS - DISABLE_CONTACTGROUP_SVC_NOTIFICATIONS - DISABLE_FAILURE_PREDICTION - DISABLE_HOST_AND_CHILD_NOTIFICATIONS - DISABLE_HOST_FRESHNESS_CHECKS - DISABLE_NOTIFICATIONS_EXPIRE_TIME - DISABLE_SERVICE_FRESHNESS_CHECKS - ENABLE_ALL_NOTIFICATIONS_BEYOND_HOST - ENABLE_CONTACT_HOST_NOTIFICATIONS - ENABLE_CONTACT_SVC_NOTIFICATIONS - ENABLE_CONTACTGROUP_HOST_NOTIFICATIONS - ENABLE_CONTACTGROUP_SVC_NOTIFICATIONS - ENABLE_FAILURE_PREDICTION - ENABLE_HOST_AND_CHILD_NOTIFICATIONS - ENABLE_HOST_FRESHNESS_CHECKS - ENABLE_SERVICE_FRESHNESS_CHECKS - READ_STATE_INFORMATION - SAVE_STATE_INFORMATION - SET_HOST_NOTIFICATION_NUMBER - SET_SVC_NOTIFICATION_NUMBER - START_ACCEPTING_PASSIVE_HOST_CHECKS - START_ACCEPTING_PASSIVE_SVC_CHECKS - START_OBSESSING_OVER_HOST - START_OBSESSING_OVER_HOST_CHECKS - START_OBSESSING_OVER_SVC - START_OBSESSING_OVER_SVC_CHECKS - STOP_ACCEPTING_PASSIVE_HOST_CHECKS - STOP_ACCEPTING_PASSIVE_SVC_CHECKS - STOP_OBSESSING_OVER_HOST - STOP_OBSESSING_OVER_HOST_CHECKS - STOP_OBSESSING_OVER_SVC - STOP_OBSESSING_OVER_SVC_CHECKS +``` +CHANGE_*MODATTR +CHANGE_CONTACT_HOST_NOTIFICATION_TIMEPERIOD +CHANGE_HOST_NOTIFICATION_TIMEPERIOD +CHANGE_SVC_NOTIFICATION_TIMEPERIOD +DEL_DOWNTIME_BY_HOSTGROUP_NAME +DEL_DOWNTIME_BY_START_TIME_COMMENT +DISABLE_ALL_NOTIFICATIONS_BEYOND_HOST +DISABLE_CONTACT_HOST_NOTIFICATIONS +DISABLE_CONTACT_SVC_NOTIFICATIONS +DISABLE_CONTACTGROUP_HOST_NOTIFICATIONS +DISABLE_CONTACTGROUP_SVC_NOTIFICATIONS +DISABLE_FAILURE_PREDICTION +DISABLE_HOST_AND_CHILD_NOTIFICATIONS +DISABLE_HOST_FRESHNESS_CHECKS +DISABLE_NOTIFICATIONS_EXPIRE_TIME +DISABLE_SERVICE_FRESHNESS_CHECKS +ENABLE_ALL_NOTIFICATIONS_BEYOND_HOST +ENABLE_CONTACT_HOST_NOTIFICATIONS +ENABLE_CONTACT_SVC_NOTIFICATIONS +ENABLE_CONTACTGROUP_HOST_NOTIFICATIONS +ENABLE_CONTACTGROUP_SVC_NOTIFICATIONS +ENABLE_FAILURE_PREDICTION +ENABLE_HOST_AND_CHILD_NOTIFICATIONS +ENABLE_HOST_FRESHNESS_CHECKS +ENABLE_SERVICE_FRESHNESS_CHECKS +READ_STATE_INFORMATION +SAVE_STATE_INFORMATION +SET_HOST_NOTIFICATION_NUMBER +SET_SVC_NOTIFICATION_NUMBER +START_ACCEPTING_PASSIVE_HOST_CHECKS +START_ACCEPTING_PASSIVE_SVC_CHECKS +START_OBSESSING_OVER_HOST +START_OBSESSING_OVER_HOST_CHECKS +START_OBSESSING_OVER_SVC +START_OBSESSING_OVER_SVC_CHECKS +STOP_ACCEPTING_PASSIVE_HOST_CHECKS +STOP_ACCEPTING_PASSIVE_SVC_CHECKS +STOP_OBSESSING_OVER_HOST +STOP_OBSESSING_OVER_HOST_CHECKS +STOP_OBSESSING_OVER_SVC +STOP_OBSESSING_OVER_SVC_CHECKS +``` ### Asynchronous Event Execution @@ -1222,27 +1333,31 @@ attribute in the object. The old way of listing all group members in the group's `members` attribute is available through `assign where` and `ignore where` expressions by using [group assign](03-monitoring-basics.md#group-assign-intro). - object Host "web-dev" { - import "generic-host" - } +``` +object Host "web-dev" { + import "generic-host" +} - object HostGroup "dev-hosts" { - display_name = "Dev Hosts" - assign where match("*-dev", host.name) - } +object HostGroup "dev-hosts" { + display_name = "Dev Hosts" + assign where match("*-dev", host.name) +} +``` #### Add Service to Hostgroup where Host is Member In order to associate a service with all hosts in a host group the [apply](03-monitoring-basics.md#using-apply) keyword can be used: - apply Service "ping4" { - import "generic-service" +``` +apply Service "ping4" { + import "generic-service" - check_command = "ping4" + check_command = "ping4" - assign where "dev-hosts" in host.groups - } + assign where "dev-hosts" in host.groups +} +``` ### Notifications @@ -1282,12 +1397,16 @@ Icinga 2 attempts to solve that problem in this way Previously in Icinga 1.x it looked like this: - service -> (contact, contactgroup) -> notification command +``` +service -> (contact, contactgroup) -> notification command +``` In Icinga 2 it will look like this: - Service -> Notification -> NotificationCommand - -> User, UserGroup +``` +Service -> Notification -> NotificationCommand + -> User, UserGroup +``` #### Escalations @@ -1295,8 +1414,10 @@ Escalations in Icinga 1.x require a separated object matching on existing objects. Escalations happen between a defined start and end time which is calculated from the notification_interval: - start = notification start + (notification_interval * first_notification) - end = notification start + (notification_interval * last_notification) +``` +start = notification start + (notification_interval * first_notification) +end = notification start + (notification_interval * last_notification) +``` In theory first_notification and last_notification can be set to readable numbers. In practice users are manipulating those attributes in combination @@ -1319,10 +1440,12 @@ Unlike Icinga 1.x with the 'notification_options' attribute with comma-separated state and type filters, Icinga 2 uses two configuration attributes for that. All state and type filter use long names OR'd with a pipe together - notification_options w,u,c,r,f,s +``` +notification_options w,u,c,r,f,s - states = [ Warning, Unknown, Critical ] - types = [ Problem, Recovery, FlappingStart, FlappingEnd, DowntimeStart, DowntimeEnd, DowntimeRemoved ] +states = [ Warning, Unknown, Critical ] +types = [ Problem, Recovery, FlappingStart, FlappingEnd, DowntimeStart, DowntimeEnd, DowntimeRemoved ] +``` Icinga 2 adds more fine-grained type filters for acknowledgements, downtime, and flapping type (start, end, ...). @@ -1358,7 +1481,9 @@ The Icinga 1.x flapping detection uses the last 21 states of a service. This value is hardcoded and cannot be changed. The algorithm on determining a flapping state is as follows: - flapping value = (number of actual state changes / number of possible state changes) +``` +flapping value = (number of actual state changes / number of possible state changes) +``` The flapping value is then compared to the low and high flapping thresholds. @@ -1455,6 +1580,6 @@ and configuration distribution problems Icinga 1.x distributed monitoring curren Icinga 2 implements a new built-in [distributed monitoring architecture](06-distributed-monitoring.md#distributed-monitoring-scenarios), -including config and check distribution, IPv4/IPv6 support, SSL certificates and zone support for DMZ. +including config and check distribution, IPv4/IPv6 support, TLS certificates and zone support for DMZ. High Availability and load balancing are also part of the Icinga 2 Cluster feature, next to local replay logs on connection loss ensuring that the event history is kept in sync. diff --git a/doc/24-appendix.md b/doc/24-appendix.md index aa293d047..e0f0b2f2a 100644 --- a/doc/24-appendix.md +++ b/doc/24-appendix.md @@ -188,7 +188,7 @@ New columns: {host,service}group | notes | TEXT | NULL | - {host,service}group | notes_url | TEXT | NULL | - {host,service}group | action_url | TEXT | NULL | - - customvariable* | is_json | integer | 0 | Defines whether `varvalue` is a json encoded string from custom attributes, or not + customvariable* | is_json | integer | 0 | Defines whether `varvalue` is a json encoded string from custom variables, or not servicestatus | original_attributes | TEXT | NULL | JSON encoded dictionary of original attributes if modified at runtime. hoststatus | original_attributes | TEXT | NULL | JSON encoded dictionary of original attributes if modified at runtime. @@ -611,7 +611,7 @@ Not supported: `neb_callbacks`, `neb_callbacks_rate`, `requests`, `requests_rate host_ | join | Prefix for attributes from implicit join with hosts table. -#### Livestatus Timeperiod Table Attributes +#### Livestatus Timeperiods Table Attributes Key | Type | Note ----------------------|-----------|------------------------- diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index bbd924b31..3be5b582e 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ file(GLOB DOCSRCS "*.md") diff --git a/doc/icinga2.8 b/doc/icinga2.8 index 81fd9dba8..aa2c34c86 100644 --- a/doc/icinga2.8 +++ b/doc/icinga2.8 @@ -13,7 +13,7 @@ icinga2 \- The Icinga 2 network monitoring daemon .I command := [ -.B api | console | daemon | feature | node | object | pki | repository | troubleshoot | variable +.B api | ca | console | daemon | feature | node | object | pki | variable ] .B --help @@ -69,9 +69,9 @@ Config files are processed in the order they are specified on the command-line. When no configuration file is specified and the .B --no-config is not used, Icinga 2 automatically falls back to using the configuration file -.B SysconfDir + "/icinga2/icinga2.conf" -(where SysconfDir is usually -.BI "/etc" ")." +.B ConfigDir + "/icinga2.conf" +(where ConfigDir is usually +.BI "/etc/icinga2" ")." .TP .B "-z, --noconfig" @@ -90,9 +90,9 @@ Detach from the controlling terminal. .SH "REPORTING BUGS" Report bugs at .br -Icinga home page: +Icinga home page: .SH COPYRIGHT -Copyright \(co 2012\-2014 Icinga Development Team (https://www.icinga.com) +Copyright \(co 2012 Icinga GmbH License GPLv2+: GNU GPL version 2 or later .br This is free software: you are free to change and redistribute it. diff --git a/doc/images/addons/dashing_icinga2.png b/doc/images/addons/dashing_icinga2.png new file mode 100644 index 000000000..d3e0e42d8 Binary files /dev/null and b/doc/images/addons/dashing_icinga2.png differ diff --git a/doc/images/addons/icinga_certificate_monitoring.png b/doc/images/addons/icinga_certificate_monitoring.png new file mode 100644 index 000000000..d1be34b82 Binary files /dev/null and b/doc/images/addons/icinga_certificate_monitoring.png differ diff --git a/doc/images/addons/icinga_reporting.png b/doc/images/addons/icinga_reporting.png new file mode 100644 index 000000000..4b561a3fe Binary files /dev/null and b/doc/images/addons/icinga_reporting.png differ diff --git a/doc/images/addons/icingaweb2_businessprocess.png b/doc/images/addons/icingaweb2_businessprocess.png new file mode 100644 index 000000000..7824ded5b Binary files /dev/null and b/doc/images/addons/icingaweb2_businessprocess.png differ diff --git a/doc/images/addons/icingaweb2_grafana.png b/doc/images/addons/icingaweb2_grafana.png new file mode 100644 index 000000000..0861543f4 Binary files /dev/null and b/doc/images/addons/icingaweb2_grafana.png differ diff --git a/doc/images/addons/icingaweb2_graphite.png b/doc/images/addons/icingaweb2_graphite.png new file mode 100644 index 000000000..4147ba5f3 Binary files /dev/null and b/doc/images/addons/icingaweb2_graphite.png differ diff --git a/doc/images/addons/icingaweb2_maps.png b/doc/images/addons/icingaweb2_maps.png new file mode 100644 index 000000000..5564edabe Binary files /dev/null and b/doc/images/addons/icingaweb2_maps.png differ diff --git a/doc/images/api/icinga2_api_powershell_ise.png b/doc/images/api/icinga2_api_powershell_ise.png new file mode 100644 index 000000000..41acbddf1 Binary files /dev/null and b/doc/images/api/icinga2_api_powershell_ise.png differ diff --git a/doc/images/configuration/icinga_web_local_server.png b/doc/images/configuration/icinga_web_local_server.png new file mode 100644 index 000000000..3dac92d07 Binary files /dev/null and b/doc/images/configuration/icinga_web_local_server.png differ diff --git a/doc/images/development/windows_boost_build_dev_cmd.png b/doc/images/development/windows_boost_build_dev_cmd.png new file mode 100644 index 000000000..1a3c30c38 Binary files /dev/null and b/doc/images/development/windows_boost_build_dev_cmd.png differ diff --git a/doc/images/development/windows_builds_gitlab_pipeline.png b/doc/images/development/windows_builds_gitlab_pipeline.png new file mode 100644 index 000000000..8110c5359 Binary files /dev/null and b/doc/images/development/windows_builds_gitlab_pipeline.png differ diff --git a/doc/images/development/windows_powershell_posh_git.png b/doc/images/development/windows_powershell_posh_git.png new file mode 100644 index 000000000..48014a4c5 Binary files /dev/null and b/doc/images/development/windows_powershell_posh_git.png differ diff --git a/doc/images/development/windows_visual_studio_installer_01.png b/doc/images/development/windows_visual_studio_installer_01.png new file mode 100644 index 000000000..a8cb44989 Binary files /dev/null and b/doc/images/development/windows_visual_studio_installer_01.png differ diff --git a/doc/images/development/windows_visual_studio_installer_02.png b/doc/images/development/windows_visual_studio_installer_02.png new file mode 100644 index 000000000..036997020 Binary files /dev/null and b/doc/images/development/windows_visual_studio_installer_02.png differ diff --git a/doc/images/development/windows_visual_studio_installer_03.png b/doc/images/development/windows_visual_studio_installer_03.png new file mode 100644 index 000000000..c29f57f56 Binary files /dev/null and b/doc/images/development/windows_visual_studio_installer_03.png differ diff --git a/doc/images/development/windows_visual_studio_tabs_c++.png b/doc/images/development/windows_visual_studio_tabs_c++.png new file mode 100644 index 000000000..d511469be Binary files /dev/null and b/doc/images/development/windows_visual_studio_tabs_c++.png differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_endpoints.png b/doc/images/distributed-monitoring/icinga2_distributed_endpoints.png deleted file mode 100644 index 2ec94445a..000000000 Binary files a/doc/images/distributed-monitoring/icinga2_distributed_endpoints.png and /dev/null differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_monitoring_agent_checks_command_endpoint.png b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_agent_checks_command_endpoint.png new file mode 100644 index 000000000..d55278ee1 Binary files /dev/null and b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_agent_checks_command_endpoint.png differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_monitoring_endpoints.png b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_endpoints.png new file mode 100644 index 000000000..aa37f6016 Binary files /dev/null and b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_endpoints.png differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_monitoring_roles.png b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_roles.png new file mode 100644 index 000000000..d9018f825 Binary files /dev/null and b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_roles.png differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_monitoring_satellite_config_sync.png b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_satellite_config_sync.png new file mode 100644 index 000000000..92dcda953 Binary files /dev/null and b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_satellite_config_sync.png differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_monitoring_scenario_ha_masters_with_agents.png b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_scenario_ha_masters_with_agents.png new file mode 100644 index 000000000..c45df2cb4 Binary files /dev/null and b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_scenario_ha_masters_with_agents.png differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_monitoring_scenarios_master_satellites_agents.png b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_scenarios_master_satellites_agents.png new file mode 100644 index 000000000..8535993a2 Binary files /dev/null and b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_scenarios_master_satellites_agents.png differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_monitoring_scenarios_master_with_agents.png b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_scenarios_master_with_agents.png new file mode 100644 index 000000000..fe7ac4dcd Binary files /dev/null and b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_scenarios_master_with_agents.png differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_monitoring_zones.png b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_zones.png new file mode 100644 index 000000000..84b42f804 Binary files /dev/null and b/doc/images/distributed-monitoring/icinga2_distributed_monitoring_zones.png differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_roles.png b/doc/images/distributed-monitoring/icinga2_distributed_roles.png deleted file mode 100644 index e581aa5c7..000000000 Binary files a/doc/images/distributed-monitoring/icinga2_distributed_roles.png and /dev/null differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_scenarios_ha_master_clients.png b/doc/images/distributed-monitoring/icinga2_distributed_scenarios_ha_master_clients.png deleted file mode 100644 index a7d0f76dd..000000000 Binary files a/doc/images/distributed-monitoring/icinga2_distributed_scenarios_ha_master_clients.png and /dev/null differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_scenarios_master_clients.png b/doc/images/distributed-monitoring/icinga2_distributed_scenarios_master_clients.png deleted file mode 100644 index 475dd0e7c..000000000 Binary files a/doc/images/distributed-monitoring/icinga2_distributed_scenarios_master_clients.png and /dev/null differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_scenarios_master_satellite_client.png b/doc/images/distributed-monitoring/icinga2_distributed_scenarios_master_satellite_client.png deleted file mode 100644 index 400f83bd7..000000000 Binary files a/doc/images/distributed-monitoring/icinga2_distributed_scenarios_master_satellite_client.png and /dev/null differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_top_down_command_endpoint.png b/doc/images/distributed-monitoring/icinga2_distributed_top_down_command_endpoint.png deleted file mode 100644 index f34d3a424..000000000 Binary files a/doc/images/distributed-monitoring/icinga2_distributed_top_down_command_endpoint.png and /dev/null differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_top_down_config_sync.png b/doc/images/distributed-monitoring/icinga2_distributed_top_down_config_sync.png deleted file mode 100644 index 6c3c0e4d1..000000000 Binary files a/doc/images/distributed-monitoring/icinga2_distributed_top_down_config_sync.png and /dev/null differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_windows_client_disk_icingaweb2.png b/doc/images/distributed-monitoring/icinga2_distributed_windows_client_disk_icingaweb2.png index 8878b99b6..de13ad780 100644 Binary files a/doc/images/distributed-monitoring/icinga2_distributed_windows_client_disk_icingaweb2.png and b/doc/images/distributed-monitoring/icinga2_distributed_windows_client_disk_icingaweb2.png differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_windows_nscp.png b/doc/images/distributed-monitoring/icinga2_distributed_windows_nscp.png deleted file mode 100644 index d48ea5932..000000000 Binary files a/doc/images/distributed-monitoring/icinga2_distributed_windows_nscp.png and /dev/null differ diff --git a/doc/images/distributed-monitoring/icinga2_distributed_zones.png b/doc/images/distributed-monitoring/icinga2_distributed_zones.png deleted file mode 100644 index e5e351639..000000000 Binary files a/doc/images/distributed-monitoring/icinga2_distributed_zones.png and /dev/null differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_cmd_admin_net_start_stop.png b/doc/images/distributed-monitoring/icinga2_windows_cmd_admin_net_start_stop.png deleted file mode 100644 index 506d5987d..000000000 Binary files a/doc/images/distributed-monitoring/icinga2_windows_cmd_admin_net_start_stop.png and /dev/null differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_installer_01.png b/doc/images/distributed-monitoring/icinga2_windows_setup_installer_01.png index 358548b0e..8460dc66d 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_installer_01.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_installer_01.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_installer_02.png b/doc/images/distributed-monitoring/icinga2_windows_setup_installer_02.png index d23b7d68c..476b6d2ea 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_installer_02.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_installer_02.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_installer_03.png b/doc/images/distributed-monitoring/icinga2_windows_setup_installer_03.png index 3e44e50af..35aad8348 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_installer_03.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_installer_03.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_installer_04.png b/doc/images/distributed-monitoring/icinga2_windows_setup_installer_04.png index 2b6b1ac6d..4d314e643 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_installer_04.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_installer_04.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_installer_05.png b/doc/images/distributed-monitoring/icinga2_windows_setup_installer_05.png index a6f974275..7b2c3d889 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_installer_05.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_installer_05.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_01.png b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_01.png index dbc9d9448..c74857ab6 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_01.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_01.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_02.png b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_02.png index b065056ca..7084985b0 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_02.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_02.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_02_global_zone.png b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_02_global_zone.png index 1f589d967..c0bc4e04c 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_02_global_zone.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_02_global_zone.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_03.png b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_03.png index 33cf736b9..0d246f4c3 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_03.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_03.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_04.png b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_04.png index 3d4c5816a..9f1a5d033 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_04.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_04.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_05_nsclient_01.png b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_05_nsclient_01.png index 628aaff99..8f9df0dc6 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_05_nsclient_01.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_05_nsclient_01.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_06_finish_no_ticket.png b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_06_finish_no_ticket.png index b7cee9013..1c91f2d50 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_06_finish_no_ticket.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_06_finish_no_ticket.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_06_finish_with_ticket.png b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_06_finish_with_ticket.png index 8d1b3c42d..3d602373e 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_06_finish_with_ticket.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_06_finish_with_ticket.png differ diff --git a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_examine_config.png b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_examine_config.png index 32f22cf21..f56877638 100644 Binary files a/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_examine_config.png and b/doc/images/distributed-monitoring/icinga2_windows_setup_wizard_examine_config.png differ diff --git a/doc/images/getting-started/mariadb-centos7.png b/doc/images/installation/mariadb-centos7.png similarity index 100% rename from doc/images/getting-started/mariadb-centos7.png rename to doc/images/installation/mariadb-centos7.png diff --git a/doc/images/getting-started/nano-syntax.png b/doc/images/installation/nano-syntax.png similarity index 100% rename from doc/images/getting-started/nano-syntax.png rename to doc/images/installation/nano-syntax.png diff --git a/doc/images/getting-started/postgr-import-ido.png b/doc/images/installation/postgr-import-ido.png similarity index 100% rename from doc/images/getting-started/postgr-import-ido.png rename to doc/images/installation/postgr-import-ido.png diff --git a/doc/images/getting-started/vim-syntax.png b/doc/images/installation/vim-syntax.png similarity index 100% rename from doc/images/getting-started/vim-syntax.png rename to doc/images/installation/vim-syntax.png diff --git a/doc/update-links.py b/doc/update-links.py index 24bfcde58..765d4a04f 100755 --- a/doc/update-links.py +++ b/doc/update-links.py @@ -1,20 +1,5 @@ #!/usr/bin/env python -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ import os import sys diff --git a/etc/CMakeLists.txt b/etc/CMakeLists.txt index bffca3016..f693bbcd4 100644 --- a/etc/CMakeLists.txt +++ b/etc/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ if(NOT WIN32) configure_file(icinga2/constants.conf.cmake ${CMAKE_CURRENT_BINARY_DIR}/icinga2/constants.conf @ONLY) @@ -24,55 +9,52 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") endif() if(NOT WIN32) - install_if_not_exists(${CMAKE_CURRENT_BINARY_DIR}/icinga2/constants.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2) - install_if_not_exists(icinga2/icinga2.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2) + install_if_not_exists(${CMAKE_CURRENT_BINARY_DIR}/icinga2/constants.conf ${ICINGA2_CONFIGDIR}) + install_if_not_exists(icinga2/icinga2.conf ${ICINGA2_CONFIGDIR}) else() - install_if_not_exists(${CMAKE_CURRENT_SOURCE_DIR}/icinga2/win32/constants.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2) - install_if_not_exists(icinga2/win32/icinga2.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2) + install_if_not_exists(${CMAKE_CURRENT_SOURCE_DIR}/icinga2/win32/constants.conf ${ICINGA2_CONFIGDIR}) + install_if_not_exists(icinga2/win32/icinga2.conf ${ICINGA2_CONFIGDIR}) endif() -install_if_not_exists(icinga2/zones.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2) -install_if_not_exists(icinga2/conf.d/app.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) -install_if_not_exists(icinga2/conf.d/commands.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) -install_if_not_exists(icinga2/conf.d/downtimes.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) -install_if_not_exists(icinga2/conf.d/groups.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) +install_if_not_exists(icinga2/zones.conf ${ICINGA2_CONFIGDIR}) +install_if_not_exists(icinga2/conf.d/app.conf ${ICINGA2_CONFIGDIR}/conf.d) +install_if_not_exists(icinga2/conf.d/commands.conf ${ICINGA2_CONFIGDIR}/conf.d) +install_if_not_exists(icinga2/conf.d/downtimes.conf ${ICINGA2_CONFIGDIR}/conf.d) +install_if_not_exists(icinga2/conf.d/groups.conf ${ICINGA2_CONFIGDIR}/conf.d) if(NOT WIN32) - install_if_not_exists(icinga2/conf.d/hosts.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) - install_if_not_exists(icinga2/conf.d/services.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) + install_if_not_exists(icinga2/conf.d/hosts.conf ${ICINGA2_CONFIGDIR}/conf.d) + install_if_not_exists(icinga2/conf.d/services.conf ${ICINGA2_CONFIGDIR}/conf.d) else() - install_if_not_exists(icinga2/conf.d/win32/hosts.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) - install_if_not_exists(icinga2/conf.d/win32/services.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) + install_if_not_exists(icinga2/conf.d/win32/hosts.conf ${ICINGA2_CONFIGDIR}/conf.d) + install_if_not_exists(icinga2/conf.d/win32/services.conf ${ICINGA2_CONFIGDIR}/conf.d) endif() -install_if_not_exists(icinga2/conf.d/notifications.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) -install_if_not_exists(icinga2/conf.d/templates.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) -install_if_not_exists(icinga2/conf.d/timeperiods.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) -install_if_not_exists(icinga2/conf.d/users.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) -install_if_not_exists(icinga2/features-available/api.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available) -install_if_not_exists(icinga2/features-available/debuglog.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available) -install_if_not_exists(icinga2/features-available/mainlog.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available) +install_if_not_exists(icinga2/conf.d/notifications.conf ${ICINGA2_CONFIGDIR}/conf.d) +install_if_not_exists(icinga2/conf.d/templates.conf ${ICINGA2_CONFIGDIR}/conf.d) +install_if_not_exists(icinga2/conf.d/timeperiods.conf ${ICINGA2_CONFIGDIR}/conf.d) +install_if_not_exists(icinga2/conf.d/users.conf ${ICINGA2_CONFIGDIR}/conf.d) +install_if_not_exists(icinga2/features-available/api.conf ${ICINGA2_CONFIGDIR}/features-available) +install_if_not_exists(icinga2/features-available/debuglog.conf ${ICINGA2_CONFIGDIR}/features-available) +install_if_not_exists(icinga2/features-available/mainlog.conf ${ICINGA2_CONFIGDIR}/features-available) if(NOT WIN32) - install_if_not_exists(icinga2/features-available/syslog.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available) + install_if_not_exists(icinga2/features-available/syslog.conf ${ICINGA2_CONFIGDIR}/features-available) endif() -install_if_not_exists(icinga2/scripts/mail-host-notification.sh ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/scripts) -install_if_not_exists(icinga2/scripts/mail-service-notification.sh ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/scripts) -install_if_not_exists(icinga2/zones.d/README ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/zones.d) +install_if_not_exists(icinga2/scripts/mail-host-notification.sh ${ICINGA2_CONFIGDIR}/scripts) +install_if_not_exists(icinga2/scripts/mail-service-notification.sh ${ICINGA2_CONFIGDIR}/scripts) +install_if_not_exists(icinga2/zones.d/README ${ICINGA2_CONFIGDIR}/zones.d) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - install_if_not_exists(${CMAKE_CURRENT_BINARY_DIR}/logrotate.d/icinga2 ${CMAKE_INSTALL_SYSCONFDIR}/logrotate.d) + install_if_not_exists(${CMAKE_CURRENT_BINARY_DIR}/logrotate.d/icinga2 ${LOGROTATE_DIR}) endif() if(NOT WIN32) - install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled\")") - install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/mainlog.conf \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled/mainlog.conf\")") + install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_CONFIGDIR}/features-enabled\")") + install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/mainlog.conf \"\$ENV{DESTDIR}${ICINGA2_FULL_CONFIGDIR}/features-enabled/mainlog.conf\")") - install( - FILES bash_completion.d/icinga2 - DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/bash_completion.d - ) + install(FILES bash_completion.d/icinga2 DESTINATION ${BASHCOMPLETION_DIR}) else() - install_if_not_exists(icinga2/features-enabled/mainlog.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-enabled) + install_if_not_exists(icinga2/features-enabled/mainlog.conf ${ICINGA2_CONFIGDIR}/features-enabled) endif() if(${CMAKE_SYSTEM_NAME} MATCHES "(Linux|Solaris|SunOS)") diff --git a/etc/icinga2/conf.d/commands.conf b/etc/icinga2/conf.d/commands.conf index 9c5153811..bd511cebc 100644 --- a/etc/icinga2/conf.d/commands.conf +++ b/etc/icinga2/conf.d/commands.conf @@ -7,7 +7,7 @@ */ object NotificationCommand "mail-host-notification" { - command = [ SysconfDir + "/icinga2/scripts/mail-host-notification.sh" ] + command = [ ConfigDir + "/scripts/mail-host-notification.sh" ] arguments += { "-4" = "$notification_address$" @@ -66,7 +66,7 @@ object NotificationCommand "mail-host-notification" { } object NotificationCommand "mail-service-notification" { - command = [ SysconfDir + "/icinga2/scripts/mail-service-notification.sh" ] + command = [ ConfigDir + "/scripts/mail-service-notification.sh" ] arguments += { "-4" = "$notification_address$" @@ -146,7 +146,7 @@ object NotificationCommand "mail-service-notification" { /* object NotificationCommand "mail-host-notification" { - command = [ SysconfDir + "/icinga2/scripts/mail-host-notification.sh" ] + command = [ ConfigDir + "/scripts/mail-host-notification.sh" ] env = { NOTIFICATIONTYPE = "$notification.type$" @@ -164,7 +164,7 @@ object NotificationCommand "mail-host-notification" { } object NotificationCommand "mail-service-notification" { - command = [ SysconfDir + "/icinga2/scripts/mail-service-notification.sh" ] + command = [ ConfigDir + "/scripts/mail-service-notification.sh" ] env = { NOTIFICATIONTYPE = "$notification.type$" diff --git a/etc/icinga2/conf.d/hosts.conf b/etc/icinga2/conf.d/hosts.conf index 5ff1f9b61..0118f033e 100644 --- a/etc/icinga2/conf.d/hosts.conf +++ b/etc/icinga2/conf.d/hosts.conf @@ -23,7 +23,7 @@ object Host NodeName { address = "127.0.0.1" address6 = "::1" - /* Set custom attribute `os` for hostgroup assignment in `groups.conf`. */ + /* Set custom variable `os` for hostgroup assignment in `groups.conf`. */ vars.os = "Linux" /* Define http vhost attributes for service apply rules in `services.conf`. */ diff --git a/etc/icinga2/conf.d/notifications.conf b/etc/icinga2/conf.d/notifications.conf index 6a7e1f5a4..ac6587546 100644 --- a/etc/icinga2/conf.d/notifications.conf +++ b/etc/icinga2/conf.d/notifications.conf @@ -2,7 +2,7 @@ * The example notification apply rules. * * Only applied if host/service objects have - * the custom attribute `notification` defined + * the custom variable `notification` defined * and containing `mail` as key. * * Check `hosts.conf` for an example. diff --git a/etc/icinga2/conf.d/services.conf b/etc/icinga2/conf.d/services.conf index 416915bc0..c8e1b3cc6 100644 --- a/etc/icinga2/conf.d/services.conf +++ b/etc/icinga2/conf.d/services.conf @@ -42,7 +42,7 @@ apply Service "ping6" { /* * Apply the `ssh` service to all hosts * with the `address` attribute defined and - * the custom attribute `os` set to `Linux`. + * the custom variable `os` set to `Linux`. */ apply Service "ssh" { import "generic-service" diff --git a/etc/icinga2/conf.d/win32/hosts.conf b/etc/icinga2/conf.d/win32/hosts.conf index 53d169a8a..ecee11a94 100644 --- a/etc/icinga2/conf.d/win32/hosts.conf +++ b/etc/icinga2/conf.d/win32/hosts.conf @@ -23,7 +23,7 @@ object Host NodeName { address = "127.0.0.1" address6 = "::1" - /* Set custom attribute `os` for hostgroup assignment in `groups.conf`. */ + /* Set custom variable `os` for hostgroup assignment in `groups.conf`. */ vars.os = "Windows" /* Define disks and attributes for service apply rules in `services.conf`. */ diff --git a/etc/icinga2/features-available/debuglog.conf b/etc/icinga2/features-available/debuglog.conf index 6e8e5813e..e66518fe1 100644 --- a/etc/icinga2/features-available/debuglog.conf +++ b/etc/icinga2/features-available/debuglog.conf @@ -6,6 +6,5 @@ object FileLogger "debug-file" { severity = "debug" - path = LocalStateDir + "/log/icinga2/debug.log" + path = LogDir + "/debug.log" } - diff --git a/etc/icinga2/features-available/mainlog.conf b/etc/icinga2/features-available/mainlog.conf index ded9a7c7d..a3bb19dab 100644 --- a/etc/icinga2/features-available/mainlog.conf +++ b/etc/icinga2/features-available/mainlog.conf @@ -4,6 +4,5 @@ object FileLogger "main-log" { severity = "information" - path = LocalStateDir + "/log/icinga2/icinga2.log" + path = LogDir + "/icinga2.log" } - diff --git a/etc/icinga2/scripts/mail-host-notification.sh b/etc/icinga2/scripts/mail-host-notification.sh index 6401ce5a0..2b0619c38 100755 --- a/etc/icinga2/scripts/mail-host-notification.sh +++ b/etc/icinga2/scripts/mail-host-notification.sh @@ -1,7 +1,6 @@ -#!/usr/bin/env bash -# -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# Except of function urlencode which is Copyright (C) by Brian White (brian@aljex.com) used under MIT license +#!/bin/sh +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ +# Except of function urlencode which is Copyright (C) by Brian White (brian@aljex.com) used under MIT license PROG="`basename $0`" ICINGA2HOST="`hostname`" @@ -51,11 +50,14 @@ Error() { } urlencode() { - local LANG=C i c e='' - for ((i=0;i<${#1};i++)); do - c=${1:$i:1} - [[ "$c" =~ [a-zA-Z0-9\.\~\_\-] ]] || printf -v c '%%%02X' "'$c" - e+="$c" + local LANG=C i=0 c e s="$1" + + while [ $i -lt ${#1} ]; do + [ "$i" -eq 0 ] || s="${s#?}" + c=${s%"${s#?}"} + [ -z "${c#[[:alnum:].~_-]}" ] || c=$(printf '%%%02X' "'$c") + e="${e}${c}" + i=$((i + 1)) done echo "$e" } @@ -156,13 +158,15 @@ if [ -n "$MAILFROM" ] ; then ## Debian/Ubuntu use mailutils which requires `-a` to append the header if [ -f /etc/debian_version ]; then - /usr/bin/printf "%b" "$NOTIFICATION_MESSAGE" | $MAILBIN -a "From: $MAILFROM" -s "$ENCODED_SUBJECT" $USEREMAIL + /usr/bin/printf "%b" "$NOTIFICATION_MESSAGE" | tr -d '\015' + | $MAILBIN -a "From: $MAILFROM" -s "$ENCODED_SUBJECT" $USEREMAIL ## Other distributions (RHEL/SUSE/etc.) prefer mailx which sets a sender address with `-r` else - /usr/bin/printf "%b" "$NOTIFICATION_MESSAGE" | $MAILBIN -r "$MAILFROM" -s "$ENCODED_SUBJECT" $USEREMAIL + /usr/bin/printf "%b" "$NOTIFICATION_MESSAGE" | tr -d '\015' \ + | $MAILBIN -r "$MAILFROM" -s "$ENCODED_SUBJECT" $USEREMAIL fi else - /usr/bin/printf "%b" "$NOTIFICATION_MESSAGE" \ + /usr/bin/printf "%b" "$NOTIFICATION_MESSAGE" | tr -d '\015' \ | $MAILBIN -s "$ENCODED_SUBJECT" $USEREMAIL fi diff --git a/etc/icinga2/scripts/mail-service-notification.sh b/etc/icinga2/scripts/mail-service-notification.sh index a0c34a129..644d9d39f 100755 --- a/etc/icinga2/scripts/mail-service-notification.sh +++ b/etc/icinga2/scripts/mail-service-notification.sh @@ -1,6 +1,5 @@ -#!/usr/bin/env bash -# -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) +#!/bin/sh +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ # Except of function urlencode which is Copyright (C) by Brian White (brian@aljex.com) used under MIT license PROG="`basename $0`" @@ -53,11 +52,14 @@ Error() { } urlencode() { - local LANG=C i c e='' - for ((i=0;i<${#1};i++)); do - c=${1:$i:1} - [[ "$c" =~ [a-zA-Z0-9\.\~\_\-] ]] || printf -v c '%%%02X' "'$c" - e+="$c" + local LANG=C i=0 c e s="$1" + + while [ $i -lt ${#1} ]; do + [ "$i" -eq 0 ] || s="${s#?}" + c=${s%"${s#?}"} + [ -z "${c#[[:alnum:].~_-]}" ] || c=$(printf '%%%02X' "'$c") + e="${e}${c}" + i=$((i + 1)) done echo "$e" } @@ -161,13 +163,15 @@ if [ -n "$MAILFROM" ] ; then ## Debian/Ubuntu use mailutils which requires `-a` to append the header if [ -f /etc/debian_version ]; then - /usr/bin/printf "%b" "$NOTIFICATION_MESSAGE" | $MAILBIN -a "From: $MAILFROM" -s "$ENCODED_SUBJECT" $USEREMAIL + /usr/bin/printf "%b" "$NOTIFICATION_MESSAGE" | tr -d '\015' \ + | $MAILBIN -a "From: $MAILFROM" -s "$ENCODED_SUBJECT" $USEREMAIL ## Other distributions (RHEL/SUSE/etc.) prefer mailx which sets a sender address with `-r` else - /usr/bin/printf "%b" "$NOTIFICATION_MESSAGE" | $MAILBIN -r "$MAILFROM" -s "$ENCODED_SUBJECT" $USEREMAIL + /usr/bin/printf "%b" "$NOTIFICATION_MESSAGE" | tr -d '\015' \ + | $MAILBIN -r "$MAILFROM" -s "$ENCODED_SUBJECT" $USEREMAIL fi else - /usr/bin/printf "%b" "$NOTIFICATION_MESSAGE" \ + /usr/bin/printf "%b" "$NOTIFICATION_MESSAGE" | tr -d '\015' \ | $MAILBIN -s "$ENCODED_SUBJECT" $USEREMAIL fi diff --git a/etc/initsystem/CMakeLists.txt b/etc/initsystem/CMakeLists.txt index dd57ab1a0..eb0f9f2da 100644 --- a/etc/initsystem/CMakeLists.txt +++ b/etc/initsystem/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ if(NOT WIN32) diff --git a/etc/initsystem/icinga2.init.d.cmake b/etc/initsystem/icinga2.init.d.cmake index 03c88a632..49a6ee290 100644 --- a/etc/initsystem/icinga2.init.d.cmake +++ b/etc/initsystem/icinga2.init.d.cmake @@ -15,6 +15,13 @@ # Description: Icinga 2 is a monitoring and management system for hosts, services and networks. ### END INIT INFO +# Get function from functions library +if [ -f /etc/rc.d/init.d/functions ]; then + . /etc/rc.d/init.d/functions +elif [ -f /etc/init.d/functions ]; then + . /etc/init.d/functions +fi + # load system specific defines SYSCONFIGFILE=@ICINGA2_SYSCONFIGFILE@ if [ -f $SYSCONFIGFILE ]; then @@ -29,10 +36,10 @@ fi : ${ICINGA2_GROUP:="@ICINGA2_GROUP@"} : ${ICINGA2_COMMAND_GROUP:="@ICINGA2_COMMAND_GROUP@"} : ${DAEMON:="@CMAKE_INSTALL_FULL_SBINDIR@/icinga2"} -: ${ICINGA2_CONFIG_FILE:="@CMAKE_INSTALL_FULL_SYSCONFDIR@/icinga2/icinga2.conf"} -: ${ICINGA2_ERROR_LOG:=@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/log/icinga2/error.log} -: ${ICINGA2_STARTUP_LOG:=@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/log/icinga2/startup.log} -: ${ICINGA2_PID_FILE:="@ICINGA2_RUNDIR@/icinga2/icinga2.pid"} +: ${ICINGA2_CONFIG_FILE:="@ICINGA2_CONFIGDIR@/icinga2.conf"} +: ${ICINGA2_ERROR_LOG:=@ICINGA2_LOGDIR@/error.log} +: ${ICINGA2_STARTUP_LOG:=@ICINGA2_LOGDIR@/startup.log} +: ${ICINGA2_PID_FILE:="@ICINGA2_INITRUNDIR@/icinga2.pid"} # Load extra environment variables if [ -f /etc/default/icinga2 ]; then @@ -50,17 +57,10 @@ getent passwd $ICINGA2_USER >/dev/null 2>&1 || (echo "Icinga user '$ICINGA2_USER getent group $ICINGA2_GROUP >/dev/null 2>&1 || (echo "Icinga group '$ICINGA2_GROUP' does not exist. Exiting." && exit 6) getent group $ICINGA2_COMMAND_GROUP >/dev/null 2>&1 || (echo "Icinga command group '$ICINGA2_COMMAND_GROUP' does not exist. Exiting." && exit 6) -# Get function from functions library -if [ -f /etc/rc.d/init.d/functions ]; then - . /etc/rc.d/init.d/functions -elif [ -f /etc/init.d/functions ]; then - . /etc/init.d/functions -fi - # Start Icinga 2 start() { printf "Starting Icinga 2: " - @CMAKE_INSTALL_PREFIX@/lib/icinga2/prepare-dirs + @CMAKE_INSTALL_PREFIX@/lib/icinga2/prepare-dirs "$SYSCONFIGFILE" if ! $DAEMON daemon -c $ICINGA2_CONFIG_FILE -d -e $ICINGA2_ERROR_LOG > $ICINGA2_STARTUP_LOG 2>&1; then echo "Error starting Icinga. Check '$ICINGA2_STARTUP_LOG' for details." @@ -90,7 +90,7 @@ stop() { if ! icinga2 internal signal -s SIGCHLD -p $pid >/dev/null 2>&1; then break fi - + printf '.' sleep 3 done @@ -105,7 +105,7 @@ stop() { # Reload Icinga 2 reload() { - exec @CMAKE_INSTALL_PREFIX@/lib/icinga2/safe-reload $SYSCONFIGFILE + exec @CMAKE_INSTALL_PREFIX@/lib/icinga2/safe-reload "$SYSCONFIGFILE" } # Check the Icinga 2 configuration @@ -138,7 +138,7 @@ status() { if [ ! -e $ICINGA2_PID_FILE ]; then echo "Not running" - exit 7 + exit 3 fi pid=`cat $ICINGA2_PID_FILE` @@ -146,7 +146,7 @@ status() { echo "Running" else echo "Not running" - exit 7 + exit 3 fi } @@ -168,7 +168,8 @@ case "$1" in start ;; condrestart) - status > /dev/null 2>&1 || exit 0 + STATUS=$(status > /dev/null 2>&1) + if [ $? != 0 ]; then exit 0; fi checkconfig restart fail stop nofail start diff --git a/etc/initsystem/icinga2.service.cmake b/etc/initsystem/icinga2.service.cmake index 27744e5aa..9b32db3e7 100644 --- a/etc/initsystem/icinga2.service.cmake +++ b/etc/initsystem/icinga2.service.cmake @@ -1,15 +1,18 @@ [Unit] Description=Icinga host/service/network monitoring system +Requires=network-online.target After=syslog.target network-online.target postgresql.service mariadb.service carbon-cache.service carbon-relay.service [Service] Type=notify +Environment="ICINGA2_ERROR_LOG=@ICINGA2_LOGDIR@/error.log" EnvironmentFile=@ICINGA2_SYSCONFIGFILE@ -ExecStartPre=@CMAKE_INSTALL_PREFIX@/lib/icinga2/prepare-dirs -ExecStart=@CMAKE_INSTALL_FULL_SBINDIR@/icinga2 daemon -e ${ICINGA2_ERROR_LOG} -PIDFile=@ICINGA2_RUNDIR@/icinga2/icinga2.pid -ExecReload=@CMAKE_INSTALL_PREFIX@/lib/icinga2/safe-reload +ExecStartPre=@CMAKE_INSTALL_PREFIX@/lib/icinga2/prepare-dirs @ICINGA2_SYSCONFIGFILE@ +ExecStart=@CMAKE_INSTALL_FULL_SBINDIR@/icinga2 daemon --close-stdio -e ${ICINGA2_ERROR_LOG} +PIDFile=@ICINGA2_INITRUNDIR@/icinga2.pid +ExecReload=@CMAKE_INSTALL_PREFIX@/lib/icinga2/safe-reload @ICINGA2_SYSCONFIGFILE@ TimeoutStartSec=30m +KillMode=mixed # Systemd >228 enforces a lower process number for services. # Depending on the distribution and Systemd version, this must diff --git a/etc/initsystem/icinga2.sysconfig.cmake b/etc/initsystem/icinga2.sysconfig.cmake index d5d7497f1..167c12578 100644 --- a/etc/initsystem/icinga2.sysconfig.cmake +++ b/etc/initsystem/icinga2.sysconfig.cmake @@ -2,15 +2,14 @@ #Make your changes here. #DAEMON=@CMAKE_INSTALL_FULL_SBINDIR@/icinga2 -#ICINGA2_CONFIG_FILE=@CMAKE_INSTALL_FULL_SYSCONFDIR@/icinga2/icinga2.conf -#ICINGA2_RUN_DIR=@ICINGA2_RUNDIR@ -#ICINGA2_STATE_DIR=@CMAKE_INSTALL_FULL_LOCALSTATEDIR@ -#ICINGA2_PID_FILE=@ICINGA2_RUNDIR@/icinga2/icinga2.pid -#ICINGA2_LOG_DIR=@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/log/icinga2 -#ICINGA2_ERROR_LOG=@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/log/icinga2/error.log -#ICINGA2_STARTUP_LOG=@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/log/icinga2/startup.log -#ICINGA2_LOG=@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/log/icinga2/icinga2.log -#ICINGA2_CACHE_DIR=@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/cache/icinga2 +#ICINGA2_CONFIG_FILE=@ICINGA2_CONFIGDIR@/icinga2.conf +#ICINGA2_INIT_RUN_DIR=@ICINGA2_INITRUNDIR@ +#ICINGA2_PID_FILE=@ICINGA2_INITRUNDIR@/icinga2.pid +#ICINGA2_LOG_DIR=@ICINGA2_LOGDIR@ +#ICINGA2_ERROR_LOG=@ICINGA2_LOGDIR@/error.log +#ICINGA2_STARTUP_LOG=@ICINGA2_LOGDIR@/startup.log +#ICINGA2_LOG=@ICINGA2_LOGDIR@/icinga2.log +#ICINGA2_CACHE_DIR=@ICINGA2_CACHEDIR@ #ICINGA2_USER=@ICINGA2_USER@ #ICINGA2_GROUP=@ICINGA2_GROUP@ #ICINGA2_COMMAND_GROUP=@ICINGA2_COMMAND_GROUP@ diff --git a/etc/initsystem/prepare-dirs.cmake b/etc/initsystem/prepare-dirs.cmake index 2a6c4a262..99dc60223 100644 --- a/etc/initsystem/prepare-dirs.cmake +++ b/etc/initsystem/prepare-dirs.cmake @@ -3,33 +3,41 @@ # This script prepares directories and files needed for running Icinga2 # +# Load sysconf on systems where the initsystem does not pass the environment +if [ "$1" != "" ]; then + if [ -r "$1" ]; then + . "$1" + else + echo "Unable to read sysconf from '$1'. Exiting." && exit 6 + fi +fi + # Set defaults, to overwrite see "@ICINGA2_SYSCONFIGFILE@" : ${ICINGA2_USER:="@ICINGA2_USER@"} : ${ICINGA2_GROUP:="@ICINGA2_GROUP@"} : ${ICINGA2_COMMAND_GROUP:="@ICINGA2_COMMAND_GROUP@"} -: ${ICINGA2_RUN_DIR:="@ICINGA2_RUNDIR@"} -: ${ICINGA2_LOG_DIR:="@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/log/icinga2"} -: ${ICINGA2_STATE_DIR:="@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/cache/icinga2"} -: ${ICINGA2_CACHE_DIR:="@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/cache/icinga2"} +: ${ICINGA2_INIT_RUN_DIR:="@ICINGA2_FULL_INITRUNDIR@"} +: ${ICINGA2_LOG_DIR:="@ICINGA2_FULL_LOGDIR@"} +: ${ICINGA2_CACHE_DIR:="@ICINGA2_FULL_CACHEDIR@"} getent passwd $ICINGA2_USER >/dev/null 2>&1 || (echo "Icinga user '$ICINGA2_USER' does not exist. Exiting." && exit 6) getent group $ICINGA2_GROUP >/dev/null 2>&1 || (echo "Icinga group '$ICINGA2_GROUP' does not exist. Exiting." && exit 6) getent group $ICINGA2_COMMAND_GROUP >/dev/null 2>&1 || (echo "Icinga command group '$ICINGA2_COMMAND_GROUP' does not exist. Exiting." && exit 6) -if [ ! -e "$ICINGA2_RUN_DIR"/icinga2 ]; then - mkdir "$ICINGA2_RUN_DIR"/icinga2 - mkdir "$ICINGA2_RUN_DIR"/icinga2/cmd +if [ ! -e "$ICINGA2_INIT_RUN_DIR" ]; then + mkdir "$ICINGA2_INIT_RUN_DIR" + mkdir "$ICINGA2_INIT_RUN_DIR"/cmd fi -chmod 755 "$ICINGA2_RUN_DIR"/icinga2 -chmod 2750 "$ICINGA2_RUN_DIR"/icinga2/cmd -chown -R $ICINGA2_USER:$ICINGA2_COMMAND_GROUP "$ICINGA2_RUN_DIR"/icinga2 +chmod 755 "$ICINGA2_INIT_RUN_DIR" +chmod 2750 "$ICINGA2_INIT_RUN_DIR"/cmd +chown -R $ICINGA2_USER:$ICINGA2_COMMAND_GROUP "$ICINGA2_INIT_RUN_DIR" test -e "$ICINGA2_LOG_DIR" || install -m 750 -o $ICINGA2_USER -g $ICINGA2_COMMAND_GROUP -d "$ICINGA2_LOG_DIR" if type restorecon >/dev/null 2>&1; then - restorecon -R "$ICINGA2_RUN_DIR"/icinga2/ + restorecon -R "$ICINGA2_INIT_RUN_DIR"/ fi test -e "$ICINGA2_CACHE_DIR" || install -m 750 -o $ICINGA2_USER -g $ICINGA2_COMMAND_GROUP -d "$ICINGA2_CACHE_DIR" diff --git a/etc/initsystem/safe-reload.cmake b/etc/initsystem/safe-reload.cmake index 57e10b1ac..a042fc66f 100644 --- a/etc/initsystem/safe-reload.cmake +++ b/etc/initsystem/safe-reload.cmake @@ -1,6 +1,17 @@ #!/bin/sh -: ${ICINGA2_PID_FILE:="@ICINGA2_RUNDIR@/icinga2/icinga2.pid"} +# Load sysconf on systems where the initsystem does not pass the environment +if [ "$1" != "" ]; then + if [ -r "$1" ]; then + . "$1" + else + echo "Unable to read sysconf from '$1'. Exiting." && exit 6 + fi +fi + +# Set defaults, to overwrite see "@ICINGA2_SYSCONFIGFILE@" + +: ${ICINGA2_PID_FILE:="@ICINGA2_FULL_INITRUNDIR@/icinga2.pid"} : ${DAEMON:="@CMAKE_INSTALL_FULL_SBINDIR@/icinga2"} printf "Validating config files: " diff --git a/etc/logrotate.d/icinga2.cmake b/etc/logrotate.d/icinga2.cmake index 91d8d23d3..f0a9e59ae 100644 --- a/etc/logrotate.d/icinga2.cmake +++ b/etc/logrotate.d/icinga2.cmake @@ -1,24 +1,21 @@ -@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/log/icinga2/icinga2.log @CMAKE_INSTALL_FULL_LOCALSTATEDIR@/log/icinga2/debug.log { +@ICINGA2_LOGDIR@/icinga2.log @ICINGA2_LOGDIR@/debug.log { daily rotate 7@LOGROTATE_USE_SU@ compress delaycompress missingok - notifempty - create 644 @ICINGA2_USER@ @ICINGA2_GROUP@ + notifempty@LOGROTATE_CREATE@ postrotate - /bin/kill -USR1 $(cat @ICINGA2_RUNDIR@/icinga2/icinga2.pid 2> /dev/null) 2> /dev/null || true + /bin/kill -USR1 $(cat @ICINGA2_INITRUNDIR@/icinga2.pid 2> /dev/null) 2> /dev/null || true endscript } -@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/log/icinga2/error.log { +@ICINGA2_LOGDIR@/error.log { daily rotate 90@LOGROTATE_USE_SU@ compress delaycompress missingok - notifempty - create 644 @ICINGA2_USER@ @ICINGA2_GROUP@ + notifempty@LOGROTATE_CREATE@ # TODO: figure out how to get Icinga to re-open this log file } - diff --git a/icinga-app/CMakeLists.txt b/icinga-app/CMakeLists.txt index 0d1293279..ee3443b28 100644 --- a/icinga-app/CMakeLists.txt +++ b/icinga-app/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ if(MSVC) set(WindowsSources icinga.rc) @@ -83,7 +68,6 @@ target_link_libraries(icinga-app ${base_DEPS}) set_target_properties ( icinga-app PROPERTIES - INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2 FOLDER Bin OUTPUT_NAME icinga2 ) @@ -107,6 +91,6 @@ install( RUNTIME DESTINATION ${InstallPath} ) -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/log/icinga2\")") -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2\")") -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_RUNDIR}/icinga2\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_LOGDIR}\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_DATADIR}\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_INITRUNDIR}\")") diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index 0e462307c..5d3e3165e 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -1,27 +1,12 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/clicommand.hpp" #include "config/configcompilercontext.hpp" #include "config/configcompiler.hpp" #include "config/configitembuilder.hpp" +#include "config/expression.hpp" #include "base/application.hpp" +#include "base/configuration.hpp" #include "base/logger.hpp" #include "base/timer.hpp" #include "base/utility.hpp" @@ -94,6 +79,70 @@ static std::vector GlobalArgumentCompletion(const String& argument, cons return std::vector(); } +static void HandleLegacyDefines() +{ +#ifdef _WIN32 + String dataPrefix = Utility::GetIcingaDataPath(); +#endif /* _WIN32 */ + + Value localStateDir = Configuration::LocalStateDir; + + if (!localStateDir.IsEmpty()) { + Log(LogWarning, "icinga-app") + << "Please do not set the deprecated 'LocalStateDir' constant," + << " use the 'DataDir', 'LogDir', 'CacheDir' and 'SpoolDir' constants instead!" + << " For compatibility reasons, these are now set based on the 'LocalStateDir' constant."; + +#ifdef _WIN32 + Configuration::DataDir = localStateDir + "\\lib\\icinga2"; + Configuration::LogDir = localStateDir + "\\log\\icinga2"; + Configuration::CacheDir = localStateDir + "\\cache\\icinga2"; + Configuration::SpoolDir = localStateDir + "\\spool\\icinga2"; + } else { + Configuration::LocalStateDir = dataPrefix + "\\var"; +#else /* _WIN32 */ + Configuration::DataDir = localStateDir + "/lib/icinga2"; + Configuration::LogDir = localStateDir + "/log/icinga2"; + Configuration::CacheDir = localStateDir + "/cache/icinga2"; + Configuration::SpoolDir = localStateDir + "/spool/icinga2"; + } else { + Configuration::LocalStateDir = ICINGA_LOCALSTATEDIR; +#endif /* _WIN32 */ + } + + Value sysconfDir = Configuration::SysconfDir; + if (!sysconfDir.IsEmpty()) { + Log(LogWarning, "icinga-app") + << "Please do not set the deprecated 'Sysconfdir' constant, use the 'ConfigDir' constant instead! For compatibility reasons, their value is set based on the 'SysconfDir' constant."; + +#ifdef _WIN32 + Configuration::ConfigDir = sysconfDir + "\\icinga2"; + } else { + Configuration::SysconfDir = dataPrefix + "\\etc"; +#else /* _WIN32 */ + Configuration::ConfigDir = sysconfDir + "/icinga2"; + } else { + Configuration::SysconfDir = ICINGA_SYSCONFDIR; +#endif /* _WIN32 */ + } + + Value runDir = Configuration::RunDir; + if (!runDir.IsEmpty()) { + Log(LogWarning, "icinga-app") + << "Please do not set the deprecated 'RunDir' constant, use the 'InitRunDir' constant instead! For compatibility reasons, their value is set based on the 'RunDir' constant."; + +#ifdef _WIN32 + Configuration::InitRunDir = runDir + "\\icinga2"; + } else { + Configuration::RunDir = dataPrefix + "\\var\\run"; +#else /* _WIN32 */ + Configuration::InitRunDir = runDir + "/icinga2"; + } else { + Configuration::RunDir = ICINGA_RUNDIR; +#endif /* _WIN32 */ + } +} + static int Main() { int argc = Application::GetArgC(); @@ -128,39 +177,52 @@ static int Main() #ifdef _WIN32 bool builtinPaths = true; + /* Programm install location, C:/Program Files/Icinga2 */ String binaryPrefix = Utility::GetIcingaInstallPath(); + /* Returns the datapath for daemons, %PROGRAMDATA%/icinga2 */ String dataPrefix = Utility::GetIcingaDataPath(); if (!binaryPrefix.IsEmpty() && !dataPrefix.IsEmpty()) { - Application::DeclarePrefixDir(binaryPrefix); - Application::DeclareSysconfDir(dataPrefix + "\\etc"); - Application::DeclareRunDir(dataPrefix + "\\var\\run"); - Application::DeclareLocalStateDir(dataPrefix + "\\var"); - Application::DeclarePkgDataDir(binaryPrefix + "\\share\\icinga2"); - Application::DeclareIncludeConfDir(binaryPrefix + "\\share\\icinga2\\include"); + Configuration::ProgramData = dataPrefix; + + Configuration::ConfigDir = dataPrefix + "\\etc\\icinga2"; + + Configuration::DataDir = dataPrefix + "\\var\\lib\\icinga2"; + Configuration::LogDir = dataPrefix + "\\var\\log\\icinga2"; + Configuration::CacheDir = dataPrefix + "\\var\\cache\\icinga2"; + Configuration::SpoolDir = dataPrefix + "\\var\\spool\\icinga2"; + + Configuration::PrefixDir = binaryPrefix; + + /* Internal constants. */ + Configuration::PkgDataDir = binaryPrefix + "\\share\\icinga2"; + Configuration::IncludeConfDir = binaryPrefix + "\\share\\icinga2\\include"; + + Configuration::InitRunDir = dataPrefix + "\\var\\run\\icinga2"; } else { Log(LogWarning, "icinga-app", "Registry key could not be read. Falling back to built-in paths."); #endif /* _WIN32 */ - Application::DeclarePrefixDir(ICINGA_PREFIX); - Application::DeclareSysconfigFile(ICINGA_SYSCONFIGFILE); - Application::DeclareSysconfDir(ICINGA_SYSCONFDIR); - Application::DeclareRunDir(ICINGA_RUNDIR); - Application::DeclareLocalStateDir(ICINGA_LOCALSTATEDIR); - Application::DeclarePkgDataDir(ICINGA_PKGDATADIR); - Application::DeclareIncludeConfDir(ICINGA_INCLUDECONFDIR); + Configuration::ConfigDir = ICINGA_CONFIGDIR; + + Configuration::DataDir = ICINGA_DATADIR; + Configuration::LogDir = ICINGA_LOGDIR; + Configuration::CacheDir = ICINGA_CACHEDIR; + Configuration::SpoolDir = ICINGA_SPOOLDIR; + + Configuration::PrefixDir = ICINGA_PREFIX; + + /* Internal constants. */ + Configuration::PkgDataDir = ICINGA_PKGDATADIR; + Configuration::IncludeConfDir = ICINGA_INCLUDECONFDIR; + + Configuration::InitRunDir = ICINGA_INITRUNDIR; + #ifdef _WIN32 } #endif /* _WIN32 */ - Application::DeclareZonesDir(Application::GetSysconfDir() + "/icinga2/zones.d"); - -#ifndef _WIN32 - if (!autocomplete && !Utility::PathExists(Application::GetSysconfigFile())) { - Log(LogWarning, "icinga-app") - << "Sysconfig file '" << Application::GetSysconfigFile() << "' cannot be read. Using default values."; - } -#endif /* _WIN32 */ + Configuration::ZonesDir = Configuration::ConfigDir + "/zones.d"; String icingaUser = Utility::GetFromEnvironment("ICINGA2_USER"); if (icingaUser.IsEmpty()) @@ -170,17 +232,17 @@ static int Main() if (icingaGroup.IsEmpty()) icingaGroup = ICINGA_GROUP; - Application::DeclareRunAsUser(icingaUser); - Application::DeclareRunAsGroup(icingaGroup); + Configuration::RunAsUser = icingaUser; + Configuration::RunAsGroup = icingaGroup; if (!autocomplete) { #ifdef RLIMIT_NOFILE String rLimitFiles = Utility::GetFromEnvironment("ICINGA2_RLIMIT_FILES"); if (rLimitFiles.IsEmpty()) - Application::DeclareRLimitFiles(Application::GetDefaultRLimitFiles()); + Configuration::RLimitFiles = Application::GetDefaultRLimitFiles(); else { try { - Application::DeclareRLimitFiles(Convert::ToLong(rLimitFiles)); + Configuration::RLimitFiles = Convert::ToLong(rLimitFiles); } catch (const std::invalid_argument& ex) { std::cout << "Error setting \"ICINGA2_RLIMIT_FILES\": " << ex.what() << '\n'; @@ -192,10 +254,10 @@ static int Main() #ifdef RLIMIT_NPROC String rLimitProcesses = Utility::GetFromEnvironment("ICINGA2_RLIMIT_PROCESSES"); if (rLimitProcesses.IsEmpty()) - Application::DeclareRLimitProcesses(Application::GetDefaultRLimitProcesses()); + Configuration::RLimitProcesses = Application::GetDefaultRLimitProcesses(); else { try { - Application::DeclareRLimitProcesses(Convert::ToLong(rLimitProcesses)); + Configuration::RLimitProcesses = Convert::ToLong(rLimitProcesses); } catch (const std::invalid_argument& ex) { std::cout << "Error setting \"ICINGA2_RLIMIT_PROCESSES\": " << ex.what() << '\n'; @@ -207,10 +269,10 @@ static int Main() #ifdef RLIMIT_STACK String rLimitStack = Utility::GetFromEnvironment("ICINGA2_RLIMIT_STACK"); if (rLimitStack.IsEmpty()) - Application::DeclareRLimitStack(Application::GetDefaultRLimitStack()); + Configuration::RLimitStack = Application::GetDefaultRLimitStack(); else { try { - Application::DeclareRLimitStack(Convert::ToLong(rLimitStack)); + Configuration::RLimitStack = Convert::ToLong(rLimitStack); } catch (const std::invalid_argument& ex) { std::cout << "Error setting \"ICINGA2_RLIMIT_STACK\": " << ex.what() << '\n'; @@ -220,22 +282,16 @@ static int Main() #endif /* RLIMIT_STACK */ } - Application::DeclareConcurrency(std::thread::hardware_concurrency()); - Application::DeclareMaxConcurrentChecks(Application::GetDefaultMaxConcurrentChecks()); + /* Calculate additional global constants. */ + ScriptGlobal::Set("System.PlatformKernel", Utility::GetPlatformKernel(), true); + ScriptGlobal::Set("System.PlatformKernelVersion", Utility::GetPlatformKernelVersion(), true); + ScriptGlobal::Set("System.PlatformName", Utility::GetPlatformName(), true); + ScriptGlobal::Set("System.PlatformVersion", Utility::GetPlatformVersion(), true); + ScriptGlobal::Set("System.PlatformArchitecture", Utility::GetPlatformArchitecture(), true); - ScriptGlobal::Set("Environment", "production"); - - ScriptGlobal::Set("AttachDebugger", false); - - ScriptGlobal::Set("PlatformKernel", Utility::GetPlatformKernel()); - ScriptGlobal::Set("PlatformKernelVersion", Utility::GetPlatformKernelVersion()); - ScriptGlobal::Set("PlatformName", Utility::GetPlatformName()); - ScriptGlobal::Set("PlatformVersion", Utility::GetPlatformVersion()); - ScriptGlobal::Set("PlatformArchitecture", Utility::GetPlatformArchitecture()); - - ScriptGlobal::Set("BuildHostName", ICINGA_BUILD_HOST_NAME); - ScriptGlobal::Set("BuildCompilerName", ICINGA_BUILD_COMPILER_NAME); - ScriptGlobal::Set("BuildCompilerVersion", ICINGA_BUILD_COMPILER_VERSION); + ScriptGlobal::Set("System.BuildHostName", ICINGA_BUILD_HOST_NAME, true); + ScriptGlobal::Set("System.BuildCompilerName", ICINGA_BUILD_COMPILER_NAME, true); + ScriptGlobal::Set("System.BuildCompilerVersion", ICINGA_BUILD_COMPILER_VERSION, true); if (!autocomplete) Application::SetResourceLimits(); @@ -285,7 +341,10 @@ static int Main() GetUserName(username, &usernameLen); std::ifstream userFile; - userFile.open(Application::GetSysconfDir() + "/icinga2/user"); + + /* The implicit string assignment is needed for Windows builds. */ + String configDir = Configuration::ConfigDir; + userFile.open(configDir + "/user"); if (userFile && command && !Application::IsProcessElevated()) { std::string userLine; @@ -365,20 +424,42 @@ static int Main() key = define; value = "1"; } - ScriptGlobal::Set(key, value); + + std::vector keyTokens = key.Split("."); + + std::unique_ptr expr; + std::unique_ptr varExpr{new VariableExpression(keyTokens[0], {}, DebugInfo())}; + expr = std::move(varExpr); + + for (size_t i = 1; i < keyTokens.size(); i++) { + std::unique_ptr indexerExpr{new IndexerExpression(std::move(expr), MakeLiteral(keyTokens[i]))}; + indexerExpr->SetOverrideFrozen(); + expr = std::move(indexerExpr); + } + + std::unique_ptr setExpr{new SetExpression(std::move(expr), OpSetLiteral, MakeLiteral(value))}; + setExpr->SetOverrideFrozen(); + + ScriptFrame frame(true); + setExpr->Evaluate(frame); } } + Configuration::SetReadOnly(true); + + /* Ensure that all defined constants work in the way we expect them. */ + HandleLegacyDefines(); + if (vm.count("script-debugger")) Application::SetScriptDebuggerEnabled(true); - Application::DeclareStatePath(Application::GetLocalStateDir() + "/lib/icinga2/icinga2.state"); - Application::DeclareModAttrPath(Application::GetLocalStateDir() + "/lib/icinga2/modified-attributes.conf"); - Application::DeclareObjectsPath(Application::GetLocalStateDir() + "/cache/icinga2/icinga2.debug"); - Application::DeclareVarsPath(Application::GetLocalStateDir() + "/cache/icinga2/icinga2.vars"); - Application::DeclarePidPath(Application::GetRunDir() + "/icinga2/icinga2.pid"); + Configuration::StatePath = Configuration::DataDir + "/icinga2.state"; + Configuration::ModAttrPath = Configuration::DataDir + "/modified-attributes.conf"; + Configuration::ObjectsPath = Configuration::CacheDir + "/icinga2.debug"; + Configuration::VarsPath = Configuration::CacheDir + "/icinga2.vars"; + Configuration::PidPath = Configuration::InitRunDir + "/icinga2.pid"; - ConfigCompiler::AddIncludeSearchDir(Application::GetIncludeConfDir()); + ConfigCompiler::AddIncludeSearchDir(Configuration::IncludeConfDir); if (!autocomplete && vm.count("include")) { for (const String& includePath : vm["include"].as >()) { @@ -444,7 +525,8 @@ static int Main() } if (vm.count("version")) { - std::cout << "Copyright (c) 2012-2018 Icinga Development Team (https://www.icinga.com/)" << std::endl + std::cout << "Copyright (c) 2012-" << Utility::FormatDateTime("%Y", Utility::GetTime()) + << " Icinga GmbH (https://icinga.com/)" << std::endl << "License GPLv2+: GNU GPL version 2 or later " << std::endl << "This is free software: you are free to change and redistribute it." << std::endl << "There is NO WARRANTY, to the extent permitted by law."; @@ -467,8 +549,9 @@ static int Main() std::cout << visibleDesc << std::endl << "Report bugs at " << std::endl - << "Get support: " << std::endl - << "Icinga home page: " << std::endl; + << "Get support: " << std::endl + << "Documentation: " << std::endl + << "Icinga home page: " << std::endl; return EXIT_SUCCESS; } } @@ -488,8 +571,8 @@ static int Main() return 0; } } else if (command && command->GetImpersonationLevel() == ImpersonateIcinga) { - String group = Application::GetRunAsGroup(); - String user = Application::GetRunAsUser(); + String group = Configuration::RunAsGroup; + String user = Configuration::RunAsUser; errno = 0; struct group *gr = getgrnam(group.CStr()); @@ -825,7 +908,8 @@ static VOID WINAPI ServiceMain(DWORD argc, LPSTR *argv) int main(int argc, char **argv) { #ifndef _WIN32 - if (!getenv("ICINGA2_KEEP_FDS")) { + String keepFDs = Utility::GetFromEnvironment("ICINGA2_KEEP_FDS"); + if (keepFDs.IsEmpty()) { rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) { rlim_t maxfds = rl.rlim_max; @@ -839,6 +923,8 @@ int main(int argc, char **argv) #ifdef I2_DEBUG if (rc >= 0) std::cerr << "Closed FD " << i << " which we inherited from our parent process." << std::endl; +#else /* I2_DEBUG */ + (void)rc; #endif /* I2_DEBUG */ } } diff --git a/icinga-app/icinga.ico b/icinga-app/icinga.ico index 6ff7e9029..9be324c5f 100644 Binary files a/icinga-app/icinga.ico and b/icinga-app/icinga.ico differ diff --git a/icinga-app/icinga.rc b/icinga-app/icinga.rc index 9294f989b..dbd317be6 100644 --- a/icinga-app/icinga.rc +++ b/icinga-app/icinga.rc @@ -16,11 +16,11 @@ BEGIN BEGIN BLOCK "040904E4" BEGIN - VALUE "CompanyName", "Icinga Development Team" + VALUE "CompanyName", "Icinga GmbH" VALUE "FileDescription", "Icinga 2" VALUE "FileVersion", VERSION VALUE "InternalName", "icinga2.exe" - VALUE "LegalCopyright", " Icinga Development Team" + VALUE "LegalCopyright", " Icinga GmbH" VALUE "OriginalFilename", "icinga2.exe" VALUE "ProductName", "Icinga 2" VALUE "ProductVersion", VERSION @@ -31,4 +31,4 @@ BEGIN BEGIN VALUE "Translation", 0x409, 0x04E4 END -END \ No newline at end of file +END diff --git a/icinga-installer/CMakeLists.txt b/icinga-installer/CMakeLists.txt index 6e51928a4..6ac5e1f04 100644 --- a/icinga-installer/CMakeLists.txt +++ b/icinga-installer/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE diff --git a/icinga-installer/bannrbmp.bmp b/icinga-installer/bannrbmp.bmp index 51c3890b7..0f68e6200 100644 Binary files a/icinga-installer/bannrbmp.bmp and b/icinga-installer/bannrbmp.bmp differ diff --git a/icinga-installer/dlgbmp.bmp b/icinga-installer/dlgbmp.bmp index d998f74eb..e46566fb3 100644 Binary files a/icinga-installer/dlgbmp.bmp and b/icinga-installer/dlgbmp.bmp differ diff --git a/icinga-installer/icinga-installer.cpp b/icinga-installer/icinga-installer.cpp index e88ec2344..233f90cab 100644 --- a/icinga-installer/icinga-installer.cpp +++ b/icinga-installer/icinga-installer.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include #include @@ -128,6 +111,7 @@ static void MkDirP(const std::string& path) static std::string GetNSISInstallPath(void) { HKEY hKey; + //TODO: Change hardcoded key if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Icinga Development Team\\ICINGA2", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS) { BYTE pvData[MAX_PATH]; diff --git a/icinga-installer/icinga2.wixpatch.cmake b/icinga-installer/icinga2.wixpatch.cmake index 0666261d2..5ee6361ae 100644 --- a/icinga-installer/icinga2.wixpatch.cmake +++ b/icinga-installer/icinga2.wixpatch.cmake @@ -3,16 +3,21 @@ 1 Disable + + + + + - + - $CM_CP_sbin.icinga2_installer.exe>2 - $CM_CP_sbin.icinga2_installer.exe>2 - $CM_CP_sbin.icinga2_installer.exe=2 + $CM_CP_sbin.icinga2_installer.exe>2 AND NOT SUPPRESS_XTRA + $CM_CP_sbin.icinga2_installer.exe>2 AND NOT SUPPRESS_XTRA + $CM_CP_sbin.icinga2_installer.exe=2 AND NOT SUPPRESS_XTRA @@ -24,9 +29,9 @@ Impersonate="no" /> - WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed diff --git a/itl/CMakeLists.txt b/itl/CMakeLists.txt index 75b2e7289..b302aa3eb 100644 --- a/itl/CMakeLists.txt +++ b/itl/CMakeLists.txt @@ -1,23 +1,8 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ add_subdirectory(plugins-contrib.d) install( FILES itl command-icinga.conf hangman plugins command-plugins.conf manubulon command-plugins-manubulon.conf windows-plugins command-plugins-windows.conf nscp command-nscp-local.conf plugins-contrib - DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2/include + DESTINATION ${ICINGA2_INCLUDEDIR} ) diff --git a/itl/command-icinga.conf b/itl/command-icinga.conf index 4141b9030..206324a4e 100644 --- a/itl/command-icinga.conf +++ b/itl/command-icinga.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "icinga" { import "icinga-check-command" @@ -52,3 +35,7 @@ object CheckCommand "random" { object CheckCommand "exception" { import "exception-check-command" } + +object CheckCommand "sleep" { + import "sleep-check-command" +} diff --git a/itl/command-nscp-local.conf b/itl/command-nscp-local.conf index 33677fccd..8498d68f8 100644 --- a/itl/command-nscp-local.conf +++ b/itl/command-nscp-local.conf @@ -1,24 +1,7 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ if (!globals.contains("NscpPath")) { - NscpPath = dirname(msi_get_component_path("{5C45463A-4AE9-4325-96DB-6E239C034F93}")) + globals.NscpPath = dirname(msi_get_component_path("{5C45463A-4AE9-4325-96DB-6E239C034F93}")) } object CheckCommand "nscp-local" { @@ -222,6 +205,10 @@ object CheckCommand "nscp-local-disk" { value = "$nscp_disk_drive$" repeat_key = true } + "--exclude" = { + value = "$nscp_disk_exclude$" + repeat_key = true + } "--warning" = { value = "$nscp_disk_op$ $nscp_disk_warning$" } @@ -293,3 +280,68 @@ object CheckCommand "nscp-local-counter" { vars.nscp_counter_less = false vars.nscp_counter_perfsyntax = "$nscp_counter_name$" } + +object CheckCommand "nscp-local-tasksched" { + import "nscp-local" + + arguments += { + "--filter" = { + set_if = {{ + var scheduler_name = macro("$nscp_tasksched_name$") + if (len(scheduler_name) > 0 ) { + return true + } else { + return false + } + }} + value = "title='$nscp_tasksched_name$'" + description = "Name of the task to check." + } + "--folder" = { + value = "$nscp_tasksched_folder$" + description = "The folder in which the tasks to check reside." + } + "--hidden" = { + set_if = "$nscp_tasksched_hidden$" + description = "Look for hidden tasks." + } + "--recursive" = { + value = "$nscp_tasksched_recursive$" + description = "Recurse sub folder (defaults to true)." + } + "--warning" = { + value = "$nscp_tasksched_warning$" + description = "Filter which marks items which generates a warning state." + } + "--critical" = { + value = "$nscp_tasksched_critical$" + description = "Filter which marks items which generates a critical state." + } + "--empty-state" = { + value = "$nscp_tasksched_emptystate$" + description = "Return status to use when nothing matched filter." + } + "--perf-syntax" = { + value = "$nscp_tasksched_perfsyntax$" + description = "Performance alias syntax." + } + "--detail-syntax" = { + value = "$nscp_tasksched_detailsyntax$" + description = "Detail level syntax." + } + "-a" = { + value = "$nscp_tasksched_arguments$" + repeat_key = true + } + } + + vars.nscp_modules = "CheckTaskSched" + vars.nscp_query = "check_tasksched" + vars.nscp_showall = "$nscp_tasksched_showall$" + vars.nscp_tasksched_recursive = true + vars.nscp_tasksched_perfsyntax = "%(title)" + vars.nscp_tasksched_detailsyntax = "%(folder)/%(title): %(exit_code) != 0" + vars.nscp_tasksched_warning = "exit_code != 0" + vars.nscp_tasksched_critical = "exit_code < 0" + +} diff --git a/itl/command-plugins-manubulon.conf b/itl/command-plugins-manubulon.conf index 910a4ceae..d5cd08c25 100644 --- a/itl/command-plugins-manubulon.conf +++ b/itl/command-plugins-manubulon.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ /** * main snmp-manubulon template @@ -211,16 +194,25 @@ object CheckCommand "snmp-storage" { set_if = "$snmp_perf$" description = "Perfparse compatible output" } + "-e" = { + set_if = "$snmp_exclude$" + description = "Select all storages except the one(s) selected by -m. No action on storage type selection." + } "-o" = { value = "$snmp_storage_olength$" description = "Max-size of the SNMP message, usefull in case of Too Long responses." } + "-q" = { + value = "$snmp_storage_type$" + description = "Storage type: Other, Ram, VirtualMemory, FixedDisk, RemovableDisk, FloppyDisk, CompactDisk, RamDisk, FlashMemory, or NetworkDisk" + } } vars.snmp_storage_name = "^/$$" vars.snmp_warn = 80 vars.snmp_crit = 90 vars.snmp_perf = true + vars.snmp_exclude = false } @@ -396,6 +388,6 @@ object CheckCommand "snmp-service" { description = "Do not use regexp to match NAME in service description." } } - + vars.snmp_service_name = ".*" } diff --git a/itl/command-plugins-windows.conf b/itl/command-plugins-windows.conf index 1e48dc6b0..22ab623b9 100644 --- a/itl/command-plugins-windows.conf +++ b/itl/command-plugins-windows.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "disk-windows" { command = [ PluginDir + "/check_disk.exe" ] @@ -85,6 +68,10 @@ object CheckCommand "memory-windows" { value = "$memory_win_unit$" description = "Use this unit to display memory" } + "-U" = { + set_if = "$memory_win_show_used$" + description = "Show used memory instead of the free memory" + } } //The default @@ -235,7 +222,7 @@ object CheckCommand "service-windows" { required = true description = "Service to check" } - "-d" = { + "--description" = { set_if = "$service_win_description$" description = "Use service description instead of name" } @@ -258,6 +245,10 @@ object CheckCommand "swap-windows" { value = "$swap_win_unit$" description = "Unit to display swap in" } + "-U" = { + set_if = "$swap_win_show_used$" + description = "Show used swap instead of the free swap" + } } // Default @@ -271,17 +262,21 @@ object CheckCommand "update-windows" { arguments = { "-w" = { - set_if = "$update_win_warn$" - description = "Warn if there are important updates available" + value = "$update_win_warn$" + description = "Number of updates to trigger a warning" } "-c" = { - set_if = "$update_win_crit$" - description = "Critical if there are important updates that require a reboot" + value = "$update_win_crit$" + description = "Number of updates to trigger a critical" } "--possible-reboot" = { set_if = "$update_win_reboot$" description = "Treat 'may need update' as 'definitely needs update'" } + "--no-reboot-critical" = { + set_if = "$ignore_reboot$" + description = "Do not automatically return critical if an update requiring reboot is present." + } } timeout = 5m diff --git a/itl/command-plugins.conf b/itl/command-plugins.conf index 0bf297fd1..c3f7ffe1f 100644 --- a/itl/command-plugins.conf +++ b/itl/command-plugins.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ template CheckCommand "ipv4-or-ipv6" { vars.check_address = {{ @@ -1475,6 +1458,11 @@ object CheckCommand "disk" { description = "Ignore all filesystems of indicated type (may be repeated)" repeat_key = true } + "-N" = { + value = "$disk_include_type$" + description = "Check only filesystems of indicated type (may be repeated)" + repeat_key = true + } } vars.disk_wfree = "20%" @@ -1495,7 +1483,8 @@ object CheckCommand "disk" { "fuse.gvfs-fuse-daemon", "fdescfs", "overlay", - "nsfs" + "nsfs", + "squashfs" ] } @@ -1842,6 +1831,10 @@ object CheckCommand "snmpv3" { value = "$snmpv3_user$" description = "SNMPv3 username" } + "-N" = { + value = "$snmpv3_context$" + description = "SNMPv3 context" + } "-A" = { value = "$snmpv3_auth_key$" description = "SNMPv3 authentication password" @@ -1957,6 +1950,10 @@ object CheckCommand "apt" { set_if = "$apt_only_critical$" description = "Only warn about critical upgrades." } + "--list" = { + set_if = "$apt_list$" + description = "List packages available for upgrade." + } } timeout = 5m @@ -3143,3 +3140,32 @@ object CheckCommand "rpc" { vars.rpc_address = "$check_address$" } + +object CheckCommand "uptime" { + command = [ PluginDir + "/check_uptime" ] + + arguments = { + "--warning" = { + value = "$uptime_warning$" + description = "Min. number of uptime to generate warning" + required = true + } + "--critical" = { + value = "$uptime_critical$" + description = "Min. number of uptime to generate critical alert ( w < c )" + required = true + } + "--for" = { + set_if = "$uptime_for$" + description = "Show uptime in a pretty format (Running for x weeks, x days, ...)" + } + "--since" = { + set_if = "$uptime_since$" + description = "Show last boot in yyyy-mm-dd HH:MM:SS format (output from 'uptime -s')" + } + } + + vars.uptime_warning = "30m" + vars.uptime_critical = "15m" +} + diff --git a/itl/hangman b/itl/hangman index f4dc118e0..b2feb3a79 100644 --- a/itl/hangman +++ b/itl/hangman @@ -1,24 +1,7 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ -if (!globals.irc) { - globals.irc = globals.log +if (!globals.contains("irc")) { + globals.irc = log } hm = { diff --git a/itl/itl b/itl/itl index 340633ca6..8f98fe60e 100644 --- a/itl/itl +++ b/itl/itl @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ /** * This is the Icinga Templare Library, a collection of general purpose Icinga diff --git a/itl/manubulon b/itl/manubulon index 094f1836e..6b6855da8 100644 --- a/itl/manubulon +++ b/itl/manubulon @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ /** * This is the SNMP Manubulon Template Library. diff --git a/itl/nscp b/itl/nscp index 49283f323..c62513e54 100644 --- a/itl/nscp +++ b/itl/nscp @@ -1,20 +1,3 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ include "command-nscp-local.conf" diff --git a/itl/plugins b/itl/plugins index d142acdc9..edfe4cea3 100644 --- a/itl/plugins +++ b/itl/plugins @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ /** * This is the Icinga Templare Library, a collection of general purpose Icinga diff --git a/itl/plugins-contrib b/itl/plugins-contrib index 9857a63ce..f2f56e042 100644 --- a/itl/plugins-contrib +++ b/itl/plugins-contrib @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ /** * This is a directory for various contributed command definitions. diff --git a/itl/plugins-contrib.d/CMakeLists.txt b/itl/plugins-contrib.d/CMakeLists.txt index 099ea2485..6f94404b9 100644 --- a/itl/plugins-contrib.d/CMakeLists.txt +++ b/itl/plugins-contrib.d/CMakeLists.txt @@ -1,21 +1,6 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ install( - FILES databases.conf hardware.conf icingacli.conf ipmi.conf logmanagement.conf metrics.conf network-components.conf network-services.conf operating-system.conf raid-controller.conf smart-attributes.conf storage.conf virtualization.conf vmware.conf web.conf - DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2/include/plugins-contrib.d + FILES big-data.conf databases.conf hardware.conf icingacli.conf ipmi.conf logmanagement.conf metrics.conf network-components.conf network-services.conf operating-system.conf raid-controller.conf smart-attributes.conf storage.conf virtualization.conf vmware.conf web.conf + DESTINATION ${ICINGA2_INCLUDEDIR}/plugins-contrib.d ) diff --git a/itl/plugins-contrib.d/big-data.conf b/itl/plugins-contrib.d/big-data.conf new file mode 100644 index 000000000..7d3d6f173 --- /dev/null +++ b/itl/plugins-contrib.d/big-data.conf @@ -0,0 +1,112 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +object CheckCommand "cloudera_service_status" { + command = [ PluginContribDir + "/check_cloudera_service_status.py" ] + + arguments = { + "-H" = { + description = "host" + value = "$cloudera_host$" + required = true + } + "-P" = { + description = "port" + value = "$cloudera_port$" + required = false + } + "-u" = { + description = "user" + value = "$cloudera_user$" + required = true + } + "-p" = { + description = "pass" + value = "$cloudera_pass$" + required = true + } + "-v" = { + description = "api_version" + value = "$cloudera_api_version$" + required = true + } + "-c" = { + description = "cluster" + value = "$cloudera_cluster$" + required = true + } + "-s" = { + description = "service" + value = "$cloudera_service$" + required = true + } + "-k" = { + description = "verify_ssl" + value = "$cloudera_verify_ssl$" + required = false + } + } +} + +object CheckCommand "cloudera_hdfs_space" { + command = [ PluginContribDir + "/check_cloudera_hdfs_space.py" ] + + arguments = { + "-H" = { + description = "Namenode host" + value = "$cloudera_hdfs_space_host$" + required = true + } + "-P" = { + description = "Namenode port (default 50070)" + value = "$cloudera_hdfs_space_port$" + required = false + } + "-d" = { + description = "HDFS disk to check" + value = "$cloudera_hdfs_space_disk$" + required = true + } + "-w" = { + description = "Warning threshold in percent" + value = "$cloudera_hdfs_space_warn$" + required = true + } + "-c" = { + description = "Critical threshold in percent" + value = "$cloudera_hdfs_space_crit$" + required = true + } + } +} + +object CheckCommand "cloudera_hdfs_files" { + command = [ PluginContribDir + "/check_cloudera_hdfs_files.py" ] + + arguments = { + "-H" = { + description = "Namenode host" + value = "$cloudera_hdfs_files_host$" + required = true + } + "-P" = { + description = "Namenode port (default 50070)" + value = "$cloudera_hdfs_files_port$" + required = false + } + "-w" = { + description = "Warning threshold" + value = "$cloudera_hdfs_files_warn$" + required = true + } + "-c" = { + description = "Critical threshold" + value = "$cloudera_hdfs_files_crit$" + required = true + } + "-m" = { + description = "Max files count that causes problems (default 140000000)" + value = "$cloudera_hdfs_files_max$" + required = false + } + } +} diff --git a/itl/plugins-contrib.d/databases.conf b/itl/plugins-contrib.d/databases.conf index 8b4c2ffd2..ecd436502 100644 --- a/itl/plugins-contrib.d/databases.conf +++ b/itl/plugins-contrib.d/databases.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "mssql_health" { import "ipv4-or-ipv6" @@ -344,6 +327,7 @@ object CheckCommand "db2_health" { arguments = { "--hostname" = { value = "$db2_health_hostname$" + set_if = "$db2_health_not_catalogued$" description = "the host to connect" order = -2 } @@ -424,6 +408,7 @@ object CheckCommand "db2_health" { } vars.db2_health_regexp = false + vars.db2_health_not_catalogued = true vars.db2_health_hostname = "$check_address$" vars.db2_health_report = "short" @@ -517,7 +502,7 @@ object CheckCommand "oracle_health" { vars.oracle_home = "/usr/lib/oracle/11.2/client64/lib" vars.oracle_ld_library_path = "/usr/lib/oracle/11.2/client64/lib" - vars.oracle_tns_admin = SysconfDir + "/icinga2/plugin-configs" + vars.oracle_tns_admin = ConfigDir + "/plugin-configs" } object CheckCommand "postgres" { @@ -840,3 +825,120 @@ object CheckCommand "redis" { vars.redis_perfparse = false vars.redis_prev_perfdata = false } + +object CheckCommand "proxysql" { + import "plugin-check-command" + command = [ PluginContribDir + "/check_proxysql" ] + + arguments = { + "--user" = { + value = "$proxysql_user$" + description = "ProxySQL admin username (default=admin)" + } + "--password" = { + value = "$proxysql_password$" + description = "ProxySQL admin password (default=admin)" + } + "--host" = { + value = "$proxysql_host$" + description = "ProxySQL hostname / IP (default=127.0.0.1)" + } + "--port" = { + value = "$proxysql_port$" + description = "ProxySQL admin port (default=6032)" + } + "--defaults-file" = { + value = "$proxysql_defaultfile$" + description = "ProxySQL defaults file" + } + "--type" = { + value = "$proxysql_type$" + description = "ProxySQL check type (one of conns,hg,rules,status,var)" + required = true + } + "--name" = { + value = "$proxysql_name$" + description = "ProxySQL variable name to check" + } + "--lower" = { + value = "$proxysql_lower$" + description = "Alert if ProxySQL value are LOWER than defined WARN / CRIT thresholds (only applies to 'var' check type)" + } + "--runtime" = { + value = "$proxysql_runtime$" + description = "Force ProxySQL Nagios check to query the runtime_mysql_XXX tables rather than the mysql_XXX tables" + } + "--warning" = { + value = "$proxysql_warning$" + description = "Warning threshold" + } + "--critical" = { + value = "$proxysql_critical$" + description = "Critical threshold" + } + "--include-hostgroup" = { + value = "$proxysql_include_hostgroup$" + description = "ProxySQL hostgroup(s) to include (only applies to '--type hg' checks, accepts comma-separated list)" + } + "--ignore-hostgroup" = { + value = "$proxysql_ignore_hostgroup$" + description = "ProxySQL hostgroup(s) to ignore (only applies to '--type hg' checks, accepts comma-separated list)" + } + } +} + +object CheckCommand "memcached" { + command = [ PluginContribDir + "/check_memcached" ] + + arguments = { + "-H" = { + value = "$memcached_hostname$" + required = true + description = "Hostname or IP address (required) optional ':port' overrides -p" + } + "-p" = { + value = "$memcached_port$" + description = "Port number (default: 11211)" + } + "-v" = { + set_if = "$memcached_verbose$" + description = "verbose messages" + } + "-n" = { + value = "$memcached_keep$" + description = "Keep up to this many items in the history object in memcached (default: 30)" + } + "-T" = { + value = "$memcached_minimum_stat_interval$" + description = "Minimum time interval (in minutes) to use to analyse stats. (default: 30)" + } + "-w" = { + value = "$memcached_warning_hits_misses$" + description = "Generate warning if quotient of hits/misses falls below this value (default: 2.0)" + } + "-E" = { + value = "$memcached_warning_evictions$" + description = "Generate warning if number of evictions exceeds this threshold. 0=disable. (default: 10)" + } + "-t" = { + value = "$memcached_timeout$" + description = "timeout in seconds (default: 1.0)" + } + "-k" = { + value = "$memcached_key$" + description = "key name for history object (default: check_memcached)" + } + "-K" = { + value = "$memcached_expiry$" + description = "expiry time in seconds for history object (default: 7200)" + } + "-r" = { + set_if = "$memcached_performance_output$" + description = "output performance statistics as rate-per-minute figures (better suited to pnp4nagios)" + } + } + + vars.memcached_hostname = "127.0.0.1" + vars.memcached_minimum_stat_interval = "10" + vars.memcached_performance_output = true +} diff --git a/itl/plugins-contrib.d/hardware.conf b/itl/plugins-contrib.d/hardware.conf index ae7c3f9ba..c412cb752 100644 --- a/itl/plugins-contrib.d/hardware.conf +++ b/itl/plugins-contrib.d/hardware.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "hpasm" { import "ipv4-or-ipv6" @@ -204,3 +187,81 @@ object CheckCommand "openmanage" { } } } + +object CheckCommand "lmsensors" { + command = [ PluginContribDir + "/check_lmsensors" ] + + arguments = { + "-w" = { + value = "$lmsensors_warning$" + description = "Exit with WARNING status if above INTEGER degrees" + required = true + } + "-c" = { + value = "$lmsensors_critical$" + description = "Exit with CRITICAL status if above INTEGER degrees" + required = true + } + "--sensor" = { + value = "$lmsensors_sensor$" + description = "Set what to monitor, for example CPU or MB (or M/B). Check sensors for the correct word. Default is CPU." + } + } + + vars.lmsensors_warning = "75" + vars.lmsensors_critical = "80" + vars.lmsensors_sensor = "Core" +} + +object CheckCommand "hddtemp" { + import "plugin-check-command" + command = [ PluginContribDir + "/check_hddtemp" ] + + arguments = { + "--server" = { + value = "$hddtemp_server$" + description = "server name or address" + required = true + } + "--port" = { + value = "$hddtemp_port$" + description = "port number" + } + "--devices" = { + value = "$hddtemp_devices$" + description = "comma separated devices list, or empty for all devices in hddtemp response" + } + "--separator" = { + value = "$hddtemp_separator$" + description = "hddtemp separator" + } + "--warning" = { + value = "$hddtemp_warning$" + description = "warning temperature" + required = true + } + "--critical" = { + value = "$hddtemp_critical$" + description = "critical temperature" + required = true + } + "--timeout" = { + value = "$hddtemp_timeout$" + description = "receiving data from hddtemp operation network timeout" + } + "--performance-data" = { + set_if = "$hddtemp_performance$" + description = "return performance data" + } + "--quiet" = { + set_if = "$hddtemp_quiet$" + description = "be quiet" + } + } + + vars.hddtemp_server = "127.0.0.1" + vars.hddtemp_warning = 55 + vars.hddtemp_critical = 60 + vars.hddtemp_performance = true + vars.hddtemp_timeout = 5 +} diff --git a/itl/plugins-contrib.d/icingacli.conf b/itl/plugins-contrib.d/icingacli.conf index 056b84ae7..3086aabec 100644 --- a/itl/plugins-contrib.d/icingacli.conf +++ b/itl/plugins-contrib.d/icingacli.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ template CheckCommand "icingacli" { command = [ PrefixDir + "/bin/icingacli" ] @@ -68,3 +51,70 @@ object CheckCommand "icingacli-director" { } } +object CheckCommand "icingacli-elasticsearch" { + import "icingacli" + + command += [ "elasticsearch", "check" ] + + arguments = { + "--instance" = { + value = "$icingacli_elasticsearch_instance$" + description = "Elasticsearch instance to connect to" + } + "--crit" = { + value = "$icingacli_elasticsearch_critical$" + description = "Critical threshold" + } + "--warn" = { + value = "$icingacli_elasticsearch_warning$" + description = "Warning threshold" + } + "--index" = { + value = "$icingacli_elasticsearch_index$" + description = "Index pattern to use when searching" + } + "--filter" = { + value = "$icingacli_elasticsearch_filter$" + description = "Filter for events" + } + "--from" = { + value = "$icingacli_elasticsearch_from$" + description = "Negative value of time to search from now" + } + } + +} + +object CheckCommand "icingacli-x509" { + import "icingacli" + + command += [ "x509", "check", "host" ] + + arguments = { + "--ip" = { + value = "$icingacli_x509_ip$" + description = "A hosts IP address" + } + "--host" = { + value = "$icingacli_x509_host$" + description = "A hosts name" + } + "--port" = { + value = "$icingacli_x509_port$" + description = "The port to check in particular" + } + "--warning" = { + value = "$icingacli_x509_warning$" + description = "Less remaining time results in state WARNING" + } + "--critical" = { + value = "$icingacli_x509_critical$" + description = "Less remaining time results in state CRITICAL" + } + "--allow-self-signed" = { + set_if = "$icingacli_x509_allow_self_signed$" + description = "Ignore if a certificate or its issuer has been self-signed" + } + } +} + diff --git a/itl/plugins-contrib.d/ipmi.conf b/itl/plugins-contrib.d/ipmi.conf index 95570406a..200d56747 100644 --- a/itl/plugins-contrib.d/ipmi.conf +++ b/itl/plugins-contrib.d/ipmi.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "ipmi-sensor" { import "ipv4-or-ipv6" @@ -103,6 +86,10 @@ object CheckCommand "ipmi-sensor" { set_if = "$ipmi_debug$" description = "Be Verbose debugging output, followed by normal multi line output" } + "-us" = { + value = "$ipmi_unify_file$" + description = "Path to the unify file to unify sensor names." + } } vars.ipmi_address = "$check_address$" diff --git a/itl/plugins-contrib.d/logmanagement.conf b/itl/plugins-contrib.d/logmanagement.conf index 6e1a910a9..16cafe044 100644 --- a/itl/plugins-contrib.d/logmanagement.conf +++ b/itl/plugins-contrib.d/logmanagement.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "logstash" { import "ipv4-or-ipv6" @@ -72,3 +55,106 @@ object CheckCommand "logstash" { vars.logstash_heap_warn = 70 vars.logstash_heap_crit = 80 } + +object CheckCommand "logfiles" { + command = [ PluginContribDir + "/check_logfiles" ] + + arguments = { + "--tag" = { + value = "$logfiles_tag$" + description = "A short unique descriptor for this search. It will appear in the output of the plugin and is used to separare the different services." + } + "--logfile" = { + value = "$logfiles_logfile$" + description = "This is the name of the log file you want to scan." + } + "--rotation" = { + value = "$logfiles_rotation$" + description = "This is the method how log files are rotated. One of the predefined methods or a regular expression, which helps identify the rotated archives. If this key is missing, check_logfiles assumes that the log file will be simply overwritten instead of rotated." + } + "--criticalpattern" = { + value = "$logfiles_critical_pattern$" + description = "A regular expression which will trigger a critical error." + } + "--warningpattern" = { + value = "$logfiles_warning_pattern$" + description = "A regular expression which will trigger a warning error." + } + "--criticalexception" = { + value = "$logfiles_critical_exception$" + description = "A regular expression, the exceptions which are not counted as critical errors." + } + "--warningexception" = { + value = "$logfiles_warning_exception$" + description = "A regular expression, the exceptions which are not counted as warning errors." + } + "--okpattern" = { + value = "$logfiles_ok_pattern$" + description = "A regular expression which resets the error counters." + } + "--noprotocol" = { + set_if = "$logfiles_no_protocol$" + description = "Normally all the matched lines are written into a protocol file with this file’s name appearing in the plugin’s output. This option switches this off." + } + "--syslogserver" = { + set_if = "$logfiles_syslog_server$" + description = "With this option you limit the pattern matching to lines originating from the host check_logfiles is running on." + } + "--syslogclient" = { + value = "$logfiles_syslog_client$" + description = "With this option you limit the pattern matching to lines originating from the host named in this option." + } + "--sticky" = { + value = "$logfiles_sticky$" + description = "Errors are propagated through successive runs." + } + "--unstick" = { + set_if = "$logfiles_unstick$" + description = "Resets sticky errors." + } + "--config" = { + value = "$logfiles_config$" + description = "The name of a configuration file." + } + "--configdir" = { + value = "$logfiles_configdir$" + description = "The name of a configuration directory. Configfiles ending in .cfg or .conf are (recursively) imported." + } + "--searches" = { + value = "$logfiles_searches$" + description = "A list of tags of those searches which are to be run. Using this parameter, not all searches listed in the config file are run, but only those selected." + } + "--selectedsearches" = { + value = "$logfiles_selectedsearches$" + description = "A list of tags of those searches which are to be run. Using this parameter, not all searches listed in the config file are run, but only those selected." + } + "--report" = { + value = "$logfiles_report$" + description = "This option turns on multiline output (Default: off). The setting html generates a table which display the last hits in the service details view. Possible values are: short, long, html or off" + } + "--maxlength" = { + value = "$logfiles_max_length$" + description = "With this parameter long lines are truncated (Default: off). Some programs (e.g. TrueScan) generate entries in the eventlog of such a length, that the output of the plugin becomes longer than 1024 characters. NSClient++ discards these." + } + "--winwarncrit" = { + value = "$logfiles_winwarncrit$" + description = "With this parameter messages in the eventlog are classified by the type WARNING/ERROR (Default: off). Replaces or complements warning/criticalpattern." + } + "--rununique" = { + set_if = "$logfiles_run_unique$" + description = "This parameter prevents check_logfiles from starting when there’s already another instance using the same config file. (exits with UNKNOWN)" + } + "--timeout" = { + value = "$logfiles_timeout$" + description = "This parameter causes an abort of a running search after a defined number of seconds. It is an aborted in a controlled manner, so that the lines which have been read so far, are used for the computation of the final result." + } + "--warning" = { + value = "$logfiles_warning$" + description = "Complex handler-scripts can be provided with a warning-parameter this way. Inside the scripts the value is accessible as the macro CL_WARNING." + } + "--critical" = { + value = "$logfiles_critical$" + description = "Complex handler-scripts can be provided with a critical-parameter this way. Inside the scripts the value is accessible as the macro CL_CRITICAL." + } + } +} diff --git a/itl/plugins-contrib.d/metrics.conf b/itl/plugins-contrib.d/metrics.conf index 180aa93c6..856ba755c 100644 --- a/itl/plugins-contrib.d/metrics.conf +++ b/itl/plugins-contrib.d/metrics.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "graphite" { command = [ PluginContribDir + "/check_graphite" ] diff --git a/itl/plugins-contrib.d/network-components.conf b/itl/plugins-contrib.d/network-components.conf index e34175952..e6fdba7d2 100644 --- a/itl/plugins-contrib.d/network-components.conf +++ b/itl/plugins-contrib.d/network-components.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "interfacetable" { command = [ PluginContribDir + "/check_interface_table_v3t" ] @@ -314,6 +297,10 @@ object CheckCommand "iftraffic" { value = "$iftraffic_community$" description = "SNMP community. Defaults to 'public' if omitted." } + "-V" = { + value = "$iftraffic_version$" + description = "SNMP version. Defaults to '1' if omitted." + } "-i" = { value = "$iftraffic_interface$" description = "Interface name." @@ -563,6 +550,10 @@ object CheckCommand "nwc_health" { value = "$nwc_health_contextname$" description = "The context name for SNMPv3 (empty represents the default context)" } + "--community2" = { + value = "$nwc_health_community2$" + description = "SNMP community which can be used to switch the context during runtime" + } "--mode" = { value = "$nwc_health_mode$" description = "Which mode should be executed. A list of all available modes can be found in the plugin documentation" @@ -603,6 +594,10 @@ object CheckCommand "nwc_health" { value = "$nwc_health_name2$" description = "The secondary name of a component" } + "--name3" = { + value = "$nwc_health_name3$" + description = "The tertiary name of a component" + } "--role" = { value = "$nwc_health_role$" description = "The role of this device in a hsrp group (active/standby/listen)" @@ -625,10 +620,12 @@ object CheckCommand "nwc_health" { } "--warningx" = { value = "$nwc_health_warningx$" + repeat_key = true description = "The extended warning thresholds" } "--criticalx" = { value = "$nwc_health_criticalx$" + repeat_key = true description = "The extended critical thresholds" } "--mitigation" = { @@ -676,3 +673,163 @@ object CheckCommand "nwc_health" { vars.nwc_health_hostname = "$check_address$" vars.nwc_health_mode = "hardware-health" } + +object CheckCommand "printer_health" { + import "ipv4-or-ipv6" + + command = [ PluginContribDir + "/check_printer_health" ] + + arguments = { + "--timeout" = { + value = "$printer_health_timeout$" + description = "Seconds before plugin times out (default: 15)" + } + "--blacklist" = { + value = "$printer_health_blacklist$" + description = "Blacklist some (missing/failed) components" + } + "--hostname" = { + value = "$printer_health_hostname$" + description = "Hostname or IP-address of the switch or router" + } + "--port" = { + value = "$printer_health_port$" + description = "The SNMP port to use (default: 161)" + } + "--domain" = { + value = "$printer_health_domain$" + description = "The transport domain to use (default: udp/ipv4, other possible values: udp6, udp/ipv6, tcp, tcp4, tcp/ipv4, tcp6, tcp/ipv6)" + } + "--protocol" = { + value = "$printer_health_protocol$" + description = "The SNMP protocol to use (default: 2c, other possibilities: 1,3)" + } + "--community" = { + value = "$printer_health_community$" + description = "SNMP community of the server (SNMP v1/2 only)" + } + "--username" = { + value = "$printer_health_username$" + description = "The securityName for the USM security model (SNMPv3 only)" + } + "--authpassword" = { + value = "$printer_health_authpassword$" + description = "The authentication password for SNMPv3" + } + "--authprotocol" = { + value = "$printer_health_authprotocol$" + description = "The authentication protocol for SNMPv3 (md5|sha)" + } + "--privpassword" = { + value = "$printer_health_privpassword$" + description = "The password for authPriv security level" + } + "--privprotocol" = { + value = "$printer_health_privprotocol$" + description = "The private protocol for SNMPv3 (des|aes|aes128|3des|3desde)" + } + "--contextengineid" = { + value = "$printer_health_contextengineid$" + description = "The context engine id for SNMPv3 (10 to 64 hex characters)" + } + "--contextname" = { + value = "$printer_health_contextname$" + description = "The context name for SNMPv3 (empty represents the default context)" + } + "--community2" = { + value = "$printer_health_community2$" + description = "SNMP community which can be used to switch the context during runtime" + } + "--mode" = { + value = "$printer_health_mode$" + description = "Which mode should be executed. Available modes: hardware-health, supplies-status and uptime." + } + "--name" = { + value = "$printer_health_name$" + description = "The name of an interface (ifDescr)" + } + "--regexp" = { + set_if = "$printer_health_regexp$" + description = "A flag indicating that --name is a regular expression" + } + "--units" = { + value = "$printer_health_units$" + description = "One of %, B, KB, MB, GB, Bit, KBi, MBi, GBi. (used for e.g. mode interface-usage)" + } + "--name2" = { + value = "$printer_health_name2$" + description = "The secondary name of a component" + } + "--name3" = { + value = "$printer_health_name3$" + description = "The teritary name of a component" + } + "--report" = { + value = "$printer_health_report$" + description = "Can be used to shorten the output." + } + "--lookback" = { + value = "$printer_health_lookback$" + description = "The amount of time you want to look back when calculating average rates. Use it for mode interface-errors or interface-usage. Without --lookback the time between two runs of check_printer_health is the base for calculations. If you want your checkresult to be based for example on the past hour, use --lookback 3600." + } + "--critical" = { + value = "$printer_health_critical$" + description = "The critical threshold" + } + "--warning" = { + value = "$printer_health_warning$" + description = "The warning threshold" + } + "--warningx" = { + value = "$printer_health_warningx$" + description = "The extended warning thresholds" + } + "--criticalx" = { + value = "$printer_health_criticalx$" + description = "The extended critical thresholds" + } + "--mitigation" = { + value = "$printer_health_mitigation$" + description = "The parameter allows you to change a critical error to a warning." + } + "--selectedperfdata" = { + value = "$printer_health_selectedperfdata$" + description = "The parameter allows you to limit the list of performance data. It's a perl regexp. Only matching perfdata show up in the output." + } + "--morphperfdata" = { + value = "$printer_health_morphperfdata$" + description = "The parameter allows you to change performance data labels. It's a perl regexp and a substitution. --morphperfdata '(.*)ISATAP(.*)'='$1patasi$2'" + } + "--negate" = { + value = "$printer_health_negate$" + description = "The parameter allows you to map exit levels, such as warning=critical" + } + "--with-mymodules-dyn-dir" = { + value = "$printer_health_mymodules-dyn-dir$" + description = "A directory where own extensions can be found" + } + "--servertype" = { + value = "$printer_health_servertype$" + description = "The type of the network device: cisco (default). Use it if auto-detection is not possible" + } + "--statefilesdir" = { + value = "$printer_health_statefilesdir$" + description = "An alternate directory where the plugin can save files" + } + "--oids" = { + value = "$printer_health_oids$" + description = "A list of oids which are downloaded and written to a cache file. Use it together with --mode oidcache" + } + "--offline" = { + value = "$printer_health_offline$" + description = "The maximum number of seconds since the last update of cache file before it is considered too old" + } + "--multiline" = { + set_if = "$printer_health_multiline$" + description = "Multiline output" + } + } + + vars.printer_health_hostname = "$check_address$" + vars.printer_health_mode = "supplies-status" +} diff --git a/itl/plugins-contrib.d/network-services.conf b/itl/plugins-contrib.d/network-services.conf index 31a9c84a0..89554e4cc 100644 --- a/itl/plugins-contrib.d/network-services.conf +++ b/itl/plugins-contrib.d/network-services.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "kdc" { import "ipv4-or-ipv6" @@ -81,3 +64,56 @@ object CheckCommand "rbl" { vars.rbl_warning = 1 vars.rbl_critical = 1 } + +object CheckCommand "lsyncd" { + import "plugin-check-command" + command = [ PluginContribDir + "/check_lsyncd" ] + + arguments = { + "-s" = { + value = "$lsyncd_statfile$" + description = "Set status file path (default: /var/run/lsyncd.status)." + } + "-w" = { + value = "$lsyncd_warning$" + description = "Warning if more than N delays (default: 10)." + } + "-c" = { + value = "$lsyncd_critical$" + description = "Critical if more then N delays (default: 100)." + } + } +} + +object CheckCommand "fail2ban" { + command = [ "sudo", PluginContribDir + "/check_fail2ban" ] + + arguments = { + "-D" = { + value = "$fail2ban_display$" + description = "To modify the output display, default is 'CHECK FAIL2BAN ACTIVITY'" + } + "-P" = { + value = "$fail2ban_path$" + description = "Specify the path to the tw_cli binary, default value is /usr/bin/fail2ban-client" + } + "-w" = { + value = "$fail2ban_warning$" + description = "Specify a warning threshold, default is 1" + } + "-c" = { + value = "$fail2ban_critical$" + description = "Specify a critical threshold, default is 2" + } + "-s" = { + value = "$fail2ban_socket$" + description = "Specify a socket path, default is unset" + } + "-p" = { + set_if = "$fail2ban_perfdata$" + description = "If set to true, activate the perfdata output" + } + } + + vars.fail2ban_perfdata = true +} diff --git a/itl/plugins-contrib.d/operating-system.conf b/itl/plugins-contrib.d/operating-system.conf index c58332c98..afab177f0 100644 --- a/itl/plugins-contrib.d/operating-system.conf +++ b/itl/plugins-contrib.d/operating-system.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "mem" { command = [ PluginContribDir + "/check_mem.pl" ] @@ -48,6 +31,26 @@ object CheckCommand "mem" { vars.mem_cache = false } +object CheckCommand "sar-perf" { + command = [ PluginContribDir + "/check_sar_perf.py" ] + + arguments = { + "sar_perf_profile" = { + value = "$sar_perf_profile$" + description = "Define the run profile: pagestat, cpu, memory_util, memory_stat, io_transfer, queueln_load, swap_util, swap_stat, task, kernel, disk . Can be a string or an array of multiple profiles." + skip_key = true + repeat_key = false + required = true + } + "sar_perf_disk" = { + value = "$sar_perf_disk$" + set_if = {{ macro("$sar_perf_profile$") == "disk" }} + description = "Disk name for the 'disk' profile" + skip_key = true + } + } +} + object CheckCommand "running_kernel" { command = {{ var use_sudo = macro("$running_kernel_use_sudo$") diff --git a/itl/plugins-contrib.d/raid-controller.conf b/itl/plugins-contrib.d/raid-controller.conf index 7b3f738a2..17fb388e1 100644 --- a/itl/plugins-contrib.d/raid-controller.conf +++ b/itl/plugins-contrib.d/raid-controller.conf @@ -6,7 +6,7 @@ object CheckCommand "adaptec-raid" { import "plugin-check-command" - command = [ PluginDir + "/check_adaptec_raid" ] + command = [ PluginContribDir + "/check_adaptec_raid" ] arguments = { "-C" = { @@ -27,20 +27,96 @@ object CheckCommand "adaptec-raid" { object CheckCommand "lsi-raid" { import "plugin-check-command" - command = [ PluginDir + "/check_lsi_raid" ] + command = [ PluginContribDir + "/check_lsi_raid" ] arguments = { "-C" = { - required = true value = "$lsi_controller_number$" description = "Insert the controller number to be checked." } "-p" = { - required = true value = "$storcli_path$" description = "Insert the path to storcli (e.g. /usr/sbin/storcli)." } + "-EID" = { + value = "$lsi_enclosure_id$" + description = "Enclosure numbers to be checked, comma-separated." + } + "-LD" = { + value = "$lsi_ld_id$" + description = "Logical devices to be checked, comma-separated." + } + "-PD" = { + value = "$lsi_pd_id$" + description = "Physical devices to be checked, comma-separated." + } + "-Tw" = { + value = "$lsi_temp_warning$" + description = "RAID controller warning temperature." + } + "-Tc" = { + value = "$lsi_temp_critical$" + description = "RAID controller critical temperature." + } + "-PDTw" = { + value = "$lsi_pd_temp_warning$" + description = "Disk warning temperature." + } + "-PDTc" = { + value = "$lsi_pd_temp_critical$" + description = "Disk critical temperature." + } + "-BBUTw" = { + value = "$lsi_bbu_temp_warning$" + description = "Battery warning temperature." + } + "-BBUTc" = { + value = "$lsi_bbu_temp_critical$" + description = "Battery critical temperature." + } + "-CVTw" = { + value = "$lsi_cv_temp_warning$" + description = "CacheVault warning temperature." + } + "-CVTc" = { + value = "$lsi_cv_temp_critical$" + description = "CacheVault critical temperature." + } + "-Im" = { + value = "$lsi_ignored_media_errors$" + description = "Warning threshold for media errors." + } + "-Io" = { + value = "$lsi_ignored_other_errors$" + description = "Warning threshold for other errors." + } + "-Ip" = { + value = "$lsi_ignored_predictive_fails$" + description = "Warning threshold for predictive failures." + } + "-Is" = { + value = "$lsi_ignored_shield_counters$" + description = "Warning threshold for shield counter." + } + "-Ib" = { + value = "$lsi_ignored_bbm_counters$" + description = "Warning threshold for BBM counter." + } + "-b" = { + value = "$lsi_bbu$" + description = "Define if BBU is present and it's state should be checked." + } + "--noenclosures" = { + set_if = "$lsi_noenclosures$" + description = "Define if enclosures are present." + } + "--nosudo" = { + set_if = "$lsi_nosudo$" + description = "Do not use sudo when running storcli." + } + "--nocleanlogs" = { + set_if = "$lsi_nocleanlogs$" + description = "Do not clean up the log files after executing storcli checks." + } } - - vars.storcli_path = "/usr/sbin/storcli" } diff --git a/itl/plugins-contrib.d/smart-attributes.conf b/itl/plugins-contrib.d/smart-attributes.conf index 25e3333dd..20eb34584 100644 --- a/itl/plugins-contrib.d/smart-attributes.conf +++ b/itl/plugins-contrib.d/smart-attributes.conf @@ -5,7 +5,7 @@ object CheckCommand "smart-attributes" { import "plugin-check-command" - command = [ PluginDir + "/check_smart_attributes" ] + command = [ PluginContribDir + "/check_smart_attributes" ] arguments = { "-dbj" = { @@ -20,5 +20,5 @@ object CheckCommand "smart-attributes" { } } - vars.smart_attributes_config_path = SysconfDir + "/icinga2/plugins-config/check_smartdb.json" + vars.smart_attributes_config_path = ConfigDir + "/plugins-config/check_smartdb.json" } diff --git a/itl/plugins-contrib.d/storage.conf b/itl/plugins-contrib.d/storage.conf index 4cddb4fdd..dca080eb0 100644 --- a/itl/plugins-contrib.d/storage.conf +++ b/itl/plugins-contrib.d/storage.conf @@ -1,24 +1,7 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "glusterfs" { - command = [ "sudo", PluginDir + "/check_glusterfs" ] + command = [ "sudo", PluginContribDir + "/check_glusterfs" ] arguments = { "--perfdata" = { @@ -56,3 +39,81 @@ object CheckCommand "glusterfs" { vars.glusterfs_inode_warning = 90 vars.glusterfs_inode_critical = 95 } + +object CheckCommand "ceph" { + command = [ PluginContribDir + "/check_ceph.py" ] + + arguments = { + "-e" = { + value = "$ceph_exec_dir$" + required = false + description = "ceph executable [/usr/bin/ceph]" + } + "-c" = { + value = "$ceph_conf_file$" + required = false + description = "alternative ceph conf file" + } + "-m" = { + value = "$ceph_mon_address$" + required = false + description = "ceph monitor address[:port]" + } + "-i" = { + value = "$ceph_client_id$" + required = false + description = "ceph client id" + } + "-n" = { + value = "$ceph_client_name$" + required = false + description = "ceph client name" + } + "-k" = { + value = "$ceph_client_key$" + required = false + description = "ceph client keyring file" + } + "-w" = { + value = "$ceph_whitelist$" + required = false + description = "whitelist regexp for ceph health warnings" + } + "-d" = { + set_if = "$ceph_details$" + description = "exec 'ceph health detail'" + } + } +} + +object CheckCommand "btrfs" { + import "plugin-check-command" + command = [ "sudo", PluginContribDir + "/check_btrfs" ] + + arguments = { + "--allocated-warning-gib" = { + value = "$btrfs_awg$" + description = "Exit with WARNING status if less than the specified amount of disk space (in GiB) is unallocated" + } + "--allocated-critical-gib" = { + value = "$btrfs_acg$" + description = "Exit with CRITICAL status if less than the specified amount of disk space (in GiB) is unallocated" + } + "--allocated-warning-percent" = { + value = "$btrfs_awp$" + description = "Exit with WARNING status if more than the specified percent of disk space is allocated" + } + "--allocated-critical-percent" = { + value = "$btrfs_acp$" + description = "Exit with CRITICAL status if more than the specified percent of disk space is allocated" + } + "--mountpoint" = { + value = "$btrfs_mountpoint$" + description = "Path to the BTRFS mountpoint" + required = true + } + } + vars.btrfs_awp = 80 + vars.btrfs_acp = 90 +} + diff --git a/itl/plugins-contrib.d/virtualization.conf b/itl/plugins-contrib.d/virtualization.conf index 7a1401c05..19a249db8 100644 --- a/itl/plugins-contrib.d/virtualization.conf +++ b/itl/plugins-contrib.d/virtualization.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "esxi_hardware" { command = [ PluginContribDir + "/check_esxi_hardware.py" ] @@ -49,6 +32,10 @@ object CheckCommand "esxi_hardware" { value = "$esxi_hardware_ignore$" description = "comma-separated list of elements to ignore" } + "-r" = { + set_if = "$esxi_hardware_regex$" + description = "Allow regular expression lookups of elements in ignore list" + } "-p" = { set_if = "$esxi_hardware_perfdata$" description = "collect performance data for pnp4nagios" @@ -81,6 +68,7 @@ object CheckCommand "esxi_hardware" { vars.esxi_hardware_host = "$address$" vars.esxi_hardware_port = 5989 + vars.esxi_hardware_regex = false vars.esxi_hardware_perfdata = false vars.esxi_hardware_nopower = false vars.esxi_hardware_novolts = false diff --git a/itl/plugins-contrib.d/vmware.conf b/itl/plugins-contrib.d/vmware.conf index 2663ca5e2..63ee3fa06 100644 --- a/itl/plugins-contrib.d/vmware.conf +++ b/itl/plugins-contrib.d/vmware.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ /** * main vmware-esx template @@ -686,6 +669,11 @@ object CheckCommand "vmware-esx-soap-host-runtime" { arguments += { "--select" = "runtime" + "--exclude" = "$vmware_exclude$" + "--include" = "$vmware_include$" + "--isregexp" = { + set_if = "$vmware_isregexp$" + } } } diff --git a/itl/plugins-contrib.d/web.conf b/itl/plugins-contrib.d/web.conf index 49bf33166..c7c4d2358 100644 --- a/itl/plugins-contrib.d/web.conf +++ b/itl/plugins-contrib.d/web.conf @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ object CheckCommand "webinject" { command = [ PluginContribDir + "/check_webinject" ] @@ -342,12 +325,20 @@ object CheckCommand "apache-status" { } "-s" = { set_if = "$apache_status_ssl$" - description = "Wether we should use HTTPS instead of HTTP" + description = "Whether we should use HTTPS instead of HTTP" } "-u" = { value = "$apache_status_uri$" description = "Specific URL to use, instead of the default 'http:///server-status'" } + "-U" = { + value = "$apache_status_username$" + description = "username for basic auth" + } + "-P" = { + value = "$apache_status_password$" + description = "password for basic auth" + } "-w" = { value = "$apache_status_warning$" description = "number of open slots, busy workers and idle workers that will cause a WARNING" @@ -360,6 +351,14 @@ object CheckCommand "apache-status" { value = "$apache_status_timeout$" description = "timeout in seconds" } + "-N" = { + set_if = "$apache_status_no_validate$" + description = "do not validate the SSL certificate chain" + } + "-R" = { + set_if = "$apache_status_unreachable$" + description = "CRITICAL if socket timed out or http code >= 500" + } } vars.apache_status_address = "$check_address$" @@ -561,3 +560,176 @@ object CheckCommand "ssl_cert" { vars.ssl_cert_address = "$check_address$" vars.ssl_cert_port = 443 } + +object CheckCommand "varnish" { + import "plugin-check-command" + command = [ PluginContribDir + "/check_varnish" ] + + arguments = { + "-n" = { + value = "$varnish_name$" + description = "Specify the Varnish instance name" + } + "-p" = { + value = "$varnish_param$" + description = "Specify the parameter to check (see below). The default is 'ratio'." + } + "-c" = { + value = "$varnish_critical$" + description = "Set critical threshold: [@][lo:]hi" + } + "-w" = { + value = "$varnish_warning$" + description = "Set warning threshold: [@][lo:]hi" + } + } +} + +object CheckCommand "haproxy" { + import "plugin-check-command" + command = [ PluginContribDir + "/check_haproxy" ] + + arguments = { + "--username" = { + value = "$haproxy_username$" + description = "Username for HTTP Auth" + } + "--password" = { + value = "$haproxy_password$" + description = "Password for HTTP Auth" + } + "--url" = { + value = "$haproxy_url$" + description = "URL of the HAProxy csv statistics page" + required = true + } + "--timeout" = { + value = "$haproxy_timeout$" + description = "Seconds before plugin times out (default: 10)" + } + "-w" = { + value = "$haproxy_warning$" + description = "Warning request time threshold (in seconds)" + } + "-c" = { + value = "$haproxy_critical$" + description = "Critical request time threshold (in seconds)" + } + } +} + +object CheckCommand "haproxy_status" { + import "plugin-check-command" + command = [ PluginContribDir + "/check_haproxy_status" ] + + arguments = { + "--defaults" = { + value = "$haproxy_status_default$" + description = "Set/Override the defaults which will be applied to all checks (unless specifically set by --overrides)." + } + "--frontends" = { + set_if = "$haproxy_status_frontends$" + description = "Enable checks for the frontends in HAProxy (that they're marked as OPEN and the session limits haven't been reached)." + } + "--nofrontends" = { + set_if = "$haproxy_status_nofrontends$" + description = "Disable checks for the frontends in HAProxy (that they're marked as OPEN and the session limits haven't been reached)." + } + "--backends" = { + set_if = "$haproxy_status_backends$" + description = "Enable checks for the backends in HAProxy (that they have the required quorum of servers, and that the session limits haven't been reached)." + } + "--nobackends" = { + set_if = "$haproxy_status_nobackends$" + description = "Disable checks for the backends in HAProxy (that they have the required quorum of servers, and that the session limits haven't been reached)." + } + "--servers" = { + set_if = "$haproxy_status_servers$" + description = "Enable checks for the servers in HAProxy (that they haven't reached the limits for the sessions or for queues)." + } + "--noservers" = { + set_if = "$haproxy_status_noservers$" + description = "Disable checks for the servers in HAProxy (that they haven't reached the limits for the sessions or for queues)." + } + "--overrides" = { + value = "$haproxy_status_overrides$" + description = "Override the defaults for a particular frontend or backend, in the form {name}:{override}, where {override} is the same format as --defaults above." + } + "--socket" = { + value = "$haproxy_status_socket$" + description = "Path to the socket check_haproxy should connect to" + required = true + } + } +} + +object CheckCommand "phpfpm_status" { + import "plugin-check-command" + command = [ PluginContribDir + "/check_phpfpm_status" ] + + arguments = { + "-H" = { + value = "$phpfpm_status_hostname$" + description = "name or IP address of host to check" + required = true + } + "-p" = { + value = "$phpfpm_status_port$" + description = "Http port, or Fastcgi port when using --fastcgi" + } + "-u" = { + value = "$phpfpm_status_url$" + description = "Specific URL (only the path part of it in fact) to use, instead of the default /fpm-status" + } + "-s" = { + value = "$phpfpm_status_servername$" + description = "ServerName, (host header of HTTP request) use it if you specified an IP in -H to match the good Virtualhost in your target" + } + "-f" = { + set_if = "$phpfpm_status_fastcgi$" + description = "Connect directly to php-fpm via network or local socket, using fastcgi protocol instead of HTTP." + } + "-U" = { + value = "$phpfpm_status_user$" + description = "Username for basic auth" + } + "-P" = { + value = "$phpfpm_status_pass$" + description = "Password for basic auth" + } + "-r" = { + value = "$phpfpm_status_realm$" + description = "Realm for basic auth" + } + "-d" = { + set_if = "$phpfpm_status_debug$" + description = "Debug mode (show http request response)" + } + "-t" = { + value = "$phpfpm_status_timeout$" + description = "timeout in seconds (Default: 15)" + } + "-S" = { + set_if = "$phpfpm_status_ssl$" + description = "Wether we should use HTTPS instead of HTTP. Note that you can give some extra parameters to this settings. Default value is 'TLSv1' but you could use things like 'TLSv1_1' or 'TLSV1_2' (or even 'SSLv23:!SSLv2:!SSLv3' for old stuff)." + } + "-x" = { + set_if = "$phpfpm_status_verifyssl$" + description = "verify certificate and hostname from ssl cert, default is 0 (no security), set it to 1 to really make SSL peer name and certificater checks." + } + "-X" = { + value = "$phpfpm_status_cacert$" + description = "Full path to the cacert.pem certificate authority used to verify ssl certificates (use with --verifyssl). if not given the cacert from Mozilla::CA cpan plugin will be used." + } + "-w" = { + value = "$phpfpm_status_warn$" + description = "MIN_AVAILABLE_PROCESSES,PROC_MAX_REACHED,QUEUE_MAX_REACHED number of available workers, or max states reached that will cause a warning. -1 for no warning" + } + "-c" = { + value = "$phpfpm_status_critical$" + description = "MIN_AVAILABLE_PROCESSES,PROC_MAX_REACHED,QUEUE_MAX_REACHED number of available workers, or max states reached that will cause an error, -1 for no CRITICAL" + } + } + + vars.phpfpm_status_hostname = "$address$" +} diff --git a/itl/windows-plugins b/itl/windows-plugins index e95f0a1e6..a6e00db86 100644 --- a/itl/windows-plugins +++ b/itl/windows-plugins @@ -1,20 +1,3 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ include "command-plugins-windows.conf" diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 67ad4afee..004fc154c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ add_subdirectory(base) add_subdirectory(cli) diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index 29daa94a0..100ca27a7 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -1,22 +1,8 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ mkclass_target(application.ti application-ti.cpp application-ti.hpp) mkclass_target(configobject.ti configobject-ti.cpp configobject-ti.hpp) +mkclass_target(configuration.ti configuration-ti.cpp configuration-ti.hpp) mkclass_target(datetime.ti datetime-ti.cpp datetime-ti.hpp) mkclass_target(filelogger.ti filelogger-ti.cpp filelogger-ti.hpp) mkclass_target(function.ti function-ti.cpp function-ti.hpp) @@ -27,12 +13,14 @@ mkclass_target(sysloglogger.ti sysloglogger-ti.cpp sysloglogger-ti.hpp) set(base_SOURCES i2-base.hpp - application.cpp application.hpp application-ti.hpp application-version.cpp + application.cpp application.hpp application-ti.hpp application-version.cpp application-environment.cpp array.cpp array.hpp array-script.cpp + atomic.hpp base64.cpp base64.hpp boolean.cpp boolean.hpp boolean-script.cpp configobject.cpp configobject.hpp configobject-ti.hpp configobject-script.cpp configtype.cpp configtype.hpp + configuration.cpp configuration.hpp configuration-ti.hpp configwriter.cpp configwriter.hpp console.cpp console.hpp context.cpp context.hpp @@ -47,20 +35,25 @@ set(base_SOURCES filelogger.cpp filelogger.hpp filelogger-ti.hpp function.cpp function.hpp function-ti.hpp function-script.cpp functionwrapper.hpp initialize.cpp initialize.hpp + io-engine.cpp io-engine.hpp json.cpp json.hpp json-script.cpp + lazy-init.hpp library.cpp library.hpp loader.cpp loader.hpp logger.cpp logger.hpp logger-ti.hpp math-script.cpp netstring.cpp netstring.hpp networkstream.cpp networkstream.hpp + namespace.cpp namespace.hpp namespace-script.cpp number.cpp number.hpp number-script.cpp object.cpp object.hpp object-script.cpp objectlock.cpp objectlock.hpp + object-packer.cpp object-packer.hpp objecttype.cpp objecttype.hpp perfdatavalue.cpp perfdatavalue.hpp perfdatavalue-ti.hpp primitivetype.cpp primitivetype.hpp process.cpp process.hpp + reference.cpp reference.hpp reference-script.cpp registry.hpp ringbuffer.cpp ringbuffer.hpp scriptframe.cpp scriptframe.hpp @@ -69,13 +62,13 @@ set(base_SOURCES serializer.cpp serializer.hpp singleton.hpp socket.cpp socket.hpp - socketevents.cpp socketevents-epoll.cpp socketevents-poll.cpp socketevents.hpp stacktrace.cpp stacktrace.hpp statsfunction.hpp stdiostream.cpp stdiostream.hpp stream.cpp stream.hpp streamlogger.cpp streamlogger.hpp streamlogger-ti.hpp string.cpp string.hpp string-script.cpp + stringbuilder.cpp stringbuilder.hpp sysloglogger.cpp sysloglogger.hpp sysloglogger-ti.hpp tcpsocket.cpp tcpsocket.hpp threadpool.cpp threadpool.hpp @@ -120,7 +113,7 @@ set_target_properties ( FOLDER Lib ) -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/cache/icinga2\")") -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/log/icinga2/crash\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_CACHEDIR}\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_LOGDIR}/crash\")") set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) diff --git a/lib/base/application-environment.cpp b/lib/base/application-environment.cpp new file mode 100644 index 000000000..819783f8d --- /dev/null +++ b/lib/base/application-environment.cpp @@ -0,0 +1,17 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/application.hpp" +#include "base/scriptglobal.hpp" + +using namespace icinga; + +String Application::GetAppEnvironment() +{ + Value defaultValue = Empty; + return ScriptGlobal::Get("Environment", &defaultValue); +} + +void Application::SetAppEnvironment(const String& name) +{ + ScriptGlobal::Set("Environment", name); +} diff --git a/lib/base/application-version.cpp b/lib/base/application-version.cpp index 0bb642519..d17775b73 100644 --- a/lib/base/application-version.cpp +++ b/lib/base/application-version.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/application.hpp" #include "icinga-version.h" diff --git a/lib/base/application.cpp b/lib/base/application.cpp index 374285404..4707f0d23 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/application.hpp" #include "base/application-ti.cpp" @@ -44,10 +27,9 @@ #endif /* __linux__ */ #ifdef _WIN32 #include +#else /* _WIN32 */ +#include #endif /* _WIN32 */ -#ifdef HAVE_SYSTEMD -#include -#endif /* HAVE_SYSTEMD */ using namespace icinga; @@ -59,6 +41,11 @@ bool Application::m_ShuttingDown = false; bool Application::m_RequestRestart = false; bool Application::m_RequestReopenLogs = false; pid_t Application::m_ReloadProcess = 0; + +#ifndef _WIN32 +pid_t Application::m_UmbrellaProcess = 0; +#endif /* _WIN32 */ + static bool l_Restarting = false; static bool l_InExceptionHandler = false; int Application::m_ArgC; @@ -90,7 +77,9 @@ void Application::Stop(bool runtimeRemoved) WSACleanup(); #endif /* _WIN32 */ +#ifdef _WIN32 ClosePidFile(true); +#endif /* _WIN32 */ ObjectImpl::Stop(runtimeRemoved); } @@ -138,8 +127,11 @@ void Application::InitializeBase() Loader::ExecuteDeferredInitializers(); - /* make sure the thread pool gets initialized */ + /* Make sure the thread pool gets initialized. */ GetTP().Start(); + + /* Make sure the timer thread gets initialized. */ + Timer::Initialize(); } void Application::UninitializeBase() @@ -165,10 +157,10 @@ void Application::SetResourceLimits() rlimit rl; # ifdef RLIMIT_NOFILE - rlim_t fileLimit = GetRLimitFiles(); + rlim_t fileLimit = Configuration::RLimitFiles; if (fileLimit != 0) { - if (fileLimit < GetDefaultRLimitFiles()) { + if (fileLimit < (rlim_t)GetDefaultRLimitFiles()) { Log(LogWarning, "Application") << "The user-specified value for RLimitFiles cannot be smaller than the default value (" << GetDefaultRLimitFiles() << "). Using the default value instead."; fileLimit = GetDefaultRLimitFiles(); @@ -186,10 +178,10 @@ void Application::SetResourceLimits() } # ifdef RLIMIT_NPROC - rlim_t processLimit = GetRLimitProcesses(); + rlim_t processLimit = Configuration::RLimitProcesses; if (processLimit != 0) { - if (processLimit < GetDefaultRLimitProcesses()) { + if (processLimit < (rlim_t)GetDefaultRLimitProcesses()) { Log(LogWarning, "Application") << "The user-specified value for RLimitProcesses cannot be smaller than the default value (" << GetDefaultRLimitProcesses() << "). Using the default value instead."; processLimit = GetDefaultRLimitProcesses(); @@ -225,10 +217,10 @@ void Application::SetResourceLimits() rlim_t stackLimit; - stackLimit = GetRLimitStack(); + stackLimit = Configuration::RLimitStack; if (stackLimit != 0) { - if (stackLimit < GetDefaultRLimitStack()) { + if (stackLimit < (rlim_t)GetDefaultRLimitStack()) { Log(LogWarning, "Application") << "The user-specified value for RLimitStack cannot be smaller than the default value (" << GetDefaultRLimitStack() << "). Using the default value instead."; stackLimit = GetDefaultRLimitStack(); @@ -300,65 +292,51 @@ void Application::SetArgV(char **argv) */ void Application::RunEventLoop() { - -#ifdef HAVE_SYSTEMD - sd_notify(0, "READY=1"); -#endif /* HAVE_SYSTEMD */ - double lastLoop = Utility::GetTime(); -mainloop: - while (!m_ShuttingDown && !m_RequestRestart) { - /* Watches for changes to the system time. Adjusts timers if necessary. */ - Utility::Sleep(2.5); + while (!m_ShuttingDown) { + if (m_RequestRestart) { + m_RequestRestart = false; // we are now handling the request, once is enough - if (m_RequestReopenLogs) { - Log(LogNotice, "Application", "Reopening log files"); - m_RequestReopenLogs = false; - OnReopenLogs(); +#ifdef _WIN32 + // are we already restarting? ignore request if we already are + if (!l_Restarting) { + l_Restarting = true; + m_ReloadProcess = StartReloadProcess(); + } +#else /* _WIN32 */ + Log(LogNotice, "Application") + << "Got reload command, forwarding to umbrella process (PID " << m_UmbrellaProcess << ")"; + + (void)kill(m_UmbrellaProcess, SIGHUP); +#endif /* _WIN32 */ + } else { + /* Watches for changes to the system time. Adjusts timers if necessary. */ + Utility::Sleep(2.5); + + if (m_RequestReopenLogs) { + Log(LogNotice, "Application", "Reopening log files"); + m_RequestReopenLogs = false; + OnReopenLogs(); + } + + double now = Utility::GetTime(); + double timeDiff = lastLoop - now; + + if (std::fabs(timeDiff) > 15) { + /* We made a significant jump in time. */ + Log(LogInformation, "Application") + << "We jumped " + << (timeDiff < 0 ? "forward" : "backward") + << " in time: " << std::fabs(timeDiff) << " seconds"; + + Timer::AdjustTimers(-timeDiff); + } + + lastLoop = now; } - - double now = Utility::GetTime(); - double timeDiff = lastLoop - now; - -#ifdef HAVE_SYSTEMD - sd_notify(0, "WATCHDOG=1"); -#endif /* HAVE_SYSTEMD */ - - if (std::fabs(timeDiff) > 15) { - /* We made a significant jump in time. */ - Log(LogInformation, "Application") - << "We jumped " - << (timeDiff < 0 ? "forward" : "backward") - << " in time: " << std::fabs(timeDiff) << " seconds"; - - Timer::AdjustTimers(-timeDiff); - } - - lastLoop = now; } - if (m_RequestRestart) { - m_RequestRestart = false; // we are now handling the request, once is enough - -#ifdef HAVE_SYSTEMD - sd_notify(0, "RELOADING=1"); -#endif /* HAVE_SYSTEMD */ - - // are we already restarting? ignore request if we already are - if (l_Restarting) - goto mainloop; - - l_Restarting = true; - m_ReloadProcess = StartReloadProcess(); - - goto mainloop; - } - -#ifdef HAVE_SYSTEMD - sd_notify(0, "STOPPING=1"); -#endif /* HAVE_SYSTEMD */ - Log(LogInformation, "Application", "Shutting down..."); ConfigObject::StopObjects(); @@ -372,6 +350,11 @@ bool Application::IsShuttingDown() return m_ShuttingDown; } +bool Application::IsRestarting() +{ + return l_Restarting; +} + void Application::OnShutdown() { /* Nothing to do here. */ @@ -399,8 +382,6 @@ static void ReloadProcessCallback(const ProcessResult& pr) pid_t Application::StartReloadProcess() { - Log(LogInformation, "Application", "Got reload command: Starting new instance."); - // prepare arguments ArrayData args; args.push_back(GetExePath(m_ArgV[0])); @@ -416,14 +397,20 @@ pid_t Application::StartReloadProcess() args.push_back("--reload-internal"); args.push_back(Convert::ToString(Utility::GetPid())); #else /* _WIN32 */ - args.push_back("--restart-service"); - args.push_back("icinga2"); + args.push_back("--validate"); #endif /* _WIN32 */ + double reloadTimeout = Application::GetReloadTimeout(); + Process::Ptr process = new Process(Process::PrepareCommand(new Array(std::move(args)))); - process->SetTimeout(300); + process->SetTimeout(reloadTimeout); process->Run(&ReloadProcessCallback); + Log(LogInformation, "Application") + << "Got reload command: Started new instance with PID '" + << (unsigned long)(process->GetPID()) << "' (timeout is " + << reloadTimeout << "s)."; + return process->GetPID(); } @@ -456,6 +443,18 @@ void Application::RequestReopenLogs() m_RequestReopenLogs = true; } +#ifndef _WIN32 +/** + * Sets the PID of the Icinga umbrella process. + * + * @param pid The PID of the Icinga umbrella process. + */ +void Application::SetUmbrellaProcess(pid_t pid) +{ + m_UmbrellaProcess = pid; +} +#endif /* _WIN32 */ + /** * Retrieves the full path of the executable. * @@ -490,8 +489,8 @@ String Application::GetExePath(const String& argv0) } if (!foundSlash) { - const char *pathEnv = getenv("PATH"); - if (pathEnv) { + String pathEnv = Utility::GetFromEnvironment("PATH"); + if (!pathEnv.IsEmpty()) { std::vector paths = String(pathEnv).Split(":"); bool foundPath = false; @@ -537,34 +536,44 @@ String Application::GetExePath(const String& argv0) */ void Application::DisplayInfoMessage(std::ostream& os, bool skipVersion) { - os << "Application information:" << "\n"; - + /* icinga-app prints its own version information, stack traces need it here. */ if (!skipVersion) - os << " Application version: " << GetAppVersion() << "\n"; + os << " Application version: " << GetAppVersion() << "\n\n"; - os << " Installation root: " << GetPrefixDir() << "\n" - << " Sysconf directory: " << GetSysconfDir() << "\n" - << " Run directory: " << GetRunDir() << "\n" - << " Local state directory: " << GetLocalStateDir() << "\n" - << " Package data directory: " << GetPkgDataDir() << "\n" - << " State path: " << GetStatePath() << "\n" - << " Modified attributes path: " << GetModAttrPath() << "\n" - << " Objects path: " << GetObjectsPath() << "\n" - << " Vars path: " << GetVarsPath() << "\n" - << " PID path: " << GetPidPath() << "\n"; - - os << "\n" - << "System information:" << "\n" + os << "System information:\n" << " Platform: " << Utility::GetPlatformName() << "\n" << " Platform version: " << Utility::GetPlatformVersion() << "\n" << " Kernel: " << Utility::GetPlatformKernel() << "\n" << " Kernel version: " << Utility::GetPlatformKernelVersion() << "\n" << " Architecture: " << Utility::GetPlatformArchitecture() << "\n"; - os << "\n" - << "Build information:" << "\n" - << " Compiler: " << ScriptGlobal::Get("BuildCompilerName") << " " << ScriptGlobal::Get("BuildCompilerVersion") << "\n" - << " Build host: " << ScriptGlobal::Get("BuildHostName") << "\n"; + Namespace::Ptr systemNS = ScriptGlobal::Get("System"); + + os << "\nBuild information:\n" + << " Compiler: " << systemNS->Get("BuildCompilerName") << " " << systemNS->Get("BuildCompilerVersion") << "\n" + << " Build host: " << systemNS->Get("BuildHostName") << "\n"; + + os << "\nApplication information:\n" + << "\nGeneral paths:\n" + << " Config directory: " << Configuration::ConfigDir << "\n" + << " Data directory: " << Configuration::DataDir << "\n" + << " Log directory: " << Configuration::LogDir << "\n" + << " Cache directory: " << Configuration::CacheDir << "\n" + << " Spool directory: " << Configuration::SpoolDir << "\n" + << " Run directory: " << Configuration::InitRunDir << "\n" + << "\nOld paths (deprecated):\n" + << " Installation root: " << Configuration::PrefixDir << "\n" + << " Sysconf directory: " << Configuration::SysconfDir << "\n" + << " Run directory (base): " << Configuration::RunDir << "\n" + << " Local state directory: " << Configuration::LocalStateDir << "\n" + << "\nInternal paths:\n" + << " Package data directory: " << Configuration::PkgDataDir << "\n" + << " State path: " << Configuration::StatePath << "\n" + << " Modified attributes path: " << Configuration::ModAttrPath << "\n" + << " Objects path: " << Configuration::ObjectsPath << "\n" + << " Vars path: " << Configuration::VarsPath << "\n" + << " PID path: " << Configuration::PidPath << "\n"; + } /** @@ -581,7 +590,7 @@ void Application::DisplayBugMessage(std::ostream& os) String Application::GetCrashReportFilename() { - return GetLocalStateDir() + "/log/icinga2/crash/report." + Convert::ToString(Utility::GetTime()); + return Configuration::LogDir + "/crash/report." + Convert::ToString(Utility::GetTime()); } @@ -680,29 +689,6 @@ void Application::AttachDebugger(const String& filename, bool interactive) #endif /* _WIN32 */ } -#ifndef _WIN32 -/** - * Signal handler for SIGINT and SIGTERM. Prepares the application for cleanly - * shutting down during the next execution of the event loop. - * - * @param - The signal number. - */ -void Application::SigIntTermHandler(int signum) -{ - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_DFL; - sigaction(signum, &sa, nullptr); - - Application::Ptr instance = Application::GetInstance(); - - if (!instance) - return; - - instance->RequestShutdown(); -} -#endif /* _WIN32 */ - /** * Signal handler for SIGUSR1. This signal causes Icinga to re-open * its log files and is mainly for use by logrotate. @@ -711,38 +697,12 @@ void Application::SigIntTermHandler(int signum) */ void Application::SigUsr1Handler(int) { + Log(LogInformation, "Application") + << "Received USR1 signal, reopening application logs."; + RequestReopenLogs(); } -/** - * Signal handler for SIGUSR2. Hands over PID to child and commits suicide - * - * @param - The signal number. - */ -void Application::SigUsr2Handler(int) -{ - Log(LogInformation, "Application", "Reload requested, letting new process take over."); -#ifdef HAVE_SYSTEMD - sd_notifyf(0, "MAINPID=%lu", (unsigned long) m_ReloadProcess); -#endif /* HAVE_SYSTEMD */ - - /* Write the PID of the new process to the pidfile before this - * process exits to keep systemd happy. - */ - Application::Ptr instance = GetInstance(); - try { - instance->UpdatePidFile(GetPidPath(), m_ReloadProcess); - } catch (const std::exception&) { - /* abort restart */ - Log(LogCritical, "Application", "Cannot update PID file. Aborting restart operation."); - return; - } - - instance->ClosePidFile(false); - - Exit(0); -} - /** * Signal handler for SIGABRT. Helps with debugging ASSERT()s. * @@ -774,7 +734,7 @@ void Application::SigAbrtHandler(int) } } - bool interactive_debugger = Convert::ToBool(ScriptGlobal::Get("AttachDebugger")); + bool interactive_debugger = Configuration::AttachDebugger; if (!interactive_debugger) { std::ofstream ofs; @@ -884,7 +844,7 @@ void Application::ExceptionHandler() } } - bool interactive_debugger = Convert::ToBool(ScriptGlobal::Get("AttachDebugger")); + bool interactive_debugger = Configuration::AttachDebugger; if (!interactive_debugger) { std::ofstream ofs; @@ -989,26 +949,21 @@ int Application::Run() #ifndef _WIN32 struct sigaction sa; memset(&sa, 0, sizeof(sa)); - sa.sa_handler = &Application::SigIntTermHandler; - sigaction(SIGINT, &sa, nullptr); - sigaction(SIGTERM, &sa, nullptr); - sa.sa_handler = &Application::SigUsr1Handler; sigaction(SIGUSR1, &sa, nullptr); - - sa.sa_handler = &Application::SigUsr2Handler; - sigaction(SIGUSR2, &sa, nullptr); #else /* _WIN32 */ SetConsoleCtrlHandler(&Application::CtrlHandler, TRUE); #endif /* _WIN32 */ +#ifdef _WIN32 try { - UpdatePidFile(GetPidPath()); + UpdatePidFile(Configuration::PidPath); } catch (const std::exception&) { Log(LogCritical, "Application") - << "Cannot update PID file '" << GetPidPath() << "'. Aborting."; + << "Cannot update PID file '" << Configuration::PidPath << "'. Aborting."; return EXIT_FAILURE; } +#endif /* _WIN32 */ SetMainTime(Utility::GetTime()); @@ -1088,7 +1043,7 @@ void Application::ClosePidFile(bool unlink) if (m_PidFile) { if (unlink) { - String pidpath = GetPidPath(); + String pidpath = Configuration::PidPath; ::unlink(pidpath.CStr()); } @@ -1156,463 +1111,24 @@ pid_t Application::ReadPidFile(const String& filename) return runningpid; } - -/** - * Retrieves the path of the installation prefix. - * - * @returns The path. - */ -String Application::GetPrefixDir() -{ - return ScriptGlobal::Get("PrefixDir"); -} - -/** - * Sets the path for the installation prefix. - * - * @param path The new path. - */ -void Application::DeclarePrefixDir(const String& path) -{ - if (!ScriptGlobal::Exists("PrefixDir")) - ScriptGlobal::Set("PrefixDir", path); -} - -/** - * Retrives the path of the sysconf dir. - * - * @returns The path. - */ -String Application::GetSysconfDir() -{ - return ScriptGlobal::Get("SysconfDir"); -} - -/** - * Sets the path of the sysconf dir. - * - * @param path The new path. - */ -void Application::DeclareSysconfDir(const String& path) -{ - if (!ScriptGlobal::Exists("SysconfDir")) - ScriptGlobal::Set("SysconfDir", path); -} - -/** - * Retrieves the path for the run dir. - * - * @returns The path. - */ -String Application::GetRunDir() -{ - return ScriptGlobal::Get("RunDir"); -} - -/** - * Sets the path of the run dir. - * - * @param path The new path. - */ -void Application::DeclareRunDir(const String& path) -{ - if (!ScriptGlobal::Exists("RunDir")) - ScriptGlobal::Set("RunDir", path); -} - -/** - * Retrieves the path for the local state dir. - * - * @returns The path. - */ -String Application::GetLocalStateDir() -{ - return ScriptGlobal::Get("LocalStateDir"); -} - -/** - * Sets the path for the local state dir. - * - * @param path The new path. - */ -void Application::DeclareLocalStateDir(const String& path) -{ - if (!ScriptGlobal::Exists("LocalStateDir")) - ScriptGlobal::Set("LocalStateDir", path); -} - -/** - * Retrieves the path for the local state dir. - * - * @returns The path. - */ -String Application::GetZonesDir() -{ - return ScriptGlobal::Get("ZonesDir", &Empty); -} - -/** - * Sets the path of the zones dir. - * - * @param path The new path. - */ -void Application::DeclareZonesDir(const String& path) -{ - if (!ScriptGlobal::Exists("ZonesDir")) - ScriptGlobal::Set("ZonesDir", path); -} - -/** - * Retrieves the path for the package data dir. - * - * @returns The path. - */ -String Application::GetPkgDataDir() -{ - String defaultValue = ""; - return ScriptGlobal::Get("PkgDataDir", &Empty); -} - -/** - * Sets the path for the package data dir. - * - * @param path The new path. - */ -void Application::DeclarePkgDataDir(const String& path) -{ - if (!ScriptGlobal::Exists("PkgDataDir")) - ScriptGlobal::Set("PkgDataDir", path); -} - -/** - * Retrieves the path for the include conf dir. - * - * @returns The path. - */ -String Application::GetIncludeConfDir() -{ - return ScriptGlobal::Get("IncludeConfDir", &Empty); -} - -/** - * Sets the path for the package data dir. - * - * @param path The new path. - */ -void Application::DeclareIncludeConfDir(const String& path) -{ - if (!ScriptGlobal::Exists("IncludeConfDir")) - ScriptGlobal::Set("IncludeConfDir", path); -} - -/** - * Retrieves the path for the state file. - * - * @returns The path. - */ -String Application::GetStatePath() -{ - return ScriptGlobal::Get("StatePath", &Empty); -} - -/** - * Sets the path for the state file. - * - * @param path The new path. - */ -void Application::DeclareStatePath(const String& path) -{ - if (!ScriptGlobal::Exists("StatePath")) - ScriptGlobal::Set("StatePath", path); -} - -/** - * Retrives the path of the sysconfig file. - * - * @returns The path. - */ -String Application::GetSysconfigFile(void) -{ - return ScriptGlobal::Get("SysconfigFile"); -} - -/** - * Sets the path of the sysconfig file. - * - * @param path The new path. - */ -void Application::DeclareSysconfigFile(const String& path) -{ - if (!ScriptGlobal::Exists("SysconfigFile")) - ScriptGlobal::Set("SysconfigFile", path); -} - -/** - * Retrieves the path for the modified attributes file. - * - * @returns The path. - */ -String Application::GetModAttrPath() -{ - return ScriptGlobal::Get("ModAttrPath", &Empty); -} - -/** - * Sets the path for the modified attributes file. - * - * @param path The new path. - */ -void Application::DeclareModAttrPath(const String& path) -{ - if (!ScriptGlobal::Exists("ModAttrPath")) - ScriptGlobal::Set("ModAttrPath", path); -} - -/** - * Retrieves the path for the objects file. - * - * @returns The path. - */ -String Application::GetObjectsPath() -{ - return ScriptGlobal::Get("ObjectsPath", &Empty); -} - -/** - * Sets the path for the objects file. - * - * @param path The new path. - */ -void Application::DeclareObjectsPath(const String& path) -{ - if (!ScriptGlobal::Exists("ObjectsPath")) - ScriptGlobal::Set("ObjectsPath", path); -} - -/** -* Retrieves the path for the vars file. -* -* @returns The path. -*/ -String Application::GetVarsPath() -{ - return ScriptGlobal::Get("VarsPath", &Empty); -} - -/** -* Sets the path for the vars file. -* -* @param path The new path. -*/ -void Application::DeclareVarsPath(const String& path) -{ - if (!ScriptGlobal::Exists("VarsPath")) - ScriptGlobal::Set("VarsPath", path); -} - -/** - * Retrieves the path for the PID file. - * - * @returns The path. - */ -String Application::GetPidPath() -{ - return ScriptGlobal::Get("PidPath", &Empty); -} - -/** - * Sets the path for the PID file. - * - * @param path The new path. - */ -void Application::DeclarePidPath(const String& path) -{ - if (!ScriptGlobal::Exists("PidPath")) - ScriptGlobal::Set("PidPath", path); -} - -/** - * Retrieves the name of the user. - * - * @returns The name. - */ -String Application::GetRunAsUser() -{ - return ScriptGlobal::Get("RunAsUser"); -} - -/** - * Sets the name of the user. - * - * @param path The new user name. - */ -void Application::DeclareRunAsUser(const String& user) -{ - if (!ScriptGlobal::Exists("RunAsUser")) - ScriptGlobal::Set("RunAsUser", user); -} - -/** - * Retrieves the name of the group. - * - * @returns The name. - */ -String Application::GetRunAsGroup() -{ - return ScriptGlobal::Get("RunAsGroup"); -} - -/** - * Sets the name of the group. - * - * @param path The new group name. - */ -void Application::DeclareRunAsGroup(const String& group) -{ - if (!ScriptGlobal::Exists("RunAsGroup")) - ScriptGlobal::Set("RunAsGroup", group); -} - -/** - * Retrieves the file rlimit. - * - * @returns The limit. - */ -int Application::GetRLimitFiles() -{ - return ScriptGlobal::Get("RLimitFiles"); -} - int Application::GetDefaultRLimitFiles() { return 16 * 1024; } -/** - * Sets the file rlimit. - * - * @param path The new file rlimit. - */ -void Application::DeclareRLimitFiles(int limit) -{ - if (!ScriptGlobal::Exists("RLimitFiles")) - ScriptGlobal::Set("RLimitFiles", limit); -} - -/** - * Retrieves the process rlimit. - * - * @returns The limit. - */ -int Application::GetRLimitProcesses() -{ - return ScriptGlobal::Get("RLimitProcesses"); -} - int Application::GetDefaultRLimitProcesses() { return 16 * 1024; } -/** - * Sets the process rlimit. - * - * @param path The new process rlimit. - */ -void Application::DeclareRLimitProcesses(int limit) -{ - if (!ScriptGlobal::Exists("RLimitProcesses")) - ScriptGlobal::Set("RLimitProcesses", limit); -} - -/** - * Retrieves the stack rlimit. - * - * @returns The limit. - */ -int Application::GetRLimitStack() -{ - return ScriptGlobal::Get("RLimitStack"); -} - int Application::GetDefaultRLimitStack() { return 256 * 1024; } -/** - * Sets the stack rlimit. - * - * @param path The new stack rlimit. - */ -void Application::DeclareRLimitStack(int limit) +double Application::GetReloadTimeout() { - if (!ScriptGlobal::Exists("RLimitStack")) - ScriptGlobal::Set("RLimitStack", limit); -} - -/** - * Sets the concurrency level. - * - * @param path The new concurrency level. - */ -void Application::DeclareConcurrency(int ncpus) -{ - if (!ScriptGlobal::Exists("Concurrency")) - ScriptGlobal::Set("Concurrency", ncpus); -} - -/** - * Retrieves the concurrency level. - * - * @returns The concurrency level. - */ -int Application::GetConcurrency() -{ - Value defaultConcurrency = std::thread::hardware_concurrency(); - return ScriptGlobal::Get("Concurrency", &defaultConcurrency); -} - -/** - * Sets the max concurrent checks. - * - * @param maxChecks The new limit. - */ -void Application::SetMaxConcurrentChecks(int maxChecks) -{ - ScriptGlobal::Set("MaxConcurrentChecks", maxChecks); -} - -/** - * Sets the max concurrent checks. - * - * @param maxChecks The new limit. - */ -void Application::DeclareMaxConcurrentChecks(int maxChecks) -{ - if (!ScriptGlobal::Exists("MaxConcurrentChecks")) - ScriptGlobal::Set("MaxConcurrentChecks", maxChecks); -} - -/** - * Retrieves the max concurrent checks. - * - * @returns The max number of concurrent checks. - */ -int Application::GetMaxConcurrentChecks() -{ - Value defaultMaxConcurrentChecks = GetDefaultMaxConcurrentChecks(); - return ScriptGlobal::Get("MaxConcurrentChecks", &defaultMaxConcurrentChecks); -} - -/** - * Retrieves the default value for max concurrent checks. - * - * @returns The default max number of concurrent checks. - */ -int Application::GetDefaultMaxConcurrentChecks() -{ - return 512; + return ScriptGlobal::Get("ReloadTimeout"); } /** diff --git a/lib/base/application.hpp b/lib/base/application.hpp index 03d83599b..a6b78ff09 100644 --- a/lib/base/application.hpp +++ b/lib/base/application.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef APPLICATION_H #define APPLICATION_H @@ -23,6 +6,7 @@ #include "base/i2-base.hpp" #include "base/application-ti.hpp" #include "base/logger.hpp" +#include "base/configuration.hpp" #include namespace icinga @@ -73,7 +57,12 @@ public: static void RequestRestart(); static void RequestReopenLogs(); +#ifndef _WIN32 + static void SetUmbrellaProcess(pid_t pid); +#endif /* _WIN32 */ + static bool IsShuttingDown(); + static bool IsRestarting(); static void SetDebuggingSeverity(LogSeverity severity); static LogSeverity GetDebuggingSeverity(); @@ -85,80 +74,24 @@ public: static String GetExePath(const String& argv0); - static String GetPrefixDir(); - static void DeclarePrefixDir(const String& path); - - static String GetSysconfDir(); - static void DeclareSysconfDir(const String& path); - - static String GetZonesDir(); - static void DeclareZonesDir(const String& path); - - static String GetRunDir(); - static void DeclareRunDir(const String& path); - - static String GetLocalStateDir(); - static void DeclareLocalStateDir(const String& path); - - static String GetPkgDataDir(); - static void DeclarePkgDataDir(const String& path); - - static String GetIncludeConfDir(); - static void DeclareIncludeConfDir(const String& path); - - static String GetSysconfigFile(void); - static void DeclareSysconfigFile(const String& path); - - static String GetStatePath(void); - static void DeclareStatePath(const String& path); - - static String GetModAttrPath(); - static void DeclareModAttrPath(const String& path); - - static String GetObjectsPath(); - static void DeclareObjectsPath(const String& path); - - static String GetVarsPath(); - static void DeclareVarsPath(const String& path); - - static String GetPidPath(); - static void DeclarePidPath(const String& path); - - static String GetRunAsUser(); - static void DeclareRunAsUser(const String& user); - - static String GetRunAsGroup(); - static void DeclareRunAsGroup(const String& group); - #ifdef _WIN32 static bool IsProcessElevated(); #endif /* _WIN32 */ - static int GetRLimitFiles(); static int GetDefaultRLimitFiles(); - static void DeclareRLimitFiles(int limit); - - static int GetRLimitProcesses(); static int GetDefaultRLimitProcesses(); - static void DeclareRLimitProcesses(int limit); - - static int GetRLimitStack(); static int GetDefaultRLimitStack(); - static void DeclareRLimitStack(int limit); - static int GetConcurrency(); - static void DeclareConcurrency(int ncpus); - - static int GetMaxConcurrentChecks(); - static int GetDefaultMaxConcurrentChecks(); - static void DeclareMaxConcurrentChecks(int maxChecks); - static void SetMaxConcurrentChecks(int maxChecks); + static double GetReloadTimeout(); static ThreadPool& GetTP(); static String GetAppVersion(); static String GetAppSpecVersion(); + static String GetAppEnvironment(); + static void SetAppEnvironment(const String& name); + static double GetStartTime(); static void SetStartTime(double ts); @@ -193,9 +126,13 @@ private: static pid_t m_ReloadProcess; /**< The PID of a subprocess doing a reload, only valid when l_Restarting==true */ static bool m_RequestReopenLogs; /**< Whether we should re-open log files. */ +#ifndef _WIN32 + static pid_t m_UmbrellaProcess; /**< The PID of the Icinga umbrella process */ +#endif /* _WIN32 */ + static int m_ArgC; /**< The number of command-line arguments. */ static char **m_ArgV; /**< Command-line arguments. */ - FILE *m_PidFile; /**< The PID file */ + FILE *m_PidFile = nullptr; /**< The PID file */ static bool m_Debugging; /**< Whether debugging is enabled. */ static LogSeverity m_DebuggingSeverity; /**< Whether debugging severity is set. */ static double m_StartTime; @@ -203,9 +140,7 @@ private: static bool m_ScriptDebuggerEnabled; static double m_LastReloadFailed; -#ifndef _WIN32 - static void SigIntTermHandler(int signum); -#else /* _WIN32 */ +#ifdef _WIN32 static BOOL WINAPI CtrlHandler(DWORD type); static LONG WINAPI SEHUnhandledExceptionFilter(PEXCEPTION_POINTERS exi); #endif /* _WIN32 */ @@ -214,7 +149,6 @@ private: static void SigAbrtHandler(int signum); static void SigUsr1Handler(int signum); - static void SigUsr2Handler(int signum); static void ExceptionHandler(); static String GetCrashReportFilename(); diff --git a/lib/base/application.ti b/lib/base/application.ti index a1dcd964f..3d5908a29 100644 --- a/lib/base/application.ti +++ b/lib/base/application.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" diff --git a/lib/base/array-script.cpp b/lib/base/array-script.cpp index c12585b1f..1c00f1106 100644 --- a/lib/base/array-script.cpp +++ b/lib/base/array-script.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/array.hpp" #include "base/function.hpp" @@ -124,22 +107,7 @@ static Value ArrayJoin(const Value& separator) ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); Array::Ptr self = static_cast(vframe->Self); REQUIRE_NOT_NULL(self); - - Value result; - bool first = true; - - ObjectLock olock(self); - for (const Value& item : self) { - if (first) { - first = false; - } else { - result = result + separator; - } - - result = result + item; - } - - return result; + return self->Join(separator); } static Array::Ptr ArrayReverse() diff --git a/lib/base/array.cpp b/lib/base/array.cpp index 30cf7eba3..08e06fad2 100644 --- a/lib/base/array.cpp +++ b/lib/base/array.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/array.hpp" #include "base/objectlock.hpp" @@ -62,13 +45,14 @@ Value Array::Get(SizeType index) const * * @param index The index. * @param value The value. + * @param overrideFrozen Whether to allow modifying frozen arrays. */ -void Array::Set(SizeType index, const Value& value) +void Array::Set(SizeType index, const Value& value, bool overrideFrozen) { ObjectLock olock(this); - if (m_Frozen) - BOOST_THROW_EXCEPTION(std::invalid_argument("Array must not be modified.")); + if (m_Frozen && !overrideFrozen) + BOOST_THROW_EXCEPTION(std::invalid_argument("Value in array must not be modified.")); m_Data.at(index) = value; } @@ -78,12 +62,13 @@ void Array::Set(SizeType index, const Value& value) * * @param index The index. * @param value The value. + * @param overrideFrozen Whether to allow modifying frozen arrays. */ -void Array::Set(SizeType index, Value&& value) +void Array::Set(SizeType index, Value&& value, bool overrideFrozen) { ObjectLock olock(this); - if (m_Frozen) + if (m_Frozen && !overrideFrozen) BOOST_THROW_EXCEPTION(std::invalid_argument("Array must not be modified.")); m_Data.at(index).Swap(value); @@ -93,12 +78,13 @@ void Array::Set(SizeType index, Value&& value) * Adds a value to the array. * * @param value The value. + * @param overrideFrozen Whether to allow modifying frozen arrays. */ -void Array::Add(Value value) +void Array::Add(Value value, bool overrideFrozen) { ObjectLock olock(this); - if (m_Frozen) + if (m_Frozen && !overrideFrozen) BOOST_THROW_EXCEPTION(std::invalid_argument("Array must not be modified.")); m_Data.push_back(std::move(value)); @@ -162,14 +148,15 @@ bool Array::Contains(const Value& value) const * * @param index The index * @param value The value to add + * @param overrideFrozen Whether to allow modifying frozen arrays. */ -void Array::Insert(SizeType index, Value value) +void Array::Insert(SizeType index, Value value, bool overrideFrozen) { ObjectLock olock(this); ASSERT(index <= m_Data.size()); - if (m_Frozen) + if (m_Frozen && !overrideFrozen) BOOST_THROW_EXCEPTION(std::invalid_argument("Array must not be modified.")); m_Data.insert(m_Data.begin() + index, std::move(value)); @@ -179,14 +166,18 @@ void Array::Insert(SizeType index, Value value) * Removes the specified index from the array. * * @param index The index. + * @param overrideFrozen Whether to allow modifying frozen arrays. */ -void Array::Remove(SizeType index) +void Array::Remove(SizeType index, bool overrideFrozen) { ObjectLock olock(this); - if (m_Frozen) + if (m_Frozen && !overrideFrozen) BOOST_THROW_EXCEPTION(std::invalid_argument("Array must not be modified.")); + if (index >= m_Data.size()) + BOOST_THROW_EXCEPTION(std::invalid_argument("Index to remove must be within bounds.")); + m_Data.erase(m_Data.begin() + index); } @@ -194,42 +185,43 @@ void Array::Remove(SizeType index) * Removes the item specified by the iterator from the array. * * @param it The iterator. + * @param overrideFrozen Whether to allow modifying frozen arrays. */ -void Array::Remove(Array::Iterator it) +void Array::Remove(Array::Iterator it, bool overrideFrozen) { ASSERT(OwnsLock()); - if (m_Frozen) + if (m_Frozen && !overrideFrozen) BOOST_THROW_EXCEPTION(std::invalid_argument("Array must not be modified.")); m_Data.erase(it); } -void Array::Resize(SizeType newSize) +void Array::Resize(SizeType newSize, bool overrideFrozen) { ObjectLock olock(this); - if (m_Frozen) + if (m_Frozen && !overrideFrozen) BOOST_THROW_EXCEPTION(std::invalid_argument("Array must not be modified.")); m_Data.resize(newSize); } -void Array::Clear() +void Array::Clear(bool overrideFrozen) { ObjectLock olock(this); - if (m_Frozen) + if (m_Frozen && !overrideFrozen) BOOST_THROW_EXCEPTION(std::invalid_argument("Array must not be modified.")); m_Data.clear(); } -void Array::Reserve(SizeType newSize) +void Array::Reserve(SizeType newSize, bool overrideFrozen) { ObjectLock olock(this); - if (m_Frozen) + if (m_Frozen && !overrideFrozen) BOOST_THROW_EXCEPTION(std::invalid_argument("Array must not be modified.")); m_Data.reserve(newSize); @@ -288,11 +280,11 @@ Array::Ptr Array::Reverse() const return result; } -void Array::Sort() +void Array::Sort(bool overrideFrozen) { ObjectLock olock(this); - if (m_Frozen) + if (m_Frozen && !overrideFrozen) BOOST_THROW_EXCEPTION(std::invalid_argument("Array must not be modified.")); std::sort(m_Data.begin(), m_Data.end()); @@ -305,6 +297,26 @@ String Array::ToString() const return msgbuf.str(); } +Value Array::Join(const Value& separator) const +{ + Value result; + bool first = true; + + ObjectLock olock(this); + + for (const Value& item : m_Data) { + if (first) { + first = false; + } else { + result = result + separator; + } + + result = result + item; + } + + return result; +} + Array::Ptr Array::Unique() const { std::set result; @@ -342,7 +354,7 @@ Value Array::GetFieldByName(const String& field, bool sandboxed, const DebugInfo return Get(index); } -void Array::SetFieldByName(const String& field, const Value& value, const DebugInfo& debugInfo) +void Array::SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) { ObjectLock olock(this); @@ -352,9 +364,9 @@ void Array::SetFieldByName(const String& field, const Value& value, const DebugI BOOST_THROW_EXCEPTION(ScriptError("Array index '" + Convert::ToString(index) + "' is out of bounds.", debugInfo)); if (static_cast(index) >= GetLength()) - Resize(index + 1); + Resize(index + 1, overrideFrozen); - Set(index, value); + Set(index, value, overrideFrozen); } Array::Iterator icinga::begin(const Array::Ptr& x) diff --git a/lib/base/array.hpp b/lib/base/array.hpp index 62bcf7e2e..2c9a9dda7 100644 --- a/lib/base/array.hpp +++ b/lib/base/array.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ARRAY_H #define ARRAY_H @@ -55,9 +38,9 @@ public: Array(std::initializer_list init); Value Get(SizeType index) const; - void Set(SizeType index, const Value& value); - void Set(SizeType index, Value&& value); - void Add(Value value); + void Set(SizeType index, const Value& value, bool overrideFrozen = false); + void Set(SizeType index, Value&& value, bool overrideFrozen = false); + void Add(Value value, bool overrideFrozen = false); Iterator Begin(); Iterator End(); @@ -65,14 +48,14 @@ public: size_t GetLength() const; bool Contains(const Value& value) const; - void Insert(SizeType index, Value value); - void Remove(SizeType index); - void Remove(Iterator it); + void Insert(SizeType index, Value value, bool overrideFrozen = false); + void Remove(SizeType index, bool overrideFrozen = false); + void Remove(Iterator it, bool overrideFrozen = false); - void Resize(SizeType newSize); - void Clear(); + void Resize(SizeType newSize, bool overrideFrozen = false); + void Clear(bool overrideFrozen = false); - void Reserve(SizeType newSize); + void Reserve(SizeType newSize, bool overrideFrozen = false); void CopyTo(const Array::Ptr& dest) const; Array::Ptr ShallowClone() const; @@ -108,15 +91,16 @@ public: Array::Ptr Reverse() const; - void Sort(); + void Sort(bool overrideFrozen = false); String ToString() const override; + Value Join(const Value& separator) const; Array::Ptr Unique() const; void Freeze(); Value GetFieldByName(const String& field, bool sandboxed, const DebugInfo& debugInfo) const override; - void SetFieldByName(const String& field, const Value& value, const DebugInfo& debugInfo) override; + void SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) override; private: std::vector m_Data; /**< The data for the array. */ diff --git a/lib/base/atomic.hpp b/lib/base/atomic.hpp new file mode 100644 index 000000000..0ebcddefb --- /dev/null +++ b/lib/base/atomic.hpp @@ -0,0 +1,43 @@ +/* Icinga 2 | (c) 2019 Icinga GmbH | GPLv2+ */ + +#ifndef ATOMIC_H +#define ATOMIC_H + +#include + +namespace icinga +{ + +/** + * Extends std::atomic with an atomic constructor. + * + * @ingroup base + */ +template +class Atomic : public std::atomic { +public: + /** + * Like std::atomic#atomic, but operates atomically + * + * @param desired Initial value + */ + inline Atomic(T desired) + { + this->store(desired); + } + + /** + * Like std::atomic#atomic, but operates atomically + * + * @param desired Initial value + * @param order Initial store operation's memory order + */ + inline Atomic(T desired, std::memory_order order) + { + this->store(desired, order); + } +}; + +} + +#endif /* ATOMIC_H */ diff --git a/lib/base/base64.cpp b/lib/base/base64.cpp index 368dca878..42999c39b 100644 --- a/lib/base/base64.cpp +++ b/lib/base/base64.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/base64.hpp" #include diff --git a/lib/base/base64.hpp b/lib/base/base64.hpp index 1cea938ca..8abbdbf36 100644 --- a/lib/base/base64.hpp +++ b/lib/base/base64.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef BASE64_H #define BASE64_H diff --git a/lib/base/boolean-script.cpp b/lib/base/boolean-script.cpp index 1116cc250..a9167ca87 100644 --- a/lib/base/boolean-script.cpp +++ b/lib/base/boolean-script.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/boolean.hpp" #include "base/convert.hpp" diff --git a/lib/base/boolean.cpp b/lib/base/boolean.cpp index 72305e1de..683a7271d 100644 --- a/lib/base/boolean.cpp +++ b/lib/base/boolean.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/boolean.hpp" #include "base/primitivetype.hpp" diff --git a/lib/base/boolean.hpp b/lib/base/boolean.hpp index 3175e9beb..6533cb418 100644 --- a/lib/base/boolean.hpp +++ b/lib/base/boolean.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef BOOLEAN_H #define BOOLEAN_H diff --git a/lib/base/configobject-script.cpp b/lib/base/configobject-script.cpp index 9f615eca7..46a9ca27c 100644 --- a/lib/base/configobject-script.cpp +++ b/lib/base/configobject-script.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" #include "base/dictionary.hpp" diff --git a/lib/base/configobject.cpp b/lib/base/configobject.cpp index 6aedfaca6..8d3e07656 100644 --- a/lib/base/configobject.cpp +++ b/lib/base/configobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" #include "base/configobject-ti.cpp" @@ -374,7 +357,7 @@ void ConfigObject::PreActivate() SetActive(true, true); } -void ConfigObject::Activate(bool runtimeCreated) +void ConfigObject::Activate(bool runtimeCreated, const Value& cookie) { CONTEXT("Activating object '" + GetName() + "' of type '" + GetReflectionType()->GetName() + "'"); @@ -389,7 +372,7 @@ void ConfigObject::Activate(bool runtimeCreated) SetAuthority(true); } - NotifyActive(); + NotifyActive(cookie); } void ConfigObject::Stop(bool runtimeRemoved) @@ -401,7 +384,7 @@ void ConfigObject::Stop(bool runtimeRemoved) SetStopCalled(true); } -void ConfigObject::Deactivate(bool runtimeRemoved) +void ConfigObject::Deactivate(bool runtimeRemoved, const Value& cookie) { CONTEXT("Deactivating object '" + GetName() + "' of type '" + GetReflectionType()->GetName() + "'"); @@ -420,7 +403,7 @@ void ConfigObject::Deactivate(bool runtimeRemoved) ASSERT(GetStopCalled()); - NotifyActive(); + NotifyActive(cookie); } void ConfigObject::OnConfigLoaded() @@ -522,16 +505,7 @@ void ConfigObject::DumpObjects(const String& filename, int attributeTypes) fp.close(); -#ifdef _WIN32 - _unlink(filename.CStr()); -#endif /* _WIN32 */ - - if (rename(tempFilename.CStr(), filename.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempFilename)); - } + Utility::RenameFile(tempFilename, filename); } void ConfigObject::RestoreObject(const String& message, int attributeTypes) @@ -571,7 +545,7 @@ void ConfigObject::RestoreObjects(const String& filename, int attributeTypes) unsigned long restored = 0; - WorkQueue upq(25000, Application::GetConcurrency()); + WorkQueue upq(25000, Configuration::Concurrency); upq.SetName("ConfigObject::RestoreObjects"); String message; @@ -617,13 +591,25 @@ void ConfigObject::RestoreObjects(const String& filename, int attributeTypes) void ConfigObject::StopObjects() { - for (const Type::Ptr& type : Type::GetAllTypes()) { + std::vector types = Type::GetAllTypes(); + + std::sort(types.begin(), types.end(), [](const Type::Ptr& a, const Type::Ptr& b) { + if (a->GetActivationPriority() > b->GetActivationPriority()) + return true; + return false; + }); + + for (const Type::Ptr& type : types) { auto *dtype = dynamic_cast(type.get()); if (!dtype) continue; for (const ConfigObject::Ptr& object : dtype->GetObjects()) { +#ifdef I2_DEBUG + Log(LogDebug, "ConfigObject") + << "Deactivate() called for config object '" << object->GetName() << "' with type '" << type->GetName() << "'."; +#endif /* I2_DEBUG */ object->Deactivate(); } } diff --git a/lib/base/configobject.hpp b/lib/base/configobject.hpp index 9afadac90..559636370 100644 --- a/lib/base/configobject.hpp +++ b/lib/base/configobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGOBJECT_H #define CONFIGOBJECT_H @@ -61,8 +44,8 @@ public: void Unregister(); void PreActivate(); - void Activate(bool runtimeCreated = false); - void Deactivate(bool runtimeRemoved = false); + void Activate(bool runtimeCreated = false, const Value& cookie = Empty); + void Deactivate(bool runtimeRemoved = false, const Value& cookie = Empty); void SetAuthority(bool authority); void Start(bool runtimeCreated = false) override; diff --git a/lib/base/configobject.ti b/lib/base/configobject.ti index 7a010b1ab..fcfcb0223 100644 --- a/lib/base/configobject.ti +++ b/lib/base/configobject.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/debuginfo.hpp" #include "base/configtype.hpp" diff --git a/lib/base/configtype.cpp b/lib/base/configtype.cpp index 0c3aa68ed..50a53d547 100644 --- a/lib/base/configtype.cpp +++ b/lib/base/configtype.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" #include "base/convert.hpp" diff --git a/lib/base/configtype.hpp b/lib/base/configtype.hpp index 739bb0111..2244cc5fa 100644 --- a/lib/base/configtype.hpp +++ b/lib/base/configtype.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGTYPE_H #define CONFIGTYPE_H diff --git a/lib/base/configuration.cpp b/lib/base/configuration.cpp new file mode 100644 index 000000000..d163937e2 --- /dev/null +++ b/lib/base/configuration.cpp @@ -0,0 +1,364 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/configuration.hpp" +#include "base/configuration-ti.cpp" +#include "base/exception.hpp" + +using namespace icinga; + +REGISTER_TYPE(Configuration); + +String Configuration::ApiBindHost; +String Configuration::ApiBindPort{"5665"}; +bool Configuration::AttachDebugger{false}; +String Configuration::CacheDir; +int Configuration::Concurrency{static_cast(std::thread::hardware_concurrency())}; +String Configuration::ConfigDir; +String Configuration::DataDir; +String Configuration::EventEngine; +String Configuration::IncludeConfDir; +String Configuration::InitRunDir; +String Configuration::LogDir; +String Configuration::ModAttrPath; +String Configuration::ObjectsPath; +String Configuration::PidPath; +String Configuration::PkgDataDir; +String Configuration::PrefixDir; +String Configuration::ProgramData; +int Configuration::RLimitFiles; +int Configuration::RLimitProcesses; +int Configuration::RLimitStack; +String Configuration::RunAsGroup; +String Configuration::RunAsUser; +String Configuration::SpoolDir; +String Configuration::StatePath; +double Configuration::TlsHandshakeTimeout{10}; +String Configuration::VarsPath; +String Configuration::ZonesDir; + +/* deprecated */ +String Configuration::LocalStateDir; +String Configuration::RunDir; +String Configuration::SysconfDir; + +/* internal */ +bool Configuration::m_ReadOnly{false}; + +template +void HandleUserWrite(const String& name, T *target, const T& value, bool readOnly) +{ + if (readOnly) + BOOST_THROW_EXCEPTION(ScriptError("Configuration attribute '" + name + "' is read-only.")); + + *target = value; +} + +String Configuration::GetApiBindHost() const +{ + return Configuration::ApiBindHost; +} + +void Configuration::SetApiBindHost(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("ApiBindHost", &Configuration::ApiBindHost, val, m_ReadOnly); +} + +String Configuration::GetApiBindPort() const +{ + return Configuration::ApiBindPort; +} + +void Configuration::SetApiBindPort(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("ApiBindPort", &Configuration::ApiBindPort, val, m_ReadOnly); +} + +bool Configuration::GetAttachDebugger() const +{ + return Configuration::AttachDebugger; +} + +void Configuration::SetAttachDebugger(bool val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("AttachDebugger", &Configuration::AttachDebugger, val, m_ReadOnly); +} + +String Configuration::GetCacheDir() const +{ + return Configuration::CacheDir; +} + +void Configuration::SetCacheDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("CacheDir", &Configuration::CacheDir, val, m_ReadOnly); +} + +int Configuration::GetConcurrency() const +{ + return Configuration::Concurrency; +} + +void Configuration::SetConcurrency(int val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("Concurrency", &Configuration::Concurrency, val, m_ReadOnly); +} + +String Configuration::GetConfigDir() const +{ + return Configuration::ConfigDir; +} + +void Configuration::SetConfigDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("ConfigDir", &Configuration::ConfigDir, val, m_ReadOnly); +} + +String Configuration::GetDataDir() const +{ + return Configuration::DataDir; +} + +void Configuration::SetDataDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("DataDir", &Configuration::DataDir, val, m_ReadOnly); +} + +String Configuration::GetEventEngine() const +{ + return Configuration::EventEngine; +} + +void Configuration::SetEventEngine(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("EventEngine", &Configuration::EventEngine, val, m_ReadOnly); +} + +String Configuration::GetIncludeConfDir() const +{ + return Configuration::IncludeConfDir; +} + +void Configuration::SetIncludeConfDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("IncludeConfDir", &Configuration::IncludeConfDir, val, m_ReadOnly); +} + +String Configuration::GetInitRunDir() const +{ + return Configuration::InitRunDir; +} + +void Configuration::SetInitRunDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("InitRunDir", &Configuration::InitRunDir, val, m_ReadOnly); +} + +String Configuration::GetLogDir() const +{ + return Configuration::LogDir; +} + +void Configuration::SetLogDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("LogDir", &Configuration::LogDir, val, m_ReadOnly); +} + +String Configuration::GetModAttrPath() const +{ + return Configuration::ModAttrPath; +} + +void Configuration::SetModAttrPath(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("ModAttrPath", &Configuration::ModAttrPath, val, m_ReadOnly); +} + +String Configuration::GetObjectsPath() const +{ + return Configuration::ObjectsPath; +} + +void Configuration::SetObjectsPath(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("ObjectsPath", &Configuration::ObjectsPath, val, m_ReadOnly); +} + +String Configuration::GetPidPath() const +{ + return Configuration::PidPath; +} + +void Configuration::SetPidPath(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("PidPath", &Configuration::PidPath, val, m_ReadOnly); +} + +String Configuration::GetPkgDataDir() const +{ + return Configuration::PkgDataDir; +} + +void Configuration::SetPkgDataDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("PkgDataDir", &Configuration::PkgDataDir, val, m_ReadOnly); +} + +String Configuration::GetPrefixDir() const +{ + return Configuration::PrefixDir; +} + +void Configuration::SetPrefixDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("PrefixDir", &Configuration::PrefixDir, val, m_ReadOnly); +} + +String Configuration::GetProgramData() const +{ + return Configuration::ProgramData; +} + +void Configuration::SetProgramData(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("ProgramData", &Configuration::ProgramData, val, m_ReadOnly); +} + +int Configuration::GetRLimitFiles() const +{ + return Configuration::RLimitFiles; +} + +void Configuration::SetRLimitFiles(int val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("RLimitFiles", &Configuration::RLimitFiles, val, m_ReadOnly); +} + +int Configuration::GetRLimitProcesses() const +{ + return RLimitProcesses; +} + +void Configuration::SetRLimitProcesses(int val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("RLimitProcesses", &Configuration::RLimitProcesses, val, m_ReadOnly); +} + +int Configuration::GetRLimitStack() const +{ + return Configuration::RLimitStack; +} + +void Configuration::SetRLimitStack(int val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("RLimitStack", &Configuration::RLimitStack, val, m_ReadOnly); +} + +String Configuration::GetRunAsGroup() const +{ + return Configuration::RunAsGroup; +} + +void Configuration::SetRunAsGroup(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("RunAsGroup", &Configuration::RunAsGroup, val, m_ReadOnly); +} + +String Configuration::GetRunAsUser() const +{ + return Configuration::RunAsUser; +} + +void Configuration::SetRunAsUser(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("RunAsUser", &Configuration::RunAsUser, val, m_ReadOnly); +} + +String Configuration::GetSpoolDir() const +{ + return Configuration::SpoolDir; +} + +void Configuration::SetSpoolDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("SpoolDir", &Configuration::SpoolDir, val, m_ReadOnly); +} + +String Configuration::GetStatePath() const +{ + return Configuration::StatePath; +} + +void Configuration::SetStatePath(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("StatePath", &Configuration::StatePath, val, m_ReadOnly); +} + +double Configuration::GetTlsHandshakeTimeout() const +{ + return Configuration::TlsHandshakeTimeout; +} + +void Configuration::SetTlsHandshakeTimeout(double val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("TlsHandshakeTimeout", &Configuration::TlsHandshakeTimeout, val, m_ReadOnly); +} + +String Configuration::GetVarsPath() const +{ + return Configuration::VarsPath; +} + +void Configuration::SetVarsPath(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("VarsPath", &Configuration::VarsPath, val, m_ReadOnly); +} + +String Configuration::GetZonesDir() const +{ + return Configuration::ZonesDir; +} + +void Configuration::SetZonesDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("ZonesDir", &Configuration::ZonesDir, val, m_ReadOnly); +} + +String Configuration::GetLocalStateDir() const +{ + return Configuration::LocalStateDir; +} + +void Configuration::SetLocalStateDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("LocalStateDir", &Configuration::LocalStateDir, val, m_ReadOnly); +} + +String Configuration::GetSysconfDir() const +{ + return Configuration::SysconfDir; +} + +void Configuration::SetSysconfDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("SysconfDir", &Configuration::SysconfDir, val, m_ReadOnly); +} + +String Configuration::GetRunDir() const +{ + return Configuration::RunDir; +} + +void Configuration::SetRunDir(const String& val, bool suppress_events, const Value& cookie) +{ + HandleUserWrite("RunDir", &Configuration::RunDir, val, m_ReadOnly); +} + +bool Configuration::GetReadOnly() +{ + return m_ReadOnly; +} + +void Configuration::SetReadOnly(bool readOnly) +{ + m_ReadOnly = readOnly; +} diff --git a/lib/base/configuration.hpp b/lib/base/configuration.hpp new file mode 100644 index 000000000..560906596 --- /dev/null +++ b/lib/base/configuration.hpp @@ -0,0 +1,156 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef CONFIGURATION_H +#define CONFIGURATION_H + +#include "base/i2-base.hpp" +#include "base/configuration-ti.hpp" + +namespace icinga +{ + +/** + * Global configuration. + * + * @ingroup base + */ +class Configuration : public ObjectImpl +{ +public: + DECLARE_OBJECT(Configuration); + + String GetApiBindHost() const override; + void SetApiBindHost(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetApiBindPort() const override; + void SetApiBindPort(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + bool GetAttachDebugger() const override; + void SetAttachDebugger(bool value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetCacheDir() const override; + void SetCacheDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + int GetConcurrency() const override; + void SetConcurrency(int value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetConfigDir() const override; + void SetConfigDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetDataDir() const override; + void SetDataDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetEventEngine() const override; + void SetEventEngine(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetIncludeConfDir() const override; + void SetIncludeConfDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetInitRunDir() const override; + void SetInitRunDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetLogDir() const override; + void SetLogDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetModAttrPath() const override; + void SetModAttrPath(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetObjectsPath() const override; + void SetObjectsPath(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetPidPath() const override; + void SetPidPath(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetPkgDataDir() const override; + void SetPkgDataDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetPrefixDir() const override; + void SetPrefixDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetProgramData() const override; + void SetProgramData(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + int GetRLimitFiles() const override; + void SetRLimitFiles(int value, bool suppress_events = false, const Value& cookie = Empty) override; + + int GetRLimitProcesses() const override; + void SetRLimitProcesses(int value, bool suppress_events = false, const Value& cookie = Empty) override; + + int GetRLimitStack() const override; + void SetRLimitStack(int value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetRunAsGroup() const override; + void SetRunAsGroup(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetRunAsUser() const override; + void SetRunAsUser(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetSpoolDir() const override; + void SetSpoolDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetStatePath() const override; + void SetStatePath(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + double GetTlsHandshakeTimeout() const override; + void SetTlsHandshakeTimeout(double value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetVarsPath() const override; + void SetVarsPath(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetZonesDir() const override; + void SetZonesDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + /* deprecated */ + String GetLocalStateDir() const override; + void SetLocalStateDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetSysconfDir() const override; + void SetSysconfDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + String GetRunDir() const override; + void SetRunDir(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + + static bool GetReadOnly(); + static void SetReadOnly(bool readOnly); + + static String ApiBindHost; + static String ApiBindPort; + static bool AttachDebugger; + static String CacheDir; + static int Concurrency; + static String ConfigDir; + static String DataDir; + static String EventEngine; + static String IncludeConfDir; + static String InitRunDir; + static String LogDir; + static String ModAttrPath; + static String ObjectsPath; + static String PidPath; + static String PkgDataDir; + static String PrefixDir; + static String ProgramData; + static int RLimitFiles; + static int RLimitProcesses; + static int RLimitStack; + static String RunAsGroup; + static String RunAsUser; + static String SpoolDir; + static String StatePath; + static double TlsHandshakeTimeout; + static String VarsPath; + static String ZonesDir; + + /* deprecated */ + static String LocalStateDir; + static String RunDir; + static String SysconfDir; + +private: + static bool m_ReadOnly; + +}; + +} + +#endif /* CONFIGURATION_H */ diff --git a/lib/base/configuration.ti b/lib/base/configuration.ti new file mode 100644 index 000000000..72fa92dcf --- /dev/null +++ b/lib/base/configuration.ti @@ -0,0 +1,164 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/configobject.hpp" + +library base; + +namespace icinga +{ + +abstract class Configuration +{ + [config, no_storage, virtual] String ApiBindHost { + get; + set; + }; + + [config, no_storage, virtual] String ApiBindPort { + get; + set; + }; + + [config, no_storage, virtual] bool AttachDebugger { + get; + set; + }; + + [config, no_storage, virtual] String CacheDir { + get; + set; + }; + + [config, no_storage, virtual] int Concurrency { + get; + set; + }; + + [config, no_storage, virtual] String ConfigDir { + get; + set; + }; + + [config, no_storage, virtual] String DataDir { + get; + set; + }; + + [config, no_storage, virtual] String EventEngine { + get; + set; + }; + + [config, no_storage, virtual] String IncludeConfDir { + get; + set; + }; + + [config, no_storage, virtual] String InitRunDir { + get; + set; + }; + + [config, no_storage, virtual] String LogDir { + get; + set; + }; + + [config, no_storage, virtual] String ModAttrPath { + get; + set; + }; + + [config, no_storage, virtual] String ObjectsPath { + get; + set; + }; + + [config, no_storage, virtual] String PidPath { + get; + set; + }; + + [config, no_storage, virtual] String PkgDataDir { + get; + set; + }; + + [config, no_storage, virtual] String PrefixDir { + get; + set; + }; + + [config, no_storage, virtual] String ProgramData { + get; + set; + }; + + [config, no_storage, virtual] int RLimitFiles { + get; + set; + }; + + [config, no_storage, virtual] int RLimitProcesses { + get; + set; + }; + + [config, no_storage, virtual] int RLimitStack { + get; + set; + }; + + [config, no_storage, virtual] String RunAsGroup { + get; + set; + }; + + [config, no_storage, virtual] String RunAsUser { + get; + set; + }; + + [config, no_storage, virtual] String SpoolDir { + get; + set; + }; + + [config, no_storage, virtual] String StatePath { + get; + set; + }; + + [config, no_storage, virtual] double TlsHandshakeTimeout { + get; + set; + }; + + [config, no_storage, virtual] String VarsPath { + get; + set; + }; + + [config, no_storage, virtual] String ZonesDir { + get; + set; + }; + + /* deprecated */ + [config, no_storage, virtual] String LocalStateDir { + get; + set; + }; + + [config, no_storage, virtual] String RunDir { + get; + set; + }; + + [config, no_storage, virtual] String SysconfDir { + get; + set; + }; +}; + +} diff --git a/lib/base/configwriter.cpp b/lib/base/configwriter.cpp index 58851c2cf..cef3f7eb8 100644 --- a/lib/base/configwriter.cpp +++ b/lib/base/configwriter.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configwriter.hpp" #include "base/exception.hpp" @@ -239,7 +222,8 @@ const std::vector& ConfigWriter::GetKeywords() keywords.emplace_back("globals"); keywords.emplace_back("locals"); keywords.emplace_back("use"); - keywords.emplace_back("__using"); + keywords.emplace_back("using"); + keywords.emplace_back("namespace"); keywords.emplace_back("default"); keywords.emplace_back("ignore_on_error"); keywords.emplace_back("current_filename"); diff --git a/lib/base/configwriter.hpp b/lib/base/configwriter.hpp index 1e3fc7759..a0c70f76f 100644 --- a/lib/base/configwriter.hpp +++ b/lib/base/configwriter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGWRITER_H #define CONFIGWRITER_H diff --git a/lib/base/console.cpp b/lib/base/console.cpp index d1b1b7d86..99a5fad3d 100644 --- a/lib/base/console.cpp +++ b/lib/base/console.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/console.hpp" #include "base/initialize.hpp" diff --git a/lib/base/console.hpp b/lib/base/console.hpp index 63bcfe417..f5b8c9a3e 100644 --- a/lib/base/console.hpp +++ b/lib/base/console.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONSOLE_H #define CONSOLE_H diff --git a/lib/base/context.cpp b/lib/base/context.cpp index a654d9909..2cac7a8db 100644 --- a/lib/base/context.cpp +++ b/lib/base/context.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/context.hpp" #include diff --git a/lib/base/context.hpp b/lib/base/context.hpp index 5b53515b8..0e4b92a78 100644 --- a/lib/base/context.hpp +++ b/lib/base/context.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONTEXT_H #define CONTEXT_H diff --git a/lib/base/convert.cpp b/lib/base/convert.cpp index 054ff591d..3c60e1b44 100644 --- a/lib/base/convert.cpp +++ b/lib/base/convert.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/convert.hpp" #include "base/datetime.hpp" diff --git a/lib/base/convert.hpp b/lib/base/convert.hpp index 84c66da5b..e0754b36d 100644 --- a/lib/base/convert.hpp +++ b/lib/base/convert.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONVERT_H #define CONVERT_H @@ -64,6 +47,11 @@ public: return val; } + static long ToLong(double val) + { + return static_cast(val); + } + static double ToDouble(const Value& val) { return val; diff --git a/lib/base/datetime-script.cpp b/lib/base/datetime-script.cpp index c26177af1..6c1838153 100644 --- a/lib/base/datetime-script.cpp +++ b/lib/base/datetime-script.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/datetime.hpp" #include "base/function.hpp" diff --git a/lib/base/datetime.cpp b/lib/base/datetime.cpp index 808312269..aa7b5e59e 100644 --- a/lib/base/datetime.cpp +++ b/lib/base/datetime.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/datetime.hpp" #include "base/datetime-ti.cpp" diff --git a/lib/base/datetime.hpp b/lib/base/datetime.hpp index 54821b3a7..e7b8a1fdd 100644 --- a/lib/base/datetime.hpp +++ b/lib/base/datetime.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DATETIME_H #define DATETIME_H diff --git a/lib/base/datetime.ti b/lib/base/datetime.ti index d2e7351e0..b9d737506 100644 --- a/lib/base/datetime.ti +++ b/lib/base/datetime.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ library base; diff --git a/lib/base/debug.hpp b/lib/base/debug.hpp index d8ac052c8..54b424c6d 100644 --- a/lib/base/debug.hpp +++ b/lib/base/debug.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DEBUG_H #define DEBUG_H diff --git a/lib/base/debuginfo.cpp b/lib/base/debuginfo.cpp index 3071e20c9..99006aca5 100644 --- a/lib/base/debuginfo.cpp +++ b/lib/base/debuginfo.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/debuginfo.hpp" #include "base/convert.hpp" diff --git a/lib/base/debuginfo.hpp b/lib/base/debuginfo.hpp index 836069e47..d47db91c1 100644 --- a/lib/base/debuginfo.hpp +++ b/lib/base/debuginfo.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DEBUGINFO_H #define DEBUGINFO_H diff --git a/lib/base/defer.hpp b/lib/base/defer.hpp new file mode 100644 index 000000000..9290c92b9 --- /dev/null +++ b/lib/base/defer.hpp @@ -0,0 +1,46 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef DEFER +#define DEFER + +#include +#include + +namespace icinga +{ + +/** + * An action to be executed at end of scope. + * + * @ingroup base + */ +class Defer +{ +public: + inline + Defer(std::function func) : m_Func(std::move(func)) + { + } + + Defer(const Defer&) = delete; + Defer(Defer&&) = delete; + Defer& operator=(const Defer&) = delete; + Defer& operator=(Defer&&) = delete; + + inline + ~Defer() + { + try { + m_Func(); + } catch (...) { + // https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor + } + } + +private: + std::function m_Func; +}; + +} + +#endif /* DEFER */ diff --git a/lib/base/dependencygraph.cpp b/lib/base/dependencygraph.cpp index 8fccef5f3..63763af95 100644 --- a/lib/base/dependencygraph.cpp +++ b/lib/base/dependencygraph.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/dependencygraph.hpp" diff --git a/lib/base/dependencygraph.hpp b/lib/base/dependencygraph.hpp index 39f50a6ae..426607488 100644 --- a/lib/base/dependencygraph.hpp +++ b/lib/base/dependencygraph.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DEPENDENCYGRAPH_H #define DEPENDENCYGRAPH_H diff --git a/lib/base/dictionary-script.cpp b/lib/base/dictionary-script.cpp index d188119e5..ad19c5b52 100644 --- a/lib/base/dictionary-script.cpp +++ b/lib/base/dictionary-script.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/dictionary.hpp" #include "base/function.hpp" @@ -57,6 +40,14 @@ static void DictionaryRemove(const String& key) self->Remove(key); } +static void DictionaryClear() +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Dictionary::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + self->Clear(); +} + static bool DictionaryContains(const String& key) { ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); @@ -115,6 +106,7 @@ Object::Ptr Dictionary::GetPrototype() { "set", new Function("Dictionary#set", DictionarySet, { "key", "value" }) }, { "get", new Function("Dictionary#get", DictionaryGet, { "key" }) }, { "remove", new Function("Dictionary#remove", DictionaryRemove, { "key" }) }, + { "clear", new Function("Dictionary#clear", DictionaryClear, {}) }, { "contains", new Function("Dictionary#contains", DictionaryContains, { "key" }, true) }, { "shallow_clone", new Function("Dictionary#shallow_clone", DictionaryShallowClone, {}, true) }, { "keys", new Function("Dictionary#keys", DictionaryKeys, {}, true) }, diff --git a/lib/base/dictionary.cpp b/lib/base/dictionary.cpp index a64540404..4fd9d24b9 100644 --- a/lib/base/dictionary.cpp +++ b/lib/base/dictionary.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/dictionary.hpp" #include "base/objectlock.hpp" @@ -89,13 +72,14 @@ bool Dictionary::Get(const String& key, Value *result) const * * @param key The key. * @param value The value. + * @param overrideFrozen Whether to allow modifying frozen dictionaries. */ -void Dictionary::Set(const String& key, Value value) +void Dictionary::Set(const String& key, Value value, bool overrideFrozen) { ObjectLock olock(this); - if (m_Frozen) - BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionary must not be modified.")); + if (m_Frozen && !overrideFrozen) + BOOST_THROW_EXCEPTION(std::invalid_argument("Value in dictionary must not be modified.")); m_Data[key] = std::move(value); } @@ -157,12 +141,13 @@ Dictionary::Iterator Dictionary::End() * Removes the item specified by the iterator from the dictionary. * * @param it The iterator. + * @param overrideFrozen Whether to allow modifying frozen dictionaries. */ -void Dictionary::Remove(Dictionary::Iterator it) +void Dictionary::Remove(Dictionary::Iterator it, bool overrideFrozen) { ASSERT(OwnsLock()); - if (m_Frozen) + if (m_Frozen && !overrideFrozen) BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionary must not be modified.")); m_Data.erase(it); @@ -172,12 +157,13 @@ void Dictionary::Remove(Dictionary::Iterator it) * Removes the specified key from the dictionary. * * @param key The key. + * @param overrideFrozen Whether to allow modifying frozen dictionaries. */ -void Dictionary::Remove(const String& key) +void Dictionary::Remove(const String& key, bool overrideFrozen) { ObjectLock olock(this); - if (m_Frozen) + if (m_Frozen && !overrideFrozen) BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionary must not be modified.")); Dictionary::Iterator it; @@ -191,12 +177,14 @@ void Dictionary::Remove(const String& key) /** * Removes all dictionary items. + * + * @param overrideFrozen Whether to allow modifying frozen dictionaries. */ -void Dictionary::Clear() +void Dictionary::Clear(bool overrideFrozen) { ObjectLock olock(this); - if (m_Frozen) + if (m_Frozen && !overrideFrozen) BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionary must not be modified.")); m_Data.clear(); @@ -288,9 +276,9 @@ Value Dictionary::GetFieldByName(const String& field, bool, const DebugInfo& deb return GetPrototypeField(const_cast(this), field, false, debugInfo); } -void Dictionary::SetFieldByName(const String& field, const Value& value, const DebugInfo&) +void Dictionary::SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo&) { - Set(field, value); + Set(field, value, overrideFrozen); } bool Dictionary::HasOwnField(const String& field) const diff --git a/lib/base/dictionary.hpp b/lib/base/dictionary.hpp index 97ca81b78..227868751 100644 --- a/lib/base/dictionary.hpp +++ b/lib/base/dictionary.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DICTIONARY_H #define DICTIONARY_H @@ -58,7 +41,7 @@ public: Value Get(const String& key) const; bool Get(const String& key, Value *result) const; - void Set(const String& key, Value value); + void Set(const String& key, Value value, bool overrideFrozen = false); bool Contains(const String& key) const; Iterator Begin(); @@ -66,11 +49,11 @@ public: size_t GetLength() const; - void Remove(const String& key); + void Remove(const String& key, bool overrideFrozen = false); - void Remove(Iterator it); + void Remove(Iterator it, bool overrideFrozen = false); - void Clear(); + void Clear(bool overrideFrozen = false); void CopyTo(const Dictionary::Ptr& dest) const; Dictionary::Ptr ShallowClone() const; @@ -86,7 +69,7 @@ public: void Freeze(); Value GetFieldByName(const String& field, bool sandboxed, const DebugInfo& debugInfo) const override; - void SetFieldByName(const String& field, const Value& value, const DebugInfo& debugInfo) override; + void SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) override; bool HasOwnField(const String& field) const override; bool GetOwnField(const String& field, Value *result) const override; diff --git a/lib/base/exception.cpp b/lib/base/exception.cpp index b74a0a6c1..c694b6bf2 100644 --- a/lib/base/exception.cpp +++ b/lib/base/exception.cpp @@ -1,23 +1,11 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/exception.hpp" +#include + +#ifdef _WIN32 +# include "base/utility.hpp" +#endif /* _WIN32 */ #ifdef HAVE_CXXABI_H # include diff --git a/lib/base/exception.hpp b/lib/base/exception.hpp index 9e06f3666..246c28a78 100644 --- a/lib/base/exception.hpp +++ b/lib/base/exception.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef EXCEPTION_H #define EXCEPTION_H @@ -24,7 +7,6 @@ #include "base/string.hpp" #include "base/stacktrace.hpp" #include "base/context.hpp" -#include "base/utility.hpp" #include "base/debuginfo.hpp" #include "base/dictionary.hpp" #include "base/configobject.hpp" @@ -52,7 +34,6 @@ class ScriptError : virtual public user_error public: ScriptError(String message); ScriptError(String message, DebugInfo di, bool incompleteExpr = false); - ~ScriptError() throw() = default; const char *what(void) const throw() final; diff --git a/lib/base/fifo.cpp b/lib/base/fifo.cpp index e0d295791..8653f5176 100644 --- a/lib/base/fifo.cpp +++ b/lib/base/fifo.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/fifo.hpp" diff --git a/lib/base/fifo.hpp b/lib/base/fifo.hpp index 6b85a2842..a8273c1ba 100644 --- a/lib/base/fifo.hpp +++ b/lib/base/fifo.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef FIFO_H #define FIFO_H diff --git a/lib/base/filelogger.cpp b/lib/base/filelogger.cpp index 5ae739d2e..60efdba26 100644 --- a/lib/base/filelogger.cpp +++ b/lib/base/filelogger.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/filelogger.hpp" #include "base/filelogger-ti.cpp" @@ -51,6 +34,9 @@ void FileLogger::Start(bool runtimeCreated) Application::OnReopenLogs.connect(std::bind(&FileLogger::ReopenLogFile, this)); ObjectImpl::Start(runtimeCreated); + + Log(LogInformation, "FileLogger") + << "'" << GetName() << "' started."; } void FileLogger::ReopenLogFile() diff --git a/lib/base/filelogger.hpp b/lib/base/filelogger.hpp index 8c9a8eee9..420337f6a 100644 --- a/lib/base/filelogger.hpp +++ b/lib/base/filelogger.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef FILELOGGER_H #define FILELOGGER_H diff --git a/lib/base/filelogger.ti b/lib/base/filelogger.ti index f19b7c9d6..8af2498ec 100644 --- a/lib/base/filelogger.ti +++ b/lib/base/filelogger.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/streamlogger.hpp" diff --git a/lib/base/function-script.cpp b/lib/base/function-script.cpp index 8a96b26f2..e59e84dbc 100644 --- a/lib/base/function-script.cpp +++ b/lib/base/function-script.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/function.hpp" #include "base/functionwrapper.hpp" diff --git a/lib/base/function.cpp b/lib/base/function.cpp index 6fec96208..f9a261dc0 100644 --- a/lib/base/function.cpp +++ b/lib/base/function.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/function.hpp" #include "base/function-ti.cpp" diff --git a/lib/base/function.hpp b/lib/base/function.hpp index dd08df6ff..d6f1d17e0 100644 --- a/lib/base/function.hpp +++ b/lib/base/function.hpp @@ -1,24 +1,7 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ -#ifndef SCRIPTFUNCTION_H -#define SCRIPTFUNCTION_H +#ifndef FUNCTION_H +#define FUNCTION_H #include "base/i2-base.hpp" #include "base/function-ti.hpp" @@ -72,50 +55,35 @@ private: bool side_effect_free, bool deprecated); }; -#define REGISTER_SCRIPTFUNCTION_NS(ns, name, callback, args) \ +/* Ensure that the priority is lower than the basic namespace initialization in scriptframe.cpp. */ +#define REGISTER_FUNCTION(ns, name, callback, args) \ INITIALIZE_ONCE_WITH_PRIORITY([]() { \ Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), false); \ - ScriptGlobal::Set(#ns "." #name, sf); \ + Namespace::Ptr nsp = ScriptGlobal::Get(#ns); \ + nsp->SetAttribute(#name, std::make_shared(sf)); \ }, 10) -#define REGISTER_SCRIPTFUNCTION_NS_PREFIX(ns, name, callback, args) \ +#define REGISTER_SAFE_FUNCTION(ns, name, callback, args) \ + INITIALIZE_ONCE_WITH_PRIORITY([]() { \ + Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), true); \ + Namespace::Ptr nsp = ScriptGlobal::Get(#ns); \ + nsp->SetAttribute(#name, std::make_shared(sf)); \ + }, 10) + +#define REGISTER_FUNCTION_NONCONST(ns, name, callback, args) \ INITIALIZE_ONCE_WITH_PRIORITY([]() { \ Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), false); \ - ScriptGlobal::Set(#ns "." #name, sf); \ - Function::Ptr dsf = new icinga::Function("Deprecated#__" #name " (deprecated)", WrapFunction(callback), String(args).Split(":"), false, true); \ - ScriptGlobal::Set("Deprecated.__" #name, dsf); \ + Namespace::Ptr nsp = ScriptGlobal::Get(#ns); \ + nsp->SetAttribute(#name, std::make_shared(sf)); \ }, 10) -#define REGISTER_SCRIPTFUNCTION_NS_DEPRECATED(ns, name, callback, args) \ - INITIALIZE_ONCE_WITH_PRIORITY([]() { \ - Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), false); \ - ScriptGlobal::Set(#ns "." #name, sf); \ - Function::Ptr dsf = new icinga::Function("Deprecated#" #name " (deprecated)", WrapFunction(callback), String(args).Split(":"), false, true); \ - ScriptGlobal::Set("Deprecated." #name, dsf); \ - }, 10) - -#define REGISTER_SAFE_SCRIPTFUNCTION_NS(ns, name, callback, args) \ +#define REGISTER_SAFE_FUNCTION_NONCONST(ns, name, callback, args) \ INITIALIZE_ONCE_WITH_PRIORITY([]() { \ Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), true); \ - ScriptGlobal::Set(#ns "." #name, sf); \ - }, 10) - -#define REGISTER_SAFE_SCRIPTFUNCTION_NS_PREFIX(ns, name, callback, args) \ - INITIALIZE_ONCE_WITH_PRIORITY([]() { \ - Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), true); \ - ScriptGlobal::Set(#ns "." #name, sf); \ - Function::Ptr dsf = new icinga::Function("Deprecated#__" #name " (deprecated)", WrapFunction(callback), String(args).Split(":"), true, true); \ - ScriptGlobal::Set("Deprecated.__" #name, dsf); \ - }, 10) - -#define REGISTER_SAFE_SCRIPTFUNCTION_NS_DEPRECATED(ns, name, callback, args) \ - INITIALIZE_ONCE_WITH_PRIORITY([]() { \ - Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), true); \ - ScriptGlobal::Set(#ns "." #name, sf); \ - Function::Ptr dsf = new icinga::Function("Deprecated#" #name " (deprecated)", WrapFunction(callback), String(args).Split(":"), true, true); \ - ScriptGlobal::Set("Deprecated." #name, dsf); \ + Namespace::Ptr nsp = ScriptGlobal::Get(#ns); \ + nsp->SetAttribute(#name, std::make_shared(sf)); \ }, 10) } -#endif /* SCRIPTFUNCTION_H */ +#endif /* FUNCTION_H */ diff --git a/lib/base/function.ti b/lib/base/function.ti index c8a1d529a..f2623c168 100644 --- a/lib/base/function.ti +++ b/lib/base/function.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" diff --git a/lib/base/functionwrapper.hpp b/lib/base/functionwrapper.hpp index da32bb7ed..03b8a44aa 100644 --- a/lib/base/functionwrapper.hpp +++ b/lib/base/functionwrapper.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef FUNCTIONWRAPPER_H #define FUNCTIONWRAPPER_H diff --git a/lib/base/i2-base.hpp b/lib/base/i2-base.hpp index 95446b2cf..58aac4803 100644 --- a/lib/base/i2-base.hpp +++ b/lib/base/i2-base.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef I2BASE_H #define I2BASE_H @@ -89,14 +72,6 @@ # pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif -#if defined(__GNUC__) -# define likely(x) __builtin_expect(!!(x), 1) -# define unlikely(x) __builtin_expect(!!(x), 0) -#else -# define likely(x) (x) -# define unlikely(x) (x) -#endif - #define BOOST_BIND_NO_PLACEHOLDERS #include diff --git a/lib/base/initialize.cpp b/lib/base/initialize.cpp index feffb4816..23373cc23 100644 --- a/lib/base/initialize.cpp +++ b/lib/base/initialize.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/initialize.hpp" #include "base/loader.hpp" diff --git a/lib/base/initialize.hpp b/lib/base/initialize.hpp index 942aa5b0f..0629abe68 100644 --- a/lib/base/initialize.hpp +++ b/lib/base/initialize.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef INITIALIZE_H #define INITIALIZE_H diff --git a/lib/base/io-engine.cpp b/lib/base/io-engine.cpp new file mode 100644 index 000000000..5dd3ee59c --- /dev/null +++ b/lib/base/io-engine.cpp @@ -0,0 +1,146 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/exception.hpp" +#include "base/io-engine.hpp" +#include "base/lazy-init.hpp" +#include "base/logger.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace icinga; + +CpuBoundWork::CpuBoundWork(boost::asio::yield_context yc) + : m_Done(false) +{ + auto& ioEngine (IoEngine::Get()); + + for (;;) { + auto availableSlots (ioEngine.m_CpuBoundSemaphore.fetch_sub(1)); + + if (availableSlots < 1) { + ioEngine.m_CpuBoundSemaphore.fetch_add(1); + ioEngine.m_AlreadyExpiredTimer.async_wait(yc); + continue; + } + + break; + } +} + +CpuBoundWork::~CpuBoundWork() +{ + if (!m_Done) { + IoEngine::Get().m_CpuBoundSemaphore.fetch_add(1); + } +} + +void CpuBoundWork::Done() +{ + if (!m_Done) { + IoEngine::Get().m_CpuBoundSemaphore.fetch_add(1); + + m_Done = true; + } +} + +IoBoundWorkSlot::IoBoundWorkSlot(boost::asio::yield_context yc) + : yc(yc) +{ + IoEngine::Get().m_CpuBoundSemaphore.fetch_add(1); +} + +IoBoundWorkSlot::~IoBoundWorkSlot() +{ + auto& ioEngine (IoEngine::Get()); + + for (;;) { + auto availableSlots (ioEngine.m_CpuBoundSemaphore.fetch_sub(1)); + + if (availableSlots < 1) { + ioEngine.m_CpuBoundSemaphore.fetch_add(1); + ioEngine.m_AlreadyExpiredTimer.async_wait(yc); + continue; + } + + break; + } +} + +LazyInit> IoEngine::m_Instance ([]() { return std::unique_ptr(new IoEngine()); }); + +IoEngine& IoEngine::Get() +{ + return *m_Instance.Get(); +} + +boost::asio::io_context& IoEngine::GetIoContext() +{ + return m_IoContext; +} + +IoEngine::IoEngine() : m_IoContext(), m_KeepAlive(boost::asio::make_work_guard(m_IoContext)), m_Threads(decltype(m_Threads)::size_type(std::thread::hardware_concurrency() * 2u)), m_AlreadyExpiredTimer(m_IoContext) +{ + m_AlreadyExpiredTimer.expires_at(boost::posix_time::neg_infin); + m_CpuBoundSemaphore.store(std::thread::hardware_concurrency() * 3u / 2u); + + for (auto& thread : m_Threads) { + thread = std::thread(&IoEngine::RunEventLoop, this); + } +} + +IoEngine::~IoEngine() +{ + for (auto& thread : m_Threads) { + boost::asio::post(m_IoContext, []() { + throw TerminateIoThread(); + }); + } + + for (auto& thread : m_Threads) { + thread.join(); + } +} + +void IoEngine::RunEventLoop() +{ + for (;;) { + try { + m_IoContext.run(); + + break; + } catch (const TerminateIoThread&) { + break; + } catch (const std::exception& e) { + Log(LogCritical, "IoEngine", "Exception during I/O operation!"); + Log(LogDebug, "IoEngine") << "Exception during I/O operation: " << DiagnosticInformation(e); + } + } +} + +AsioConditionVariable::AsioConditionVariable(boost::asio::io_context& io, bool init) + : m_Timer(io) +{ + m_Timer.expires_at(init ? boost::posix_time::neg_infin : boost::posix_time::pos_infin); +} + +void AsioConditionVariable::Set() +{ + m_Timer.expires_at(boost::posix_time::neg_infin); +} + +void AsioConditionVariable::Clear() +{ + m_Timer.expires_at(boost::posix_time::pos_infin); +} + +void AsioConditionVariable::Wait(boost::asio::yield_context yc) +{ + boost::system::error_code ec; + m_Timer.async_wait(yc[ec]); +} diff --git a/lib/base/io-engine.hpp b/lib/base/io-engine.hpp new file mode 100644 index 000000000..b9e4ee6c3 --- /dev/null +++ b/lib/base/io-engine.hpp @@ -0,0 +1,196 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef IO_ENGINE_H +#define IO_ENGINE_H + +#include "base/lazy-init.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace icinga +{ + +/** + * Scope lock for CPU-bound work done in an I/O thread + * + * @ingroup base + */ +class CpuBoundWork +{ +public: + CpuBoundWork(boost::asio::yield_context yc); + CpuBoundWork(const CpuBoundWork&) = delete; + CpuBoundWork(CpuBoundWork&&) = delete; + CpuBoundWork& operator=(const CpuBoundWork&) = delete; + CpuBoundWork& operator=(CpuBoundWork&&) = delete; + ~CpuBoundWork(); + + void Done(); + +private: + bool m_Done; +}; + +/** + * Scope break for CPU-bound work done in an I/O thread + * + * @ingroup base + */ +class IoBoundWorkSlot +{ +public: + IoBoundWorkSlot(boost::asio::yield_context yc); + IoBoundWorkSlot(const IoBoundWorkSlot&) = delete; + IoBoundWorkSlot(IoBoundWorkSlot&&) = delete; + IoBoundWorkSlot& operator=(const IoBoundWorkSlot&) = delete; + IoBoundWorkSlot& operator=(IoBoundWorkSlot&&) = delete; + ~IoBoundWorkSlot(); + +private: + boost::asio::yield_context yc; +}; + +/** + * Async I/O engine + * + * @ingroup base + */ +class IoEngine +{ + friend CpuBoundWork; + friend IoBoundWorkSlot; + +public: + IoEngine(const IoEngine&) = delete; + IoEngine(IoEngine&&) = delete; + IoEngine& operator=(const IoEngine&) = delete; + IoEngine& operator=(IoEngine&&) = delete; + ~IoEngine(); + + static IoEngine& Get(); + + boost::asio::io_context& GetIoContext(); + + /* + * Custom exceptions thrown in a Boost.Coroutine may cause stack corruption. + * Ensure that these are wrapped correctly. + * + * Inspired by https://github.com/niekbouman/commelec-api/blob/master/commelec-api/coroutine-exception.hpp + * Source: http://boost.2283326.n4.nabble.com/coroutine-only-std-exceptions-are-caught-from-coroutines-td4683671.html + */ + static inline boost::exception_ptr convertExceptionPtr(std::exception_ptr ex) { + try { + throw boost::enable_current_exception(ex); + } catch (...) { + return boost::current_exception(); + } + } + + static inline void rethrowBoostExceptionPointer() { + std::exception_ptr sep; + sep = std::current_exception(); + boost::exception_ptr bep = convertExceptionPtr(sep); + boost::rethrow_exception(bep); + } + + static inline size_t GetCoroutineStackSize() { +#ifdef _WIN32 + // Increase the stack size for Windows coroutines to prevent exception corruption. + // Rationale: Low cost Windows agent only & https://github.com/Icinga/icinga2/issues/7431 + return 8 * 1024 * 1024; +#else /* _WIN32 */ + return boost::coroutines::stack_allocator::traits_type::default_size(); // Default 64 KB +#endif /* _WIN32 */ + } + + /* With dedicated strand in *Connection classes. */ + template + static void SpawnCoroutine(Handler h, Function f) { + + boost::asio::spawn(std::forward(h), + [f](boost::asio::yield_context yc) { + + try { + f(yc); + } catch (const boost::coroutines::detail::forced_unwind &) { + // Required for proper stack unwinding when coroutines are destroyed. + // https://github.com/boostorg/coroutine/issues/39 + throw; + } catch (...) { + // Handle uncaught exceptions outside of the coroutine. + rethrowBoostExceptionPointer(); + } + }, + boost::coroutines::attributes(GetCoroutineStackSize()) // Set a pre-defined stack size. + ); + } + + /* Without strand in the IO executor's context. */ + template + static void SpawnCoroutine(boost::asio::io_context& io, Function f) { + + boost::asio::spawn(io, + [f](boost::asio::yield_context yc) { + + try { + f(yc); + } catch (const boost::coroutines::detail::forced_unwind &) { + // Required for proper stack unwinding when coroutines are destroyed. + // https://github.com/boostorg/coroutine/issues/39 + throw; + } catch (...) { + // Handle uncaught exceptions outside of the coroutine. + rethrowBoostExceptionPointer(); + } + }, + boost::coroutines::attributes(GetCoroutineStackSize()) // Set a pre-defined stack size. + ); + } + +private: + IoEngine(); + + void RunEventLoop(); + + static LazyInit> m_Instance; + + boost::asio::io_context m_IoContext; + boost::asio::executor_work_guard m_KeepAlive; + std::vector m_Threads; + boost::asio::deadline_timer m_AlreadyExpiredTimer; + std::atomic_int_fast32_t m_CpuBoundSemaphore; +}; + +class TerminateIoThread : public std::exception +{ +}; + +/** + * Condition variable which doesn't block I/O threads + * + * @ingroup base + */ +class AsioConditionVariable +{ +public: + AsioConditionVariable(boost::asio::io_context& io, bool init = false); + + void Set(); + void Clear(); + void Wait(boost::asio::yield_context yc); + +private: + boost::asio::deadline_timer m_Timer; +}; + +} + +#endif /* IO_ENGINE_H */ diff --git a/lib/base/json-script.cpp b/lib/base/json-script.cpp index 9df6ee783..ee4221142 100644 --- a/lib/base/json-script.cpp +++ b/lib/base/json-script.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/dictionary.hpp" #include "base/function.hpp" @@ -32,11 +15,15 @@ static String JsonEncodeShim(const Value& value) } INITIALIZE_ONCE([]() { - Dictionary::Ptr jsonObj = new Dictionary({ - /* Methods */ - { "encode", new Function("Json#encode", JsonEncodeShim, { "value" }, true) }, - { "decode", new Function("Json#decode", JsonDecode, { "value" }, true) } - }); + auto jsonNSBehavior = new ConstNamespaceBehavior(); + Namespace::Ptr jsonNS = new Namespace(jsonNSBehavior); - ScriptGlobal::Set("Json", jsonObj); + /* Methods */ + jsonNS->Set("encode", new Function("Json#encode", JsonEncodeShim, { "value" }, true)); + jsonNS->Set("decode", new Function("Json#decode", JsonDecode, { "value" }, true)); + + jsonNSBehavior->Freeze(); + + Namespace::Ptr systemNS = ScriptGlobal::Get("System"); + systemNS->SetAttribute("Json", std::make_shared(jsonNS)); }); diff --git a/lib/base/json.cpp b/lib/base/json.cpp index c074aed56..c1a47c788 100644 --- a/lib/base/json.cpp +++ b/lib/base/json.cpp @@ -1,110 +1,186 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/json.hpp" #include "base/debug.hpp" +#include "base/namespace.hpp" #include "base/dictionary.hpp" #include "base/array.hpp" #include "base/objectlock.hpp" #include "base/convert.hpp" +#include "base/utility.hpp" +#include #include -#include -#include -#include +#include +#include #include +#include +#include using namespace icinga; -static void Encode(yajl_gen handle, const Value& value); - -#if YAJL_MAJOR < 2 -typedef unsigned int yajl_size; -#else /* YAJL_MAJOR */ -typedef size_t yajl_size; -#endif /* YAJL_MAJOR */ - -static void EncodeDictionary(yajl_gen handle, const Dictionary::Ptr& dict) +class JsonSax : public nlohmann::json_sax { - yajl_gen_map_open(handle); +public: + bool null() override; + bool boolean(bool val) override; + bool number_integer(number_integer_t val) override; + bool number_unsigned(number_unsigned_t val) override; + bool number_float(number_float_t val, const string_t& s) override; + bool string(string_t& val) override; + bool start_object(std::size_t elements) override; + bool key(string_t& val) override; + bool end_object() override; + bool start_array(std::size_t elements) override; + bool end_array() override; + bool parse_error(std::size_t position, const std::string& last_token, const nlohmann::detail::exception& ex) override; + + Value GetResult(); + +private: + Value m_Root; + std::stack> m_CurrentSubtree; + String m_CurrentKey; + + void FillCurrentTarget(Value value); +}; + +const char l_Null[] = "null"; +const char l_False[] = "false"; +const char l_True[] = "true"; +const char l_Indent[] = " "; + +// https://github.com/nlohmann/json/issues/1512 +template +class JsonEncoder +{ +public: + void Null(); + void Boolean(bool value); + void NumberFloat(double value); + void Strng(String value); + void StartObject(); + void Key(String value); + void EndObject(); + void StartArray(); + void EndArray(); + + String GetResult(); + +private: + std::vector m_Result; + String m_CurrentKey; + std::stack> m_CurrentSubtree; + + void AppendChar(char c); + + template + void AppendChars(Iterator begin, Iterator end); + + void AppendJson(nlohmann::json json); + + void BeforeItem(); + + void FinishContainer(char terminator); +}; + +template +void Encode(JsonEncoder& stateMachine, const Value& value); + +template +inline +void EncodeNamespace(JsonEncoder& stateMachine, const Namespace::Ptr& ns) +{ + stateMachine.StartObject(); + + ObjectLock olock(ns); + for (const Namespace::Pair& kv : ns) { + stateMachine.Key(Utility::ValidateUTF8(kv.first)); + Encode(stateMachine, kv.second->Get()); + } + + stateMachine.EndObject(); +} + +template +inline +void EncodeDictionary(JsonEncoder& stateMachine, const Dictionary::Ptr& dict) +{ + stateMachine.StartObject(); ObjectLock olock(dict); for (const Dictionary::Pair& kv : dict) { - yajl_gen_string(handle, reinterpret_cast(kv.first.CStr()), kv.first.GetLength()); - Encode(handle, kv.second); + stateMachine.Key(Utility::ValidateUTF8(kv.first)); + Encode(stateMachine, kv.second); } - yajl_gen_map_close(handle); + stateMachine.EndObject(); } -static void EncodeArray(yajl_gen handle, const Array::Ptr& arr) +template +inline +void EncodeArray(JsonEncoder& stateMachine, const Array::Ptr& arr) { - yajl_gen_array_open(handle); + stateMachine.StartArray(); ObjectLock olock(arr); for (const Value& value : arr) { - Encode(handle, value); + Encode(stateMachine, value); } - yajl_gen_array_close(handle); + stateMachine.EndArray(); } -static void Encode(yajl_gen handle, const Value& value) +template +void Encode(JsonEncoder& stateMachine, const Value& value) { switch (value.GetType()) { case ValueNumber: - if (yajl_gen_double(handle, value.Get()) == yajl_gen_invalid_number) - yajl_gen_double(handle, 0); - + stateMachine.NumberFloat(value.Get()); break; + case ValueBoolean: - yajl_gen_bool(handle, value.ToBool()); - + stateMachine.Boolean(value.ToBool()); break; + case ValueString: - yajl_gen_string(handle, reinterpret_cast(value.Get().CStr()), value.Get().GetLength()); - + stateMachine.Strng(Utility::ValidateUTF8(value.Get())); break; + case ValueObject: { const Object::Ptr& obj = value.Get(); - Dictionary::Ptr dict = dynamic_pointer_cast(obj); - if (dict) { - EncodeDictionary(handle, dict); - break; + { + Namespace::Ptr ns = dynamic_pointer_cast(obj); + if (ns) { + EncodeNamespace(stateMachine, ns); + break; + } + } + + { + Dictionary::Ptr dict = dynamic_pointer_cast(obj); + if (dict) { + EncodeDictionary(stateMachine, dict); + break; + } } Array::Ptr arr = dynamic_pointer_cast(obj); - if (arr) { - EncodeArray(handle, arr); + EncodeArray(stateMachine, arr); break; } } - yajl_gen_null(handle); + stateMachine.Null(); break; + case ValueEmpty: - yajl_gen_null(handle); - + stateMachine.Null(); break; + default: VERIFY(!"Invalid variant type."); } @@ -112,254 +188,309 @@ static void Encode(yajl_gen handle, const Value& value) String icinga::JsonEncode(const Value& value, bool pretty_print) { -#if YAJL_MAJOR < 2 - yajl_gen_config conf = { pretty_print, "" }; - yajl_gen handle = yajl_gen_alloc(&conf, nullptr); -#else /* YAJL_MAJOR */ - yajl_gen handle = yajl_gen_alloc(nullptr); - if (pretty_print) - yajl_gen_config(handle, yajl_gen_beautify, 1); -#endif /* YAJL_MAJOR */ + if (pretty_print) { + JsonEncoder stateMachine; - Encode(handle, value); + Encode(stateMachine, value); - const unsigned char *buf; - yajl_size len; + return stateMachine.GetResult(); + } else { + JsonEncoder stateMachine; - yajl_gen_get_buf(handle, &buf, &len); + Encode(stateMachine, value); - String result = String(buf, buf + len); - - yajl_gen_free(handle); - - return result; -} - -struct JsonElement -{ - String Key; - bool KeySet{false}; - Value EValue; -}; - -struct JsonContext -{ -public: - void Push(const Value& value) - { - JsonElement element; - element.EValue = value; - - m_Stack.push(element); + return stateMachine.GetResult(); } - - JsonElement Pop() - { - JsonElement value = m_Stack.top(); - m_Stack.pop(); - return value; - } - - void AddValue(const Value& value) - { - if (m_Stack.empty()) { - JsonElement element; - element.EValue = value; - m_Stack.push(element); - return; - } - - JsonElement& element = m_Stack.top(); - - if (element.EValue.IsObjectType()) { - if (!element.KeySet) { - element.Key = value; - element.KeySet = true; - } else { - Dictionary::Ptr dict = element.EValue; - dict->Set(element.Key, value); - element.KeySet = false; - } - } else if (element.EValue.IsObjectType()) { - Array::Ptr arr = element.EValue; - arr->Add(value); - } else { - BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot add value to JSON element.")); - } - } - - Value GetValue() const - { - ASSERT(m_Stack.size() == 1); - return m_Stack.top().EValue; - } - - void SaveException() - { - m_Exception = boost::current_exception(); - } - - void ThrowException() const - { - if (m_Exception) - boost::rethrow_exception(m_Exception); - } - -private: - std::stack m_Stack; - Value m_Key; - boost::exception_ptr m_Exception; -}; - -static int DecodeNull(void *ctx) -{ - auto *context = static_cast(ctx); - - try { - context->AddValue(Empty); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - -static int DecodeBoolean(void *ctx, int value) -{ - auto *context = static_cast(ctx); - - try { - context->AddValue(static_cast(value)); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - -static int DecodeNumber(void *ctx, const char *str, yajl_size len) -{ - auto *context = static_cast(ctx); - - try { - String jstr = String(str, str + len); - context->AddValue(Convert::ToDouble(jstr)); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - -static int DecodeString(void *ctx, const unsigned char *str, yajl_size len) -{ - auto *context = static_cast(ctx); - - try { - context->AddValue(String(str, str + len)); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - -static int DecodeStartMap(void *ctx) -{ - auto *context = static_cast(ctx); - - try { - context->Push(new Dictionary()); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - -static int DecodeEndMapOrArray(void *ctx) -{ - auto *context = static_cast(ctx); - - try { - context->AddValue(context->Pop().EValue); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; -} - -static int DecodeStartArray(void *ctx) -{ - auto *context = static_cast(ctx); - - try { - context->Push(new Array()); - } catch (...) { - context->SaveException(); - return 0; - } - - return 1; } Value icinga::JsonDecode(const String& data) { - static const yajl_callbacks callbacks = { - DecodeNull, - DecodeBoolean, - nullptr, - nullptr, - DecodeNumber, - DecodeString, - DecodeStartMap, - DecodeString, - DecodeEndMapOrArray, - DecodeStartArray, - DecodeEndMapOrArray - }; + String sanitized (Utility::ValidateUTF8(data)); - yajl_handle handle; -#if YAJL_MAJOR < 2 - yajl_parser_config cfg = { 1, 0 }; -#endif /* YAJL_MAJOR */ - JsonContext context; + JsonSax stateMachine; -#if YAJL_MAJOR < 2 - handle = yajl_alloc(&callbacks, &cfg, nullptr, &context); -#else /* YAJL_MAJOR */ - handle = yajl_alloc(&callbacks, nullptr, &context); - yajl_config(handle, yajl_dont_validate_strings, 1); - yajl_config(handle, yajl_allow_comments, 1); -#endif /* YAJL_MAJOR */ + nlohmann::json::sax_parse(sanitized.Begin(), sanitized.End(), &stateMachine); - yajl_parse(handle, reinterpret_cast(data.CStr()), data.GetLength()); + return stateMachine.GetResult(); +} -#if YAJL_MAJOR < 2 - if (yajl_parse_complete(handle) != yajl_status_ok) { -#else /* YAJL_MAJOR */ - if (yajl_complete_parse(handle) != yajl_status_ok) { -#endif /* YAJL_MAJOR */ - unsigned char *internal_err_str = yajl_get_error(handle, 1, reinterpret_cast(data.CStr()), data.GetLength()); - String msg = reinterpret_cast(internal_err_str); - yajl_free_error(handle, internal_err_str); +inline +bool JsonSax::null() +{ + FillCurrentTarget(Value()); - yajl_free(handle); + return true; +} - /* throw saved exception (if there is one) */ - context.ThrowException(); +inline +bool JsonSax::boolean(bool val) +{ + FillCurrentTarget(val); - BOOST_THROW_EXCEPTION(std::invalid_argument(msg)); + return true; +} + +inline +bool JsonSax::number_integer(JsonSax::number_integer_t val) +{ + FillCurrentTarget((double)val); + + return true; +} + +inline +bool JsonSax::number_unsigned(JsonSax::number_unsigned_t val) +{ + FillCurrentTarget((double)val); + + return true; +} + +inline +bool JsonSax::number_float(JsonSax::number_float_t val, const JsonSax::string_t&) +{ + FillCurrentTarget((double)val); + + return true; +} + +inline +bool JsonSax::string(JsonSax::string_t& val) +{ + FillCurrentTarget(String(std::move(val))); + + return true; +} + +inline +bool JsonSax::start_object(std::size_t) +{ + auto object (new Dictionary()); + + FillCurrentTarget(object); + + m_CurrentSubtree.push({object, nullptr}); + + return true; +} + +inline +bool JsonSax::key(JsonSax::string_t& val) +{ + m_CurrentKey = String(std::move(val)); + + return true; +} + +inline +bool JsonSax::end_object() +{ + m_CurrentSubtree.pop(); + m_CurrentKey = String(); + + return true; +} + +inline +bool JsonSax::start_array(std::size_t) +{ + auto array (new Array()); + + FillCurrentTarget(array); + + m_CurrentSubtree.push({nullptr, array}); + + return true; +} + +inline +bool JsonSax::end_array() +{ + m_CurrentSubtree.pop(); + + return true; +} + +inline +bool JsonSax::parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) +{ + throw std::invalid_argument(ex.what()); +} + +inline +Value JsonSax::GetResult() +{ + return m_Root; +} + +inline +void JsonSax::FillCurrentTarget(Value value) +{ + if (m_CurrentSubtree.empty()) { + m_Root = value; + } else { + auto& node (m_CurrentSubtree.top()); + + if (node.first) { + node.first->Set(m_CurrentKey, value); + } else { + node.second->Add(value); + } + } +} + +template +inline +void JsonEncoder::Null() +{ + BeforeItem(); + AppendChars((const char*)l_Null, (const char*)l_Null + 4); +} + +template +inline +void JsonEncoder::Boolean(bool value) +{ + BeforeItem(); + + if (value) { + AppendChars((const char*)l_True, (const char*)l_True + 4); + } else { + AppendChars((const char*)l_False, (const char*)l_False + 5); + } +} + +template +inline +void JsonEncoder::NumberFloat(double value) +{ + BeforeItem(); + AppendJson(value); +} + +template +inline +void JsonEncoder::Strng(String value) +{ + BeforeItem(); + AppendJson(std::move(value)); +} + +template +inline +void JsonEncoder::StartObject() +{ + BeforeItem(); + AppendChar('{'); + + m_CurrentSubtree.push(2); +} + +template +inline +void JsonEncoder::Key(String value) +{ + m_CurrentKey = std::move(value); +} + +template +inline +void JsonEncoder::EndObject() +{ + FinishContainer('}'); +} + +template +inline +void JsonEncoder::StartArray() +{ + BeforeItem(); + AppendChar('['); + + m_CurrentSubtree.push(0); +} + +template +inline +void JsonEncoder::EndArray() +{ + FinishContainer(']'); +} + +template +inline +String JsonEncoder::GetResult() +{ + return String(m_Result.begin(), m_Result.end()); +} + +template +inline +void JsonEncoder::AppendChar(char c) +{ + m_Result.emplace_back(c); +} + +template +template +inline +void JsonEncoder::AppendChars(Iterator begin, Iterator end) +{ + m_Result.insert(m_Result.end(), begin, end); +} + +template +inline +void JsonEncoder::AppendJson(nlohmann::json json) +{ + nlohmann::detail::serializer(nlohmann::detail::output_adapter(m_Result), ' ').dump(std::move(json), prettyPrint, true, 0); +} + +template +inline +void JsonEncoder::BeforeItem() +{ + if (!m_CurrentSubtree.empty()) { + auto& node (m_CurrentSubtree.top()); + + if (node[0]) { + AppendChar(','); + } else { + node[0] = true; + } + + if (prettyPrint) { + AppendChar('\n'); + + for (auto i (m_CurrentSubtree.size()); i; --i) { + AppendChars((const char*)l_Indent, (const char*)l_Indent + 4); + } + } + + if (node[1]) { + AppendJson(std::move(m_CurrentKey)); + AppendChar(':'); + + if (prettyPrint) { + AppendChar(' '); + } + } + } +} + +template +inline +void JsonEncoder::FinishContainer(char terminator) +{ + if (prettyPrint && m_CurrentSubtree.top()[0]) { + AppendChar('\n'); + + for (auto i (m_CurrentSubtree.size() - 1u); i; --i) { + AppendChars((const char*)l_Indent, (const char*)l_Indent + 4); + } } - yajl_free(handle); + AppendChar(terminator); - return context.GetValue(); + m_CurrentSubtree.pop(); } diff --git a/lib/base/json.hpp b/lib/base/json.hpp index 368a25cbd..df0ea18a0 100644 --- a/lib/base/json.hpp +++ b/lib/base/json.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef JSON_H #define JSON_H diff --git a/lib/base/lazy-init.hpp b/lib/base/lazy-init.hpp new file mode 100644 index 000000000..c1da2cd93 --- /dev/null +++ b/lib/base/lazy-init.hpp @@ -0,0 +1,72 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef LAZY_INIT +#define LAZY_INIT + +#include +#include +#include +#include + +namespace icinga +{ + +/** + * Lazy object initialization abstraction inspired from + * . + * + * @ingroup base + */ +template +class LazyInit +{ +public: + inline + LazyInit(std::function initializer = []() { return T(); }) : m_Initializer(std::move(initializer)) + { + m_Underlying.store(nullptr, std::memory_order_release); + } + + LazyInit(const LazyInit&) = delete; + LazyInit(LazyInit&&) = delete; + LazyInit& operator=(const LazyInit&) = delete; + LazyInit& operator=(LazyInit&&) = delete; + + inline + ~LazyInit() + { + auto ptr (m_Underlying.load(std::memory_order_acquire)); + + if (ptr != nullptr) { + delete ptr; + } + } + + inline + T& Get() + { + auto ptr (m_Underlying.load(std::memory_order_acquire)); + + if (ptr == nullptr) { + std::unique_lock lock (m_Mutex); + + ptr = m_Underlying.load(std::memory_order_acquire); + + if (ptr == nullptr) { + ptr = new T(m_Initializer()); + m_Underlying.store(ptr, std::memory_order_release); + } + } + + return *ptr; + } + +private: + std::function m_Initializer; + std::mutex m_Mutex; + std::atomic m_Underlying; +}; + +} + +#endif /* LAZY_INIT */ diff --git a/lib/base/library.cpp b/lib/base/library.cpp index d1a4c5f06..541ed74ad 100644 --- a/lib/base/library.cpp +++ b/lib/base/library.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/library.hpp" #include "base/loader.hpp" diff --git a/lib/base/library.hpp b/lib/base/library.hpp index 3832ab41d..6bd2065ec 100644 --- a/lib/base/library.hpp +++ b/lib/base/library.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef LIBRARY_H #define LIBRARY_H diff --git a/lib/base/loader.cpp b/lib/base/loader.cpp index a44beae76..e969f371d 100644 --- a/lib/base/loader.cpp +++ b/lib/base/loader.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/loader.hpp" #include "base/logger.hpp" diff --git a/lib/base/loader.hpp b/lib/base/loader.hpp index d54cada10..704a32a09 100644 --- a/lib/base/loader.hpp +++ b/lib/base/loader.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef LOADER_H #define LOADER_H diff --git a/lib/base/logger.cpp b/lib/base/logger.cpp index 8c20231fb..1401975c5 100644 --- a/lib/base/logger.cpp +++ b/lib/base/logger.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/logger.hpp" #include "base/logger-ti.cpp" @@ -49,11 +32,11 @@ bool Logger::m_TimestampEnabled = true; LogSeverity Logger::m_ConsoleLogSeverity = LogInformation; INITIALIZE_ONCE([]() { - ScriptGlobal::Set("LogDebug", LogDebug); - ScriptGlobal::Set("LogNotice", LogNotice); - ScriptGlobal::Set("LogInformation", LogInformation); - ScriptGlobal::Set("LogWarning", LogWarning); - ScriptGlobal::Set("LogCritical", LogCritical); + ScriptGlobal::Set("System.LogDebug", LogDebug, true); + ScriptGlobal::Set("System.LogNotice", LogNotice, true); + ScriptGlobal::Set("System.LogInformation", LogInformation, true); + ScriptGlobal::Set("System.LogWarning", LogWarning, true); + ScriptGlobal::Set("System.LogCritical", LogCritical, true); }); /** @@ -238,10 +221,20 @@ Log::~Log() if (entry.Severity >= logger->GetMinSeverity()) logger->ProcessLogEntry(entry); + +#ifdef I2_DEBUG /* I2_DEBUG */ + /* Always flush, don't depend on the timer. Enable this for development sprints on Linux/macOS only. Windows crashes. */ + //logger->Flush(); +#endif /* I2_DEBUG */ } - if (Logger::IsConsoleLogEnabled() && entry.Severity >= Logger::GetConsoleLogSeverity()) + if (Logger::IsConsoleLogEnabled() && entry.Severity >= Logger::GetConsoleLogSeverity()) { StreamLogger::ProcessLogEntry(std::cout, entry); + + /* "Console" might be a pipe/socket (systemd, daemontools, docker, ...), + * then cout will not flush lines automatically. */ + std::cout << std::flush; + } } Log& Log::operator<<(const char *val) diff --git a/lib/base/logger.hpp b/lib/base/logger.hpp index 31ed6c821..4200fcc6c 100644 --- a/lib/base/logger.hpp +++ b/lib/base/logger.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef LOGGER_H #define LOGGER_H diff --git a/lib/base/logger.ti b/lib/base/logger.ti index faff9f936..df68b2540 100644 --- a/lib/base/logger.ti +++ b/lib/base/logger.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" diff --git a/lib/base/math-script.cpp b/lib/base/math-script.cpp index dab48a5df..21dbee411 100644 --- a/lib/base/math-script.cpp +++ b/lib/base/math-script.cpp @@ -1,27 +1,11 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/dictionary.hpp" #include "base/function.hpp" #include "base/functionwrapper.hpp" #include "base/scriptframe.hpp" #include "base/initialize.hpp" +#include "base/namespace.hpp" #include #include @@ -158,40 +142,44 @@ static double MathSign(double x) } INITIALIZE_ONCE([]() { - Dictionary::Ptr mathObj = new Dictionary({ - /* Constants */ - { "E", 2.71828182845904523536 }, - { "LN2", 0.693147180559945309417 }, - { "LN10", 2.30258509299404568402 }, - { "LOG2E", 1.44269504088896340736 }, - { "LOG10E", 0.434294481903251827651 }, - { "PI", 3.14159265358979323846 }, - { "SQRT1_2", 0.707106781186547524401 }, - { "SQRT2", 1.41421356237309504880 }, + auto mathNSBehavior = new ConstNamespaceBehavior(); + Namespace::Ptr mathNS = new Namespace(mathNSBehavior); - /* Methods */ - { "abs", new Function("Math#abs", MathAbs, { "x" }, true) }, - { "acos", new Function("Math#acos", MathAcos, { "x" }, true) }, - { "asin", new Function("Math#asin", MathAsin, { "x" }, true) }, - { "atan", new Function("Math#atan", MathAtan, { "x" }, true) }, - { "atan2", new Function("Math#atan2", MathAtan2, { "x", "y" }, true) }, - { "ceil", new Function("Math#ceil", MathCeil, { "x" }, true) }, - { "cos", new Function("Math#cos", MathCos, { "x" }, true) }, - { "exp", new Function("Math#exp", MathExp, { "x" }, true) }, - { "floor", new Function("Math#floor", MathFloor, { "x" }, true) }, - { "log", new Function("Math#log", MathLog, { "x" }, true) }, - { "max", new Function("Math#max", MathMax, {}, true) }, - { "min", new Function("Math#min", MathMin, {}, true) }, - { "pow", new Function("Math#pow", MathPow, { "x", "y" }, true) }, - { "random", new Function("Math#random", MathRandom, {}, true) }, - { "round", new Function("Math#round", MathRound, { "x" }, true) }, - { "sin", new Function("Math#sin", MathSin, { "x" }, true) }, - { "sqrt", new Function("Math#sqrt", MathSqrt, { "x" }, true) }, - { "tan", new Function("Math#tan", MathTan, { "x" }, true) }, - { "isnan", new Function("Math#isnan", MathIsnan, { "x" }, true) }, - { "isinf", new Function("Math#isinf", MathIsinf, { "x" }, true) }, - { "sign", new Function("Math#sign", MathSign, { "x" }, true) } - }); + /* Constants */ + mathNS->Set("E", 2.71828182845904523536); + mathNS->Set("LN2", 0.693147180559945309417); + mathNS->Set("LN10", 2.30258509299404568402); + mathNS->Set("LOG2E", 1.44269504088896340736); + mathNS->Set("LOG10E", 0.434294481903251827651); + mathNS->Set("PI", 3.14159265358979323846); + mathNS->Set("SQRT1_2", 0.707106781186547524401); + mathNS->Set("SQRT2", 1.41421356237309504880); - ScriptGlobal::Set("Math", mathObj); + /* Methods */ + mathNS->Set("abs", new Function("Math#abs", MathAbs, { "x" }, true)); + mathNS->Set("acos", new Function("Math#acos", MathAcos, { "x" }, true)); + mathNS->Set("asin", new Function("Math#asin", MathAsin, { "x" }, true)); + mathNS->Set("atan", new Function("Math#atan", MathAtan, { "x" }, true)); + mathNS->Set("atan2", new Function("Math#atan2", MathAtan2, { "x", "y" }, true)); + mathNS->Set("ceil", new Function("Math#ceil", MathCeil, { "x" }, true)); + mathNS->Set("cos", new Function("Math#cos", MathCos, { "x" }, true)); + mathNS->Set("exp", new Function("Math#exp", MathExp, { "x" }, true)); + mathNS->Set("floor", new Function("Math#floor", MathFloor, { "x" }, true)); + mathNS->Set("log", new Function("Math#log", MathLog, { "x" }, true)); + mathNS->Set("max", new Function("Math#max", MathMax, {}, true)); + mathNS->Set("min", new Function("Math#min", MathMin, {}, true)); + mathNS->Set("pow", new Function("Math#pow", MathPow, { "x", "y" }, true)); + mathNS->Set("random", new Function("Math#random", MathRandom, {}, true)); + mathNS->Set("round", new Function("Math#round", MathRound, { "x" }, true)); + mathNS->Set("sin", new Function("Math#sin", MathSin, { "x" }, true)); + mathNS->Set("sqrt", new Function("Math#sqrt", MathSqrt, { "x" }, true)); + mathNS->Set("tan", new Function("Math#tan", MathTan, { "x" }, true)); + mathNS->Set("isnan", new Function("Math#isnan", MathIsnan, { "x" }, true)); + mathNS->Set("isinf", new Function("Math#isinf", MathIsinf, { "x" }, true)); + mathNS->Set("sign", new Function("Math#sign", MathSign, { "x" }, true)); + + mathNSBehavior->Freeze(); + + Namespace::Ptr systemNS = ScriptGlobal::Get("System"); + systemNS->SetAttribute("Math", std::make_shared(mathNS)); }); diff --git a/lib/base/namespace-script.cpp b/lib/base/namespace-script.cpp new file mode 100644 index 000000000..29d23a000 --- /dev/null +++ b/lib/base/namespace-script.cpp @@ -0,0 +1,84 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/namespace.hpp" +#include "base/function.hpp" +#include "base/functionwrapper.hpp" +#include "base/scriptframe.hpp" +#include "base/array.hpp" + +using namespace icinga; + +static void NamespaceSet(const String& key, const Value& value) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Namespace::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + self->Set(key, value); +} + +static Value NamespaceGet(const String& key) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Namespace::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + return self->Get(key); +} + +static void NamespaceRemove(const String& key) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Namespace::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + self->Remove(key); +} + +static bool NamespaceContains(const String& key) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Namespace::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + return self->Contains(key); +} + +static Array::Ptr NamespaceKeys() +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Namespace::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + + ArrayData keys; + ObjectLock olock(self); + for (const Namespace::Pair& kv : self) { + keys.push_back(kv.first); + } + return new Array(std::move(keys)); +} + +static Array::Ptr NamespaceValues() +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Namespace::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + + ArrayData values; + ObjectLock olock(self); + for (const Namespace::Pair& kv : self) { + values.push_back(kv.second->Get()); + } + return new Array(std::move(values)); +} + +Object::Ptr Namespace::GetPrototype() +{ + static Dictionary::Ptr prototype = new Dictionary({ + { "set", new Function("Namespace#set", NamespaceSet, { "key", "value" }) }, + { "get", new Function("Namespace#get", NamespaceGet, { "key" }) }, + { "remove", new Function("Namespace#remove", NamespaceRemove, { "key" }) }, + { "contains", new Function("Namespace#contains", NamespaceContains, { "key" }, true) }, + { "keys", new Function("Namespace#keys", NamespaceKeys, {}, true) }, + { "values", new Function("Namespace#values", NamespaceValues, {}, true) }, + }); + + return prototype; +} + diff --git a/lib/base/namespace.cpp b/lib/base/namespace.cpp new file mode 100644 index 000000000..c02ac95e6 --- /dev/null +++ b/lib/base/namespace.cpp @@ -0,0 +1,224 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/namespace.hpp" +#include "base/objectlock.hpp" +#include "base/debug.hpp" +#include "base/primitivetype.hpp" +#include "base/debuginfo.hpp" +#include "base/exception.hpp" +#include + +using namespace icinga; + +template class std::map >; + +REGISTER_PRIMITIVE_TYPE(Namespace, Object, Namespace::GetPrototype()); + +Namespace::Namespace(NamespaceBehavior *behavior) + : m_Behavior(std::unique_ptr(behavior)) +{ } + +Value Namespace::Get(const String& field) const +{ + ObjectLock olock(this); + + Value value; + if (!GetOwnField(field, &value)) + BOOST_THROW_EXCEPTION(ScriptError("Namespace does not contain field '" + field + "'")); + return value; +} + +bool Namespace::Get(const String& field, Value *value) const +{ + ObjectLock olock(this); + + auto nsVal = GetAttribute(field); + + if (!nsVal) + return false; + + *value = nsVal->Get(DebugInfo()); + return true; +} + +void Namespace::Set(const String& field, const Value& value, bool overrideFrozen) +{ + ObjectLock olock(this); + + return SetFieldByName(field, value, overrideFrozen, DebugInfo()); +} + +bool Namespace::Contains(const String& field) const +{ + ObjectLock olock(this); + + return HasOwnField(field); +} + +void Namespace::Remove(const String& field, bool overrideFrozen) +{ + ObjectLock olock(this); + + m_Behavior->Remove(this, field, overrideFrozen); +} + +void Namespace::RemoveAttribute(const String& field) +{ + ObjectLock olock(this); + + Namespace::Iterator it; + it = m_Data.find(field); + + if (it == m_Data.end()) + return; + + m_Data.erase(it); +} + +std::shared_ptr Namespace::GetAttribute(const String& key) const +{ + ObjectLock olock(this); + + auto it = m_Data.find(key); + + if (it == m_Data.end()) + return nullptr; + + return it->second; +} + +void Namespace::SetAttribute(const String& key, const std::shared_ptr& nsVal) +{ + ObjectLock olock(this); + + m_Data[key] = nsVal; +} + +Value Namespace::GetFieldByName(const String& field, bool, const DebugInfo& debugInfo) const +{ + ObjectLock olock(this); + + auto nsVal = GetAttribute(field); + + if (nsVal) + return nsVal->Get(debugInfo); + else + return GetPrototypeField(const_cast(this), field, false, debugInfo); /* Ignore indexer not found errors similar to the Dictionary class. */ +} + +void Namespace::SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) +{ + ObjectLock olock(this); + + auto nsVal = GetAttribute(field); + + if (!nsVal) + m_Behavior->Register(this, field, value, overrideFrozen, debugInfo); + else + nsVal->Set(value, overrideFrozen, debugInfo); +} + +bool Namespace::HasOwnField(const String& field) const +{ + ObjectLock olock(this); + + return GetAttribute(field) != nullptr; +} + +bool Namespace::GetOwnField(const String& field, Value *result) const +{ + ObjectLock olock(this); + + auto nsVal = GetAttribute(field); + + if (!nsVal) + return false; + + *result = nsVal->Get(DebugInfo()); + return true; +} + +EmbeddedNamespaceValue::EmbeddedNamespaceValue(const Value& value) + : m_Value(value) +{ } + +Value EmbeddedNamespaceValue::Get(const DebugInfo& debugInfo) const +{ + return m_Value; +} + +void EmbeddedNamespaceValue::Set(const Value& value, bool, const DebugInfo&) +{ + m_Value = value; +} + +void ConstEmbeddedNamespaceValue::Set(const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) +{ + if (!overrideFrozen) + BOOST_THROW_EXCEPTION(ScriptError("Constant must not be modified.", debugInfo)); + + EmbeddedNamespaceValue::Set(value, overrideFrozen, debugInfo); +} + +void NamespaceBehavior::Register(const Namespace::Ptr& ns, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) const +{ + ns->SetAttribute(field, std::make_shared(value)); +} + +void NamespaceBehavior::Remove(const Namespace::Ptr& ns, const String& field, bool overrideFrozen) +{ + if (!overrideFrozen) { + auto attr = ns->GetAttribute(field); + + if (dynamic_pointer_cast(attr)) + BOOST_THROW_EXCEPTION(ScriptError("Constants must not be removed.")); + } + + ns->RemoveAttribute(field); +} + +void ConstNamespaceBehavior::Register(const Namespace::Ptr& ns, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) const +{ + if (m_Frozen && !overrideFrozen) + BOOST_THROW_EXCEPTION(ScriptError("Namespace is read-only and must not be modified.", debugInfo)); + + ns->SetAttribute(field, std::make_shared(value)); +} + +void ConstNamespaceBehavior::Remove(const Namespace::Ptr& ns, const String& field, bool overrideFrozen) +{ + if (m_Frozen && !overrideFrozen) + BOOST_THROW_EXCEPTION(ScriptError("Namespace is read-only and must not be modified.")); + + NamespaceBehavior::Remove(ns, field, overrideFrozen); +} + +void ConstNamespaceBehavior::Freeze() +{ + m_Frozen = true; +} + +Namespace::Iterator Namespace::Begin() +{ + ASSERT(OwnsLock()); + + return m_Data.begin(); +} + +Namespace::Iterator Namespace::End() +{ + ASSERT(OwnsLock()); + + return m_Data.end(); +} + +Namespace::Iterator icinga::begin(const Namespace::Ptr& x) +{ + return x->Begin(); +} + +Namespace::Iterator icinga::end(const Namespace::Ptr& x) +{ + return x->End(); +} + diff --git a/lib/base/namespace.hpp b/lib/base/namespace.hpp new file mode 100644 index 000000000..6b2e92018 --- /dev/null +++ b/lib/base/namespace.hpp @@ -0,0 +1,107 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef NAMESPACE_H +#define NAMESPACE_H + +#include "base/i2-base.hpp" +#include "base/object.hpp" +#include "base/value.hpp" +#include "base/debuginfo.hpp" +#include +#include +#include + +namespace icinga +{ + +struct NamespaceValue +{ + virtual Value Get(const DebugInfo& debugInfo = DebugInfo()) const = 0; + virtual void Set(const Value& value, bool overrideFrozen, const DebugInfo& debugInfo = DebugInfo()) = 0; +}; + +struct EmbeddedNamespaceValue : public NamespaceValue +{ + EmbeddedNamespaceValue(const Value& value); + + Value Get(const DebugInfo& debugInfo) const override; + void Set(const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) override; + +private: + Value m_Value; +}; + +struct ConstEmbeddedNamespaceValue : public EmbeddedNamespaceValue +{ + using EmbeddedNamespaceValue::EmbeddedNamespaceValue; + + void Set(const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) override; +}; + +class Namespace; + +struct NamespaceBehavior +{ + virtual void Register(const boost::intrusive_ptr& ns, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) const; + virtual void Remove(const boost::intrusive_ptr& ns, const String& field, bool overrideFrozen); +}; + +struct ConstNamespaceBehavior : public NamespaceBehavior +{ + void Register(const boost::intrusive_ptr& ns, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) const override; + void Remove(const boost::intrusive_ptr& ns, const String& field, bool overrideFrozen) override; + void Freeze(); + +private: + bool m_Frozen; +}; + +/** + * A namespace. + * + * @ingroup base + */ +class Namespace final : public Object +{ +public: + DECLARE_OBJECT(Namespace); + + typedef std::map >::iterator Iterator; + + typedef std::map >::value_type Pair; + + Namespace(NamespaceBehavior *behavior = new NamespaceBehavior); + + Value Get(const String& field) const; + bool Get(const String& field, Value *value) const; + void Set(const String& field, const Value& value, bool overrideFrozen = false); + bool Contains(const String& field) const; + void Remove(const String& field, bool overrideFrozen = false); + + std::shared_ptr GetAttribute(const String& field) const; + void SetAttribute(const String& field, const std::shared_ptr& nsVal); + void RemoveAttribute(const String& field); + + Iterator Begin(); + Iterator End(); + + Value GetFieldByName(const String& field, bool sandboxed, const DebugInfo& debugInfo) const override; + void SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) override; + bool HasOwnField(const String& field) const override; + bool GetOwnField(const String& field, Value *result) const override; + + static Object::Ptr GetPrototype(); + +private: + std::map > m_Data; + std::unique_ptr m_Behavior; +}; + +Namespace::Iterator begin(const Namespace::Ptr& x); +Namespace::Iterator end(const Namespace::Ptr& x); + +} + +extern template class std::map >; + +#endif /* NAMESPACE_H */ diff --git a/lib/base/netstring.cpp b/lib/base/netstring.cpp index 7d11d8928..2be7675a7 100644 --- a/lib/base/netstring.cpp +++ b/lib/base/netstring.cpp @@ -1,25 +1,16 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/netstring.hpp" #include "base/debug.hpp" +#include "base/tlsstream.hpp" +#include +#include #include +#include +#include +#include +#include +#include using namespace icinga; @@ -85,7 +76,7 @@ StreamReadStatus NetString::ReadStringFromStream(const Stream::Ptr& stream, Stri /* read the whole message */ size_t data_length = len + 1; - if (maxMessageLength >= 0 && data_length > maxMessageLength) { + if (maxMessageLength >= 0 && data_length > (size_t)maxMessageLength) { std::stringstream errorMessage; errorMessage << "Max data length exceeded: " << (maxMessageLength / 1024) << " KB"; @@ -127,6 +118,210 @@ size_t NetString::WriteStringToStream(const Stream::Ptr& stream, const String& s return msg.GetLength(); } +/** + * Reads data from a stream in netstring format. + * + * @param stream The stream to read from. + * @returns The String that has been read from the IOQueue. + * @exception invalid_argument The input stream is invalid. + * @see https://github.com/PeterScott/netstring-c/blob/master/netstring.c + */ +String NetString::ReadStringFromStream(const std::shared_ptr& stream, + ssize_t maxMessageLength) +{ + namespace asio = boost::asio; + + size_t len = 0; + bool leadingZero = false; + + for (uint_fast8_t readBytes = 0;; ++readBytes) { + char byte = 0; + + { + asio::mutable_buffer byteBuf (&byte, 1); + asio::read(*stream, byteBuf); + } + + if (isdigit(byte)) { + if (readBytes == 9) { + BOOST_THROW_EXCEPTION(std::invalid_argument("Length specifier must not exceed 9 characters")); + } + + if (leadingZero) { + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid NetString (leading zero)")); + } + + len = len * 10u + size_t(byte - '0'); + + if (!readBytes && byte == '0') { + leadingZero = true; + } + } else if (byte == ':') { + if (!readBytes) { + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid NetString (no length specifier)")); + } + + break; + } else { + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid NetString (missing :)")); + } + } + + if (maxMessageLength >= 0 && len > maxMessageLength) { + std::stringstream errorMessage; + errorMessage << "Max data length exceeded: " << (maxMessageLength / 1024) << " KB"; + + BOOST_THROW_EXCEPTION(std::invalid_argument(errorMessage.str())); + } + + String payload; + + if (len) { + payload.Append(len, 0); + + asio::mutable_buffer payloadBuf (&*payload.Begin(), payload.GetLength()); + asio::read(*stream, payloadBuf); + } + + char trailer = 0; + + { + asio::mutable_buffer trailerBuf (&trailer, 1); + asio::read(*stream, trailerBuf); + } + + if (trailer != ',') { + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid NetString (missing ,)")); + } + + return std::move(payload); +} + +/** + * Reads data from a stream in netstring format. + * + * @param stream The stream to read from. + * @returns The String that has been read from the IOQueue. + * @exception invalid_argument The input stream is invalid. + * @see https://github.com/PeterScott/netstring-c/blob/master/netstring.c + */ +String NetString::ReadStringFromStream(const std::shared_ptr& stream, + boost::asio::yield_context yc, ssize_t maxMessageLength) +{ + namespace asio = boost::asio; + + size_t len = 0; + bool leadingZero = false; + + for (uint_fast8_t readBytes = 0;; ++readBytes) { + char byte = 0; + + { + asio::mutable_buffer byteBuf (&byte, 1); + asio::async_read(*stream, byteBuf, yc); + } + + if (isdigit(byte)) { + if (readBytes == 9) { + BOOST_THROW_EXCEPTION(std::invalid_argument("Length specifier must not exceed 9 characters")); + } + + if (leadingZero) { + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid NetString (leading zero)")); + } + + len = len * 10u + size_t(byte - '0'); + + if (!readBytes && byte == '0') { + leadingZero = true; + } + } else if (byte == ':') { + if (!readBytes) { + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid NetString (no length specifier)")); + } + + break; + } else { + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid NetString (missing :)")); + } + } + + if (maxMessageLength >= 0 && len > maxMessageLength) { + std::stringstream errorMessage; + errorMessage << "Max data length exceeded: " << (maxMessageLength / 1024) << " KB"; + + BOOST_THROW_EXCEPTION(std::invalid_argument(errorMessage.str())); + } + + String payload; + + if (len) { + payload.Append(len, 0); + + asio::mutable_buffer payloadBuf (&*payload.Begin(), payload.GetLength()); + asio::async_read(*stream, payloadBuf, yc); + } + + char trailer = 0; + + { + asio::mutable_buffer trailerBuf (&trailer, 1); + asio::async_read(*stream, trailerBuf, yc); + } + + if (trailer != ',') { + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid NetString (missing ,)")); + } + + return std::move(payload); +} + +/** + * Writes data into a stream using the netstring format and returns bytes written. + * + * @param stream The stream. + * @param str The String that is to be written. + * + * @return The amount of bytes written. + */ +size_t NetString::WriteStringToStream(const std::shared_ptr& stream, const String& str) +{ + namespace asio = boost::asio; + + std::ostringstream msgbuf; + WriteStringToStream(msgbuf, str); + + String msg = msgbuf.str(); + asio::const_buffer msgBuf (msg.CStr(), msg.GetLength()); + + asio::write(*stream, msgBuf); + + return msg.GetLength(); +} + +/** + * Writes data into a stream using the netstring format and returns bytes written. + * + * @param stream The stream. + * @param str The String that is to be written. + * + * @return The amount of bytes written. + */ +size_t NetString::WriteStringToStream(const std::shared_ptr& stream, const String& str, boost::asio::yield_context yc) +{ + namespace asio = boost::asio; + + std::ostringstream msgbuf; + WriteStringToStream(msgbuf, str); + + String msg = msgbuf.str(); + asio::const_buffer msgBuf (msg.CStr(), msg.GetLength()); + + asio::async_write(*stream, msgBuf, yc); + + return msg.GetLength(); +} + /** * Writes data into a stream using the netstring format. * diff --git a/lib/base/netstring.hpp b/lib/base/netstring.hpp index 1daaa979a..2d2435907 100644 --- a/lib/base/netstring.hpp +++ b/lib/base/netstring.hpp @@ -1,27 +1,13 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef NETSTRING_H #define NETSTRING_H #include "base/i2-base.hpp" #include "base/stream.hpp" +#include "base/tlsstream.hpp" +#include +#include namespace icinga { @@ -40,7 +26,12 @@ class NetString public: static StreamReadStatus ReadStringFromStream(const Stream::Ptr& stream, String *message, StreamReadContext& context, bool may_wait = false, ssize_t maxMessageLength = -1); + static String ReadStringFromStream(const std::shared_ptr& stream, ssize_t maxMessageLength = -1); + static String ReadStringFromStream(const std::shared_ptr& stream, + boost::asio::yield_context yc, ssize_t maxMessageLength = -1); static size_t WriteStringToStream(const Stream::Ptr& stream, const String& message); + static size_t WriteStringToStream(const std::shared_ptr& stream, const String& message); + static size_t WriteStringToStream(const std::shared_ptr& stream, const String& message, boost::asio::yield_context yc); static void WriteStringToStream(std::ostream& stream, const String& message); private: diff --git a/lib/base/networkstream.cpp b/lib/base/networkstream.cpp index 4752b3fbf..57da507d8 100644 --- a/lib/base/networkstream.cpp +++ b/lib/base/networkstream.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/networkstream.hpp" diff --git a/lib/base/networkstream.hpp b/lib/base/networkstream.hpp index edbd32f1c..453d7ad0b 100644 --- a/lib/base/networkstream.hpp +++ b/lib/base/networkstream.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef NETWORKSTREAM_H #define NETWORKSTREAM_H @@ -28,7 +11,7 @@ namespace icinga { /** - * A network stream. + * A network stream. DEPRECATED - Use Boost ASIO instead. * * @ingroup base */ diff --git a/lib/base/number-script.cpp b/lib/base/number-script.cpp index 8ec9ae554..0dcaca58b 100644 --- a/lib/base/number-script.cpp +++ b/lib/base/number-script.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/number.hpp" #include "base/convert.hpp" diff --git a/lib/base/number.cpp b/lib/base/number.cpp index 8a2a7ff6e..a33651988 100644 --- a/lib/base/number.cpp +++ b/lib/base/number.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/number.hpp" #include "base/primitivetype.hpp" diff --git a/lib/base/number.hpp b/lib/base/number.hpp index 239ec299a..dd5196f98 100644 --- a/lib/base/number.hpp +++ b/lib/base/number.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef NUMBER_H #define NUMBER_H diff --git a/lib/base/object-packer.cpp b/lib/base/object-packer.cpp new file mode 100644 index 000000000..024325e3d --- /dev/null +++ b/lib/base/object-packer.cpp @@ -0,0 +1,247 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/object-packer.hpp" +#include "base/debug.hpp" +#include "base/dictionary.hpp" +#include "base/array.hpp" +#include "base/objectlock.hpp" +#include "base/stringbuilder.hpp" +#include +#include +#include +#include +#include + +using namespace icinga; + +union EndiannessDetector +{ + EndiannessDetector() + { + i = 1; + } + + int i; + char buf[sizeof(int)]; +}; + +static const EndiannessDetector l_EndiannessDetector; + +// Assumption: The compiler will optimize (away) if/else statements using this. +#define MACHINE_LITTLE_ENDIAN (l_EndiannessDetector.buf[0]) + +static void PackAny(const Value& value, StringBuilder& builder); + +/** + * std::swap() seems not to work + */ +static inline void SwapBytes(char& a, char& b) +{ + char c = a; + a = b; + b = c; +} + +#if CHAR_MIN != 0 +union CharU2SConverter +{ + CharU2SConverter() + { + s = 0; + } + + unsigned char u; + signed char s; +}; +#endif + +/** + * Avoid implementation-defined overflows during unsigned to signed casts + */ +static inline char UIntToByte(unsigned i) +{ +#if CHAR_MIN == 0 + return i; +#else + CharU2SConverter converter; + + converter.u = i; + return converter.s; +#endif +} + +/** + * Append the given int as big-endian 64-bit unsigned int + */ +static inline void PackUInt64BE(uint_least64_t i, StringBuilder& builder) +{ + char buf[8] = { + UIntToByte(i >> 56u), + UIntToByte((i >> 48u) & 255u), + UIntToByte((i >> 40u) & 255u), + UIntToByte((i >> 32u) & 255u), + UIntToByte((i >> 24u) & 255u), + UIntToByte((i >> 16u) & 255u), + UIntToByte((i >> 8u) & 255u), + UIntToByte(i & 255u) + }; + + builder.Append((char*)buf, (char*)buf + 8); +} + +union Double2BytesConverter +{ + Double2BytesConverter() + { + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + } + + double f; + char buf[8]; +}; + +/** + * Append the given double as big-endian IEEE 754 binary64 + */ +static inline void PackFloat64BE(double f, StringBuilder& builder) +{ + Double2BytesConverter converter; + + converter.f = f; + + if (MACHINE_LITTLE_ENDIAN) { + SwapBytes(converter.buf[0], converter.buf[7]); + SwapBytes(converter.buf[1], converter.buf[6]); + SwapBytes(converter.buf[2], converter.buf[5]); + SwapBytes(converter.buf[3], converter.buf[4]); + } + + builder.Append((char*)converter.buf, (char*)converter.buf + 8); +} + +/** + * Append the given string's length (BE uint64) and the string itself + */ +static inline void PackString(const String& string, StringBuilder& builder) +{ + PackUInt64BE(string.GetLength(), builder); + builder.Append(string); +} + +/** + * Append the given array + */ +static inline void PackArray(const Array::Ptr& arr, StringBuilder& builder) +{ + ObjectLock olock(arr); + + builder.Append('\5'); + PackUInt64BE(arr->GetLength(), builder); + + for (const Value& value : arr) { + PackAny(value, builder); + } +} + +/** + * Append the given dictionary + */ +static inline void PackDictionary(const Dictionary::Ptr& dict, StringBuilder& builder) +{ + ObjectLock olock(dict); + + builder.Append('\6'); + PackUInt64BE(dict->GetLength(), builder); + + for (const Dictionary::Pair& kv : dict) { + PackString(kv.first, builder); + PackAny(kv.second, builder); + } +} + +/** + * Append any JSON-encodable value + */ +static void PackAny(const Value& value, StringBuilder& builder) +{ + switch (value.GetType()) { + case ValueString: + builder.Append('\4'); + PackString(value.Get(), builder); + break; + + case ValueNumber: + builder.Append('\3'); + PackFloat64BE(value.Get(), builder); + break; + + case ValueBoolean: + builder.Append(value.ToBool() ? '\2' : '\1'); + break; + + case ValueEmpty: + builder.Append('\0'); + break; + + case ValueObject: + { + const Object::Ptr& obj = value.Get(); + + Dictionary::Ptr dict = dynamic_pointer_cast(obj); + if (dict) { + PackDictionary(dict, builder); + break; + } + + Array::Ptr arr = dynamic_pointer_cast(obj); + if (arr) { + PackArray(arr, builder); + break; + } + } + + builder.Append('\0'); + break; + + default: + VERIFY(!"Invalid variant type."); + } +} + +/** + * Pack any JSON-encodable value to a BSON-similar structure suitable for consistent hashing + * + * Spec: + * null: 0x00 + * false: 0x01 + * true: 0x02 + * number: 0x03 (ieee754_binary64_bigendian)payload + * string: 0x04 (uint64_bigendian)payload.length (char[])payload + * array: 0x05 (uint64_bigendian)payload.length (any[])payload + * object: 0x06 (uint64_bigendian)payload.length (keyvalue[])payload.sort() + * + * any: null|false|true|number|string|array|object + * keyvalue: (uint64_bigendian)key.length (char[])key (any)value + * + * Assumptions: + * - double is IEEE 754 binary64 + * - all int types (signed and unsigned) and all float types share the same endianness + * - char is exactly 8 bits wide and one char is exactly one byte affected by the machine endianness + * - all input strings, arrays and dictionaries are at most 2^64-1 long + * + * If not, this function will silently produce invalid results. + */ +String icinga::PackObject(const Value& value) +{ + StringBuilder builder; + PackAny(value, builder); + + return builder.ToString(); +} diff --git a/lib/base/object-packer.hpp b/lib/base/object-packer.hpp new file mode 100644 index 000000000..00f7b998b --- /dev/null +++ b/lib/base/object-packer.hpp @@ -0,0 +1,18 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef OBJECT_PACKER +#define OBJECT_PACKER + +#include "base/i2-base.hpp" + +namespace icinga +{ + +class String; +class Value; + +String PackObject(const Value& value); + +} + +#endif /* OBJECT_PACKER */ diff --git a/lib/base/object-script.cpp b/lib/base/object-script.cpp index 6db6374e8..fff7df0c9 100644 --- a/lib/base/object-script.cpp +++ b/lib/base/object-script.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/object.hpp" #include "base/dictionary.hpp" diff --git a/lib/base/object.cpp b/lib/base/object.cpp index b1fff79d2..72f99adaf 100644 --- a/lib/base/object.cpp +++ b/lib/base/object.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/object.hpp" #include "base/value.hpp" @@ -27,6 +10,7 @@ #include "base/exception.hpp" #include #include +#include using namespace icinga; @@ -38,12 +22,23 @@ static std::map l_ObjectCounts; static Timer::Ptr l_ObjectCountTimer; #endif /* I2_LEAK_DEBUG */ +/** + * Constructor for the Object class. + */ +Object::Object() +{ + m_References.store(0); + +#ifdef I2_DEBUG + m_LockOwner.store(decltype(m_LockOwner.load())()); +#endif /* I2_DEBUG */ +} + /** * Destructor for the Object class. */ Object::~Object() { - delete reinterpret_cast(m_Mutex); } /** @@ -62,15 +57,7 @@ String Object::ToString() const */ bool Object::OwnsLock() const { -#ifdef _WIN32 - DWORD tid = InterlockedExchangeAdd(&m_LockOwner, 0); - - return (tid == GetCurrentThreadId()); -#else /* _WIN32 */ - pthread_t tid = __sync_fetch_and_add(&m_LockOwner, 0); - - return (tid == pthread_self()); -#endif /* _WIN32 */ + return m_LockOwner.load() == std::this_thread::get_id(); } #endif /* I2_DEBUG */ @@ -138,7 +125,7 @@ Value Object::GetFieldByName(const String& field, bool sandboxed, const DebugInf return GetField(fid); } -void Object::SetFieldByName(const String& field, const Value& value, const DebugInfo& debugInfo) +void Object::SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) { Type::Ptr type = GetReflectionType(); @@ -255,28 +242,18 @@ INITIALIZE_ONCE([]() { void icinga::intrusive_ptr_add_ref(Object *object) { #ifdef I2_LEAK_DEBUG - if (object->m_References == 0) + if (object->m_References.fetch_add(1) == 0u) TypeAddObject(object); +#else /* I2_LEAK_DEBUG */ + object->m_References.fetch_add(1); #endif /* I2_LEAK_DEBUG */ - -#ifdef _WIN32 - InterlockedIncrement(&object->m_References); -#else /* _WIN32 */ - __sync_add_and_fetch(&object->m_References, 1); -#endif /* _WIN32 */ } void icinga::intrusive_ptr_release(Object *object) { - uintptr_t refs; + auto previous (object->m_References.fetch_sub(1)); -#ifdef _WIN32 - refs = InterlockedDecrement(&object->m_References); -#else /* _WIN32 */ - refs = __sync_sub_and_fetch(&object->m_References, 1); -#endif /* _WIN32 */ - - if (unlikely(refs == 0)) { + if (previous == 1u) { #ifdef I2_LEAK_DEBUG TypeRemoveObject(object); #endif /* I2_LEAK_DEBUG */ diff --git a/lib/base/object.hpp b/lib/base/object.hpp index a29e4f73b..5a90cfa64 100644 --- a/lib/base/object.hpp +++ b/lib/base/object.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef OBJECT_H #define OBJECT_H @@ -23,6 +6,11 @@ #include "base/i2-base.hpp" #include "base/debug.hpp" #include +#include +#include +#include +#include +#include #include using boost::intrusive_ptr; @@ -170,7 +158,7 @@ class Object public: DECLARE_PTR_TYPEDEFS(Object); - Object() = default; + Object(); virtual ~Object(); virtual String ToString() const; @@ -182,7 +170,7 @@ public: virtual void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty); virtual Value GetField(int id) const; virtual Value GetFieldByName(const String& field, bool sandboxed, const DebugInfo& debugInfo) const; - virtual void SetFieldByName(const String& field, const Value& value, const DebugInfo& debugInfo); + virtual void SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo); virtual bool HasOwnField(const String& field) const; virtual bool GetOwnField(const String& field, Value *result) const; virtual void ValidateField(int id, const Lazy& lvalue, const ValidationUtils& utils); @@ -203,15 +191,12 @@ private: Object(const Object& other) = delete; Object& operator=(const Object& rhs) = delete; - uintptr_t m_References{0}; - mutable uintptr_t m_Mutex{0}; + std::atomic m_References; + mutable std::recursive_mutex m_Mutex; #ifdef I2_DEBUG -# ifndef _WIN32 - mutable pthread_t m_LockOwner; -# else /* _WIN32 */ - mutable DWORD m_LockOwner; -# endif /* _WIN32 */ + mutable std::atomic m_LockOwner; + mutable size_t m_LockCount = 0; #endif /* I2_DEBUG */ friend struct ObjectLock; diff --git a/lib/base/objectlock.cpp b/lib/base/objectlock.cpp index 2abb43fba..fc0c7c631 100644 --- a/lib/base/objectlock.cpp +++ b/lib/base/objectlock.cpp @@ -1,24 +1,7 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/objectlock.hpp" -#include +#include using namespace icinga; @@ -31,10 +14,8 @@ ObjectLock::~ObjectLock() } ObjectLock::ObjectLock(const Object::Ptr& object) - : m_Object(object.get()), m_Locked(false) + : ObjectLock(object.get()) { - if (m_Object) - Lock(); } ObjectLock::ObjectLock(const Object *object) @@ -44,93 +25,31 @@ ObjectLock::ObjectLock(const Object *object) Lock(); } -void ObjectLock::LockMutex(const Object *object) -{ - unsigned int it = 0; - -#ifdef _WIN32 -# ifdef _WIN64 - while (likely(InterlockedCompareExchange64((LONGLONG *)&object->m_Mutex, I2MUTEX_LOCKED, I2MUTEX_UNLOCKED) != I2MUTEX_UNLOCKED)) { -# else /* _WIN64 */ - while (likely(InterlockedCompareExchange(&object->m_Mutex, I2MUTEX_LOCKED, I2MUTEX_UNLOCKED) != I2MUTEX_UNLOCKED)) { -# endif /* _WIN64 */ -#else /* _WIN32 */ - while (likely(!__sync_bool_compare_and_swap(&object->m_Mutex, I2MUTEX_UNLOCKED, I2MUTEX_LOCKED))) { -#endif /* _WIN32 */ - if (likely(object->m_Mutex > I2MUTEX_LOCKED)) { - auto *mtx = reinterpret_cast(object->m_Mutex); - mtx->lock(); - - return; - } - - Spin(it); - it++; - } - - auto *mtx = new boost::recursive_mutex(); - mtx->lock(); -#ifdef _WIN32 -# ifdef _WIN64 - InterlockedCompareExchange64((LONGLONG *)&object->m_Mutex, reinterpret_cast(mtx), I2MUTEX_LOCKED); -# else /* _WIN64 */ - InterlockedCompareExchange(&object->m_Mutex, reinterpret_cast(mtx), I2MUTEX_LOCKED); -# endif /* _WIN64 */ -#else /* _WIN32 */ - __sync_bool_compare_and_swap(&object->m_Mutex, I2MUTEX_LOCKED, reinterpret_cast(mtx)); -#endif /* _WIN32 */ -} - void ObjectLock::Lock() { ASSERT(!m_Locked && m_Object); - LockMutex(m_Object); + m_Object->m_Mutex.lock(); m_Locked = true; #ifdef I2_DEBUG -# ifdef _WIN32 - InterlockedExchange(&m_Object->m_LockOwner, GetCurrentThreadId()); -# else /* _WIN32 */ - __sync_lock_test_and_set(&m_Object->m_LockOwner, pthread_self()); -# endif /* _WIN32 */ + if (++m_Object->m_LockCount == 1u) { + m_Object->m_LockOwner.store(std::this_thread::get_id()); + } #endif /* I2_DEBUG */ } -void ObjectLock::Spin(unsigned int it) -{ - if (it < 8) { - /* Do nothing. */ - } -#ifdef SPIN_PAUSE - else if (it < 16) { - SPIN_PAUSE(); - } -#endif /* SPIN_PAUSE */ - else { -#ifdef _WIN32 - Sleep(0); -#else /* _WIN32 */ - sched_yield(); -#endif /* _WIN32 */ - } -} - void ObjectLock::Unlock() { #ifdef I2_DEBUG - if (m_Locked) { -# ifdef _WIN32 - InterlockedExchange(&m_Object->m_LockOwner, 0); -# else /* _WIN32 */ - __sync_lock_release(&m_Object->m_LockOwner); -# endif /* _WIN32 */ + if (m_Locked && !--m_Object->m_LockCount) { + m_Object->m_LockOwner.store(decltype(m_Object->m_LockOwner.load())()); } #endif /* I2_DEBUG */ if (m_Locked) { - reinterpret_cast(m_Object->m_Mutex)->unlock(); + m_Object->m_Mutex.unlock(); m_Locked = false; } } diff --git a/lib/base/objectlock.hpp b/lib/base/objectlock.hpp index 1c2f993e8..277f99041 100644 --- a/lib/base/objectlock.hpp +++ b/lib/base/objectlock.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef OBJECTLOCK_H #define OBJECTLOCK_H @@ -36,12 +19,7 @@ public: ~ObjectLock(); - static void LockMutex(const Object *object); - void Lock(); - - static void Spin(unsigned int it); - void Unlock(); private: diff --git a/lib/base/objecttype.cpp b/lib/base/objecttype.cpp index d77bfcdae..7ab0f2ad8 100644 --- a/lib/base/objecttype.cpp +++ b/lib/base/objecttype.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/objecttype.hpp" #include "base/initialize.hpp" @@ -23,6 +6,7 @@ using namespace icinga; +/* Ensure that the priority is lower than the basic namespace initialization in scriptframe.cpp. */ INITIALIZE_ONCE_WITH_PRIORITY([]() { Type::Ptr type = new ObjectType(); type->SetPrototype(Object::GetPrototype()); diff --git a/lib/base/objecttype.hpp b/lib/base/objecttype.hpp index 4fb0e5c3d..0db715e18 100644 --- a/lib/base/objecttype.hpp +++ b/lib/base/objecttype.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef OBJECTTYPE_H #define OBJECTTYPE_H diff --git a/lib/base/perfdatavalue.cpp b/lib/base/perfdatavalue.cpp index f2a93e128..422988d80 100644 --- a/lib/base/perfdatavalue.cpp +++ b/lib/base/perfdatavalue.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/perfdatavalue.hpp" #include "base/perfdatavalue-ti.cpp" @@ -27,7 +10,7 @@ using namespace icinga; REGISTER_TYPE(PerfdataValue); -REGISTER_SCRIPTFUNCTION_NS(System, parse_performance_data, PerfdataValue::Parse, "perfdata"); +REGISTER_FUNCTION(System, parse_performance_data, PerfdataValue::Parse, "perfdata"); PerfdataValue::PerfdataValue(const String& label, double value, bool counter, const String& unit, const Value& warn, const Value& crit, const Value& min, diff --git a/lib/base/perfdatavalue.hpp b/lib/base/perfdatavalue.hpp index a72d267be..05b2c34ac 100644 --- a/lib/base/perfdatavalue.hpp +++ b/lib/base/perfdatavalue.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PERFDATAVALUE_H #define PERFDATAVALUE_H diff --git a/lib/base/perfdatavalue.ti b/lib/base/perfdatavalue.ti index f234df2b1..b2692e98a 100644 --- a/lib/base/perfdatavalue.ti +++ b/lib/base/perfdatavalue.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ library base; diff --git a/lib/base/primitivetype.cpp b/lib/base/primitivetype.cpp index 9f1a6a3e8..10286c781 100644 --- a/lib/base/primitivetype.cpp +++ b/lib/base/primitivetype.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/primitivetype.hpp" #include "base/dictionary.hpp" diff --git a/lib/base/primitivetype.hpp b/lib/base/primitivetype.hpp index 45ae8411a..ae7454e07 100644 --- a/lib/base/primitivetype.hpp +++ b/lib/base/primitivetype.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PRIMITIVETYPE_H #define PRIMITIVETYPE_H @@ -48,6 +31,7 @@ private: ObjectFactory m_Factory; }; +/* Ensure that the priority is lower than the basic namespace initialization in scriptframe.cpp. */ #define REGISTER_BUILTIN_TYPE(type, prototype) \ INITIALIZE_ONCE_WITH_PRIORITY([]() { \ icinga::Type::Ptr t = new PrimitiveType(#type, "None"); \ diff --git a/lib/base/process.cpp b/lib/base/process.cpp index d5d3d69d2..97754c19e 100644 --- a/lib/base/process.cpp +++ b/lib/base/process.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/process.hpp" #include "base/exception.hpp" @@ -164,8 +147,11 @@ static Value ProcessSpawnImpl(struct msghdr *msgh, const Dictionary::Ptr& reques (void)close(fds[2]); #ifdef HAVE_NICE - if (adjustPriority) - nice(5); + if (adjustPriority) { + // Cheating the compiler on "warning: ignoring return value of 'int nice(int)', declared with attribute warn_unused_result [-Wunused-result]". + auto x (nice(5)); + (void)x; + } #endif /* HAVE_NICE */ sigset_t mask; @@ -413,12 +399,11 @@ static pid_t ProcessSpawn(const std::vector& arguments, const Dictionary msg.msg_controllen = cmsg->cmsg_len; -send_message: - while (sendmsg(l_ProcessControlFD, &msg, 0) < 0) - StartSpawnProcessHelper(); - - if (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0) - goto send_message; + do { + while (sendmsg(l_ProcessControlFD, &msg, 0) < 0) { + StartSpawnProcessHelper(); + } + } while (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0); char buf[4096]; @@ -450,12 +435,11 @@ static int ProcessKill(pid_t pid, int signum) boost::mutex::scoped_lock lock(l_ProcessControlMutex); -send_message: - while (send(l_ProcessControlFD, &length, sizeof(length), 0) < 0) - StartSpawnProcessHelper(); - - if (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0) - goto send_message; + do { + while (send(l_ProcessControlFD, &length, sizeof(length), 0) < 0) { + StartSpawnProcessHelper(); + } + } while (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0); char buf[4096]; @@ -482,12 +466,11 @@ static int ProcessWaitPID(pid_t pid, int *status) boost::mutex::scoped_lock lock(l_ProcessControlMutex); -send_message: - while (send(l_ProcessControlFD, &length, sizeof(length), 0) < 0) - StartSpawnProcessHelper(); - - if (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0) - goto send_message; + do { + while (send(l_ProcessControlFD, &length, sizeof(length), 0) < 0) { + StartSpawnProcessHelper(); + } + } while (send(l_ProcessControlFD, jrequest.CStr(), jrequest.GetLength(), 0) < 0); char buf[4096]; diff --git a/lib/base/process.hpp b/lib/base/process.hpp index 259f19e33..7fbfe0ac0 100644 --- a/lib/base/process.hpp +++ b/lib/base/process.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PROCESS_H #define PROCESS_H diff --git a/lib/base/reference-script.cpp b/lib/base/reference-script.cpp new file mode 100644 index 000000000..940824578 --- /dev/null +++ b/lib/base/reference-script.cpp @@ -0,0 +1,35 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/reference.hpp" +#include "base/function.hpp" +#include "base/functionwrapper.hpp" +#include "base/scriptframe.hpp" +#include "base/exception.hpp" + +using namespace icinga; + +static void ReferenceSet(const Value& value) +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Reference::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + self->Set(value); +} + +static Value ReferenceGet() +{ + ScriptFrame *vframe = ScriptFrame::GetCurrentFrame(); + Reference::Ptr self = static_cast(vframe->Self); + REQUIRE_NOT_NULL(self); + return self->Get(); +} + +Object::Ptr Reference::GetPrototype() +{ + static Dictionary::Ptr prototype = new Dictionary({ + { "set", new Function("Reference#set", ReferenceSet, { "value" }) }, + { "get", new Function("Reference#get", ReferenceGet, {}, true) }, + }); + + return prototype; +} diff --git a/lib/base/reference.cpp b/lib/base/reference.cpp new file mode 100644 index 000000000..b0104af6c --- /dev/null +++ b/lib/base/reference.cpp @@ -0,0 +1,38 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/reference.hpp" +#include "base/debug.hpp" +#include "base/primitivetype.hpp" +#include "base/dictionary.hpp" +#include "base/configwriter.hpp" +#include "base/convert.hpp" +#include "base/exception.hpp" + +using namespace icinga; + +REGISTER_PRIMITIVE_TYPE_NOINST(Reference, Object, Reference::GetPrototype()); + +Reference::Reference(const Object::Ptr& parent, const String& index) + : m_Parent(parent), m_Index(index) +{ +} + +Value Reference::Get() const +{ + return m_Parent->GetFieldByName(m_Index, true, DebugInfo()); +} + +void Reference::Set(const Value& value) +{ + m_Parent->SetFieldByName(m_Index, value, false, DebugInfo()); +} + +Object::Ptr Reference::GetParent() const +{ + return m_Parent; +} + +String Reference::GetIndex() const +{ + return m_Index; +} diff --git a/lib/base/reference.hpp b/lib/base/reference.hpp new file mode 100644 index 000000000..30faabe5f --- /dev/null +++ b/lib/base/reference.hpp @@ -0,0 +1,40 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef REFERENCE_H +#define REFERENCE_H + +#include "base/i2-base.hpp" +#include "base/objectlock.hpp" +#include "base/value.hpp" + +namespace icinga +{ + +/** + * A reference. + * + * @ingroup base + */ +class Reference final : public Object +{ +public: + DECLARE_OBJECT(Reference); + + Reference(const Object::Ptr& parent, const String& index); + + Value Get() const; + void Set(const Value& value); + + Object::Ptr GetParent() const; + String GetIndex() const; + + static Object::Ptr GetPrototype(); + +private: + Object::Ptr m_Parent; + String m_Index; +}; + +} + +#endif /* REFERENCE_H */ diff --git a/lib/base/registry.hpp b/lib/base/registry.hpp index 9f13866d6..b8b0aced2 100644 --- a/lib/base/registry.hpp +++ b/lib/base/registry.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef REGISTRY_H #define REGISTRY_H diff --git a/lib/base/ringbuffer.cpp b/lib/base/ringbuffer.cpp index 9e3b1c840..75ffff3eb 100644 --- a/lib/base/ringbuffer.cpp +++ b/lib/base/ringbuffer.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/ringbuffer.hpp" #include "base/objectlock.hpp" diff --git a/lib/base/ringbuffer.hpp b/lib/base/ringbuffer.hpp index 44a96f868..ed5802829 100644 --- a/lib/base/ringbuffer.hpp +++ b/lib/base/ringbuffer.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef RINGBUFFER_H #define RINGBUFFER_H diff --git a/lib/base/scriptframe.cpp b/lib/base/scriptframe.cpp index 65597a783..7510c8a11 100644 --- a/lib/base/scriptframe.cpp +++ b/lib/base/scriptframe.cpp @@ -1,44 +1,48 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/scriptframe.hpp" #include "base/scriptglobal.hpp" +#include "base/namespace.hpp" #include "base/exception.hpp" +#include "base/configuration.hpp" using namespace icinga; boost::thread_specific_ptr > ScriptFrame::m_ScriptFrames; -Array::Ptr ScriptFrame::m_Imports; + +static auto l_InternalNSBehavior = new ConstNamespaceBehavior(); + +/* Ensure that this gets called with highest priority + * and wins against other static initializers in lib/icinga, etc. + * LTO-enabled builds will cause trouble otherwise, see GH #6575. + */ +INITIALIZE_ONCE_WITH_PRIORITY([]() { + Namespace::Ptr globalNS = ScriptGlobal::GetGlobals(); + + auto systemNSBehavior = new ConstNamespaceBehavior(); + systemNSBehavior->Freeze(); + Namespace::Ptr systemNS = new Namespace(systemNSBehavior); + globalNS->SetAttribute("System", std::make_shared(systemNS)); + + systemNS->SetAttribute("Configuration", std::make_shared(new Configuration())); + + auto typesNSBehavior = new ConstNamespaceBehavior(); + typesNSBehavior->Freeze(); + Namespace::Ptr typesNS = new Namespace(typesNSBehavior); + globalNS->SetAttribute("Types", std::make_shared(typesNS)); + + auto statsNSBehavior = new ConstNamespaceBehavior(); + statsNSBehavior->Freeze(); + Namespace::Ptr statsNS = new Namespace(statsNSBehavior); + globalNS->SetAttribute("StatsFunctions", std::make_shared(statsNS)); + + Namespace::Ptr internalNS = new Namespace(l_InternalNSBehavior); + globalNS->SetAttribute("Internal", std::make_shared(internalNS)); +}, 1000); INITIALIZE_ONCE_WITH_PRIORITY([]() { - Dictionary::Ptr systemNS = new Dictionary(); - ScriptGlobal::Set("System", systemNS); - ScriptFrame::AddImport(systemNS); - - Dictionary::Ptr typesNS = new Dictionary(); - ScriptGlobal::Set("Types", typesNS); - ScriptFrame::AddImport(typesNS); - - Dictionary::Ptr deprecatedNS = new Dictionary(); - ScriptGlobal::Set("Deprecated", deprecatedNS); - ScriptFrame::AddImport(deprecatedNS); -}, 50); + l_InternalNSBehavior->Freeze(); +}, 0); ScriptFrame::ScriptFrame(bool allocLocals) : Locals(allocLocals ? new Dictionary() : nullptr), Self(ScriptGlobal::GetGlobals()), Sandboxed(false), Depth(0) @@ -69,6 +73,10 @@ ScriptFrame::~ScriptFrame() { ScriptFrame *frame = PopFrame(); ASSERT(frame == this); + +#ifndef I2_DEBUG + (void)frame; +#endif /* I2_DEBUG */ } void ScriptFrame::IncreaseStackDepth() @@ -120,23 +128,3 @@ void ScriptFrame::PushFrame(ScriptFrame *frame) frames->push(frame); } - -Array::Ptr ScriptFrame::GetImports() -{ - return m_Imports; -} - -void ScriptFrame::AddImport(const Object::Ptr& import) -{ - Array::Ptr imports; - - if (!m_Imports) - imports = new Array(); - else - imports = m_Imports->ShallowClone(); - - imports->Add(import); - - m_Imports = imports; -} - diff --git a/lib/base/scriptframe.hpp b/lib/base/scriptframe.hpp index 16a6592f5..18e23ef2f 100644 --- a/lib/base/scriptframe.hpp +++ b/lib/base/scriptframe.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SCRIPTFRAME_H #define SCRIPTFRAME_H @@ -45,12 +28,8 @@ struct ScriptFrame static ScriptFrame *GetCurrentFrame(); - static Array::Ptr GetImports(); - static void AddImport(const Object::Ptr& import); - private: static boost::thread_specific_ptr > m_ScriptFrames; - static Array::Ptr m_Imports; static void PushFrame(ScriptFrame *frame); static ScriptFrame *PopFrame(); diff --git a/lib/base/scriptglobal.cpp b/lib/base/scriptglobal.cpp index 225bb695b..b567b79c2 100644 --- a/lib/base/scriptglobal.cpp +++ b/lib/base/scriptglobal.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/scriptglobal.hpp" #include "base/singleton.hpp" @@ -26,11 +9,13 @@ #include "base/convert.hpp" #include "base/objectlock.hpp" #include "base/exception.hpp" +#include "base/namespace.hpp" +#include "base/utility.hpp" #include using namespace icinga; -Dictionary::Ptr ScriptGlobal::m_Globals = new Dictionary(); +Namespace::Ptr ScriptGlobal::m_Globals = new Namespace(); Value ScriptGlobal::Get(const String& name, const Value *defaultValue) { @@ -46,7 +31,7 @@ Value ScriptGlobal::Get(const String& name, const Value *defaultValue) return result; } -void ScriptGlobal::Set(const String& name, const Value& value) +void ScriptGlobal::Set(const String& name, const Value& value, bool overrideFrozen) { std::vector tokens = name.Split("."); @@ -56,7 +41,7 @@ void ScriptGlobal::Set(const String& name, const Value& value) { ObjectLock olock(m_Globals); - Dictionary::Ptr parent = m_Globals; + Namespace::Ptr parent = m_Globals; for (std::vector::size_type i = 0; i < tokens.size(); i++) { const String& token = tokens[i]; @@ -65,7 +50,7 @@ void ScriptGlobal::Set(const String& name, const Value& value) Value vparent; if (!parent->Get(token, &vparent)) { - Dictionary::Ptr dict = new Dictionary(); + Namespace::Ptr dict = new Namespace(); parent->Set(token, dict); parent = dict; } else { @@ -74,16 +59,21 @@ void ScriptGlobal::Set(const String& name, const Value& value) } } - parent->Set(tokens[tokens.size() - 1], value); + parent->SetFieldByName(tokens[tokens.size() - 1], value, overrideFrozen, DebugInfo()); } } +void ScriptGlobal::SetConst(const String& name, const Value& value) +{ + GetGlobals()->SetAttribute(name, std::make_shared(value)); +} + bool ScriptGlobal::Exists(const String& name) { return m_Globals->Contains(name); } -Dictionary::Ptr ScriptGlobal::GetGlobals() +Namespace::Ptr ScriptGlobal::GetGlobals() { return m_Globals; } @@ -102,8 +92,8 @@ void ScriptGlobal::WriteToFile(const String& filename) StdioStream::Ptr sfp = new StdioStream(&fp, false); ObjectLock olock(m_Globals); - for (const Dictionary::Pair& kv : m_Globals) { - Value value = kv.second; + for (const Namespace::Pair& kv : m_Globals) { + Value value = kv.second->Get(); if (value.IsObject()) value = Convert::ToString(value); @@ -122,15 +112,6 @@ void ScriptGlobal::WriteToFile(const String& filename) fp.close(); -#ifdef _WIN32 - _unlink(filename.CStr()); -#endif /* _WIN32 */ - - if (rename(tempFilename.CStr(), filename.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempFilename)); - } + Utility::RenameFile(tempFilename, filename); } diff --git a/lib/base/scriptglobal.hpp b/lib/base/scriptglobal.hpp index e2b5a8f41..c31cdcdf6 100644 --- a/lib/base/scriptglobal.hpp +++ b/lib/base/scriptglobal.hpp @@ -1,27 +1,10 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SCRIPTGLOBAL_H #define SCRIPTGLOBAL_H #include "base/i2-base.hpp" -#include "base/dictionary.hpp" +#include "base/namespace.hpp" namespace icinga { @@ -35,15 +18,16 @@ class ScriptGlobal { public: static Value Get(const String& name, const Value *defaultValue = nullptr); - static void Set(const String& name, const Value& value); + static void Set(const String& name, const Value& value, bool overrideFrozen = false); + static void SetConst(const String& name, const Value& value); static bool Exists(const String& name); static void WriteToFile(const String& filename); - static Dictionary::Ptr GetGlobals(); + static Namespace::Ptr GetGlobals(); private: - static Dictionary::Ptr m_Globals; + static Namespace::Ptr m_Globals; }; } diff --git a/lib/base/scriptutils.cpp b/lib/base/scriptutils.cpp index cbedd9f97..47f2ccdea 100644 --- a/lib/base/scriptutils.cpp +++ b/lib/base/scriptutils.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/scriptutils.hpp" #include "base/function.hpp" @@ -30,6 +13,7 @@ #include "base/application.hpp" #include "base/dependencygraph.hpp" #include "base/initialize.hpp" +#include "base/namespace.hpp" #include #include #include @@ -39,39 +23,40 @@ using namespace icinga; -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, regex, &ScriptUtils::Regex, "pattern:text:mode"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, match, &ScriptUtils::Match, "pattern:text:mode"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, cidr_match, &ScriptUtils::CidrMatch, "pattern:ip:mode"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, len, &ScriptUtils::Len, "value"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, union, &ScriptUtils::Union, ""); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, intersection, &ScriptUtils::Intersection, ""); -REGISTER_SCRIPTFUNCTION_NS(System, log, &ScriptUtils::Log, "severity:facility:value"); -REGISTER_SCRIPTFUNCTION_NS(System, range, &ScriptUtils::Range, "start:end:increment"); -REGISTER_SCRIPTFUNCTION_NS(System, exit, &Application::Exit, "status"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, typeof, &ScriptUtils::TypeOf, "value"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, keys, &ScriptUtils::Keys, "value"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, random, &Utility::Random, ""); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, get_object, &ScriptUtils::GetObject, "type:name"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, get_objects, &ScriptUtils::GetObjects, "type"); -REGISTER_SCRIPTFUNCTION_NS(System, assert, &ScriptUtils::Assert, "value"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, string, &ScriptUtils::CastString, "value"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, number, &ScriptUtils::CastNumber, "value"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, bool, &ScriptUtils::CastBool, "value"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, get_time, &Utility::GetTime, ""); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, basename, &Utility::BaseName, "path"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, dirname, &Utility::DirName, "path"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, msi_get_component_path, &ScriptUtils::MsiGetComponentPathShim, "component"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, track_parents, &ScriptUtils::TrackParents, "child"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, escape_shell_cmd, &Utility::EscapeShellCmd, "cmd"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, escape_shell_arg, &Utility::EscapeShellArg, "arg"); +REGISTER_SAFE_FUNCTION(System, regex, &ScriptUtils::Regex, "pattern:text:mode"); +REGISTER_SAFE_FUNCTION(System, match, &ScriptUtils::Match, "pattern:text:mode"); +REGISTER_SAFE_FUNCTION(System, cidr_match, &ScriptUtils::CidrMatch, "pattern:ip:mode"); +REGISTER_SAFE_FUNCTION(System, len, &ScriptUtils::Len, "value"); +REGISTER_SAFE_FUNCTION(System, union, &ScriptUtils::Union, ""); +REGISTER_SAFE_FUNCTION(System, intersection, &ScriptUtils::Intersection, ""); +REGISTER_FUNCTION(System, log, &ScriptUtils::Log, "severity:facility:value"); +REGISTER_FUNCTION(System, range, &ScriptUtils::Range, "start:end:increment"); +REGISTER_FUNCTION(System, exit, &Application::Exit, "status"); +REGISTER_SAFE_FUNCTION(System, typeof, &ScriptUtils::TypeOf, "value"); +REGISTER_SAFE_FUNCTION(System, keys, &ScriptUtils::Keys, "value"); +REGISTER_SAFE_FUNCTION(System, random, &Utility::Random, ""); +REGISTER_SAFE_FUNCTION(System, get_object, &ScriptUtils::GetObject, "type:name"); +REGISTER_SAFE_FUNCTION(System, get_objects, &ScriptUtils::GetObjects, "type"); +REGISTER_FUNCTION(System, assert, &ScriptUtils::Assert, "value"); +REGISTER_SAFE_FUNCTION(System, string, &ScriptUtils::CastString, "value"); +REGISTER_SAFE_FUNCTION(System, number, &ScriptUtils::CastNumber, "value"); +REGISTER_SAFE_FUNCTION(System, bool, &ScriptUtils::CastBool, "value"); +REGISTER_SAFE_FUNCTION(System, get_time, &Utility::GetTime, ""); +REGISTER_SAFE_FUNCTION(System, basename, &Utility::BaseName, "path"); +REGISTER_SAFE_FUNCTION(System, dirname, &Utility::DirName, "path"); +REGISTER_SAFE_FUNCTION(System, getenv, &ScriptUtils::GetEnv, "value"); +REGISTER_SAFE_FUNCTION(System, msi_get_component_path, &ScriptUtils::MsiGetComponentPathShim, "component"); +REGISTER_SAFE_FUNCTION(System, track_parents, &ScriptUtils::TrackParents, "child"); +REGISTER_SAFE_FUNCTION(System, escape_shell_cmd, &Utility::EscapeShellCmd, "cmd"); +REGISTER_SAFE_FUNCTION(System, escape_shell_arg, &Utility::EscapeShellArg, "arg"); #ifdef _WIN32 -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, escape_create_process_arg, &Utility::EscapeCreateProcessArg, "arg"); +REGISTER_SAFE_FUNCTION(System, escape_create_process_arg, &Utility::EscapeCreateProcessArg, "arg"); #endif /* _WIN32 */ -REGISTER_SCRIPTFUNCTION_NS(System, ptr, &ScriptUtils::Ptr, "object"); -REGISTER_SCRIPTFUNCTION_NS(System, sleep, &Utility::Sleep, "interval"); -REGISTER_SCRIPTFUNCTION_NS(System, path_exists, &Utility::PathExists, "path"); -REGISTER_SCRIPTFUNCTION_NS(System, glob, &ScriptUtils::Glob, "pathspec:callback:type"); -REGISTER_SCRIPTFUNCTION_NS(System, glob_recursive, &ScriptUtils::GlobRecursive, "pathspec:callback:type"); +REGISTER_FUNCTION(System, ptr, &ScriptUtils::Ptr, "object"); +REGISTER_FUNCTION(System, sleep, &Utility::Sleep, "interval"); +REGISTER_FUNCTION(System, path_exists, &Utility::PathExists, "path"); +REGISTER_FUNCTION(System, glob, &ScriptUtils::Glob, "pathspec:callback:type"); +REGISTER_FUNCTION(System, glob_recursive, &ScriptUtils::GlobRecursive, "pathspec:callback:type"); INITIALIZE_ONCE(&ScriptUtils::StaticInitialize); @@ -83,11 +68,11 @@ enum MatchType void ScriptUtils::StaticInitialize() { - ScriptGlobal::Set("MatchAll", MatchAll); - ScriptGlobal::Set("MatchAny", MatchAny); + ScriptGlobal::Set("System.MatchAll", MatchAll, true); + ScriptGlobal::Set("System.MatchAny", MatchAny, true); - ScriptGlobal::Set("GlobFile", GlobFile); - ScriptGlobal::Set("GlobDirectory", GlobDirectory); + ScriptGlobal::Set("System.GlobFile", GlobFile, true); + ScriptGlobal::Set("System.GlobDirectory", GlobDirectory, true); } String ScriptUtils::CastString(const Value& value) @@ -108,10 +93,14 @@ bool ScriptUtils::CastBool(const Value& value) bool ScriptUtils::Regex(const std::vector& args) { if (args.size() < 2) - BOOST_THROW_EXCEPTION(std::invalid_argument("Regular expression and text must be specified.")); + BOOST_THROW_EXCEPTION(std::invalid_argument("Regular expression and text must be specified for regex().")); String pattern = args[0]; const Value& argTexts = args[1]; + + if (argTexts.IsObjectType()) + BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionaries are not supported by regex().")); + MatchType mode; if (args.size() > 2) @@ -147,7 +136,8 @@ bool ScriptUtils::Regex(const std::vector& args) return false; } - return true; + /* MatchAny: Nothing matched. MatchAll: Everything matched. */ + return mode == MatchAll; } else { String text = argTexts; boost::smatch what; @@ -158,10 +148,14 @@ bool ScriptUtils::Regex(const std::vector& args) bool ScriptUtils::Match(const std::vector& args) { if (args.size() < 2) - BOOST_THROW_EXCEPTION(std::invalid_argument("Pattern and text must be specified.")); + BOOST_THROW_EXCEPTION(std::invalid_argument("Pattern and text must be specified for match().")); String pattern = args[0]; const Value& argTexts = args[1]; + + if (argTexts.IsObjectType()) + BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionaries are not supported by match().")); + MatchType mode; if (args.size() > 2) @@ -189,7 +183,8 @@ bool ScriptUtils::Match(const std::vector& args) return false; } - return true; + /* MatchAny: Nothing matched. MatchAll: Everything matched. */ + return mode == MatchAll; } else { String text = argTexts; return Utility::Match(pattern, argTexts); @@ -199,10 +194,14 @@ bool ScriptUtils::Match(const std::vector& args) bool ScriptUtils::CidrMatch(const std::vector& args) { if (args.size() < 2) - BOOST_THROW_EXCEPTION(std::invalid_argument("CIDR and IP address must be specified.")); + BOOST_THROW_EXCEPTION(std::invalid_argument("CIDR and IP address must be specified for cidr_match().")); String pattern = args[0]; const Value& argIps = args[1]; + + if (argIps.IsObjectType()) + BOOST_THROW_EXCEPTION(std::invalid_argument("Dictionaries are not supported by cidr_match().")); + MatchType mode; if (args.size() > 2) @@ -230,7 +229,8 @@ bool ScriptUtils::CidrMatch(const std::vector& args) return false; } - return true; + /* MatchAny: Nothing matched. MatchAll: Everything matched. */ + return mode == MatchAll; } else { String ip = argIps; return Utility::CidrMatch(pattern, ip); @@ -382,10 +382,12 @@ Type::Ptr ScriptUtils::TypeOf(const Value& value) return value.GetReflectionType(); } -Array::Ptr ScriptUtils::Keys(const Dictionary::Ptr& dict) +Array::Ptr ScriptUtils::Keys(const Object::Ptr& obj) { ArrayData result; + Dictionary::Ptr dict = dynamic_pointer_cast(obj); + if (dict) { ObjectLock olock(dict); for (const Dictionary::Pair& kv : dict) { @@ -393,6 +395,15 @@ Array::Ptr ScriptUtils::Keys(const Dictionary::Ptr& dict) } } + Namespace::Ptr ns = dynamic_pointer_cast(obj); + + if (ns) { + ObjectLock olock(ns); + for (const Namespace::Pair& kv : ns) { + result.push_back(kv.first); + } + } + return new Array(std::move(result)); } @@ -503,3 +514,8 @@ Value ScriptUtils::GlobRecursive(const std::vector& args) return Array::FromVector(paths); } + +String ScriptUtils::GetEnv(const String& key) +{ + return Utility::GetFromEnvironment(key); +} diff --git a/lib/base/scriptutils.hpp b/lib/base/scriptutils.hpp index 9515b381e..c874ff857 100644 --- a/lib/base/scriptutils.hpp +++ b/lib/base/scriptutils.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SCRIPTUTILS_H #define SCRIPTUTILS_H @@ -49,7 +32,7 @@ public: static void Log(const std::vector& arguments); static Array::Ptr Range(const std::vector& arguments); static Type::Ptr TypeOf(const Value& value); - static Array::Ptr Keys(const Dictionary::Ptr& dict); + static Array::Ptr Keys(const Object::Ptr& obj); static ConfigObject::Ptr GetObject(const Value& type, const String& name); static Array::Ptr GetObjects(const Type::Ptr& type); static void Assert(const Value& arg); @@ -58,6 +41,7 @@ public: static double Ptr(const Object::Ptr& object); static Value Glob(const std::vector& args); static Value GlobRecursive(const std::vector& args); + static String GetEnv(const String& key); private: ScriptUtils(); diff --git a/lib/base/serializer.cpp b/lib/base/serializer.cpp index 7296ec3ce..c081c440d 100644 --- a/lib/base/serializer.cpp +++ b/lib/base/serializer.cpp @@ -1,30 +1,72 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/serializer.hpp" #include "base/type.hpp" #include "base/application.hpp" #include "base/objectlock.hpp" +#include "base/convert.hpp" +#include "base/exception.hpp" +#include "base/namespace.hpp" +#include +#include using namespace icinga; -static Array::Ptr SerializeArray(const Array::Ptr& input, int attributeTypes) +struct SerializeStackEntry +{ + String Name; + Value Val; +}; + +CircularReferenceError::CircularReferenceError(String message, std::vector path) + : m_Message(message), m_Path(path) +{ } + +const char *CircularReferenceError::what(void) const throw() +{ + return m_Message.CStr(); +} + +std::vector CircularReferenceError::GetPath() const +{ + return m_Path; +} + +struct SerializeStack +{ + std::deque Entries; + + inline void Push(const String& name, const Value& val) + { + Object::Ptr obj; + + if (val.IsObject()) + obj = val; + + if (obj) { + for (const auto& entry : Entries) { + if (entry.Val == obj) { + std::vector path; + for (const auto& entry : Entries) + path.push_back(entry.Name); + path.push_back(name); + BOOST_THROW_EXCEPTION(CircularReferenceError("Cannot serialize object which recursively refers to itself. Attribute path which leads to the cycle: " + boost::algorithm::join(path, " -> "), path)); + } + } + } + + Entries.push_back({ name, obj }); + } + + inline void Pop() + { + Entries.pop_back(); + } +}; + +static Value SerializeInternal(const Value& value, int attributeTypes, SerializeStack& stack); + +static Array::Ptr SerializeArray(const Array::Ptr& input, int attributeTypes, SerializeStack& stack) { ArrayData result; @@ -32,14 +74,19 @@ static Array::Ptr SerializeArray(const Array::Ptr& input, int attributeTypes) ObjectLock olock(input); + int index = 0; + for (const Value& value : input) { - result.emplace_back(Serialize(value, attributeTypes)); + stack.Push(Convert::ToString(index), value); + result.emplace_back(SerializeInternal(value, attributeTypes, stack)); + stack.Pop(); + index++; } return new Array(std::move(result)); } -static Dictionary::Ptr SerializeDictionary(const Dictionary::Ptr& input, int attributeTypes) +static Dictionary::Ptr SerializeDictionary(const Dictionary::Ptr& input, int attributeTypes, SerializeStack& stack) { DictionaryData result; @@ -48,13 +95,31 @@ static Dictionary::Ptr SerializeDictionary(const Dictionary::Ptr& input, int att ObjectLock olock(input); for (const Dictionary::Pair& kv : input) { - result.emplace_back(kv.first, Serialize(kv.second, attributeTypes)); + stack.Push(kv.first, kv.second); + result.emplace_back(kv.first, SerializeInternal(kv.second, attributeTypes, stack)); + stack.Pop(); } return new Dictionary(std::move(result)); } -static Object::Ptr SerializeObject(const Object::Ptr& input, int attributeTypes) +static Dictionary::Ptr SerializeNamespace(const Namespace::Ptr& input, int attributeTypes, SerializeStack& stack) +{ + DictionaryData result; + + ObjectLock olock(input); + + for (const Namespace::Pair& kv : input) { + Value val = kv.second->Get(); + stack.Push(kv.first, val); + result.emplace_back(kv.first, Serialize(val, attributeTypes)); + stack.Pop(); + } + + return new Dictionary(std::move(result)); +} + +static Object::Ptr SerializeObject(const Object::Ptr& input, int attributeTypes, SerializeStack& stack) { Type::Ptr type = input->GetReflectionType(); @@ -64,6 +129,8 @@ static Object::Ptr SerializeObject(const Object::Ptr& input, int attributeTypes) DictionaryData fields; fields.reserve(type->GetFieldCount() + 1); + ObjectLock olock(input); + for (int i = 0; i < type->GetFieldCount(); i++) { Field field = type->GetFieldInfo(i); @@ -73,7 +140,10 @@ static Object::Ptr SerializeObject(const Object::Ptr& input, int attributeTypes) if (strcmp(field.Name, "type") == 0) continue; - fields.emplace_back(field.Name, Serialize(input->GetField(i), attributeTypes)); + Value value = input->GetField(i); + stack.Push(field.Name, value); + fields.emplace_back(field.Name, SerializeInternal(value, attributeTypes, stack)); + stack.Pop(); } fields.emplace_back("type", type->GetName()); @@ -158,7 +228,7 @@ static Object::Ptr DeserializeObject(const Object::Ptr& object, const Dictionary return instance; } -Value icinga::Serialize(const Value& value, int attributeTypes) +static Value SerializeInternal(const Value& value, int attributeTypes, SerializeStack& stack) { if (!value.IsObject()) return value; @@ -168,14 +238,25 @@ Value icinga::Serialize(const Value& value, int attributeTypes) Array::Ptr array = dynamic_pointer_cast(input); if (array) - return SerializeArray(array, attributeTypes); + return SerializeArray(array, attributeTypes, stack); Dictionary::Ptr dict = dynamic_pointer_cast(input); if (dict) - return SerializeDictionary(dict, attributeTypes); + return SerializeDictionary(dict, attributeTypes, stack); - return SerializeObject(input, attributeTypes); + Namespace::Ptr ns = dynamic_pointer_cast(input); + + if (ns) + return SerializeNamespace(ns, attributeTypes, stack); + + return SerializeObject(input, attributeTypes, stack); +} + +Value icinga::Serialize(const Value& value, int attributeTypes) +{ + SerializeStack stack; + return SerializeInternal(value, attributeTypes, stack); } Value icinga::Deserialize(const Value& value, bool safe_mode, int attributeTypes) diff --git a/lib/base/serializer.hpp b/lib/base/serializer.hpp index b5e8cd31a..77b31cecc 100644 --- a/lib/base/serializer.hpp +++ b/lib/base/serializer.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SERIALIZER_H #define SERIALIZER_H @@ -23,10 +6,24 @@ #include "base/i2-base.hpp" #include "base/type.hpp" #include "base/value.hpp" +#include "base/exception.hpp" namespace icinga { +class CircularReferenceError : virtual public user_error +{ +public: + CircularReferenceError(String message, std::vector path); + + const char *what(void) const throw() final; + std::vector GetPath() const; + +private: + String m_Message; + std::vector m_Path; +}; + Value Serialize(const Value& value, int attributeTypes = FAState); Value Deserialize(const Value& value, bool safe_mode = false, int attributeTypes = FAState); Value Deserialize(const Object::Ptr& object, const Value& value, bool safe_mode = false, int attributeTypes = FAState); diff --git a/lib/base/singleton.hpp b/lib/base/singleton.hpp index 535c3faee..bc4fa9cc2 100644 --- a/lib/base/singleton.hpp +++ b/lib/base/singleton.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SINGLETON_H #define SINGLETON_H diff --git a/lib/base/socket.cpp b/lib/base/socket.cpp index fae5665f3..cc3183b96 100644 --- a/lib/base/socket.cpp +++ b/lib/base/socket.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/socket.hpp" #include "base/objectlock.hpp" @@ -94,7 +77,7 @@ void Socket::Close() } /** - * Retrieves the last error that occured for the socket. + * Retrieves the last error that occurred for the socket. * * @returns An error code. */ @@ -114,9 +97,21 @@ int Socket::GetError() const /** * Formats a sockaddr in a human-readable way. * - * @returns A String describing the sockaddr. + * @returns A pair of host and service. */ -String Socket::GetAddressFromSockaddr(sockaddr *address, socklen_t len) +String Socket::GetHumanReadableAddress(const std::pair& socketDetails) +{ + std::ostringstream s; + s << "[" << socketDetails.first << "]:" << socketDetails.second; + return s.str(); +} + +/** + * Returns host and service as pair. + * + * @returns A pair with host and service. + */ +std::pair Socket::GetDetailsFromSockaddr(sockaddr *address, socklen_t len) { char host[NI_MAXHOST]; char service[NI_MAXSERV]; @@ -140,17 +135,15 @@ String Socket::GetAddressFromSockaddr(sockaddr *address, socklen_t len) #endif /* _WIN32 */ } - std::ostringstream s; - s << "[" << host << "]:" << service; - return s.str(); + return std::make_pair(host, service); } /** - * Returns a String describing the local address of the socket. + * Returns a pair describing the local host and service of the socket. * - * @returns A String describing the local address. + * @returns A pair describing the local host and service. */ -String Socket::GetClientAddress() +std::pair Socket::GetClientAddressDetails() { boost::mutex::scoped_lock lock(m_SocketMutex); @@ -175,22 +168,32 @@ String Socket::GetClientAddress() #endif /* _WIN32 */ } - String address; + std::pair details; try { - address = GetAddressFromSockaddr((sockaddr *)&sin, len); + details = GetDetailsFromSockaddr((sockaddr *)&sin, len); } catch (const std::exception&) { /* already logged */ } - return address; + return details; } /** - * Returns a String describing the peer address of the socket. + * Returns a String describing the local address of the socket. * - * @returns A String describing the peer address. + * @returns A String describing the local address. */ -String Socket::GetPeerAddress() +String Socket::GetClientAddress() +{ + return GetHumanReadableAddress(GetClientAddressDetails()); +} + +/** + * Returns a pair describing the peer host and service of the socket. + * + * @returns A pair describing the peer host and service. + */ +std::pair Socket::GetPeerAddressDetails() { boost::mutex::scoped_lock lock(m_SocketMutex); @@ -215,14 +218,24 @@ String Socket::GetPeerAddress() #endif /* _WIN32 */ } - String address; + std::pair details; try { - address = GetAddressFromSockaddr((sockaddr *)&sin, len); + details = GetDetailsFromSockaddr((sockaddr *)&sin, len); } catch (const std::exception&) { /* already logged */ } - return address; + return details; +} + +/** + * Returns a String describing the peer address of the socket. + * + * @returns A String describing the peer address. + */ +String Socket::GetPeerAddress() +{ + return GetHumanReadableAddress(GetPeerAddressDetails()); } /** @@ -415,4 +428,3 @@ void Socket::SocketPair(SOCKET s[2]) << boost::errinfo_api_function("socketpair") << boost::errinfo_errno(errno)); } - diff --git a/lib/base/socket.hpp b/lib/base/socket.hpp index 89147c57d..a642fc496 100644 --- a/lib/base/socket.hpp +++ b/lib/base/socket.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SOCKET_H #define SOCKET_H @@ -45,7 +28,9 @@ public: void Close(); + std::pair GetClientAddressDetails(); String GetClientAddress(); + std::pair GetPeerAddressDetails(); String GetPeerAddress(); size_t Read(void *buffer, size_t size); @@ -70,7 +55,8 @@ protected: private: SOCKET m_FD{INVALID_SOCKET}; /**< The socket descriptor. */ - static String GetAddressFromSockaddr(sockaddr *address, socklen_t len); + static std::pair GetDetailsFromSockaddr(sockaddr *address, socklen_t len); + static String GetHumanReadableAddress(const std::pair& socketDetails); }; class socket_error : virtual public std::exception, virtual public boost::exception { }; diff --git a/lib/base/socketevents-epoll.cpp b/lib/base/socketevents-epoll.cpp deleted file mode 100644 index 7b810a6d6..000000000 --- a/lib/base/socketevents-epoll.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#include "base/socketevents.hpp" -#include "base/exception.hpp" -#include "base/logger.hpp" -#include -#include -#ifdef __linux__ -# include - -using namespace icinga; - -void SocketEventEngineEpoll::InitializeThread(int tid) -{ - m_PollFDs[tid] = epoll_create(128); - Utility::SetCloExec(m_PollFDs[tid]); - - SocketEventDescriptor sed; - - m_Sockets[tid][m_EventFDs[tid][0]] = sed; - m_FDChanged[tid] = true; - - epoll_event event; - memset(&event, 0, sizeof(event)); - event.data.fd = m_EventFDs[tid][0]; - event.events = EPOLLIN; - epoll_ctl(m_PollFDs[tid], EPOLL_CTL_ADD, m_EventFDs[tid][0], &event); -} - -int SocketEventEngineEpoll::PollToEpoll(int events) -{ - int result = 0; - - if (events & POLLIN) - result |= EPOLLIN; - - if (events & POLLOUT) - result |= EPOLLOUT; - - return events; -} - -int SocketEventEngineEpoll::EpollToPoll(int events) -{ - int result = 0; - - if (events & EPOLLIN) - result |= POLLIN; - - if (events & EPOLLOUT) - result |= POLLOUT; - - return events; -} - -void SocketEventEngineEpoll::ThreadProc(int tid) -{ - Utility::SetThreadName("SocketIO"); - - for (;;) { - { - boost::mutex::scoped_lock lock(m_EventMutex[tid]); - - if (m_FDChanged[tid]) { - m_FDChanged[tid] = false; - m_CV[tid].notify_all(); - } - } - - epoll_event pevents[64]; - int ready = epoll_wait(m_PollFDs[tid], pevents, sizeof(pevents) / sizeof(pevents[0]), -1); - - std::vector events; - - { - boost::mutex::scoped_lock lock(m_EventMutex[tid]); - - if (m_FDChanged[tid]) { - m_FDChanged[tid] = false; - - continue; - } - - for (int i = 0; i < ready; i++) { - if (pevents[i].data.fd == m_EventFDs[tid][0]) { - char buffer[512]; - if (recv(m_EventFDs[tid][0], buffer, sizeof(buffer), 0) < 0) - Log(LogCritical, "SocketEvents", "Read from event FD failed."); - - continue; - } - - if ((pevents[i].events & (EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLERR)) == 0) - continue; - - EventDescription event; - event.REvents = SocketEventEngineEpoll::EpollToPoll(pevents[i].events); - event.Descriptor = m_Sockets[tid][pevents[i].data.fd]; - event.LifesupportReference = event.Descriptor.LifesupportObject; - VERIFY(event.LifesupportReference); - - events.emplace_back(std::move(event)); - } - } - - for (const EventDescription& event : events) { - try { - event.Descriptor.EventInterface->OnEvent(event.REvents); - } catch (const std::exception& ex) { - Log(LogCritical, "SocketEvents") - << "Exception thrown in socket I/O handler:\n" - << DiagnosticInformation(ex); - } catch (...) { - Log(LogCritical, "SocketEvents", "Exception of unknown type thrown in socket I/O handler."); - } - } - } -} - -void SocketEventEngineEpoll::Register(SocketEvents *se, Object *lifesupportObject) -{ - int tid = se->m_ID % SOCKET_IOTHREADS; - - { - boost::mutex::scoped_lock lock(m_EventMutex[tid]); - - VERIFY(se->m_FD != INVALID_SOCKET); - - SocketEventDescriptor desc; - desc.EventInterface = se; - desc.LifesupportObject = lifesupportObject; - - VERIFY(m_Sockets[tid].find(se->m_FD) == m_Sockets[tid].end()); - - m_Sockets[tid][se->m_FD] = desc; - - epoll_event event; - memset(&event, 0, sizeof(event)); - event.data.fd = se->m_FD; - event.events = 0; - epoll_ctl(m_PollFDs[tid], EPOLL_CTL_ADD, se->m_FD, &event); - - se->m_Events = true; - } -} - -void SocketEventEngineEpoll::Unregister(SocketEvents *se) -{ - int tid = se->m_ID % SOCKET_IOTHREADS; - - { - boost::mutex::scoped_lock lock(m_EventMutex[tid]); - - if (se->m_FD == INVALID_SOCKET) - return; - - m_Sockets[tid].erase(se->m_FD); - m_FDChanged[tid] = true; - - epoll_ctl(m_PollFDs[tid], EPOLL_CTL_DEL, se->m_FD, nullptr); - - se->m_FD = INVALID_SOCKET; - se->m_Events = false; - } - - WakeUpThread(tid, true); -} - -void SocketEventEngineEpoll::ChangeEvents(SocketEvents *se, int events) -{ - if (se->m_FD == INVALID_SOCKET) - BOOST_THROW_EXCEPTION(std::runtime_error("Tried to read/write from a closed socket.")); - - int tid = se->m_ID % SOCKET_IOTHREADS; - - { - boost::mutex::scoped_lock lock(m_EventMutex[tid]); - - auto it = m_Sockets[tid].find(se->m_FD); - - if (it == m_Sockets[tid].end()) - return; - - epoll_event event; - memset(&event, 0, sizeof(event)); - event.data.fd = se->m_FD; - event.events = SocketEventEngineEpoll::PollToEpoll(events); - epoll_ctl(m_PollFDs[tid], EPOLL_CTL_MOD, se->m_FD, &event); - } -} -#endif /* __linux__ */ diff --git a/lib/base/socketevents-poll.cpp b/lib/base/socketevents-poll.cpp deleted file mode 100644 index 19748434e..000000000 --- a/lib/base/socketevents-poll.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#include "base/socketevents.hpp" -#include "base/exception.hpp" -#include "base/logger.hpp" -#include -#include - -using namespace icinga; - -void SocketEventEnginePoll::InitializeThread(int tid) -{ - SocketEventDescriptor sed; - sed.Events = POLLIN; - - m_Sockets[tid][m_EventFDs[tid][0]] = sed; - m_FDChanged[tid] = true; -} - -void SocketEventEnginePoll::ThreadProc(int tid) -{ - Utility::SetThreadName("SocketIO"); - - std::vector pfds; - std::vector descriptors; - - for (;;) { - { - boost::mutex::scoped_lock lock(m_EventMutex[tid]); - - if (m_FDChanged[tid]) { - pfds.resize(m_Sockets[tid].size()); - descriptors.resize(m_Sockets[tid].size()); - - int i = 0; - - typedef std::map::value_type kv_pair; - - for (const kv_pair& desc : m_Sockets[tid]) { - if (desc.second.Events == 0) - continue; - - int events = desc.second.Events; - - if (desc.second.EventInterface) { - desc.second.EventInterface->m_EnginePrivate = &pfds[i]; - - if (!desc.second.EventInterface->m_Events) - events = 0; - } - - pfds[i].fd = desc.first; - pfds[i].events = events; - descriptors[i] = desc.second; - - i++; - } - - pfds.resize(i); - - m_FDChanged[tid] = false; - m_CV[tid].notify_all(); - } - } - - ASSERT(!pfds.empty()); - -#ifdef _WIN32 - (void) WSAPoll(&pfds[0], pfds.size(), -1); -#else /* _WIN32 */ - (void) poll(&pfds[0], pfds.size(), -1); -#endif /* _WIN32 */ - - std::vector events; - - { - boost::mutex::scoped_lock lock(m_EventMutex[tid]); - - if (m_FDChanged[tid]) - continue; - - for (std::vector::size_type i = 0; i < pfds.size(); i++) { - if ((pfds[i].revents & (POLLIN | POLLOUT | POLLHUP | POLLERR)) == 0) - continue; - - if (pfds[i].fd == m_EventFDs[tid][0]) { - char buffer[512]; - if (recv(m_EventFDs[tid][0], buffer, sizeof(buffer), 0) < 0) - Log(LogCritical, "SocketEvents", "Read from event FD failed."); - - continue; - } - - EventDescription event; - event.REvents = pfds[i].revents; - event.Descriptor = descriptors[i]; - event.LifesupportReference = event.Descriptor.LifesupportObject; - VERIFY(event.LifesupportReference); - - events.emplace_back(std::move(event)); - } - } - - for (const EventDescription& event : events) { - try { - event.Descriptor.EventInterface->OnEvent(event.REvents); - } catch (const std::exception& ex) { - Log(LogCritical, "SocketEvents") - << "Exception thrown in socket I/O handler:\n" - << DiagnosticInformation(ex); - } catch (...) { - Log(LogCritical, "SocketEvents", "Exception of unknown type thrown in socket I/O handler."); - } - } - } -} - -void SocketEventEnginePoll::Register(SocketEvents *se, Object *lifesupportObject) -{ - int tid = se->m_ID % SOCKET_IOTHREADS; - - { - boost::mutex::scoped_lock lock(m_EventMutex[tid]); - - VERIFY(se->m_FD != INVALID_SOCKET); - - SocketEventDescriptor desc; - desc.Events = 0; - desc.EventInterface = se; - desc.LifesupportObject = lifesupportObject; - - VERIFY(m_Sockets[tid].find(se->m_FD) == m_Sockets[tid].end()); - - m_Sockets[tid][se->m_FD] = desc; - - m_FDChanged[tid] = true; - - se->m_Events = true; - } - - WakeUpThread(tid, true); -} - -void SocketEventEnginePoll::Unregister(SocketEvents *se) -{ - int tid = se->m_ID % SOCKET_IOTHREADS; - - { - boost::mutex::scoped_lock lock(m_EventMutex[tid]); - - if (se->m_FD == INVALID_SOCKET) - return; - - m_Sockets[tid].erase(se->m_FD); - m_FDChanged[tid] = true; - - se->m_FD = INVALID_SOCKET; - se->m_Events = false; - } - - WakeUpThread(tid, true); -} - -void SocketEventEnginePoll::ChangeEvents(SocketEvents *se, int events) -{ - if (se->m_FD == INVALID_SOCKET) - BOOST_THROW_EXCEPTION(std::runtime_error("Tried to read/write from a closed socket.")); - - int tid = se->m_ID % SOCKET_IOTHREADS; - - { - boost::mutex::scoped_lock lock(m_EventMutex[tid]); - - auto it = m_Sockets[tid].find(se->m_FD); - - if (it == m_Sockets[tid].end()) - return; - - if (it->second.Events == events) - return; - - it->second.Events = events; - - if (se->m_EnginePrivate && std::this_thread::get_id() == m_Threads[tid].get_id()) - ((pollfd *)se->m_EnginePrivate)->events = events; - else - m_FDChanged[tid] = true; - } - - WakeUpThread(tid, false); -} - diff --git a/lib/base/socketevents.cpp b/lib/base/socketevents.cpp deleted file mode 100644 index e54ca0e6f..000000000 --- a/lib/base/socketevents.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#include "base/socketevents.hpp" -#include "base/exception.hpp" -#include "base/logger.hpp" -#include "base/application.hpp" -#include "base/scriptglobal.hpp" -#include -#include -#ifdef __linux__ -# include -#endif /* __linux__ */ - -using namespace icinga; - -static boost::once_flag l_SocketIOOnceFlag = BOOST_ONCE_INIT; -static SocketEventEngine *l_SocketIOEngine; - -int SocketEvents::m_NextID = 0; - -void SocketEventEngine::Start() -{ - for (int tid = 0; tid < SOCKET_IOTHREADS; tid++) { - Socket::SocketPair(m_EventFDs[tid]); - - Utility::SetNonBlockingSocket(m_EventFDs[tid][0]); - Utility::SetNonBlockingSocket(m_EventFDs[tid][1]); - -#ifndef _WIN32 - Utility::SetCloExec(m_EventFDs[tid][0]); - Utility::SetCloExec(m_EventFDs[tid][1]); -#endif /* _WIN32 */ - - InitializeThread(tid); - - m_Threads[tid] = std::thread(std::bind(&SocketEventEngine::ThreadProc, this, tid)); - } -} - -void SocketEventEngine::WakeUpThread(int sid, bool wait) -{ - int tid = sid % SOCKET_IOTHREADS; - - if (std::this_thread::get_id() == m_Threads[tid].get_id()) - return; - - if (wait) { - boost::mutex::scoped_lock lock(m_EventMutex[tid]); - - m_FDChanged[tid] = true; - - while (m_FDChanged[tid]) { - (void) send(m_EventFDs[tid][1], "T", 1, 0); - - boost::system_time const timeout = boost::get_system_time() + boost::posix_time::milliseconds(50); - m_CV[tid].timed_wait(lock, timeout); - } - } else { - (void) send(m_EventFDs[tid][1], "T", 1, 0); - } -} - -void SocketEvents::InitializeEngine() -{ - String eventEngine = ScriptGlobal::Get("EventEngine", &Empty); - - if (eventEngine.IsEmpty()) -#ifdef __linux__ - eventEngine = "epoll"; -#else /* __linux__ */ - eventEngine = "poll"; -#endif /* __linux__ */ - - if (eventEngine == "poll") - l_SocketIOEngine = new SocketEventEnginePoll(); -#ifdef __linux__ - else if (eventEngine == "epoll") - l_SocketIOEngine = new SocketEventEngineEpoll(); -#endif /* __linux__ */ - else { - Log(LogWarning, "SocketEvents") - << "Invalid event engine selected: " << eventEngine << " - Falling back to 'poll'"; - - eventEngine = "poll"; - - l_SocketIOEngine = new SocketEventEnginePoll(); - } - - l_SocketIOEngine->Start(); - - ScriptGlobal::Set("EventEngine", eventEngine); -} - -/** - * Constructor for the SocketEvents class. - */ -SocketEvents::SocketEvents(const Socket::Ptr& socket, Object *lifesupportObject) - : m_ID(m_NextID++), m_FD(socket->GetFD()), m_EnginePrivate(nullptr) -{ - boost::call_once(l_SocketIOOnceFlag, &SocketEvents::InitializeEngine); - - Register(lifesupportObject); -} - -SocketEvents::~SocketEvents() -{ - VERIFY(m_FD == INVALID_SOCKET); -} - -void SocketEvents::Register(Object *lifesupportObject) -{ - l_SocketIOEngine->Register(this, lifesupportObject); -} - -void SocketEvents::Unregister() -{ - l_SocketIOEngine->Unregister(this); -} - -void SocketEvents::ChangeEvents(int events) -{ - l_SocketIOEngine->ChangeEvents(this, events); -} - -boost::mutex& SocketEventEngine::GetMutex(int tid) -{ - return m_EventMutex[tid]; -} - -bool SocketEvents::IsHandlingEvents() const -{ - int tid = m_ID % SOCKET_IOTHREADS; - boost::mutex::scoped_lock lock(l_SocketIOEngine->GetMutex(tid)); - return m_Events; -} - -void SocketEvents::OnEvent(int revents) -{ - -} - diff --git a/lib/base/socketevents.hpp b/lib/base/socketevents.hpp deleted file mode 100644 index 360a03477..000000000 --- a/lib/base/socketevents.hpp +++ /dev/null @@ -1,153 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#ifndef SOCKETEVENTS_H -#define SOCKETEVENTS_H - -#include "base/i2-base.hpp" -#include "base/socket.hpp" -#include -#include - -#ifndef _WIN32 -# include -#endif /* _WIN32 */ - -namespace icinga -{ - -/** - * Socket event interface - * - * @ingroup base - */ -class SocketEvents -{ -public: - ~SocketEvents(); - - virtual void OnEvent(int revents); - - void Unregister(); - - void ChangeEvents(int events); - - bool IsHandlingEvents() const; - - void *GetEnginePrivate() const; - void SetEnginePrivate(void *priv); - -protected: - SocketEvents(const Socket::Ptr& socket, Object *lifesupportObject); - -private: - int m_ID; - SOCKET m_FD; - bool m_Events; - void *m_EnginePrivate; - - static int m_NextID; - - static void InitializeEngine(); - - void WakeUpThread(bool wait = false); - - void Register(Object *lifesupportObject); - - friend class SocketEventEnginePoll; - friend class SocketEventEngineEpoll; -}; - -#define SOCKET_IOTHREADS 8 - -struct SocketEventDescriptor -{ - int Events{POLLIN}; - SocketEvents *EventInterface{nullptr}; - Object *LifesupportObject{nullptr}; -}; - -struct EventDescription -{ - int REvents; - SocketEventDescriptor Descriptor; - Object::Ptr LifesupportReference; -}; - -class SocketEventEngine -{ -public: - void Start(); - - void WakeUpThread(int sid, bool wait); - - boost::mutex& GetMutex(int tid); - -protected: - virtual void InitializeThread(int tid) = 0; - virtual void ThreadProc(int tid) = 0; - virtual void Register(SocketEvents *se, Object *lifesupportObject) = 0; - virtual void Unregister(SocketEvents *se) = 0; - virtual void ChangeEvents(SocketEvents *se, int events) = 0; - - std::thread m_Threads[SOCKET_IOTHREADS]; - SOCKET m_EventFDs[SOCKET_IOTHREADS][2]; - bool m_FDChanged[SOCKET_IOTHREADS]; - boost::mutex m_EventMutex[SOCKET_IOTHREADS]; - boost::condition_variable m_CV[SOCKET_IOTHREADS]; - std::map m_Sockets[SOCKET_IOTHREADS]; - - friend class SocketEvents; -}; - -class SocketEventEnginePoll final : public SocketEventEngine -{ -public: - void Register(SocketEvents *se, Object *lifesupportObject) override; - void Unregister(SocketEvents *se) override; - void ChangeEvents(SocketEvents *se, int events) override; - -protected: - void InitializeThread(int tid) override; - void ThreadProc(int tid) override; -}; - -#ifdef __linux__ -class SocketEventEngineEpoll : public SocketEventEngine -{ -public: - virtual void Register(SocketEvents *se, Object *lifesupportObject); - virtual void Unregister(SocketEvents *se); - virtual void ChangeEvents(SocketEvents *se, int events); - -protected: - virtual void InitializeThread(int tid); - virtual void ThreadProc(int tid); - -private: - SOCKET m_PollFDs[SOCKET_IOTHREADS]; - - static int PollToEpoll(int events); - static int EpollToPoll(int events); -}; -#endif /* __linux__ */ - -} - -#endif /* SOCKETEVENTS_H */ diff --git a/lib/base/stacktrace.cpp b/lib/base/stacktrace.cpp index 9fb6c519e..81fdd3380 100644 --- a/lib/base/stacktrace.cpp +++ b/lib/base/stacktrace.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/stacktrace.hpp" #include "base/utility.hpp" diff --git a/lib/base/stacktrace.hpp b/lib/base/stacktrace.hpp index 1a197de2b..1416d6558 100644 --- a/lib/base/stacktrace.hpp +++ b/lib/base/stacktrace.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef STACKTRACE_H #define STACKTRACE_H diff --git a/lib/base/statsfunction.hpp b/lib/base/statsfunction.hpp index 8dc5af4d7..ecac33c57 100644 --- a/lib/base/statsfunction.hpp +++ b/lib/base/statsfunction.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef STATSFUNCTION_H #define STATSFUNCTION_H @@ -27,7 +10,7 @@ namespace icinga { #define REGISTER_STATSFUNCTION(name, callback) \ - REGISTER_SCRIPTFUNCTION_NS(StatsFunctions, name, callback, "status:perfdata") + REGISTER_FUNCTION(StatsFunctions, name, callback, "status:perfdata") } diff --git a/lib/base/stdiostream.cpp b/lib/base/stdiostream.cpp index 773ee35c3..449036f03 100644 --- a/lib/base/stdiostream.cpp +++ b/lib/base/stdiostream.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/stdiostream.hpp" #include "base/objectlock.hpp" diff --git a/lib/base/stdiostream.hpp b/lib/base/stdiostream.hpp index e72cf4c43..05c7f0d57 100644 --- a/lib/base/stdiostream.hpp +++ b/lib/base/stdiostream.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef STDIOSTREAM_H #define STDIOSTREAM_H diff --git a/lib/base/stream.cpp b/lib/base/stream.cpp index 3048f096c..77d39d3aa 100644 --- a/lib/base/stream.cpp +++ b/lib/base/stream.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/stream.hpp" #include @@ -91,16 +74,6 @@ bool Stream::WaitForData(int timeout) return IsDataAvailable() || IsEof(); } -void Stream::SetCorked(bool corked) -{ - m_Corked = corked; -} - -bool Stream::IsCorked() const -{ - return m_Corked; -} - static void StreamDummyCallback() { } @@ -129,31 +102,19 @@ StreamReadStatus Stream::ReadLine(String *line, StreamReadContext& context, bool } } - int count = 0; - size_t first_newline; - for (size_t i = 0; i < context.Size; i++) { if (context.Buffer[i] == '\n') { - count++; + *line = String(context.Buffer, context.Buffer + i); + boost::algorithm::trim_right(*line); - if (count == 1) - first_newline = i; - else if (count > 1) - break; + context.DropData(i + 1u); + + context.MustRead = !context.Size; + return StatusNewItem; } } - context.MustRead = (count <= 1); - - if (count > 0) { - *line = String(context.Buffer, &(context.Buffer[first_newline])); - boost::algorithm::trim_right(*line); - - context.DropData(first_newline + 1); - - return StatusNewItem; - } - + context.MustRead = true; return StatusNeedData; } diff --git a/lib/base/stream.hpp b/lib/base/stream.hpp index bb4129138..393a0aee4 100644 --- a/lib/base/stream.hpp +++ b/lib/base/stream.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef STREAM_H #define STREAM_H @@ -127,9 +110,6 @@ public: bool WaitForData(); bool WaitForData(int timeout); - virtual void SetCorked(bool corked); - bool IsCorked() const; - virtual bool SupportsWaiting() const; virtual bool IsDataAvailable() const; @@ -146,8 +126,6 @@ private: boost::mutex m_Mutex; boost::condition_variable m_CV; - - bool m_Corked{false}; }; } diff --git a/lib/base/streamlogger.cpp b/lib/base/streamlogger.cpp index fa93d740b..146fe3c91 100644 --- a/lib/base/streamlogger.cpp +++ b/lib/base/streamlogger.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/streamlogger.hpp" #include "base/streamlogger-ti.cpp" @@ -47,7 +30,7 @@ StreamLogger::~StreamLogger() if (m_FlushLogTimer) m_FlushLogTimer->Stop(); - if (m_OwnsStream) + if (m_Stream && m_OwnsStream) delete m_Stream; } @@ -66,16 +49,18 @@ void StreamLogger::BindStream(std::ostream *stream, bool ownsStream) { ObjectLock olock(this); - if (m_OwnsStream) + if (m_Stream && m_OwnsStream) delete m_Stream; m_Stream = stream; m_OwnsStream = ownsStream; - m_FlushLogTimer = new Timer(); - m_FlushLogTimer->SetInterval(1); - m_FlushLogTimer->OnTimerExpired.connect(std::bind(&StreamLogger::FlushLogTimerHandler, this)); - m_FlushLogTimer->Start(); + if (!m_FlushLogTimer) { + m_FlushLogTimer = new Timer(); + m_FlushLogTimer->SetInterval(1); + m_FlushLogTimer->OnTimerExpired.connect(std::bind(&StreamLogger::FlushLogTimerHandler, this)); + m_FlushLogTimer->Start(); + } } /** diff --git a/lib/base/streamlogger.hpp b/lib/base/streamlogger.hpp index ae08361c5..81482aa98 100644 --- a/lib/base/streamlogger.hpp +++ b/lib/base/streamlogger.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef STREAMLOGGER_H #define STREAMLOGGER_H diff --git a/lib/base/streamlogger.ti b/lib/base/streamlogger.ti index 750c9f689..6dc36e0cb 100644 --- a/lib/base/streamlogger.ti +++ b/lib/base/streamlogger.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/logger.hpp" diff --git a/lib/base/string-script.cpp b/lib/base/string-script.cpp index 2edb6dd01..323f99c41 100644 --- a/lib/base/string-script.cpp +++ b/lib/base/string-script.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/object.hpp" #include "base/dictionary.hpp" diff --git a/lib/base/string.cpp b/lib/base/string.cpp index 5970f6ebf..c4617e357 100644 --- a/lib/base/string.cpp +++ b/lib/base/string.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/string.hpp" #include "base/value.hpp" diff --git a/lib/base/string.hpp b/lib/base/string.hpp index 70b6f9e37..e9799e7eb 100644 --- a/lib/base/string.hpp +++ b/lib/base/string.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef STRING_H #define STRING_H diff --git a/lib/base/stringbuilder.cpp b/lib/base/stringbuilder.cpp new file mode 100644 index 000000000..5b5a508ec --- /dev/null +++ b/lib/base/stringbuilder.cpp @@ -0,0 +1,36 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/stringbuilder.hpp" +#include + +using namespace icinga; + +void StringBuilder::Append(const String& str) +{ + m_Buffer.insert(m_Buffer.end(), str.Begin(), str.End()); +} + +void StringBuilder::Append(const std::string& str) +{ + m_Buffer.insert(m_Buffer.end(), str.begin(), str.end()); +} + +void StringBuilder::Append(const char *begin, const char *end) +{ + m_Buffer.insert(m_Buffer.end(), begin, end); +} + +void StringBuilder::Append(const char *cstr) +{ + m_Buffer.insert(m_Buffer.end(), cstr, cstr + std::strlen(cstr)); +} + +void StringBuilder::Append(char chr) +{ + m_Buffer.emplace_back(chr); +} + +String StringBuilder::ToString() const +{ + return String(m_Buffer.data(), m_Buffer.data() + m_Buffer.size()); +} diff --git a/lib/base/stringbuilder.hpp b/lib/base/stringbuilder.hpp new file mode 100644 index 000000000..4d806f4b7 --- /dev/null +++ b/lib/base/stringbuilder.hpp @@ -0,0 +1,36 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef STRINGBUILDER_H +#define STRINGBUILDER_H + +#include "base/i2-base.hpp" +#include "base/string.hpp" +#include +#include + +namespace icinga +{ + +/** + * A string builder. + * + * @ingroup base + */ +class StringBuilder final +{ +public: + void Append(const String&); + void Append(const std::string&); + void Append(const char *, const char *); + void Append(const char *); + void Append(char); + + String ToString() const; + +private: + std::vector m_Buffer; +}; + +} + +#endif /* STRINGBUILDER_H */ diff --git a/lib/base/sysloglogger.cpp b/lib/base/sysloglogger.cpp index b5d735a80..44babc31c 100644 --- a/lib/base/sysloglogger.cpp +++ b/lib/base/sysloglogger.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef _WIN32 #include "base/sysloglogger.hpp" @@ -35,26 +18,26 @@ std::map SyslogLogger::m_FacilityMap; void SyslogLogger::StaticInitialize() { - ScriptGlobal::Set("FacilityAuth", "LOG_AUTH"); - ScriptGlobal::Set("FacilityAuthPriv", "LOG_AUTHPRIV"); - ScriptGlobal::Set("FacilityCron", "LOG_CRON"); - ScriptGlobal::Set("FacilityDaemon", "LOG_DAEMON"); - ScriptGlobal::Set("FacilityFtp", "LOG_FTP"); - ScriptGlobal::Set("FacilityKern", "LOG_KERN"); - ScriptGlobal::Set("FacilityLocal0", "LOG_LOCAL0"); - ScriptGlobal::Set("FacilityLocal1", "LOG_LOCAL1"); - ScriptGlobal::Set("FacilityLocal2", "LOG_LOCAL2"); - ScriptGlobal::Set("FacilityLocal3", "LOG_LOCAL3"); - ScriptGlobal::Set("FacilityLocal4", "LOG_LOCAL4"); - ScriptGlobal::Set("FacilityLocal5", "LOG_LOCAL5"); - ScriptGlobal::Set("FacilityLocal6", "LOG_LOCAL6"); - ScriptGlobal::Set("FacilityLocal7", "LOG_LOCAL7"); - ScriptGlobal::Set("FacilityLpr", "LOG_LPR"); - ScriptGlobal::Set("FacilityMail", "LOG_MAIL"); - ScriptGlobal::Set("FacilityNews", "LOG_NEWS"); - ScriptGlobal::Set("FacilitySyslog", "LOG_SYSLOG"); - ScriptGlobal::Set("FacilityUser", "LOG_USER"); - ScriptGlobal::Set("FacilityUucp", "LOG_UUCP"); + ScriptGlobal::Set("System.FacilityAuth", "LOG_AUTH", true); + ScriptGlobal::Set("System.FacilityAuthPriv", "LOG_AUTHPRIV", true); + ScriptGlobal::Set("System.FacilityCron", "LOG_CRON", true); + ScriptGlobal::Set("System.FacilityDaemon", "LOG_DAEMON", true); + ScriptGlobal::Set("System.FacilityFtp", "LOG_FTP", true); + ScriptGlobal::Set("System.FacilityKern", "LOG_KERN", true); + ScriptGlobal::Set("System.FacilityLocal0", "LOG_LOCAL0", true); + ScriptGlobal::Set("System.FacilityLocal1", "LOG_LOCAL1", true); + ScriptGlobal::Set("System.FacilityLocal2", "LOG_LOCAL2", true); + ScriptGlobal::Set("System.FacilityLocal3", "LOG_LOCAL3", true); + ScriptGlobal::Set("System.FacilityLocal4", "LOG_LOCAL4", true); + ScriptGlobal::Set("System.FacilityLocal5", "LOG_LOCAL5", true); + ScriptGlobal::Set("System.FacilityLocal6", "LOG_LOCAL6", true); + ScriptGlobal::Set("System.FacilityLocal7", "LOG_LOCAL7", true); + ScriptGlobal::Set("System.FacilityLpr", "LOG_LPR", true); + ScriptGlobal::Set("System.FacilityMail", "LOG_MAIL", true); + ScriptGlobal::Set("System.FacilityNews", "LOG_NEWS", true); + ScriptGlobal::Set("System.FacilitySyslog", "LOG_SYSLOG", true); + ScriptGlobal::Set("System.FacilityUser", "LOG_USER", true); + ScriptGlobal::Set("System.FacilityUucp", "LOG_UUCP", true); m_FacilityMap["LOG_AUTH"] = LOG_AUTH; m_FacilityMap["LOG_AUTHPRIV"] = LOG_AUTHPRIV; diff --git a/lib/base/sysloglogger.hpp b/lib/base/sysloglogger.hpp index 8718a21d0..168c5d9a5 100644 --- a/lib/base/sysloglogger.hpp +++ b/lib/base/sysloglogger.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SYSLOGLOGGER_H #define SYSLOGLOGGER_H diff --git a/lib/base/sysloglogger.ti b/lib/base/sysloglogger.ti index 9ca92d637..8f343597f 100644 --- a/lib/base/sysloglogger.ti +++ b/lib/base/sysloglogger.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/logger.hpp" diff --git a/lib/base/tcpsocket.cpp b/lib/base/tcpsocket.cpp index a25d52d77..a9390e5a7 100644 --- a/lib/base/tcpsocket.cpp +++ b/lib/base/tcpsocket.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/tcpsocket.hpp" #include "base/logger.hpp" @@ -89,10 +72,11 @@ void TcpSocket::Bind(const String& node, const String& service, int family) const int optFalse = 0; setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&optFalse), sizeof(optFalse)); -#ifndef _WIN32 const int optTrue = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&optTrue), sizeof(optTrue)); -#endif /* _WIN32 */ +#ifdef SO_REUSEPORT + setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast(&optTrue), sizeof(optTrue)); +#endif /* SO_REUSEPORT */ int rc = bind(fd, info->ai_addr, info->ai_addrlen); diff --git a/lib/base/tcpsocket.hpp b/lib/base/tcpsocket.hpp index 1b99128ae..e0f502256 100644 --- a/lib/base/tcpsocket.hpp +++ b/lib/base/tcpsocket.hpp @@ -1,33 +1,19 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef TCPSOCKET_H #define TCPSOCKET_H #include "base/i2-base.hpp" +#include "base/io-engine.hpp" #include "base/socket.hpp" +#include +#include namespace icinga { /** - * A TCP socket. + * A TCP socket. DEPRECATED - Use Boost ASIO instead. * * @ingroup base */ @@ -42,6 +28,69 @@ public: void Connect(const String& node, const String& service); }; +/** + * TCP Connect based on Boost ASIO. + * + * @ingroup base + */ +template +void Connect(Socket& socket, const String& node, const String& service) +{ + using boost::asio::ip::tcp; + + tcp::resolver resolver (IoEngine::Get().GetIoContext()); + tcp::resolver::query query (node, service); + auto result (resolver.resolve(query)); + auto current (result.begin()); + + for (;;) { + try { + socket.open(current->endpoint().protocol()); + socket.set_option(tcp::socket::keep_alive(true)); + socket.connect(current->endpoint()); + + break; + } catch (const std::exception&) { + if (++current == result.end()) { + throw; + } + + if (socket.is_open()) { + socket.close(); + } + } + } +} + +template +void Connect(Socket& socket, const String& node, const String& service, boost::asio::yield_context yc) +{ + using boost::asio::ip::tcp; + + tcp::resolver resolver (IoEngine::Get().GetIoContext()); + tcp::resolver::query query (node, service); + auto result (resolver.async_resolve(query, yc)); + auto current (result.begin()); + + for (;;) { + try { + socket.open(current->endpoint().protocol()); + socket.set_option(tcp::socket::keep_alive(true)); + socket.async_connect(current->endpoint(), yc); + + break; + } catch (const std::exception&) { + if (++current == result.end()) { + throw; + } + + if (socket.is_open()) { + socket.close(); + } + } + } +} + } #endif /* TCPSOCKET_H */ diff --git a/lib/base/threadpool.cpp b/lib/base/threadpool.cpp index 996931d00..26787ab52 100644 --- a/lib/base/threadpool.cpp +++ b/lib/base/threadpool.cpp @@ -1,40 +1,13 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/threadpool.hpp" -#include "base/logger.hpp" -#include "base/debug.hpp" -#include "base/utility.hpp" -#include "base/exception.hpp" -#include "base/application.hpp" -#include +#include using namespace icinga; -int ThreadPool::m_NextID = 1; - -ThreadPool::ThreadPool(size_t max_threads) - : m_ID(m_NextID++), m_MaxThreads(max_threads) +ThreadPool::ThreadPool(size_t threads) + : m_Threads(threads), m_Pending(0) { - if (m_MaxThreads != UINT_MAX && m_MaxThreads < sizeof(m_Queues) / sizeof(m_Queues[0])) - m_MaxThreads = sizeof(m_Queues) / sizeof(m_Queues[0]); - Start(); } @@ -45,354 +18,19 @@ ThreadPool::~ThreadPool() void ThreadPool::Start() { - if (!m_Stopped) - return; + boost::unique_lock lock (m_Mutex); - m_Stopped = false; - - for (auto& queue : m_Queues) - queue.SpawnWorker(m_ThreadGroup); - - m_MgmtThread = std::thread(std::bind(&ThreadPool::ManagerThreadProc, this)); + if (!m_Pool) { + m_Pool = decltype(m_Pool)(new boost::asio::thread_pool(m_Threads)); + } } void ThreadPool::Stop() { - if (m_Stopped) - return; + boost::unique_lock lock (m_Mutex); - { - boost::mutex::scoped_lock lock(m_MgmtMutex); - m_Stopped = true; - m_MgmtCV.notify_all(); - } - - if (m_MgmtThread.joinable()) - m_MgmtThread.join(); - - for (auto& queue : m_Queues) { - boost::mutex::scoped_lock lock(queue.Mutex); - queue.Stopped = true; - queue.CV.notify_all(); - } - - m_ThreadGroup.join_all(); - m_ThreadGroup.~thread_group(); - new (&m_ThreadGroup) boost::thread_group(); - - for (auto& queue : m_Queues) - queue.Stopped = false; - - m_Stopped = true; -} - -/** - * Waits for work items and processes them. - */ -void ThreadPool::WorkerThread::ThreadProc(Queue& queue) -{ - std::ostringstream idbuf; - idbuf << "Q #" << &queue << " W #" << this; - Utility::SetThreadName(idbuf.str()); - - for (;;) { - WorkItem wi; - - { - boost::mutex::scoped_lock lock(queue.Mutex); - - UpdateUtilization(ThreadIdle); - - while (queue.Items.empty() && !queue.Stopped && !Zombie) { - if (queue.Items.empty()) - queue.CVStarved.notify_all(); - - queue.CV.wait(lock); - } - - if (Zombie) - break; - - if (queue.Items.empty() && queue.Stopped) - break; - - wi = queue.Items.front(); - queue.Items.pop_front(); - - UpdateUtilization(ThreadBusy); - } - - double st = Utility::GetTime(); - -#ifdef I2_DEBUG -# ifdef RUSAGE_THREAD - struct rusage usage_start, usage_end; - - (void) getrusage(RUSAGE_THREAD, &usage_start); -# endif /* RUSAGE_THREAD */ -#endif /* I2_DEBUG */ - - try { - if (wi.Callback) - wi.Callback(); - } catch (const std::exception& ex) { - Log(LogCritical, "ThreadPool") - << "Exception thrown in event handler:\n" - << DiagnosticInformation(ex); - } catch (...) { - Log(LogCritical, "ThreadPool", "Exception of unknown type thrown in event handler."); - } - - double et = Utility::GetTime(); - double latency = st - wi.Timestamp; - - { - boost::mutex::scoped_lock lock(queue.Mutex); - - queue.WaitTime += latency; - queue.ServiceTime += et - st; - queue.TaskCount++; - } - -#ifdef I2_DEBUG -# ifdef RUSAGE_THREAD - (void) getrusage(RUSAGE_THREAD, &usage_end); - - double duser = (usage_end.ru_utime.tv_sec - usage_start.ru_utime.tv_sec) + - (usage_end.ru_utime.tv_usec - usage_start.ru_utime.tv_usec) / 1000000.0; - - double dsys = (usage_end.ru_stime.tv_sec - usage_start.ru_stime.tv_sec) + - (usage_end.ru_stime.tv_usec - usage_start.ru_stime.tv_usec) / 1000000.0; - - double dwait = (et - st) - (duser + dsys); - - int dminfaults = usage_end.ru_minflt - usage_start.ru_minflt; - int dmajfaults = usage_end.ru_majflt - usage_start.ru_majflt; - - int dvctx = usage_end.ru_nvcsw - usage_start.ru_nvcsw; - int divctx = usage_end.ru_nivcsw - usage_start.ru_nivcsw; -# endif /* RUSAGE_THREAD */ - if (et - st > 0.5) { - Log(LogWarning, "ThreadPool") -# ifdef RUSAGE_THREAD - << "Event call took user:" << duser << "s, system:" << dsys << "s, wait:" << dwait << "s, minor_faults:" << dminfaults << ", major_faults:" << dmajfaults << ", voluntary_csw:" << dvctx << ", involuntary_csw:" << divctx; -# else - << "Event call took " << (et - st) << "s"; -# endif /* RUSAGE_THREAD */ - } -#endif /* I2_DEBUG */ - } - - boost::mutex::scoped_lock lock(queue.Mutex); - UpdateUtilization(ThreadDead); - Zombie = false; -} - -/** - * Appends a work item to the work queue. Work items will be processed in FIFO order. - * - * @param callback The callback function for the work item. - * @param policy The scheduling policy - * @returns true if the item was queued, false otherwise. - */ -bool ThreadPool::Post(const ThreadPool::WorkFunction& callback, SchedulerPolicy policy) -{ - WorkItem wi; - wi.Callback = callback; - wi.Timestamp = Utility::GetTime(); - - Queue& queue = m_Queues[Utility::Random() % (sizeof(m_Queues) / sizeof(m_Queues[0]))]; - - { - boost::mutex::scoped_lock lock(queue.Mutex); - - if (queue.Stopped) - return false; - - if (policy == LowLatencyScheduler) - queue.SpawnWorker(m_ThreadGroup); - - queue.Items.emplace_back(std::move(wi)); - queue.CV.notify_one(); - } - - return true; -} - -void ThreadPool::ManagerThreadProc() -{ - std::ostringstream idbuf; - idbuf << "TP #" << m_ID << " Manager"; - Utility::SetThreadName(idbuf.str()); - - double lastStats = 0; - - for (;;) { - size_t total_pending = 0, total_alive = 0; - double total_avg_latency = 0; - double total_utilization = 0; - - { - boost::mutex::scoped_lock lock(m_MgmtMutex); - - if (!m_Stopped) - m_MgmtCV.timed_wait(lock, boost::posix_time::milliseconds(500)); - - if (m_Stopped) - break; - } - - for (auto& queue : m_Queues) { - size_t pending, alive = 0; - double avg_latency; - double utilization = 0; - - boost::mutex::scoped_lock lock(queue.Mutex); - - for (auto& thread : queue.Threads) - thread.UpdateUtilization(); - - pending = queue.Items.size(); - - for (auto& thread : queue.Threads) { - if (thread.State != ThreadDead && !thread.Zombie) { - alive++; - utilization += thread.Utilization * 100; - } - } - - utilization /= alive; - - if (queue.TaskCount > 0) - avg_latency = queue.WaitTime / (queue.TaskCount * 1.0); - else - avg_latency = 0; - - if (utilization < 60 || utilization > 80 || alive < 8) { - double wthreads = std::ceil((utilization * alive) / 80.0); - - int tthreads = wthreads - alive; - - /* Make sure there is at least one thread per queue */ - if (alive + tthreads < 1) - tthreads = 1 - alive; - - /* Don't kill more than 2 threads at once. */ - if (tthreads < -2) - tthreads = -2; - - /* Spawn more workers if there are outstanding work items. */ - if (tthreads > 0 && pending > 0) - tthreads = 2; - - if (m_MaxThreads != UINT_MAX && (alive + tthreads) * (sizeof(m_Queues) / sizeof(m_Queues[0])) > m_MaxThreads) - tthreads = m_MaxThreads / (sizeof(m_Queues) / sizeof(m_Queues[0])) - alive; - - if (tthreads != 0) { - Log(LogNotice, "ThreadPool") - << "Thread pool; current: " << alive << "; adjustment: " << tthreads; - } - - for (int i = 0; i < -tthreads; i++) - queue.KillWorker(m_ThreadGroup); - - for (int i = 0; i < tthreads; i++) - queue.SpawnWorker(m_ThreadGroup); - } - - queue.WaitTime = 0; - queue.ServiceTime = 0; - queue.TaskCount = 0; - - total_pending += pending; - total_alive += alive; - total_avg_latency += avg_latency; - total_utilization += utilization; - } - - double now = Utility::GetTime(); - - if (lastStats < now - 15) { - lastStats = now; - - Log(LogNotice, "ThreadPool") - << "Pool #" << m_ID << ": Pending tasks: " << total_pending << "; Average latency: " - << (long)(total_avg_latency * 1000 / (sizeof(m_Queues) / sizeof(m_Queues[0]))) << "ms" - << "; Threads: " << total_alive - << "; Pool utilization: " << (total_utilization / (sizeof(m_Queues) / sizeof(m_Queues[0]))) << "%"; - } + if (m_Pool) { + m_Pool->join(); + m_Pool = nullptr; } } - -/** - * Note: Caller must hold m_Mutex - */ -void ThreadPool::Queue::SpawnWorker(boost::thread_group& group) -{ - for (auto& thread : Threads) { - if (thread.State == ThreadDead) { - Log(LogDebug, "ThreadPool", "Spawning worker thread."); - - thread = WorkerThread(ThreadIdle); - thread.Thread = group.create_thread(std::bind(&ThreadPool::WorkerThread::ThreadProc, std::ref(thread), std::ref(*this))); - - break; - } - } -} - -/** - * Note: Caller must hold Mutex. - */ -void ThreadPool::Queue::KillWorker(boost::thread_group& group) -{ - for (auto& thread : Threads) { - if (thread.State == ThreadIdle && !thread.Zombie) { - Log(LogDebug, "ThreadPool", "Killing worker thread."); - - group.remove_thread(thread.Thread); - thread.Thread->detach(); - delete thread.Thread; - - thread.Zombie = true; - CV.notify_all(); - - break; - } - } -} - -/** - * Note: Caller must hold queue Mutex. - */ -void ThreadPool::WorkerThread::UpdateUtilization(ThreadState state) -{ - double utilization; - - switch (State) { - case ThreadDead: - return; - case ThreadIdle: - utilization = 0; - break; - case ThreadBusy: - utilization = 1; - break; - default: - VERIFY(0); - } - - double now = Utility::GetTime(); - double time = now - LastUpdate; - - const double avg_time = 5.0; - - if (time > avg_time) - time = avg_time; - - Utilization = (Utilization * (avg_time - time) + utilization * time) / avg_time; - LastUpdate = now; - - if (state != ThreadUnspecified) - State = state; -} diff --git a/lib/base/threadpool.hpp b/lib/base/threadpool.hpp index 06277c70c..af351cd7a 100644 --- a/lib/base/threadpool.hpp +++ b/lib/base/threadpool.hpp @@ -1,37 +1,25 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef THREADPOOL_H #define THREADPOOL_H -#include "base/i2-base.hpp" -#include -#include -#include -#include +#include "base/atomic.hpp" +#include "base/exception.hpp" +#include "base/logger.hpp" +#include +#include +#include +#include #include +#include +#include +#include +#include +#include namespace icinga { -#define QUEUECOUNT 4U - enum SchedulerPolicy { DefaultScheduler, @@ -48,85 +36,63 @@ class ThreadPool public: typedef std::function WorkFunction; - ThreadPool(size_t max_threads = UINT_MAX); + ThreadPool(size_t threads = std::thread::hardware_concurrency() * 2u); ~ThreadPool(); void Start(); void Stop(); - bool Post(const WorkFunction& callback, SchedulerPolicy policy = DefaultScheduler); + /** + * Appends a work item to the work queue. Work items will be processed in FIFO order. + * + * @param callback The callback function for the work item. + * @returns true if the item was queued, false otherwise. + */ + template + bool Post(T callback, SchedulerPolicy) + { + boost::shared_lock lock (m_Mutex); + + if (m_Pool) { + m_Pending.fetch_add(1); + + boost::asio::post(*m_Pool, [this, callback]() { + m_Pending.fetch_sub(1); + + try { + callback(); + } catch (const std::exception& ex) { + Log(LogCritical, "ThreadPool") + << "Exception thrown in event handler:\n" + << DiagnosticInformation(ex); + } catch (...) { + Log(LogCritical, "ThreadPool", "Exception of unknown type thrown in event handler."); + } + }); + + return true; + } else { + return false; + } + } + + /** + * Returns the amount of queued tasks not started yet. + * + * @returns amount of queued tasks. + */ + inline uint_fast64_t GetPending() + { + return m_Pending.load(); + } private: - enum ThreadState - { - ThreadUnspecified, - ThreadDead, - ThreadIdle, - ThreadBusy - }; - - struct WorkItem - { - WorkFunction Callback; - double Timestamp; - }; - - struct Queue; - - struct WorkerThread - { - ThreadState State{ThreadDead}; - bool Zombie{false}; - double Utilization{0}; - double LastUpdate{0}; - boost::thread *Thread{nullptr}; - - WorkerThread(ThreadState state = ThreadDead) - : State(state) - { } - - void UpdateUtilization(ThreadState state = ThreadUnspecified); - - void ThreadProc(Queue& queue); - }; - - struct Queue - { - boost::mutex Mutex; - boost::condition_variable CV; - boost::condition_variable CVStarved; - - std::deque Items; - - double WaitTime{0}; - double ServiceTime{0}; - int TaskCount{0}; - - bool Stopped{false}; - - WorkerThread Threads[16]; - - void SpawnWorker(boost::thread_group& group); - void KillWorker(boost::thread_group& group); - }; - - int m_ID; - static int m_NextID; - - size_t m_MaxThreads; - - boost::thread_group m_ThreadGroup; - - std::thread m_MgmtThread; - boost::mutex m_MgmtMutex; - boost::condition_variable m_MgmtCV; - bool m_Stopped{true}; - - Queue m_Queues[QUEUECOUNT]; - - void ManagerThreadProc(); + boost::shared_mutex m_Mutex; + std::unique_ptr m_Pool; + size_t m_Threads; + Atomic m_Pending; }; } -#endif /* EVENTQUEUE_H */ +#endif /* THREADPOOL_H */ diff --git a/lib/base/timer.cpp b/lib/base/timer.cpp index 0eb03f754..6abac8566 100644 --- a/lib/base/timer.cpp +++ b/lib/base/timer.cpp @@ -1,24 +1,9 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ +#include "base/defer.hpp" #include "base/timer.hpp" #include "base/debug.hpp" +#include "base/logger.hpp" #include "base/utility.hpp" #include #include @@ -71,7 +56,9 @@ static boost::condition_variable l_TimerCV; static std::thread l_TimerThread; static bool l_StopTimerThread; static TimerSet l_Timers; -static int l_AliveTimers; +static int l_AliveTimers = 0; + +static Defer l_ShutdownTimersCleanlyOnExit (&Timer::Uninitialize); /** * Destructor for the Timer class. @@ -81,16 +68,43 @@ Timer::~Timer() Stop(true); } +void Timer::Initialize() +{ + boost::mutex::scoped_lock lock(l_TimerMutex); + + if (l_AliveTimers > 0) { + InitializeThread(); + } +} + void Timer::Uninitialize() +{ + boost::mutex::scoped_lock lock(l_TimerMutex); + + if (l_AliveTimers > 0) { + UninitializeThread(); + } +} + +void Timer::InitializeThread() +{ + l_StopTimerThread = false; + l_TimerThread = std::thread(&Timer::TimerThreadProc); +} + +void Timer::UninitializeThread() { { - boost::mutex::scoped_lock lock(l_TimerMutex); l_StopTimerThread = true; l_TimerCV.notify_all(); } + l_TimerMutex.unlock(); + if (l_TimerThread.joinable()) l_TimerThread.join(); + + l_TimerMutex.lock(); } /** @@ -99,7 +113,7 @@ void Timer::Uninitialize() void Timer::Call() { try { - OnTimerExpired(Timer::Ptr(this)); + OnTimerExpired(this); } catch (...) { InternalReschedule(true); @@ -140,9 +154,8 @@ void Timer::Start() boost::mutex::scoped_lock lock(l_TimerMutex); m_Started = true; - if (l_AliveTimers++ == 0) { - l_StopTimerThread = false; - l_TimerThread = std::thread(&Timer::TimerThreadProc); + if (++l_AliveTimers == 1) { + InitializeThread(); } } @@ -160,15 +173,7 @@ void Timer::Stop(bool wait) boost::mutex::scoped_lock lock(l_TimerMutex); if (m_Started && --l_AliveTimers == 0) { - l_StopTimerThread = true; - l_TimerCV.notify_all(); - - lock.unlock(); - - if (l_TimerThread.joinable() && l_TimerThread.get_id() != std::this_thread::get_id()) - l_TimerThread.join(); - - lock.lock(); + UninitializeThread(); } m_Started = false; @@ -270,6 +275,8 @@ void Timer::AdjustTimers(double adjustment) */ void Timer::TimerThreadProc() { + Log(LogDebug, "Timer", "TimerThreadProc started."); + Utility::SetThreadName("Timer Thread"); for (;;) { @@ -297,8 +304,6 @@ void Timer::TimerThreadProc() continue; } - Timer::Ptr ptimer = timer; - /* Remove the timer from the list so it doesn't get called again * until the current call is completed. */ l_Timers.erase(timer); @@ -308,6 +313,6 @@ void Timer::TimerThreadProc() lock.unlock(); /* Asynchronously call the timer. */ - Utility::QueueAsyncCallback(std::bind(&Timer::Call, ptimer)); + Utility::QueueAsyncCallback([timer]() { timer->Call(); }); } } diff --git a/lib/base/timer.hpp b/lib/base/timer.hpp index 6d5e115ef..2088a6660 100644 --- a/lib/base/timer.hpp +++ b/lib/base/timer.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef TIMER_H #define TIMER_H @@ -40,7 +23,10 @@ public: ~Timer() override; + static void Initialize(); static void Uninitialize(); + static void InitializeThread(); + static void UninitializeThread(); void SetInterval(double interval); double GetInterval() const; @@ -53,7 +39,7 @@ public: void Reschedule(double next = -1); double GetNext() const; - boost::signals2::signal OnTimerExpired; + boost::signals2::signal OnTimerExpired; private: double m_Interval{0}; /**< The interval of the timer. */ diff --git a/lib/base/tlsstream.cpp b/lib/base/tlsstream.cpp index a05a3d7d5..b72a88030 100644 --- a/lib/base/tlsstream.cpp +++ b/lib/base/tlsstream.cpp @@ -1,426 +1,67 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/tlsstream.hpp" +#include "base/application.hpp" #include "base/utility.hpp" #include "base/exception.hpp" #include "base/logger.hpp" +#include "base/configuration.hpp" +#include "base/convert.hpp" +#include +#include +#include #include - -#ifndef _WIN32 -# include -#endif /* _WIN32 */ - -#define TLS_TIMEOUT_SECONDS 10 +#include +#include +#include +#include using namespace icinga; -int TlsStream::m_SSLIndex; -bool TlsStream::m_SSLIndexInitialized = false; - -/** - * Constructor for the TlsStream class. - * - * @param role The role of the client. - * @param sslContext The SSL context for the client. - */ -TlsStream::TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const std::shared_ptr& sslContext) - : SocketEvents(socket, this), m_Eof(false), m_HandshakeOK(false), m_VerifyOK(true), m_ErrorCode(0), - m_ErrorOccurred(false), m_Socket(socket), m_Role(role), m_SendQ(new FIFO()), m_RecvQ(new FIFO()), - m_CurrentAction(TlsActionNone), m_Retry(false), m_Shutdown(false) -{ - std::ostringstream msgbuf; - char errbuf[120]; - - m_SSL = std::shared_ptr(SSL_new(sslContext.get()), SSL_free); - - if (!m_SSL) { - msgbuf << "SSL_new() failed with code " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; - Log(LogCritical, "TlsStream", msgbuf.str()); - - BOOST_THROW_EXCEPTION(openssl_error() - << boost::errinfo_api_function("SSL_new") - << errinfo_openssl_error(ERR_peek_error())); - } - - if (!m_SSLIndexInitialized) { - m_SSLIndex = SSL_get_ex_new_index(0, const_cast("TlsStream"), nullptr, nullptr, nullptr); - m_SSLIndexInitialized = true; - } - - SSL_set_ex_data(m_SSL.get(), m_SSLIndex, this); - - SSL_set_verify(m_SSL.get(), SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, &TlsStream::ValidateCertificate); - - socket->MakeNonBlocking(); - - SSL_set_fd(m_SSL.get(), socket->GetFD()); - - if (m_Role == RoleServer) - SSL_set_accept_state(m_SSL.get()); - else { -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (!hostname.IsEmpty()) - SSL_set_tlsext_host_name(m_SSL.get(), hostname.CStr()); -#endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */ - - SSL_set_connect_state(m_SSL.get()); - } -} - -TlsStream::~TlsStream() -{ - CloseInternal(true); -} - -int TlsStream::ValidateCertificate(int preverify_ok, X509_STORE_CTX *ctx) -{ - auto *ssl = static_cast(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); - auto *stream = static_cast(SSL_get_ex_data(ssl, m_SSLIndex)); - - if (!preverify_ok) { - stream->m_VerifyOK = false; - - std::ostringstream msgbuf; - int err = X509_STORE_CTX_get_error(ctx); - msgbuf << "code " << err << ": " << X509_verify_cert_error_string(err); - stream->m_VerifyError = msgbuf.str(); - } - - return 1; -} - -bool TlsStream::IsVerifyOK() const +bool UnbufferedAsioTlsStream::IsVerifyOK() const { return m_VerifyOK; } -String TlsStream::GetVerifyError() const +String UnbufferedAsioTlsStream::GetVerifyError() const { return m_VerifyError; } -/** - * Retrieves the X509 certficate for this client. - * - * @returns The X509 certificate. - */ -std::shared_ptr TlsStream::GetClientCertificate() const +std::shared_ptr UnbufferedAsioTlsStream::GetPeerCertificate() { - boost::mutex::scoped_lock lock(m_Mutex); - return std::shared_ptr(SSL_get_certificate(m_SSL.get()), &Utility::NullDeleter); + return std::shared_ptr(SSL_get_peer_certificate(native_handle()), X509_free); } -/** - * Retrieves the X509 certficate for the peer. - * - * @returns The X509 certificate. - */ -std::shared_ptr TlsStream::GetPeerCertificate() const +void UnbufferedAsioTlsStream::BeforeHandshake(handshake_type type) { - boost::mutex::scoped_lock lock(m_Mutex); - return std::shared_ptr(SSL_get_peer_certificate(m_SSL.get()), X509_free); -} + namespace ssl = boost::asio::ssl; -void TlsStream::OnEvent(int revents) -{ - int rc; - size_t count; + set_verify_mode(ssl::verify_peer | ssl::verify_client_once); - boost::mutex::scoped_lock lock(m_Mutex); + set_verify_callback([this](bool preverified, ssl::verify_context& ctx) { + if (!preverified) { + m_VerifyOK = false; - if (!m_SSL) - return; + std::ostringstream msgbuf; + int err = X509_STORE_CTX_get_error(ctx.native_handle()); - char buffer[64 * 1024]; - - if (m_CurrentAction == TlsActionNone) { - bool corked = IsCorked(); - if (!corked && (revents & (POLLIN | POLLERR | POLLHUP))) - m_CurrentAction = TlsActionRead; - else if (m_SendQ->GetAvailableBytes() > 0 && (revents & POLLOUT)) - m_CurrentAction = TlsActionWrite; - else { - if (corked) - ChangeEvents(0); - else - ChangeEvents(POLLIN); - - return; - } - } - - bool success = false; - - /* Clear error queue for this thread before using SSL_{read,write,do_handshake}. - * Otherwise SSL_*_error() does not work reliably. - */ - ERR_clear_error(); - - size_t readTotal = 0; - - switch (m_CurrentAction) { - case TlsActionRead: - do { - rc = SSL_read(m_SSL.get(), buffer, sizeof(buffer)); - - if (rc > 0) { - m_RecvQ->Write(buffer, rc); - success = true; - - readTotal += rc; - } - } while (rc > 0 && readTotal < 64 * 1024); - - if (success) - m_CV.notify_all(); - - break; - case TlsActionWrite: - count = m_SendQ->Peek(buffer, sizeof(buffer), true); - - rc = SSL_write(m_SSL.get(), buffer, count); - - if (rc > 0) { - m_SendQ->Read(nullptr, rc, true); - success = true; - } - - break; - case TlsActionHandshake: - rc = SSL_do_handshake(m_SSL.get()); - - if (rc > 0) { - success = true; - m_HandshakeOK = true; - m_CV.notify_all(); - } - - break; - default: - VERIFY(!"Invalid TlsAction"); - } - - if (rc <= 0) { - int err = SSL_get_error(m_SSL.get(), rc); - - switch (err) { - case SSL_ERROR_WANT_READ: - m_Retry = true; - ChangeEvents(POLLIN); - - break; - case SSL_ERROR_WANT_WRITE: - m_Retry = true; - ChangeEvents(POLLOUT); - - break; - case SSL_ERROR_ZERO_RETURN: - lock.unlock(); - - Close(); - - return; - default: - m_ErrorCode = ERR_peek_error(); - m_ErrorOccurred = true; - - if (m_ErrorCode != 0) { - Log(LogWarning, "TlsStream") - << "OpenSSL error: " << ERR_error_string(m_ErrorCode, nullptr); - } else { - Log(LogWarning, "TlsStream", "TLS stream was disconnected."); - } - - lock.unlock(); - - Close(); - - return; - } - } - - if (success) { - m_CurrentAction = TlsActionNone; - - if (!m_Eof) { - if (m_SendQ->GetAvailableBytes() > 0) - ChangeEvents(POLLIN|POLLOUT); - else - ChangeEvents(POLLIN); + msgbuf << "code " << err << ": " << X509_verify_cert_error_string(err); + m_VerifyError = msgbuf.str(); } - lock.unlock(); + return true; + }); - while (!IsCorked() && m_RecvQ->IsDataAvailable() && IsHandlingEvents()) - SignalDataAvailable(); - } - - if (m_Shutdown && !m_SendQ->IsDataAvailable()) { - if (!success) - lock.unlock(); - - Close(); +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + if (type == client && !m_Hostname.IsEmpty()) { + String environmentName = Application::GetAppEnvironment(); + String serverName = m_Hostname; + + if (!environmentName.IsEmpty()) + serverName += ":" + environmentName; + + SSL_set_tlsext_host_name(native_handle(), serverName.CStr()); } -} - -void TlsStream::HandleError() const -{ - if (m_ErrorOccurred) { - BOOST_THROW_EXCEPTION(openssl_error() - << boost::errinfo_api_function("TlsStream::OnEvent") - << errinfo_openssl_error(m_ErrorCode)); - } -} - -void TlsStream::Handshake() -{ - boost::mutex::scoped_lock lock(m_Mutex); - - m_CurrentAction = TlsActionHandshake; - ChangeEvents(POLLOUT); - - boost::system_time const timeout = boost::get_system_time() + boost::posix_time::seconds(TLS_TIMEOUT_SECONDS); - - while (!m_HandshakeOK && !m_ErrorOccurred && !m_Eof && timeout > boost::get_system_time()) - m_CV.timed_wait(lock, timeout); - - // We should _NOT_ (underline, bold, itallic and wordart) throw an exception for a timeout. - if (timeout < boost::get_system_time()) - BOOST_THROW_EXCEPTION(std::runtime_error("Timeout during handshake.")); - - if (m_Eof) - BOOST_THROW_EXCEPTION(std::runtime_error("Socket was closed during TLS handshake.")); - - HandleError(); -} - -/** - * Processes data for the stream. - */ -size_t TlsStream::Peek(void *buffer, size_t count, bool allow_partial) -{ - boost::mutex::scoped_lock lock(m_Mutex); - - if (!allow_partial) - while (m_RecvQ->GetAvailableBytes() < count && !m_ErrorOccurred && !m_Eof) - m_CV.wait(lock); - - HandleError(); - - return m_RecvQ->Peek(buffer, count, true); -} - -size_t TlsStream::Read(void *buffer, size_t count, bool allow_partial) -{ - boost::mutex::scoped_lock lock(m_Mutex); - - if (!allow_partial) - while (m_RecvQ->GetAvailableBytes() < count && !m_ErrorOccurred && !m_Eof) - m_CV.wait(lock); - - HandleError(); - - return m_RecvQ->Read(buffer, count, true); -} - -void TlsStream::Write(const void *buffer, size_t count) -{ - boost::mutex::scoped_lock lock(m_Mutex); - - m_SendQ->Write(buffer, count); - - ChangeEvents(POLLIN|POLLOUT); -} - -void TlsStream::Shutdown() -{ - m_Shutdown = true; - ChangeEvents(POLLOUT); -} - -/** - * Closes the stream. - */ -void TlsStream::Close() -{ - CloseInternal(false); -} - -void TlsStream::CloseInternal(bool inDestructor) -{ - if (m_Eof) - return; - - m_Eof = true; - - if (!inDestructor) - SignalDataAvailable(); - - SocketEvents::Unregister(); - - Stream::Close(); - - boost::mutex::scoped_lock lock(m_Mutex); - - if (!m_SSL) - return; - - (void)SSL_shutdown(m_SSL.get()); - m_SSL.reset(); - - m_Socket->Close(); - m_Socket.reset(); - - m_CV.notify_all(); -} - -bool TlsStream::IsEof() const -{ - return m_Eof; -} - -bool TlsStream::SupportsWaiting() const -{ - return true; -} - -bool TlsStream::IsDataAvailable() const -{ - boost::mutex::scoped_lock lock(m_Mutex); - - return m_RecvQ->GetAvailableBytes() > 0; -} - -void TlsStream::SetCorked(bool corked) -{ - Stream::SetCorked(corked); - - boost::mutex::scoped_lock lock(m_Mutex); - - if (corked) - m_CurrentAction = TlsActionNone; - else - ChangeEvents(POLLIN | POLLOUT); -} - -Socket::Ptr TlsStream::GetSocket() const -{ - return m_Socket; +#endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */ } diff --git a/lib/base/tlsstream.hpp b/lib/base/tlsstream.hpp index ad5b92d0f..70a459114 100644 --- a/lib/base/tlsstream.hpp +++ b/lib/base/tlsstream.hpp @@ -1,114 +1,92 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef TLSSTREAM_H #define TLSSTREAM_H #include "base/i2-base.hpp" #include "base/socket.hpp" -#include "base/socketevents.hpp" #include "base/stream.hpp" #include "base/tlsutility.hpp" #include "base/fifo.hpp" +#include +#include +#include +#include +#include +#include +#include namespace icinga { -enum TlsAction +struct UnbufferedAsioTlsStreamParams { - TlsActionNone, - TlsActionRead, - TlsActionWrite, - TlsActionHandshake + boost::asio::io_context& IoContext; + boost::asio::ssl::context& SslContext; + const String& Hostname; }; -/** - * A TLS stream. - * - * @ingroup base - */ -class TlsStream final : public Stream, private SocketEvents +typedef boost::asio::ssl::stream AsioTcpTlsStream; + +class UnbufferedAsioTlsStream : public AsioTcpTlsStream { public: - DECLARE_PTR_TYPEDEFS(TlsStream); - - TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const std::shared_ptr& sslContext = MakeSSLContext()); - ~TlsStream() override; - - Socket::Ptr GetSocket() const; - - std::shared_ptr GetClientCertificate() const; - std::shared_ptr GetPeerCertificate() const; - - void Handshake(); - - void Close() override; - void Shutdown() override; - - size_t Peek(void *buffer, size_t count, bool allow_partial = false) override; - size_t Read(void *buffer, size_t count, bool allow_partial = false) override; - void Write(const void *buffer, size_t count) override; - - bool IsEof() const override; - - bool SupportsWaiting() const override; - bool IsDataAvailable() const override; - - void SetCorked(bool corked) override; + inline + UnbufferedAsioTlsStream(UnbufferedAsioTlsStreamParams& init) + : stream(init.IoContext, init.SslContext), m_VerifyOK(true), m_Hostname(init.Hostname) + { + } bool IsVerifyOK() const; String GetVerifyError() const; + std::shared_ptr GetPeerCertificate(); + + template + inline + auto async_handshake(handshake_type type, Args&&... args) -> decltype(((AsioTcpTlsStream*)nullptr)->async_handshake(type, std::forward(args)...)) + { + BeforeHandshake(type); + + return AsioTcpTlsStream::async_handshake(type, std::forward(args)...); + } + + template + inline + auto handshake(handshake_type type, Args&&... args) -> decltype(((AsioTcpTlsStream*)nullptr)->handshake(type, std::forward(args)...)) + { + BeforeHandshake(type); + + return AsioTcpTlsStream::handshake(type, std::forward(args)...); + } private: - std::shared_ptr m_SSL; - bool m_Eof; - mutable boost::mutex m_Mutex; - mutable boost::condition_variable m_CV; - bool m_HandshakeOK; bool m_VerifyOK; String m_VerifyError; - int m_ErrorCode; - bool m_ErrorOccurred; + String m_Hostname; - Socket::Ptr m_Socket; - ConnectionRole m_Role; - - FIFO::Ptr m_SendQ; - FIFO::Ptr m_RecvQ; - - TlsAction m_CurrentAction; - bool m_Retry; - bool m_Shutdown; - - static int m_SSLIndex; - static bool m_SSLIndexInitialized; - - void OnEvent(int revents) override; - - void HandleError() const; - - static int ValidateCertificate(int preverify_ok, X509_STORE_CTX *ctx); - static void NullCertificateDeleter(X509 *certificate); - - void CloseInternal(bool inDestructor); + void BeforeHandshake(handshake_type type); }; +class AsioTlsStream : public boost::asio::buffered_stream +{ +public: + inline + AsioTlsStream(boost::asio::io_context& ioContext, boost::asio::ssl::context& sslContext, const String& hostname = String()) + : AsioTlsStream(UnbufferedAsioTlsStreamParams{ioContext, sslContext, hostname}) + { + } + +private: + inline + AsioTlsStream(UnbufferedAsioTlsStreamParams init) + : buffered_stream(init) + { + } +}; + +typedef boost::asio::buffered_stream AsioTcpStream; +typedef std::pair, std::shared_ptr> OptionalTlsStream; + } #endif /* TLSSTREAM_H */ diff --git a/lib/base/tlsutility.cpp b/lib/base/tlsutility.cpp index 9b3c33fb2..3c6751002 100644 --- a/lib/base/tlsutility.cpp +++ b/lib/base/tlsutility.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/tlsutility.hpp" #include "base/convert.hpp" @@ -24,6 +7,7 @@ #include "base/utility.hpp" #include "base/application.hpp" #include "base/exception.hpp" +#include #include namespace icinga @@ -74,35 +58,42 @@ void InitializeOpenSSL() l_SSLInitialized = true; } -/** - * Initializes an SSL context using the specified certificates. - * - * @param pubkey The public key. - * @param privkey The matching private key. - * @param cakey CA certificate chain file. - * @returns An SSL context. - */ -std::shared_ptr MakeSSLContext(const String& pubkey, const String& privkey, const String& cakey) +static void SetupSslContext(const std::shared_ptr& context, const String& pubkey, const String& privkey, const String& cakey) { - char errbuf[120]; + char errbuf[256]; - InitializeOpenSSL(); + // Enforce TLS v1.2 as minimum + context->set_options( + boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_compression | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::no_sslv3 | + boost::asio::ssl::context::no_tlsv1 | + boost::asio::ssl::context::no_tlsv1_1 + ); - std::shared_ptr sslContext = std::shared_ptr(SSL_CTX_new(SSLv23_method()), SSL_CTX_free); + // Custom TLS flags + SSL_CTX *sslContext = context->native_handle(); - long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_CIPHER_SERVER_PREFERENCE; + long flags = SSL_CTX_get_options(sslContext); -#ifdef SSL_OP_NO_COMPRESSION - flags |= SSL_OP_NO_COMPRESSION; -#endif /* SSL_OP_NO_COMPRESSION */ + flags |= SSL_OP_CIPHER_SERVER_PREFERENCE; - SSL_CTX_set_options(sslContext.get(), flags); + SSL_CTX_set_options(sslContext, flags); - SSL_CTX_set_mode(sslContext.get(), SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_set_session_id_context(sslContext.get(), (const unsigned char *)"Icinga 2", 8); + SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_CTX_set_session_id_context(sslContext, (const unsigned char *)"Icinga 2", 8); + + // Explicitly load ECC ciphers, required on el7 - https://github.com/Icinga/icinga2/issues/7247 + // SSL_CTX_set_ecdh_auto is deprecated and removed in OpenSSL 1.1.x - https://github.com/openssl/openssl/issues/1437 +#if OPENSSL_VERSION_NUMBER < 0x10100000L +# ifdef SSL_CTX_set_ecdh_auto + SSL_CTX_set_ecdh_auto(sslContext, 1); +# endif /* SSL_CTX_set_ecdh_auto */ +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ if (!pubkey.IsEmpty()) { - if (!SSL_CTX_use_certificate_chain_file(sslContext.get(), pubkey.CStr())) { + if (!SSL_CTX_use_certificate_chain_file(sslContext, pubkey.CStr())) { Log(LogCritical, "SSL") << "Error with public key file '" << pubkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; BOOST_THROW_EXCEPTION(openssl_error() @@ -113,7 +104,7 @@ std::shared_ptr MakeSSLContext(const String& pubkey, const String& priv } if (!privkey.IsEmpty()) { - if (!SSL_CTX_use_PrivateKey_file(sslContext.get(), privkey.CStr(), SSL_FILETYPE_PEM)) { + if (!SSL_CTX_use_PrivateKey_file(sslContext, privkey.CStr(), SSL_FILETYPE_PEM)) { Log(LogCritical, "SSL") << "Error with private key file '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; BOOST_THROW_EXCEPTION(openssl_error() @@ -122,7 +113,7 @@ std::shared_ptr MakeSSLContext(const String& pubkey, const String& priv << boost::errinfo_file_name(privkey)); } - if (!SSL_CTX_check_private_key(sslContext.get())) { + if (!SSL_CTX_check_private_key(sslContext)) { Log(LogCritical, "SSL") << "Error checking private key '" << privkey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; BOOST_THROW_EXCEPTION(openssl_error() @@ -132,7 +123,7 @@ std::shared_ptr MakeSSLContext(const String& pubkey, const String& priv } if (!cakey.IsEmpty()) { - if (!SSL_CTX_load_verify_locations(sslContext.get(), cakey.CStr(), nullptr)) { + if (!SSL_CTX_load_verify_locations(sslContext, cakey.CStr(), nullptr)) { Log(LogCritical, "SSL") << "Error loading and verifying locations in ca key file '" << cakey << "': " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; BOOST_THROW_EXCEPTION(openssl_error() @@ -153,10 +144,29 @@ std::shared_ptr MakeSSLContext(const String& pubkey, const String& priv << boost::errinfo_file_name(cakey)); } - SSL_CTX_set_client_CA_list(sslContext.get(), cert_names); + SSL_CTX_set_client_CA_list(sslContext, cert_names); } +} - return sslContext; +/** + * Initializes an SSL context using the specified certificates. + * + * @param pubkey The public key. + * @param privkey The matching private key. + * @param cakey CA certificate chain file. + * @returns An SSL context. + */ +std::shared_ptr MakeAsioSslContext(const String& pubkey, const String& privkey, const String& cakey) +{ + namespace ssl = boost::asio::ssl; + + InitializeOpenSSL(); + + auto context (std::make_shared(ssl::context::tlsv12)); + + SetupSslContext(context, pubkey, privkey, cakey); + + return context; } /** @@ -164,11 +174,11 @@ std::shared_ptr MakeSSLContext(const String& pubkey, const String& priv * @param context The ssl context. * @param cipherList The ciper list. **/ -void SetCipherListToSSLContext(const std::shared_ptr& context, const String& cipherList) +void SetCipherListToSSLContext(const std::shared_ptr& context, const String& cipherList) { char errbuf[256]; - if (SSL_CTX_set_cipher_list(context.get(), cipherList.CStr()) == 0) { + if (SSL_CTX_set_cipher_list(context->native_handle(), cipherList.CStr()) == 0) { Log(LogCritical, "SSL") << "Cipher list '" << cipherList @@ -180,6 +190,23 @@ void SetCipherListToSSLContext(const std::shared_ptr& context, const St << boost::errinfo_api_function("SSL_CTX_set_cipher_list") << errinfo_openssl_error(ERR_peek_error())); } + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + //With OpenSSL 1.1.0, there might not be any returned 0. + STACK_OF(SSL_CIPHER) *ciphers; + Array::Ptr cipherNames = new Array(); + + ciphers = SSL_CTX_get_ciphers(context->native_handle()); + for (int i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) { + const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i); + String cipher_name = SSL_CIPHER_get_name(cipher); + + cipherNames->Add(cipher_name); + } + + Log(LogNotice, "TlsUtility") + << "Available TLS cipher list: " << cipherNames->Join(" "); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ } /** @@ -188,26 +215,18 @@ void SetCipherListToSSLContext(const std::shared_ptr& context, const St * @param context The ssl context. * @param tlsProtocolmin The minimum TLS protocol version. */ -void SetTlsProtocolminToSSLContext(const std::shared_ptr& context, const String& tlsProtocolmin) +void SetTlsProtocolminToSSLContext(const std::shared_ptr& context, const String& tlsProtocolmin) { - long flags = SSL_CTX_get_options(context.get()); + // tlsProtocolmin has no effect since we enforce TLS 1.2 since 2.11. + /* + std::shared_ptr sslContext = std::shared_ptr(context->native_handle()); - flags |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + long flags = SSL_CTX_get_options(sslContext.get()); -#ifdef SSL_TXT_TLSV1_1 - if (tlsProtocolmin == SSL_TXT_TLSV1_1) - flags |= SSL_OP_NO_TLSv1; - else -#endif /* SSL_TXT_TLSV1_1 */ -#ifdef SSL_TXT_TLSV1_2 - if (tlsProtocolmin == SSL_TXT_TLSV1_2) - flags |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1; - else -#endif /* SSL_TXT_TLSV1_2 */ - if (tlsProtocolmin != SSL_TXT_TLSV1) - BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid TLS protocol version specified.")); + flags |= ...; - SSL_CTX_set_options(context.get(), flags); + SSL_CTX_set_options(sslContext.get(), flags); + */ } /** @@ -216,10 +235,10 @@ void SetTlsProtocolminToSSLContext(const std::shared_ptr& context, cons * @param context The SSL context. * @param crlPath The path to the CRL file. */ -void AddCRLToSSLContext(const std::shared_ptr& context, const String& crlPath) +void AddCRLToSSLContext(const std::shared_ptr& context, const String& crlPath) { - char errbuf[120]; - X509_STORE *x509_store = SSL_CTX_get_cert_store(context.get()); + char errbuf[256]; + X509_STORE *x509_store = SSL_CTX_get_cert_store(context->native_handle()); X509_LOOKUP *lookup; lookup = X509_STORE_add_lookup(x509_store, X509_LOOKUP_file()); @@ -249,7 +268,7 @@ void AddCRLToSSLContext(const std::shared_ptr& context, const String& c static String GetX509NameCN(X509_NAME *name) { - char errbuf[120]; + char errbuf[256]; char buffer[256]; int rc = X509_NAME_get_text_by_NID(name, NID_commonName, buffer, sizeof(buffer)); @@ -284,7 +303,7 @@ String GetCertificateCN(const std::shared_ptr& certificate) */ std::shared_ptr GetX509Certificate(const String& pemfile) { - char errbuf[120]; + char errbuf[256]; X509 *cert; BIO *fpcert = BIO_new(BIO_s_file()); @@ -322,11 +341,32 @@ std::shared_ptr GetX509Certificate(const String& pemfile) int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile, const String& certfile, bool ca) { - char errbuf[120]; + char errbuf[256]; InitializeOpenSSL(); - RSA *rsa = RSA_generate_key(4096, RSA_F4, nullptr, nullptr); + RSA *rsa = RSA_new(); + BIGNUM *e = BN_new(); + + if (!rsa || !e) { + Log(LogCritical, "SSL") + << "Error while creating RSA key: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; + BOOST_THROW_EXCEPTION(openssl_error() + << boost::errinfo_api_function("RSA_generate_key") + << errinfo_openssl_error(ERR_peek_error())); + } + + BN_set_word(e, RSA_F4); + + if (!RSA_generate_key_ex(rsa, 4096, e, nullptr)) { + Log(LogCritical, "SSL") + << "Error while creating RSA key: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\""; + BOOST_THROW_EXCEPTION(openssl_error() + << boost::errinfo_api_function("RSA_generate_key") + << errinfo_openssl_error(ERR_peek_error())); + } + + BN_free(e); Log(LogInformation, "base") << "Writing private key to '" << keyfile << "'."; @@ -535,7 +575,7 @@ std::shared_ptr CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME String GetIcingaCADir() { - return Application::GetLocalStateDir() + "/lib/icinga2/ca"; + return Configuration::DataDir + "/ca"; } std::shared_ptr CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject) @@ -784,34 +824,4 @@ std::string to_string(const errinfo_openssl_error& e) return "[errinfo_openssl_error]" + tmp.str() + "\n"; } -bool ComparePassword(const String& hash, const String& password, const String& salt) -{ - String otherHash = PBKDF2_SHA256(password, salt, 1000); - VERIFY(otherHash.GetLength() == 64 && hash.GetLength() == 64); - - const char *p1 = otherHash.CStr(); - const char *p2 = hash.CStr(); - - /* By Novelocrat, https://stackoverflow.com/a/25374036 */ - volatile char c = 0; - - for (size_t i = 0; i < 64; ++i) - c |= p1[i] ^ p2[i]; - - return (c == 0); -} - -/* Returns a String in the format $algorithm$salt$hash or returns an empty string in case of an error */ -String CreateHashedPasswordString(const String& password, const String& salt, int algorithm) -{ - // We currently only support SHA256 - if (algorithm != 5) - return String(); - - if (salt.FindFirstOf('$') != String::NPos) - return String(); - - return String("$5$" + salt + "$" + PBKDF2_SHA256(password, salt, 1000)); -} - } diff --git a/lib/base/tlsutility.hpp b/lib/base/tlsutility.hpp index 31bb4e466..de7033311 100644 --- a/lib/base/tlsutility.hpp +++ b/lib/base/tlsutility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef TLSUTILITY_H #define TLSUTILITY_H @@ -31,33 +14,38 @@ #include #include #include +#include #include namespace icinga { void InitializeOpenSSL(); -std::shared_ptr MakeSSLContext(const String& pubkey = String(), const String& privkey = String(), const String& cakey = String()); -void AddCRLToSSLContext(const std::shared_ptr& context, const String& crlPath); -void SetCipherListToSSLContext(const std::shared_ptr& context, const String& cipherList); -void SetTlsProtocolminToSSLContext(const std::shared_ptr& context, const String& tlsProtocolmin); + +std::shared_ptr MakeAsioSslContext(const String& pubkey = String(), const String& privkey = String(), const String& cakey = String()); +void AddCRLToSSLContext(const std::shared_ptr& context, const String& crlPath); +void SetCipherListToSSLContext(const std::shared_ptr& context, const String& cipherList); +void SetTlsProtocolminToSSLContext(const std::shared_ptr& context, const String& tlsProtocolmin); + String GetCertificateCN(const std::shared_ptr& certificate); std::shared_ptr GetX509Certificate(const String& pemfile); int MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile = String(), const String& certfile = String(), bool ca = false); std::shared_ptr CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NAME *issuer, EVP_PKEY *cakey, bool ca); + String GetIcingaCADir(); String CertificateToString(const std::shared_ptr& cert); + std::shared_ptr StringToCertificate(const String& cert); std::shared_ptr CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject); std::shared_ptr CreateCertIcingaCA(const std::shared_ptr& cert); + String PBKDF2_SHA1(const String& password, const String& salt, int iterations); String PBKDF2_SHA256(const String& password, const String& salt, int iterations); String SHA1(const String& s, bool binary = false); String SHA256(const String& s); String RandomString(int length); + bool VerifyCertificate(const std::shared_ptr& caCertificate, const std::shared_ptr& certificate); -bool ComparePassword(const String& hash, const String& password, const String& Salt); -String CreateHashedPasswordString(const String& password, const String& salt, int algorithm = 5); class openssl_error : virtual public std::exception, virtual public boost::exception { }; diff --git a/lib/base/type.cpp b/lib/base/type.cpp index 138269a87..493833d0c 100644 --- a/lib/base/type.cpp +++ b/lib/base/type.cpp @@ -1,30 +1,15 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/type.hpp" #include "base/scriptglobal.hpp" +#include "base/namespace.hpp" #include "base/objectlock.hpp" using namespace icinga; Type::Ptr Type::TypeInstance; +/* Ensure that the priority is lower than the basic namespace initialization in scriptframe.cpp. */ INITIALIZE_ONCE_WITH_PRIORITY([]() { Type::Ptr type = new TypeType(); type->SetPrototype(TypeType::GetPrototype()); @@ -39,19 +24,19 @@ String Type::ToString() const void Type::Register(const Type::Ptr& type) { - VERIFY(!GetByName(type->GetName())); - - ScriptGlobal::Set("Types." + type->GetName(), type); + ScriptGlobal::Set("Types." + type->GetName(), type, true); } Type::Ptr Type::GetByName(const String& name) { - Dictionary::Ptr typesNS = ScriptGlobal::Get("Types", &Empty); + Namespace::Ptr typesNS = ScriptGlobal::Get("Types", &Empty); if (!typesNS) return nullptr; - Value ptype = typesNS->Get(name); + Value ptype; + if (!typesNS->Get(name, &ptype)) + return nullptr; if (!ptype.IsObjectType()) return nullptr; @@ -63,14 +48,16 @@ std::vector Type::GetAllTypes() { std::vector types; - Dictionary::Ptr typesNS = ScriptGlobal::Get("Types", &Empty); + Namespace::Ptr typesNS = ScriptGlobal::Get("Types", &Empty); if (typesNS) { ObjectLock olock(typesNS); - for (const Dictionary::Pair& kv : typesNS) { - if (kv.second.IsObjectType()) - types.push_back(kv.second); + for (const Namespace::Pair& kv : typesNS) { + Value value = kv.second->Get(); + + if (value.IsObjectType()) + types.push_back(value); } } diff --git a/lib/base/type.hpp b/lib/base/type.hpp index ef8ce6d23..2bf54ccf5 100644 --- a/lib/base/type.hpp +++ b/lib/base/type.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef TYPE_H #define TYPE_H @@ -138,6 +121,7 @@ class TypeImpl { }; +/* Ensure that the priority is lower than the basic namespace initialization in scriptframe.cpp. */ #define REGISTER_TYPE(type) \ INITIALIZE_ONCE_WITH_PRIORITY([]() { \ icinga::Type::Ptr t = new TypeImpl(); \ diff --git a/lib/base/typetype-script.cpp b/lib/base/typetype-script.cpp index 88ebe3c92..df6a1873b 100644 --- a/lib/base/typetype-script.cpp +++ b/lib/base/typetype-script.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/type.hpp" #include "base/dictionary.hpp" diff --git a/lib/base/unix.hpp b/lib/base/unix.hpp index 5312f51fa..7413a5b15 100644 --- a/lib/base/unix.hpp +++ b/lib/base/unix.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef UNIX_H #define UNIX_H diff --git a/lib/base/unixsocket.cpp b/lib/base/unixsocket.cpp index c5aacbc54..dcc56ffa1 100644 --- a/lib/base/unixsocket.cpp +++ b/lib/base/unixsocket.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/unixsocket.hpp" #include "base/exception.hpp" diff --git a/lib/base/unixsocket.hpp b/lib/base/unixsocket.hpp index ab5546b50..80a9f255e 100644 --- a/lib/base/unixsocket.hpp +++ b/lib/base/unixsocket.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef UNIXSOCKET_H #define UNIXSOCKET_H @@ -26,6 +9,11 @@ namespace icinga { +/** + * A TCP socket. DEPRECATED - Use Boost ASIO instead. + * + * @ingroup base + */ class UnixSocket final : public Socket { public: diff --git a/lib/base/utility.cpp b/lib/base/utility.cpp index 8d2162011..1add7616c 100644 --- a/lib/base/utility.cpp +++ b/lib/base/utility.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/utility.hpp" #include "base/convert.hpp" @@ -26,17 +9,26 @@ #include "base/utility.hpp" #include "base/json.hpp" #include "base/objectlock.hpp" +#include #include +#include +#include #include +#include #include #include #include #include #include +#include #include #include #include +#include +#include #include +#include +#include #ifdef __FreeBSD__ # include @@ -48,6 +40,7 @@ #ifndef _WIN32 # include +# include # include # include # include @@ -260,47 +253,7 @@ bool Utility::CidrMatch(const String& pattern, const String& ip) */ String Utility::DirName(const String& path) { - char *dir; - -#ifdef _WIN32 - String dupPath = path; - - /* PathRemoveFileSpec doesn't properly handle forward slashes. */ - for (char& ch : dupPath) { - if (ch == '/') - ch = '\\'; - } - - dir = strdup(dupPath.CStr()); -#else /* _WIN32 */ - dir = strdup(path.CStr()); -#endif /* _WIN32 */ - - if (!dir) - BOOST_THROW_EXCEPTION(std::bad_alloc()); - - String result; - -#ifndef _WIN32 - result = dirname(dir); -#else /* _WIN32 */ - if (dir[0] != 0 && !PathRemoveFileSpec(dir)) { - free(dir); - - BOOST_THROW_EXCEPTION(win32_error() - << boost::errinfo_api_function("PathRemoveFileSpec") - << errinfo_win32_error(GetLastError())); - } - - result = dir; - - if (result.IsEmpty()) - result = "."; -#endif /* _WIN32 */ - - free(dir); - - return result; + return boost::filesystem::path(path.Begin(), path.End()).parent_path().string(); } /** @@ -311,21 +264,7 @@ String Utility::DirName(const String& path) */ String Utility::BaseName(const String& path) { - char *dir = strdup(path.CStr()); - String result; - - if (!dir) - BOOST_THROW_EXCEPTION(std::bad_alloc()); - -#ifndef _WIN32 - result = basename(dir); -#else /* _WIN32 */ - result = PathFindFileName(dir); -#endif /* _WIN32 */ - - free(dir); - - return result; + return boost::filesystem::path(path.Begin(), path.End()).filename().string(); } /** @@ -764,38 +703,18 @@ void Utility::MkDirP(const String& path, int mode) } } -void Utility::RemoveDirRecursive(const String& path) +void Utility::Remove(const String& path) { - std::vector paths; - Utility::GlobRecursive(path, "*", std::bind(&Utility::CollectPaths, _1, std::ref(paths)), GlobFile | GlobDirectory); + namespace fs = boost::filesystem; - /* This relies on the fact that GlobRecursive lists the parent directory - * first before recursing into subdirectories. - */ - std::reverse(paths.begin(), paths.end()); - - for (const String& path : paths) { - if (remove(path.CStr()) < 0) - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("remove") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(path)); - } - -#ifndef _WIN32 - if (rmdir(path.CStr()) < 0) -#else /* _WIN32 */ - if (_rmdir(path.CStr()) < 0) -#endif /* _WIN32 */ - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rmdir") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(path)); + (void)fs::remove(fs::path(path.Begin(), path.End())); } -void Utility::CollectPaths(const String& path, std::vector& paths) +void Utility::RemoveDirRecursive(const String& path) { - paths.push_back(path); + namespace fs = boost::filesystem; + + (void)fs::remove_all(fs::path(path.Begin(), path.End())); } /* @@ -804,10 +723,20 @@ void Utility::CollectPaths(const String& path, std::vector& paths) */ void Utility::CopyFile(const String& source, const String& target) { - std::ifstream ifs(source.CStr(), std::ios::binary); - std::ofstream ofs(target.CStr(), std::ios::binary | std::ios::trunc); + namespace fs = boost::filesystem; - ofs << ifs.rdbuf(); + fs::copy_file(fs::path(source.Begin(), source.End()), fs::path(target.Begin(), target.End()), fs::copy_option::overwrite_if_exists); +} + +/* + * Renames a source file to a target location. + * Caller must ensure that the target's base directory exists and is writable. + */ +void Utility::RenameFile(const String& source, const String& target) +{ + namespace fs = boost::filesystem; + + fs::rename(fs::path(source.Begin(), source.End()), fs::path(target.Begin(), target.End())); } /* @@ -1244,6 +1173,26 @@ unsigned long Utility::SDBM(const String& str, size_t len) return hash; } +String Utility::ParseVersion(const String& v) +{ + /* + * 2.11.0-0.rc1.1 + * v2.10.5 + * r2.10.3 + * v2.11.0-rc1-58-g7c1f716da + */ + boost::regex pattern("^[vr]?(2\\.\\d+\\.\\d+).*$"); + boost::smatch result; + + if (boost::regex_search(v.GetData(), result, pattern)) { + String res(result[1].first, result[1].second); + return res; + } + + // Couldn't not extract anything, return unparsed version + return v; +} + int Utility::CompareVersion(const String& v1, const String& v2) { std::vector tokensv1 = v1.Split("."); @@ -1350,13 +1299,11 @@ tm Utility::LocalTime(time_t ts) bool Utility::PathExists(const String& path) { -#ifndef _WIN32 - struct stat statbuf; - return (lstat(path.CStr(), &statbuf) >= 0); -#else /* _WIN32 */ - struct _stat statbuf; - return (_stat(path.CStr(), &statbuf) >= 0); -#endif /* _WIN32 */ + namespace fs = boost::filesystem; + + boost::system::error_code ec; + + return fs::exists(fs::path(path.Begin(), path.End()), ec) && !ec; } Value Utility::LoadJsonFile(const String& path) @@ -1376,6 +1323,8 @@ Value Utility::LoadJsonFile(const String& path) void Utility::SaveJsonFile(const String& path, int mode, const Value& value) { + namespace fs = boost::filesystem; + std::fstream fp; String tempFilename = Utility::CreateTempFile(path + ".XXXXXX", mode, fp); @@ -1383,16 +1332,7 @@ void Utility::SaveJsonFile(const String& path, int mode, const Value& value) fp << JsonEncode(value); fp.close(); -#ifdef _WIN32 - _unlink(path.CStr()); -#endif /* _WIN32 */ - - if (rename(tempFilename.CStr(), path.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempFilename)); - } + RenameFile(tempFilename, path); } static void HexEncode(char ch, std::ostream& os) @@ -1462,28 +1402,23 @@ String Utility::UnescapeString(const String& s) #ifndef _WIN32 static String UnameHelper(char type) { - /* Unfortunately the uname() system call doesn't support some of the - * query types we're interested in - so we're using popen() instead. */ + struct utsname name; + uname(&name); - char cmd[] = "uname -X 2>&1"; - cmd[7] = type; - - FILE *fp = popen(cmd, "r"); - - if (!fp) - return "Unknown"; - - char line[1024]; - std::ostringstream msgbuf; - - while (fgets(line, sizeof(line), fp)) - msgbuf << line; - - pclose(fp); - - String result = msgbuf.str(); - - return result.Trim(); + switch (type) { + case 'm': + return (char*)name.machine; + case 'n': + return (char*)name.nodename; + case 'r': + return (char*)name.release; + case 's': + return (char*)name.sysname; + case 'v': + return (char*)name.version; + default: + VERIFY(!"Invalid uname query."); + } } #endif /* _WIN32 */ static bool ReleaseHelper(String *platformName, String *platformVersion) @@ -1720,40 +1655,20 @@ String Utility::GetPlatformArchitecture() #endif /* _WIN32 */ } +const char l_Utf8Replacement[] = "\xEF\xBF\xBD"; + String Utility::ValidateUTF8(const String& input) { - String output; - size_t length = input.GetLength(); + std::vector output; + output.reserve(input.GetLength() * 3u); - for (size_t i = 0; i < length; i++) { - if ((input[i] & 0x80) == 0) { - output += input[i]; - continue; - } - - if ((input[i] & 0xE0) == 0xC0 && length > i + 1 && - (input[i + 1] & 0xC0) == 0x80) { - output += input[i]; - output += input[i + 1]; - i++; - continue; - } - - if ((input[i] & 0xF0) == 0xE0 && length > i + 2 && - (input[i + 1] & 0xC0) == 0x80 && (input[i + 2] & 0xC0) == 0x80) { - output += input[i]; - output += input[i + 1]; - output += input[i + 2]; - i += 2; - continue; - } - - output += '\xEF'; - output += '\xBF'; - output += '\xBD'; + try { + utf8::replace_invalid(input.Begin(), input.End(), std::back_inserter(output)); + } catch (const utf8::not_enough_room&) { + output.insert(output.end(), (const char*)l_Utf8Replacement, (const char*)l_Utf8Replacement + 3); } - return output; + return String(output.begin(), output.end()); } String Utility::CreateTempFile(const String& path, int mode, std::fstream& fp) @@ -1936,16 +1851,53 @@ String Utility::GetIcingaDataPath() #endif /* _WIN32 */ +/** + * Retrieve the environment variable value by given key. + * + * @param env Environment variable name. + */ + String Utility::GetFromEnvironment(const String& env) { -#ifndef _WIN32 const char *envValue = getenv(env.CStr()); + if (envValue == NULL) return String(); else return String(envValue); -#else /* _WIN32 */ - // While getenv exists on Windows, we don't set them. Therefore there is no reason to read them. - return String(); -#endif /* _WIN32 */ +} + +/** + * Compare the password entered by a client with the actual password. + * The comparision is safe against timing attacks. + */ +bool Utility::ComparePasswords(const String& enteredPassword, const String& actualPassword) +{ + volatile const char * volatile enteredPasswordCStr = enteredPassword.CStr(); + volatile size_t enteredPasswordLen = enteredPassword.GetLength(); + + volatile const char * volatile actualPasswordCStr = actualPassword.CStr(); + volatile size_t actualPasswordLen = actualPassword.GetLength(); + + volatile uint_fast8_t result = enteredPasswordLen == actualPasswordLen; + + if (result) { + auto cStr (actualPasswordCStr); + auto len (actualPasswordLen); + + actualPasswordCStr = cStr; + actualPasswordLen = len; + } else { + auto cStr (enteredPasswordCStr); + auto len (enteredPasswordLen); + + actualPasswordCStr = cStr; + actualPasswordLen = len; + } + + for (volatile size_t i = 0; i < enteredPasswordLen; ++i) { + result &= uint_fast8_t(enteredPasswordCStr[i] == actualPasswordCStr[i]); + } + + return result; } diff --git a/lib/base/utility.hpp b/lib/base/utility.hpp index 8311f8d54..4505dc918 100644 --- a/lib/base/utility.hpp +++ b/lib/base/utility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef UTILITY_H #define UTILITY_H @@ -69,6 +52,8 @@ public: static String DirName(const String& path); static String BaseName(const String& path); + static String GetEnv(const String& key); + static void NullDeleter(void *); static double GetTime(); @@ -115,6 +100,7 @@ public: static unsigned long SDBM(const String& str, size_t len = String::NPos); + static String ParseVersion(const String& v); static int CompareVersion(const String& v1, const String& v2); static int Random(); @@ -126,8 +112,10 @@ public: static bool PathExists(const String& path); + static void Remove(const String& path); static void RemoveDirRecursive(const String& path); static void CopyFile(const String& source, const String& target); + static void RenameFile(const String& source, const String& target); static Value LoadJsonFile(const String& path); static void SaveJsonFile(const String& path, int mode, const Value& value); @@ -149,6 +137,8 @@ public: static String GetFromEnvironment(const String& env); + static bool ComparePasswords(const String& enteredPassword, const String& actualPassword); + #ifdef I2_DEBUG static void SetTime(double); static void IncrementTime(double); @@ -156,7 +146,6 @@ public: private: Utility(); - static void CollectPaths(const String& path, std::vector& paths); #ifdef _WIN32 static int MksTemp (char *tmpl); diff --git a/lib/base/value-operators.cpp b/lib/base/value-operators.cpp index 9ce5a2d24..69974d038 100644 --- a/lib/base/value-operators.cpp +++ b/lib/base/value-operators.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/value.hpp" #include "base/array.hpp" diff --git a/lib/base/value.cpp b/lib/base/value.cpp index 93c515a2b..867c8217e 100644 --- a/lib/base/value.cpp +++ b/lib/base/value.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/value.hpp" #include "base/array.hpp" diff --git a/lib/base/value.hpp b/lib/base/value.hpp index 9d036c2b7..86a3b115d 100644 --- a/lib/base/value.hpp +++ b/lib/base/value.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef VALUE_H #define VALUE_H diff --git a/lib/base/win32.hpp b/lib/base/win32.hpp index be840ff9d..064c5d669 100644 --- a/lib/base/win32.hpp +++ b/lib/base/win32.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef WIN32_H #define WIN32_H diff --git a/lib/base/workqueue.cpp b/lib/base/workqueue.cpp index ae3dad9b0..cacd17b76 100644 --- a/lib/base/workqueue.cpp +++ b/lib/base/workqueue.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/workqueue.hpp" #include "base/utility.hpp" @@ -24,6 +7,7 @@ #include "base/application.hpp" #include "base/exception.hpp" #include +#include using namespace icinga; @@ -185,13 +169,13 @@ std::vector WorkQueue::GetExceptions() const return m_Exceptions; } -void WorkQueue::ReportExceptions(const String& facility) const +void WorkQueue::ReportExceptions(const String& facility, bool verbose) const { std::vector exceptions = GetExceptions(); for (const auto& eptr : exceptions) { Log(LogCritical, facility) - << DiagnosticInformation(eptr); + << DiagnosticInformation(eptr, verbose); } Log(LogCritical, facility) @@ -221,7 +205,7 @@ void WorkQueue::StatusTimerHandler() if (pending > GetTaskCount(5)) { timeInfo = " empty in "; - if (timeToZero < 0) + if (timeToZero < 0 || std::isinf(timeToZero)) timeInfo += "infinite time, your task handler isn't able to keep up"; else timeInfo += Utility::FormatDuration(timeToZero); diff --git a/lib/base/workqueue.hpp b/lib/base/workqueue.hpp index 002e5f21c..bc84d9176 100644 --- a/lib/base/workqueue.hpp +++ b/lib/base/workqueue.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef WORKQUEUE_H #define WORKQUEUE_H @@ -36,9 +19,10 @@ namespace icinga enum WorkQueuePriority { - PriorityLow, - PriorityNormal, - PriorityHigh + PriorityLow = 0, + PriorityNormal = 1, + PriorityHigh = 2, + PriorityImmediate = 4 }; using TaskFunction = std::function; @@ -119,7 +103,7 @@ public: bool HasExceptions() const; std::vector GetExceptions() const; - void ReportExceptions(const String& facility) const; + void ReportExceptions(const String& facility, bool verbose = false) const; protected: void IncreaseTaskCount(); diff --git a/lib/checker/CMakeLists.txt b/lib/checker/CMakeLists.txt index 8f3de0b1b..5a8334c7f 100644 --- a/lib/checker/CMakeLists.txt +++ b/lib/checker/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ mkclass_target(checkercomponent.ti checkercomponent-ti.cpp checkercomponent-ti.hpp) @@ -36,14 +21,14 @@ set_target_properties ( install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/checker.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) if(NOT WIN32) - install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled\")") - install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/checker.conf \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled/checker.conf\")") + install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_CONFIGDIR}/features-enabled\")") + install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/checker.conf \"\$ENV{DESTDIR}${ICINGA2_FULL_CONFIGDIR}/features-enabled/checker.conf\")") else() - install_if_not_exists(${PROJECT_SOURCE_DIR}/etc/icinga2/features-enabled/checker.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-enabled) + install_if_not_exists(${PROJECT_SOURCE_DIR}/etc/icinga2/features-enabled/checker.conf ${ICINGA2_CONFIGDIR}/features-enabled) endif() set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) diff --git a/lib/checker/checkercomponent.cpp b/lib/checker/checkercomponent.cpp index c26db9de1..20300b3ab 100644 --- a/lib/checker/checkercomponent.cpp +++ b/lib/checker/checkercomponent.cpp @@ -1,27 +1,11 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "checker/checkercomponent.hpp" #include "checker/checkercomponent-ti.cpp" #include "icinga/icingaapplication.hpp" #include "icinga/cib.hpp" #include "remote/apilistener.hpp" +#include "base/configuration.hpp" #include "base/configtype.hpp" #include "base/objectlock.hpp" #include "base/utility.hpp" @@ -84,24 +68,49 @@ void CheckerComponent::Start(bool runtimeCreated) void CheckerComponent::Stop(bool runtimeRemoved) { - Log(LogInformation, "CheckerComponent") - << "'" << GetName() << "' stopped."; - { boost::mutex::scoped_lock lock(m_Mutex); m_Stopped = true; m_CV.notify_all(); } + double wait = 0.0; + + while (Checkable::GetPendingChecks() > 0) { + Log(LogDebug, "CheckerComponent") + << "Waiting for running checks (" << Checkable::GetPendingChecks() + << ") to finish. Waited for " << wait << " seconds now."; + + Utility::Sleep(0.1); + wait += 0.1; + + /* Pick a timeout slightly shorther than the process reload timeout. */ + double reloadTimeout = Application::GetReloadTimeout(); + double waitMax = reloadTimeout - 30; + if (waitMax <= 0) + waitMax = 1; + + if (wait > waitMax) { + Log(LogWarning, "CheckerComponent") + << "Checks running too long for " << wait + << " seconds, hard shutdown before reload timeout: " << reloadTimeout << "."; + break; + } + } + m_ResultTimer->Stop(); m_Thread.join(); + Log(LogInformation, "CheckerComponent") + << "'" << GetName() << "' stopped."; + ObjectImpl::Stop(runtimeRemoved); } void CheckerComponent::CheckThreadProc() { Utility::SetThreadName("Check Scheduler"); + IcingaApplication::Ptr icingaApp = IcingaApplication::GetInstance(); boost::mutex::scoped_lock lock(m_Mutex); @@ -120,7 +129,13 @@ void CheckerComponent::CheckThreadProc() double wait = csi.NextCheck - Utility::GetTime(); - if (Checkable::GetPendingChecks() >= GetConcurrentChecks()) +//#ifdef I2_DEBUG +// Log(LogDebug, "CheckerComponent") +// << "Pending checks " << Checkable::GetPendingChecks() +// << " vs. max concurrent checks " << icingaApp->GetMaxConcurrentChecks() << "."; +//#endif /* I2_DEBUG */ + + if (Checkable::GetPendingChecks() >= icingaApp->GetMaxConcurrentChecks()) wait = 0.5; if (wait > 0) { @@ -148,12 +163,12 @@ void CheckerComponent::CheckThreadProc() Service::Ptr service; tie(host, service) = GetHostService(checkable); - if (host && !service && (!checkable->GetEnableActiveChecks() || !IcingaApplication::GetInstance()->GetEnableHostChecks())) { + if (host && !service && (!checkable->GetEnableActiveChecks() || !icingaApp->GetEnableHostChecks())) { Log(LogNotice, "CheckerComponent") << "Skipping check for host '" << host->GetName() << "': active host checks are disabled"; check = false; } - if (host && service && (!checkable->GetEnableActiveChecks() || !IcingaApplication::GetInstance()->GetEnableServiceChecks())) { + if (host && service && (!checkable->GetEnableActiveChecks() || !icingaApp->GetEnableServiceChecks())) { Log(LogNotice, "CheckerComponent") << "Skipping check for service '" << service->GetName() << "': active service checks are disabled"; check = false; @@ -174,6 +189,9 @@ void CheckerComponent::CheckThreadProc() m_IdleCheckables.insert(GetCheckableScheduleInfo(checkable)); lock.unlock(); + Log(LogDebug, "CheckerComponent") + << "Checks for checkable '" << checkable->GetName() << "' are disabled. Rescheduling check."; + checkable->UpdateNextCheck(); lock.lock(); @@ -181,7 +199,16 @@ void CheckerComponent::CheckThreadProc() continue; } - m_PendingCheckables.insert(GetCheckableScheduleInfo(checkable)); + + csi = GetCheckableScheduleInfo(checkable); + + Log(LogDebug, "CheckerComponent") + << "Scheduling info for checkable '" << checkable->GetName() << "' (" + << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", checkable->GetNextCheck()) << "): Object '" + << csi.Object->GetName() << "', Next Check: " + << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", csi.NextCheck) << "(" << csi.NextCheck << ")."; + + m_PendingCheckables.insert(csi); lock.unlock(); @@ -209,7 +236,7 @@ void CheckerComponent::ExecuteCheckHelper(const Checkable::Ptr& checkable) CheckResult::Ptr cr = new CheckResult(); cr->SetState(ServiceUnknown); - String output = "Exception occured while checking '" + checkable->GetName() + "': " + DiagnosticInformation(ex); + String output = "Exception occurred while checking '" + checkable->GetName() + "': " + DiagnosticInformation(ex); cr->SetOutput(output); double now = Utility::GetTime(); diff --git a/lib/checker/checkercomponent.hpp b/lib/checker/checkercomponent.hpp index 601bb58b2..b3589e5fb 100644 --- a/lib/checker/checkercomponent.hpp +++ b/lib/checker/checkercomponent.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CHECKERCOMPONENT_H #define CHECKERCOMPONENT_H diff --git a/lib/checker/checkercomponent.ti b/lib/checker/checkercomponent.ti index d6a76c9df..3959aeb48 100644 --- a/lib/checker/checkercomponent.ti +++ b/lib/checker/checkercomponent.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" @@ -26,19 +9,10 @@ namespace icinga class CheckerComponent : ConfigObject { - activation_priority 100; + activation_priority 300; - [config, no_storage] int concurrent_checks { - get {{{ - return Application::GetMaxConcurrentChecks(); - }}} - set {{{ - Application::SetMaxConcurrentChecks(value); - }}} - default {{{ - return Application::GetDefaultMaxConcurrentChecks(); - }}} - }; + /* Has no effect. Keep this here to avoid breaking config changes. */ + [deprecated, config] int concurrent_checks; }; } diff --git a/lib/cli/CMakeLists.txt b/lib/cli/CMakeLists.txt index d92c22c45..38756b5ce 100644 --- a/lib/cli/CMakeLists.txt +++ b/lib/cli/CMakeLists.txt @@ -1,26 +1,12 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ set(cli_SOURCES i2-cli.hpp apisetupcommand.cpp apisetupcommand.hpp - apiusercommand.cpp apiusercommand.hpp apisetuputility.cpp apisetuputility.hpp calistcommand.cpp calistcommand.hpp + caremovecommand.cpp caremovecommand.hpp + carestorecommand.cpp carestorecommand.hpp casigncommand.cpp casigncommand.hpp clicommand.cpp clicommand.hpp consolecommand.cpp consolecommand.hpp @@ -43,7 +29,6 @@ set(cli_SOURCES pkisavecertcommand.cpp pkisavecertcommand.hpp pkisigncsrcommand.cpp pkisigncsrcommand.hpp pkiticketcommand.cpp pkiticketcommand.hpp - troubleshootcommand.cpp troubleshootcommand.hpp variablegetcommand.cpp variablegetcommand.hpp variablelistcommand.cpp variablelistcommand.hpp variableutility.cpp variableutility.hpp diff --git a/lib/cli/apisetupcommand.cpp b/lib/cli/apisetupcommand.cpp index bc832c525..81b9d8db6 100644 --- a/lib/cli/apisetupcommand.cpp +++ b/lib/cli/apisetupcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/apisetupcommand.hpp" #include "cli/apisetuputility.hpp" @@ -41,12 +24,14 @@ String ApiSetupCommand::GetShortDescription() const ImpersonationLevel ApiSetupCommand::GetImpersonationLevel() const { - return ImpersonateRoot; + return ImpersonateIcinga; } -int ApiSetupCommand::GetMaxArguments() const +void ApiSetupCommand::InitParameters(boost::program_options::options_description& visibleDesc, + boost::program_options::options_description& hiddenDesc) const { - return -1; + visibleDesc.add_options() + ("cn", po::value(), "The certificate's common name"); } /** @@ -56,10 +41,16 @@ int ApiSetupCommand::GetMaxArguments() const */ int ApiSetupCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const { - String cn = VariableUtility::GetVariable("NodeName"); + String cn; - if (cn.IsEmpty()) - cn = Utility::GetFQDN(); + if (vm.count("cn")) { + cn = vm["cn"].as(); + } else { + cn = VariableUtility::GetVariable("NodeName"); + + if (cn.IsEmpty()) + cn = Utility::GetFQDN(); + } if (!ApiSetupUtility::SetupMaster(cn, true)) return 1; diff --git a/lib/cli/apisetupcommand.hpp b/lib/cli/apisetupcommand.hpp index f54da0685..be2693d13 100644 --- a/lib/cli/apisetupcommand.hpp +++ b/lib/cli/apisetupcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef APISETUPCOMMAND_H #define APISETUPCOMMAND_H @@ -37,7 +20,8 @@ public: String GetDescription() const override; String GetShortDescription() const override; - int GetMaxArguments() const override; + void InitParameters(boost::program_options::options_description& visibleDesc, + boost::program_options::options_description& hiddenDesc) const override; int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const override; ImpersonationLevel GetImpersonationLevel() const override; }; diff --git a/lib/cli/apisetuputility.cpp b/lib/cli/apisetuputility.cpp index 070e78cff..21583af0a 100644 --- a/lib/cli/apisetuputility.cpp +++ b/lib/cli/apisetuputility.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/apisetuputility.hpp" #include "cli/nodeutility.hpp" @@ -28,6 +11,7 @@ #include "base/tlsutility.hpp" #include "base/scriptglobal.hpp" #include "base/exception.hpp" +#include "base/utility.hpp" #include #include #include @@ -40,7 +24,7 @@ using namespace icinga; String ApiSetupUtility::GetConfdPath() { - return Application::GetSysconfDir() + "/icinga2/conf.d"; + return Configuration::ConfigDir + "/conf.d"; } String ApiSetupUtility::GetApiUsersConfPath() @@ -80,8 +64,8 @@ bool ApiSetupUtility::SetupMasterCertificates(const String& cn) String pki_path = ApiListener::GetCertsDir(); Utility::MkDirP(pki_path, 0700); - String user = ScriptGlobal::Get("RunAsUser"); - String group = ScriptGlobal::Get("RunAsGroup"); + String user = Configuration::RunAsUser; + String group = Configuration::RunAsGroup; if (!Utility::SetFileOwnership(pki_path, user, group)) { Log(LogWarning, "cli") @@ -152,6 +136,15 @@ bool ApiSetupUtility::SetupMasterCertificates(const String& cn) bool ApiSetupUtility::SetupMasterApiUser() { + if (!Utility::PathExists(GetConfdPath())) { + Log(LogWarning, "cli") + << "Path '" << GetConfdPath() << "' do not exist."; + Log(LogInformation, "cli") + << "Creating path '" << GetConfdPath() << "'."; + + Utility::MkDirP(GetConfdPath(), 0755); + } + String api_username = "root"; // TODO make this available as cli parameter? String api_password = RandomString(8); String apiUsersPath = GetConfdPath() + "/api-users.conf"; @@ -171,7 +164,7 @@ bool ApiSetupUtility::SetupMasterApiUser() String tempFilename = Utility::CreateTempFile(apiUsersPath + ".XXXXXX", 0644, fp); fp << "/**\n" - << " * The APIUser objects are used for authentication against the API.\n" + << " * The ApiUser objects are used for authentication against the API.\n" << " */\n" << "object ApiUser \"" << api_username << "\" {\n" << " password = \"" << api_password << "\"\n" @@ -182,22 +175,22 @@ bool ApiSetupUtility::SetupMasterApiUser() fp.close(); -#ifdef _WIN32 - _unlink(apiUsersPath.CStr()); -#endif /* _WIN32 */ - - if (rename(tempFilename.CStr(), apiUsersPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempFilename)); - } + Utility::RenameFile(tempFilename, apiUsersPath); return true; } bool ApiSetupUtility::SetupMasterEnableApi() { + /* + * Ensure the api-users.conf file is included, when conf.d inclusion is disabled. + */ + if (!NodeUtility::GetConfigurationIncludeState("\"conf.d\"", true)) + NodeUtility::UpdateConfiguration("\"conf.d/api-users.conf\"", true, false); + + /* + * Enable the API feature + */ Log(LogInformation, "cli", "Enabling the 'api' feature."); FeatureUtility::EnableFeatures({ "api" }); diff --git a/lib/cli/apisetuputility.hpp b/lib/cli/apisetuputility.hpp index 9523ebffc..d3614462d 100644 --- a/lib/cli/apisetuputility.hpp +++ b/lib/cli/apisetuputility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef APISETUPUTILITY_H #define APISETUPUTILITY_H diff --git a/lib/cli/apiusercommand.cpp b/lib/cli/apiusercommand.cpp deleted file mode 100644 index 5bd77ab89..000000000 --- a/lib/cli/apiusercommand.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#include "cli/apiusercommand.hpp" -#include "base/logger.hpp" -#include "base/tlsutility.hpp" -#include "base/configwriter.hpp" -#include "remote/apiuser.hpp" -#include - -using namespace icinga; -namespace po = boost::program_options; - -REGISTER_CLICOMMAND("api/user", ApiUserCommand); - -String ApiUserCommand::GetDescription(void) const -{ - return "Create a hashed user and password string for the Icinga 2 API"; -} - -String ApiUserCommand::GetShortDescription(void) const -{ - return "API user creation helper"; -} - -void ApiUserCommand::InitParameters(boost::program_options::options_description& visibleDesc, - boost::program_options::options_description& hiddenDesc) const -{ - visibleDesc.add_options() - ("user", po::value(), "API username") - ("password", po::value(), "Password in clear text") - ("salt", po::value(), "Optional salt (default: 8 random chars)") - ("oneline", "Print only the password hash"); -} - -/** - * The entry point for the "api user" CLI command. - * - * @returns An exit status. - */ -int ApiUserCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const -{ - String passwd, salt; - if (!vm.count("user") && !vm.count("oneline")) { - Log(LogCritical, "cli", "Username (--user) must be specified."); - return 1; - } - - if (!vm.count("password")) { - Log(LogCritical, "cli", "Password (--password) must be specified."); - return 1; - } - - passwd = vm["password"].as(); - salt = vm.count("salt") ? String(vm["salt"].as()) : RandomString(8); - - if (salt.FindFirstOf('$') != String::NPos) { - Log(LogCritical, "cli", "Salt (--salt) may not contain '$'"); - return 1; - } - - String hashedPassword = CreateHashedPasswordString(passwd, salt, 5); - if (hashedPassword == String()) { - Log(LogCritical, "cli") << "Failed to hash password \"" << passwd << "\" with salt \"" << salt << "\""; - return 1; - } - - if (vm.count("oneline")) - std::cout << hashedPassword << std::endl; - else { - std::cout << "object ApiUser "; - - ConfigWriter::EmitString(std::cout, vm["user"].as()); - - std::cout << "{\n" - << " password_hash = "; - - ConfigWriter::EmitString(std::cout, hashedPassword); - - std::cout << "\n" - << " // client_cn = \"\"\n" - << "\n" - << " permissions = [ \"*\" ]\n" - << "}\n"; - } - - return 0; -} diff --git a/lib/cli/apiusercommand.hpp b/lib/cli/apiusercommand.hpp deleted file mode 100644 index 4a4bfb280..000000000 --- a/lib/cli/apiusercommand.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#ifndef APIUSERCOMMAND_H -#define APIUSERCOMMAND_H - -#include "cli/clicommand.hpp" - -namespace icinga -{ - -/** - * The "api user" command. - * - * @ingroup cli - */ -class ApiUserCommand : public CLICommand -{ -public: - DECLARE_PTR_TYPEDEFS(ApiUserCommand); - - virtual String GetDescription(void) const override; - virtual String GetShortDescription(void) const override; - virtual void InitParameters(boost::program_options::options_description& visibleDesc, - boost::program_options::options_description& hiddenDesc) const override; - virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const override; -}; - -} - -#endif /* APIUSERCOMMAND_H */ diff --git a/lib/cli/calistcommand.cpp b/lib/cli/calistcommand.cpp index 8d9caf382..f693ad72f 100644 --- a/lib/cli/calistcommand.cpp +++ b/lib/cli/calistcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/calistcommand.hpp" #include "remote/apilistener.hpp" @@ -31,32 +14,49 @@ namespace po = boost::program_options; REGISTER_CLICOMMAND("ca/list", CAListCommand); +/** + * Provide a long CLI description sentence. + * + * @return text + */ String CAListCommand::GetDescription() const { - return "Lists all certificate signing requests."; + return "Lists pending certificate signing requests."; } +/** + * Provide a short CLI description. + * + * @return text + */ String CAListCommand::GetShortDescription() const { - return "lists all certificate signing requests"; + return "lists pending certificate signing requests"; } +/** + * Initialize available CLI parameters. + * + * @param visibleDesc Register visible parameters. + * @param hiddenDesc Register hidden parameters. + */ void CAListCommand::InitParameters(boost::program_options::options_description& visibleDesc, boost::program_options::options_description& hiddenDesc) const { visibleDesc.add_options() - ("json", "encode output as JSON") - ; + ("all", "List all certificate signing requests, including signed. Note: Old requests are automatically cleaned by Icinga after 1 week.") + ("removed", "List all removed CSRs (for use with 'ca restore')") + ("json", "encode output as JSON"); } /** * The entry point for the "ca list" CLI command. * - * @returns An exit status. + * @return An exit status. */ int CAListCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const { - Dictionary::Ptr requests = PkiUtility::GetCertificateRequests(); + Dictionary::Ptr requests = PkiUtility::GetCertificateRequests(vm.count("removed")); if (vm.count("json")) std::cout << JsonEncode(requests); @@ -69,6 +69,10 @@ int CAListCommand::Run(const boost::program_options::variables_map& vm, const st for (auto& kv : requests) { Dictionary::Ptr request = kv.second; + /* Skip signed requests by default. */ + if (!vm.count("all") && request->Contains("cert_response")) + continue; + std::cout << kv.first << " | " /* << Utility::FormatDateTime("%Y/%m/%d %H:%M:%S", request->Get("timestamp")) */ diff --git a/lib/cli/calistcommand.hpp b/lib/cli/calistcommand.hpp index e71ffd872..ddf44d43b 100644 --- a/lib/cli/calistcommand.hpp +++ b/lib/cli/calistcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CALISTCOMMAND_H #define CALISTCOMMAND_H diff --git a/lib/cli/caremovecommand.cpp b/lib/cli/caremovecommand.cpp new file mode 100644 index 000000000..d8944944e --- /dev/null +++ b/lib/cli/caremovecommand.cpp @@ -0,0 +1,93 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "cli/caremovecommand.hpp" +#include "base/logger.hpp" +#include "base/application.hpp" +#include "base/tlsutility.hpp" +#include "remote/apilistener.hpp" + +using namespace icinga; + +REGISTER_CLICOMMAND("ca/remove", CARemoveCommand); + +/** + * Provide a long CLI description sentence. + * + * @return text + */ +String CARemoveCommand::GetDescription() const +{ + return "Removes an outstanding certificate request."; +} + +/** + * Provide a short CLI description. + * + * @return text + */ +String CARemoveCommand::GetShortDescription() const +{ + return "removes an outstanding certificate request"; +} + +/** + * Define minimum arguments without key parameter. + * + * @return number of arguments + */ +int CARemoveCommand::GetMinArguments() const +{ + return 1; +} + +/** + * Impersonate as Icinga user. + * + * @return impersonate level + */ +ImpersonationLevel CARemoveCommand::GetImpersonationLevel() const +{ + return ImpersonateIcinga; +} + +/** + * The entry point for the "ca remove" CLI command. + * + * @returns An exit status. + */ +int CARemoveCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const +{ + String fingerPrint = ap[0]; + String requestFile = ApiListener::GetCertificateRequestsDir() + "/" + fingerPrint + ".json"; + + if (!Utility::PathExists(requestFile)) { + Log(LogCritical, "cli") + << "No request exists for fingerprint '" << fingerPrint << "'."; + return 1; + } + + Dictionary::Ptr request = Utility::LoadJsonFile(requestFile); + std::shared_ptr certRequest = StringToCertificate(request->Get("cert_request")); + + if (!certRequest) { + Log(LogCritical, "cli", "Certificate request is invalid. Could not parse X.509 certificate for the 'cert_request' attribute."); + return 1; + } + + String cn = GetCertificateCN(certRequest); + + if (request->Contains("cert_response")) { + Log(LogCritical, "cli") + << "Certificate request for CN '" << cn << "' already signed, removal is not possible."; + return 1; + } + + Utility::SaveJsonFile(ApiListener::GetCertificateRequestsDir() + "/" + fingerPrint + ".removed", 0600, request); + + Utility::Remove(requestFile); + + Log(LogInformation, "cli") + << "Certificate request for CN " << cn << " removed."; + + return 0; +} diff --git a/lib/cli/caremovecommand.hpp b/lib/cli/caremovecommand.hpp new file mode 100644 index 000000000..2da92d39e --- /dev/null +++ b/lib/cli/caremovecommand.hpp @@ -0,0 +1,30 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef CAREMOVECOMMAND_H +#define CAREMOVECOMMAND_H + +#include "cli/clicommand.hpp" + +namespace icinga +{ + +/** + * The "ca remove" command. + * + * @ingroup cli + */ +class CARemoveCommand final : public CLICommand +{ +public: + DECLARE_PTR_TYPEDEFS(CARemoveCommand); + + String GetDescription() const override; + String GetShortDescription() const override; + int GetMinArguments() const override; + ImpersonationLevel GetImpersonationLevel() const override; + int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const override; +}; + +} + +#endif /* CAREMOVECOMMAND_H */ diff --git a/lib/cli/carestorecommand.cpp b/lib/cli/carestorecommand.cpp new file mode 100644 index 000000000..502036830 --- /dev/null +++ b/lib/cli/carestorecommand.cpp @@ -0,0 +1,88 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "cli/carestorecommand.hpp" +#include "base/logger.hpp" +#include "base/application.hpp" +#include "base/tlsutility.hpp" +#include "remote/apilistener.hpp" + +using namespace icinga; + +REGISTER_CLICOMMAND("ca/restore", CARestoreCommand); + +/** + * Provide a long CLI description sentence. + * + * @return text + */ +String CARestoreCommand::GetDescription() const +{ + return "Restores a previously removed certificate request."; +} + +/** + * Provide a short CLI description. + * + * @return text + */ +String CARestoreCommand::GetShortDescription() const +{ + return "restores a removed certificate request"; +} + +/** + * Define minimum arguments without key parameter. + * + * @return number of arguments + */ +int CARestoreCommand::GetMinArguments() const +{ + return 1; +} + +/** + * Impersonate as Icinga user. + * + * @return impersonate level + */ +ImpersonationLevel CARestoreCommand::GetImpersonationLevel() const +{ + return ImpersonateIcinga; +} + +/** + * The entry point for the "ca restore" CLI command. + * + * @returns An exit status. + */ +int CARestoreCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const +{ + String fingerPrint = ap[0]; + String removedRequestFile = ApiListener::GetCertificateRequestsDir() + "/" + fingerPrint + ".removed"; + + if (!Utility::PathExists(removedRequestFile)) { + Log(LogCritical, "cli") + << "Cannot find removed fingerprint '" << fingerPrint << "', bailing out."; + return 1; + } + + Dictionary::Ptr request = Utility::LoadJsonFile(removedRequestFile); + std::shared_ptr certRequest = StringToCertificate(request->Get("cert_request")); + + if (!certRequest) { + Log(LogCritical, "cli", "Certificate request is invalid. Could not parse X.509 certificate for the 'cert_request' attribute."); + /* Purge the file when we know that it is broken. */ + Utility::Remove(removedRequestFile); + return 1; + } + + Utility::SaveJsonFile(ApiListener::GetCertificateRequestsDir() + "/" + fingerPrint + ".json", 0600, request); + + Utility::Remove(removedRequestFile); + + Log(LogInformation, "cli") + << "Restored certificate request for CN '" << GetCertificateCN(certRequest) << "', sign it with:\n" + << "\"icinga2 ca sign " << fingerPrint << "\""; + + return 0; +} diff --git a/lib/cli/carestorecommand.hpp b/lib/cli/carestorecommand.hpp new file mode 100644 index 000000000..74a27dff6 --- /dev/null +++ b/lib/cli/carestorecommand.hpp @@ -0,0 +1,30 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef CARESTORECOMMAND_H +#define CARESTORECOMMAND_H + +#include "cli/clicommand.hpp" + +namespace icinga +{ + +/** + * The "ca restore" command. + * + * @ingroup cli + */ +class CARestoreCommand final : public CLICommand +{ +public: + DECLARE_PTR_TYPEDEFS(CARestoreCommand); + + String GetDescription() const override; + String GetShortDescription() const override; + int GetMinArguments() const override; + ImpersonationLevel GetImpersonationLevel() const override; + int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const override; +}; + +} + +#endif /* CASTORECOMMAND_H */ diff --git a/lib/cli/casigncommand.cpp b/lib/cli/casigncommand.cpp index 57c11d8f8..96d2c2c87 100644 --- a/lib/cli/casigncommand.cpp +++ b/lib/cli/casigncommand.cpp @@ -1,47 +1,50 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/casigncommand.hpp" -#include "remote/apilistener.hpp" #include "base/logger.hpp" #include "base/application.hpp" #include "base/tlsutility.hpp" +#include "remote/apilistener.hpp" using namespace icinga; REGISTER_CLICOMMAND("ca/sign", CASignCommand); +/** + * Provide a long CLI description sentence. + * + * @return text + */ String CASignCommand::GetDescription() const { return "Signs an outstanding certificate request."; } +/** + * Provide a short CLI description. + * + * @return text + */ String CASignCommand::GetShortDescription() const { return "signs an outstanding certificate request"; } +/** + * Define minimum arguments without key parameter. + * + * @return number of arguments + */ int CASignCommand::GetMinArguments() const { return 1; } +/** + * Impersonate as Icinga user. + * + * @return impersonate level + */ ImpersonationLevel CASignCommand::GetImpersonationLevel() const { return ImpersonateIcinga; @@ -50,7 +53,7 @@ ImpersonationLevel CASignCommand::GetImpersonationLevel() const /** * The entry point for the "ca sign" CLI command. * - * @returns An exit status. + * @return An exit status. */ int CASignCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const { diff --git a/lib/cli/casigncommand.hpp b/lib/cli/casigncommand.hpp index 28ae8b82a..0089af76c 100644 --- a/lib/cli/casigncommand.hpp +++ b/lib/cli/casigncommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CASIGNCOMMAND_H #define CASIGNCOMMAND_H diff --git a/lib/cli/clicommand.cpp b/lib/cli/clicommand.cpp index ece65d4e6..878beac77 100644 --- a/lib/cli/clicommand.cpp +++ b/lib/cli/clicommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/clicommand.hpp" #include "base/logger.hpp" @@ -186,7 +169,7 @@ bool CLICommand::ParseCommand(int argc, char **argv, po::options_description& vi std::vector::size_type i; int k; for (i = 0, k = 1; i < vname.size() && k < argc; i++, k++) { - if (strncmp(argv[k], "--", 2) == 0) { + if (strncmp(argv[k], "-", 1) == 0 || strncmp(argv[k], "--", 2) == 0) { i--; continue; } diff --git a/lib/cli/clicommand.hpp b/lib/cli/clicommand.hpp index 3023ed4f9..e3bbae171 100644 --- a/lib/cli/clicommand.hpp +++ b/lib/cli/clicommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CLICOMMAND_H #define CLICOMMAND_H diff --git a/lib/cli/consolecommand.cpp b/lib/cli/consolecommand.cpp index 774879207..9f8a2cf8a 100644 --- a/lib/cli/consolecommand.cpp +++ b/lib/cli/consolecommand.cpp @@ -1,25 +1,7 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/consolecommand.hpp" #include "config/configcompiler.hpp" -#include "remote/apiclient.hpp" #include "remote/consolehandler.hpp" #include "remote/url.hpp" #include "base/configwriter.hpp" @@ -31,9 +13,26 @@ #include "base/unixsocket.hpp" #include "base/utility.hpp" #include "base/networkstream.hpp" +#include "base/defer.hpp" +#include "base/io-engine.hpp" +#include "base/stream.hpp" +#include "base/tcpsocket.hpp" /* include global icinga::Connect */ +#include #include "base/exception.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include + + #ifdef HAVE_EDITLINE #include "cli/editline.hpp" #endif /* HAVE_EDITLINE */ @@ -42,7 +41,8 @@ using namespace icinga; namespace po = boost::program_options; static ScriptFrame *l_ScriptFrame; -static ApiClient::Ptr l_ApiClient; +static Url::Ptr l_Url; +static std::shared_ptr l_TlsStream; static String l_Session; REGISTER_CLICOMMAND("console", ConsoleCommand); @@ -184,24 +184,16 @@ char *ConsoleCommand::ConsoleCompleteHelper(const char *word, int state) static std::vector matches; if (state == 0) { - if (!l_ApiClient) + if (!l_Url) matches = ConsoleHandler::GetAutocompletionSuggestions(word, *l_ScriptFrame); else { - boost::mutex mutex; - boost::condition_variable cv; - bool ready = false; Array::Ptr suggestions; - l_ApiClient->AutocompleteScript(l_Session, word, l_ScriptFrame->Sandboxed, - std::bind(&ConsoleCommand::AutocompleteScriptCompletionHandler, - std::ref(mutex), std::ref(cv), std::ref(ready), - _1, _2, - std::ref(suggestions))); - - { - boost::mutex::scoped_lock lock(mutex); - while (!ready) - cv.wait(lock); + /* Remote debug console. */ + try { + suggestions = AutoCompleteScript(l_Session, word, l_ScriptFrame->Sandboxed); + } catch (...) { + return nullptr; //Errors are just ignored here. } matches.clear(); @@ -244,14 +236,42 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector(); + try { + l_Url = new Url(addr); + } catch (const std::exception& ex) { + Log(LogCritical, "ConsoleCommand", ex.what()); + return EXIT_FAILURE; + } + + String usernameEnv = Utility::GetFromEnvironment("ICINGA2_API_USERNAME"); + String passwordEnv = Utility::GetFromEnvironment("ICINGA2_API_PASSWORD"); + + if (!usernameEnv.IsEmpty()) + l_Url->SetUsername(usernameEnv); + if (!passwordEnv.IsEmpty()) + l_Url->SetPassword(passwordEnv); + + if (l_Url->GetPort().IsEmpty()) + l_Url->SetPort("5665"); + + /* User passed --connect and wants to run the expression via REST API. + * Evaluate this now before any user input happens. + */ + try { + l_TlsStream = ConsoleCommand::Connect(); + } catch (const std::exception& ex) { + return EXIT_FAILURE; + } + } + String command; bool syntaxOnly = false; @@ -284,18 +304,19 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector lines; int next_line = 1; #ifdef HAVE_EDITLINE - char *homeEnv = getenv("HOME"); + String homeEnv = Utility::GetFromEnvironment("HOME"); String historyPath; std::fstream historyfp; - if (homeEnv) { + if (!homeEnv.IsEmpty()) { historyPath = String(homeEnv) + "/.icinga2_history"; historyfp.open(historyPath.CStr(), std::fstream::in); @@ -311,30 +332,6 @@ int ConsoleCommand::RunScriptConsole(ScriptFrame& scriptFrame, const String& add l_ScriptFrame = &scriptFrame; l_Session = session; - if (!addr.IsEmpty()) { - Url::Ptr url; - - try { - url = new Url(addr); - } catch (const std::exception& ex) { - Log(LogCritical, "ConsoleCommand", ex.what()); - return EXIT_FAILURE; - } - - const char *usernameEnv = getenv("ICINGA2_API_USERNAME"); - const char *passwordEnv = getenv("ICINGA2_API_PASSWORD"); - - if (usernameEnv) - url->SetUsername(usernameEnv); - if (passwordEnv) - url->SetPassword(passwordEnv); - - if (url->GetPort().IsEmpty()) - url->SetPort("5665"); - - l_ApiClient = new ApiClient(url->GetHost(), url->GetPort(), url->GetUsername(), url->GetPassword()); - } - while (std::cin.good()) { String fileName; @@ -422,7 +419,8 @@ incomplete: Value result; - if (!l_ApiClient) { + /* Local debug console. */ + if (connectAddr.IsEmpty()) { expr = ConfigCompiler::CompileText(fileName, command); /* This relies on the fact that - for syntax errors - CompileText() @@ -434,25 +432,23 @@ incomplete: } else result = true; } else { - boost::mutex mutex; - boost::condition_variable cv; - bool ready = false; - boost::exception_ptr eptr; + /* Remote debug console. */ + try { + result = ExecuteScript(l_Session, command, scriptFrame.Sandboxed); + } catch (const ScriptError&) { + /* Re-throw the exception for the outside try-catch block. */ + boost::rethrow_exception(boost::current_exception()); + } catch (const std::exception& ex) { + Log(LogCritical, "ConsoleCommand") + << "HTTP query failed: " << ex.what(); - l_ApiClient->ExecuteScript(l_Session, command, scriptFrame.Sandboxed, - std::bind(&ConsoleCommand::ExecuteScriptCompletionHandler, - std::ref(mutex), std::ref(cv), std::ref(ready), - _1, _2, - std::ref(result), std::ref(eptr))); +#ifdef HAVE_EDITLINE + /* Ensures that the terminal state is reset */ + rl_deprep_terminal(); +#endif /* HAVE_EDITLINE */ - { - boost::mutex::scoped_lock lock(mutex); - while (!ready) - cv.wait(lock); + return EXIT_FAILURE; } - - if (eptr) - boost::rethrow_exception(eptr); } if (commandOnce.IsEmpty()) { @@ -476,15 +472,15 @@ incomplete: std::vector ulines = text.Split("\n"); - for (int i = 1; i <= ulines.size(); i++) { + for (decltype(ulines.size()) i = 1; i <= ulines.size(); i++) { int start, len; - if (i == di.FirstLine) + if (i == (decltype(i))di.FirstLine) start = di.FirstColumn; else start = 0; - if (i == di.LastLine) + if (i == (decltype(i))di.LastLine) len = di.LastColumn - di.FirstColumn + 1; else len = ulines[i - 1].GetLength(); @@ -497,7 +493,7 @@ incomplete: } else offset = 4; - if (i >= di.FirstLine && i <= di.LastLine) { + if (i >= (decltype(i))di.FirstLine && i <= (decltype(i))di.LastLine) { std::cout << String(di.Path.GetLength() + offset, ' '); std::cout << String(start, ' ') << String(len, '^') << "\n"; } @@ -521,48 +517,207 @@ incomplete: return EXIT_SUCCESS; } -void ConsoleCommand::ExecuteScriptCompletionHandler(boost::mutex& mutex, boost::condition_variable& cv, - bool& ready, const boost::exception_ptr& eptr, const Value& result, Value& resultOut, boost::exception_ptr& eptrOut) +/** + * Connects to host:port and performs a TLS shandshake + * + * @returns AsioTlsStream pointer for future HTTP connections. + */ +std::shared_ptr ConsoleCommand::Connect() { - if (eptr) { - try { - boost::rethrow_exception(eptr); - } catch (const ScriptError&) { - eptrOut = boost::current_exception(); - } catch (const std::exception& ex) { - Log(LogCritical, "ConsoleCommand") - << "HTTP query failed: " << ex.what(); - Application::Exit(EXIT_FAILURE); + std::shared_ptr sslContext; + + try { + sslContext = MakeAsioSslContext(Empty, Empty, Empty); //TODO: Add support for cert, key, ca parameters + } catch(const std::exception& ex) { + Log(LogCritical, "DebugConsole") + << "Cannot make SSL context: " << ex.what(); + throw; + } + + String host = l_Url->GetHost(); + String port = l_Url->GetPort(); + + std::shared_ptr stream = std::make_shared(IoEngine::Get().GetIoContext(), *sslContext, host); + + try { + icinga::Connect(stream->lowest_layer(), host, port); + } catch (const std::exception& ex) { + Log(LogWarning, "DebugConsole") + << "Cannot connect to REST API on host '" << host << "' port '" << port << "': " << ex.what(); + throw; + } + + auto& tlsStream (stream->next_layer()); + + try { + tlsStream.handshake(tlsStream.client); + } catch (const std::exception& ex) { + Log(LogWarning, "DebugConsole") + << "TLS handshake with host '" << host << "' failed: " << ex.what(); + throw; + } + + return std::move(stream); +} + +/** + * Sends the request via REST API and returns the parsed response. + * + * @param tlsStream Caller must prepare TLS stream/handshake. + * @param url Fully prepared Url object. + * @return A dictionary decoded from JSON. + */ +Dictionary::Ptr ConsoleCommand::SendRequest() +{ + namespace beast = boost::beast; + namespace http = beast::http; + + l_TlsStream = ConsoleCommand::Connect(); + + Defer s ([&]() { + l_TlsStream->next_layer().shutdown(); + }); + + http::request request(http::verb::post, std::string(l_Url->Format(false)), 10); + + request.set(http::field::user_agent, "Icinga/DebugConsole/" + Application::GetAppVersion()); + request.set(http::field::host, l_Url->GetHost() + ":" + l_Url->GetPort()); + + request.set(http::field::accept, "application/json"); + request.set(http::field::authorization, "Basic " + Base64::Encode(l_Url->GetUsername() + ":" + l_Url->GetPassword())); + + try { + http::write(*l_TlsStream, request); + l_TlsStream->flush(); + } catch (const std::exception &ex) { + Log(LogWarning, "DebugConsole") + << "Cannot write HTTP request to REST API at URL '" << l_Url->Format(true) << "': " << ex.what(); + throw; + } + + http::parser parser; + beast::flat_buffer buf; + + try { + http::read(*l_TlsStream, buf, parser); + } catch (const std::exception &ex) { + Log(LogWarning, "DebugConsole") + << "Failed to parse HTTP response from REST API at URL '" << l_Url->Format(true) << "': " << ex.what(); + throw; + } + + auto &response(parser.get()); + + /* Handle HTTP errors first. */ + if (response.result() != http::status::ok) { + String message = "HTTP request failed; Code: " + Convert::ToString(response.result()) + + "; Body: " + response.body(); + BOOST_THROW_EXCEPTION(ScriptError(message)); + } + + Dictionary::Ptr jsonResponse; + auto &body(response.body()); + + //Log(LogWarning, "Console") + // << "Got response: " << response.body(); + + try { + jsonResponse = JsonDecode(body); + } catch (...) { + String message = "Cannot parse JSON response body: " + response.body(); + BOOST_THROW_EXCEPTION(ScriptError(message)); + } + + return jsonResponse; +} + +/** + * Executes the DSL script via HTTP and returns HTTP and user errors. + * + * @param session Local session handler. + * @param command The DSL string. + * @param sandboxed Whether to run this sandboxed. + * @return Result value, also contains user errors. + */ +Value ConsoleCommand::ExecuteScript(const String& session, const String& command, bool sandboxed) +{ + /* Extend the url parameters for the request. */ + l_Url->SetPath({"v1", "console", "execute-script"}); + + l_Url->SetQuery({ + {"session", session}, + {"command", command}, + {"sandboxed", sandboxed ? "1" : "0"} + }); + + Dictionary::Ptr jsonResponse = SendRequest(); + + /* Extract the result, and handle user input errors too. */ + Array::Ptr results = jsonResponse->Get("results"); + Value result; + + if (results && results->GetLength() > 0) { + Dictionary::Ptr resultInfo = results->Get(0); + + if (resultInfo->Get("code") >= 200 && resultInfo->Get("code") <= 299) { + result = resultInfo->Get("result"); + } else { + String errorMessage = resultInfo->Get("status"); + + DebugInfo di; + Dictionary::Ptr debugInfo = resultInfo->Get("debug_info"); + + if (debugInfo) { + di.Path = debugInfo->Get("path"); + di.FirstLine = debugInfo->Get("first_line"); + di.FirstColumn = debugInfo->Get("first_column"); + di.LastLine = debugInfo->Get("last_line"); + di.LastColumn = debugInfo->Get("last_column"); + } + + bool incompleteExpression = resultInfo->Get("incomplete_expression"); + BOOST_THROW_EXCEPTION(ScriptError(errorMessage, di, incompleteExpression)); } } - resultOut = result; - - { - boost::mutex::scoped_lock lock(mutex); - ready = true; - cv.notify_all(); - } + return result; } -void ConsoleCommand::AutocompleteScriptCompletionHandler(boost::mutex& mutex, boost::condition_variable& cv, - bool& ready, const boost::exception_ptr& eptr, const Array::Ptr& result, Array::Ptr& resultOut) +/** + * Executes the auto completion script via HTTP and returns HTTP and user errors. + * + * @param session Local session handler. + * @param command The auto completion string. + * @param sandboxed Whether to run this sandboxed. + * @return Result value, also contains user errors. + */ +Array::Ptr ConsoleCommand::AutoCompleteScript(const String& session, const String& command, bool sandboxed) { - if (eptr) { - try { - boost::rethrow_exception(eptr); - } catch (const std::exception& ex) { - Log(LogCritical, "ConsoleCommand") - << "HTTP query failed: " << ex.what(); - Application::Exit(EXIT_FAILURE); + /* Extend the url parameters for the request. */ + l_Url->SetPath({ "v1", "console", "auto-complete-script" }); + + l_Url->SetQuery({ + {"session", session}, + {"command", command}, + {"sandboxed", sandboxed ? "1" : "0"} + }); + + Dictionary::Ptr jsonResponse = SendRequest(); + + /* Extract the result, and handle user input errors too. */ + Array::Ptr results = jsonResponse->Get("results"); + Array::Ptr suggestions; + + if (results && results->GetLength() > 0) { + Dictionary::Ptr resultInfo = results->Get(0); + + if (resultInfo->Get("code") >= 200 && resultInfo->Get("code") <= 299) { + suggestions = resultInfo->Get("suggestions"); + } else { + String errorMessage = resultInfo->Get("status"); + BOOST_THROW_EXCEPTION(ScriptError(errorMessage)); } } - resultOut = result; - - { - boost::mutex::scoped_lock lock(mutex); - ready = true; - cv.notify_all(); - } + return suggestions; } diff --git a/lib/cli/consolecommand.hpp b/lib/cli/consolecommand.hpp index 9369080c4..95d6dce12 100644 --- a/lib/cli/consolecommand.hpp +++ b/lib/cli/consolecommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONSOLECOMMAND_H #define CONSOLECOMMAND_H @@ -23,6 +6,9 @@ #include "cli/clicommand.hpp" #include "base/exception.hpp" #include "base/scriptframe.hpp" +#include "base/tlsstream.hpp" +#include "remote/url.hpp" + namespace icinga { @@ -46,7 +32,7 @@ public: boost::program_options::options_description& hiddenDesc) const override; int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const override; - static int RunScriptConsole(ScriptFrame& scriptFrame, const String& addr = String(), + static int RunScriptConsole(ScriptFrame& scriptFrame, const String& connectAddr = String(), const String& session = String(), const String& commandOnce = String(), const String& commandOnceFileName = String(), bool syntaxOnly = false); @@ -54,11 +40,12 @@ private: mutable boost::mutex m_Mutex; mutable boost::condition_variable m_CV; - static void ExecuteScriptCompletionHandler(boost::mutex& mutex, boost::condition_variable& cv, - bool& ready, const boost::exception_ptr& eptr, const Value& result, Value& resultOut, - boost::exception_ptr& eptrOut); - static void AutocompleteScriptCompletionHandler(boost::mutex& mutex, boost::condition_variable& cv, - bool& ready, const boost::exception_ptr& eptr, const Array::Ptr& result, Array::Ptr& resultOut); + static std::shared_ptr Connect(); + + static Value ExecuteScript(const String& session, const String& command, bool sandboxed); + static Array::Ptr AutoCompleteScript(const String& session, const String& command, bool sandboxed); + + static Dictionary::Ptr SendRequest(); #ifdef HAVE_EDITLINE static char *ConsoleCompleteHelper(const char *word, int state); diff --git a/lib/cli/daemoncommand.cpp b/lib/cli/daemoncommand.cpp index d113dac11..28fce401d 100644 --- a/lib/cli/daemoncommand.cpp +++ b/lib/cli/daemoncommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/daemoncommand.hpp" #include "cli/daemonutility.hpp" @@ -24,6 +7,8 @@ #include "config/configcompiler.hpp" #include "config/configcompilercontext.hpp" #include "config/configitembuilder.hpp" +#include "base/atomic.hpp" +#include "base/defer.hpp" #include "base/logger.hpp" #include "base/application.hpp" #include "base/timer.hpp" @@ -33,11 +18,25 @@ #include "base/scriptglobal.hpp" #include "base/context.hpp" #include "config.h" +#include +#include #include -#include #include #include +#ifdef _WIN32 +#include +#else /* _WIN32 */ +#include +#include +#include +#include +#endif /* _WIN32 */ + +#ifdef HAVE_SYSTEMD +#include +#endif /* HAVE_SYSTEMD */ + using namespace icinga; namespace po = boost::program_options; @@ -45,21 +44,28 @@ static po::variables_map g_AppParams; REGISTER_CLICOMMAND("daemon", DaemonCommand); -#ifndef _WIN32 -static void SigHupHandler(int) -{ - Application::RequestRestart(); -} -#endif /* _WIN32 */ - -static bool Daemonize() +/* + * Daemonize(). On error, this function logs by itself and exits (i.e. does not return). + * + * Implementation note: We're only supposed to call exit() in one of the forked processes. + * The other process calls _exit(). This prevents issues with exit handlers like atexit(). + */ +static void Daemonize() noexcept { #ifndef _WIN32 - Application::UninitializeBase(); + try { + Application::UninitializeBase(); + } catch (const std::exception& ex) { + Log(LogCritical, "cli") + << "Failed to stop thread pool before daemonizing, unexpected error: " << DiagnosticInformation(ex); + exit(EXIT_FAILURE); + } pid_t pid = fork(); if (pid == -1) { - return false; + Log(LogCritical, "cli") + << "fork() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; + exit(EXIT_FAILURE); } if (pid) { @@ -72,7 +78,7 @@ static bool Daemonize() do { Utility::Sleep(0.1); - readpid = Application::ReadPidFile(Application::GetPidPath()); + readpid = Application::ReadPidFile(Configuration::PidPath); ret = waitpid(pid, &status, WNOHANG); } while (readpid != pid && ret == 0); @@ -88,13 +94,28 @@ static bool Daemonize() _exit(EXIT_SUCCESS); } - Application::InitializeBase(); -#endif /* _WIN32 */ + Log(LogDebug, "Daemonize()") + << "Child process with PID " << Utility::GetPid() << " continues; re-initializing base."; - return true; + // Detach from controlling terminal + pid_t sid = setsid(); + if (sid == -1) { + Log(LogCritical, "cli") + << "setsid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; + exit(EXIT_FAILURE); + } + + try { + Application::InitializeBase(); + } catch (const std::exception& ex) { + Log(LogCritical, "cli") + << "Failed to re-initialize thread pool after daemonizing: " << DiagnosticInformation(ex); + exit(EXIT_FAILURE); + } +#endif /* _WIN32 */ } -static bool SetDaemonIO(const String& stderrFile) +static void CloseStdIO(const String& stderrFile) { #ifndef _WIN32 int fdnull = open("/dev/null", O_RDWR); @@ -126,14 +147,7 @@ static bool SetDaemonIO(const String& stderrFile) if (fderr > 2) close(fderr); } - - pid_t sid = setsid(); - if (sid == -1) { - return false; - } #endif - - return true; } String DaemonCommand::GetDescription() const @@ -153,19 +167,12 @@ void DaemonCommand::InitParameters(boost::program_options::options_description& ("config,c", po::value >(), "parse a configuration file") ("no-config,z", "start without a configuration file") ("validate,C", "exit after validating the configuration") - ("errorlog,e", po::value(), "log fatal errors to the specified log file (only works in combination with --daemonize)") + ("errorlog,e", po::value(), "log fatal errors to the specified log file (only works in combination with --daemonize or --close-stdio)") #ifndef _WIN32 ("daemonize,d", "detach from the controlling terminal") + ("close-stdio", "do not log to stdout (or stderr) after startup") #endif /* _WIN32 */ ; - -#ifndef _WIN32 - hiddenDesc.add_options() - ("reload-internal", po::value(), "used internally to implement config reload: do not call manually, send SIGHUP instead"); -#else - hiddenDesc.add_options() - ("restart-service", po::value(), "tries to restart the Icinga 2 service"); -#endif /* _WIN32 */ } std::vector DaemonCommand::GetArgumentSuggestions(const String& argument, const String& word) const @@ -176,128 +183,68 @@ std::vector DaemonCommand::GetArgumentSuggestions(const String& argument return CLICommand::GetArgumentSuggestions(argument, word); } +#ifndef _WIN32 +// The PID of the Icinga umbrella process +pid_t l_UmbrellaPid = 0; + +// Whether the umbrella process allowed us to continue working beyond config validation +static Atomic l_AllowedToWork (false); +#endif /* _WIN32 */ + /** - * The entry point for the "daemon" CLI command. + * Do the actual work (config loading, ...) * - * @returns An exit status. + * @param configs Files to read config from + * @param closeConsoleLog Whether to close the console log after config loading + * @param stderrFile Where to log errors + * + * @return Exit code */ -int DaemonCommand::Run(const po::variables_map& vm, const std::vector& ap) const +static inline +int RunWorker(const std::vector& configs, bool closeConsoleLog = false, const String& stderrFile = String()) { - Logger::EnableTimestamp(); - - Log(LogInformation, "cli") - << "Icinga application loader (version: " << Application::GetAppVersion() -#ifdef I2_DEBUG - << "; debug" -#endif /* I2_DEBUG */ - << ")"; - - if (!vm.count("validate") && !vm.count("reload-internal")) { - pid_t runningpid = Application::ReadPidFile(Application::GetPidPath()); - if (runningpid > 0) { - Log(LogCritical, "cli") - << "Another instance of Icinga already running with PID " << runningpid; - return EXIT_FAILURE; - } - } - - std::vector configs; - if (vm.count("config") > 0) - configs = vm["config"].as >(); - else if (!vm.count("no-config")) - configs.push_back(Application::GetSysconfDir() + "/icinga2/icinga2.conf"); - Log(LogInformation, "cli", "Loading configuration file(s)."); - std::vector newItems; + { + std::vector newItems; - if (!DaemonUtility::LoadConfigFiles(configs, newItems, Application::GetObjectsPath(), Application::GetVarsPath())) - return EXIT_FAILURE; - -#ifdef _WIN32 - if (vm.count("restart-service")) { - SC_HANDLE handleManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); - if (!handleManager) { - Log(LogCritical, "cli") << "Failed to open service manager. Error code: " << GetLastError(); + if (!DaemonUtility::LoadConfigFiles(configs, newItems, Configuration::ObjectsPath, Configuration::VarsPath)) { + Log(LogCritical, "cli", "Config validation failed. Re-run with 'icinga2 daemon -C' after fixing the config."); return EXIT_FAILURE; } - std::string service = vm["restart-service"].as(); - - SC_HANDLE handleService = OpenService(handleManager, service.c_str(), SERVICE_START | SERVICE_STOP); - if (!handleService) { - Log(LogCritical, "cli") << "Failed to open service handle of '" << service << "'. Error code: " << GetLastError(); - return EXIT_FAILURE; - } - - SERVICE_STATUS serviceStatus; - if (!ControlService(handleService, SERVICE_CONTROL_STOP, &serviceStatus)) { - DWORD error = GetLastError(); - if (error = ERROR_SERVICE_NOT_ACTIVE) - Log(LogInformation, "cli") << "Service '" << service << "' is not running."; - else { - Log(LogCritical, "cli") << "Failed to stop service. Error code: " << GetLastError(); - return EXIT_FAILURE; - } - } - - if (!StartService(handleService, 0, NULL)) { - Log(LogCritical, "cli") << "Failed to start up service '" << service << "'. Error code: " << GetLastError(); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; - } -#endif /* _WIN32 */ - - if (vm.count("validate")) { - Log(LogInformation, "cli", "Finished validating the configuration file(s)."); - return EXIT_SUCCESS; - } - #ifndef _WIN32 - if (vm.count("reload-internal")) { - /* We went through validation and now ask the old process kindly to die */ - Log(LogInformation, "cli", "Requesting to take over."); - int rc = kill(vm["reload-internal"].as(), SIGUSR2); - if (rc) { + Log(LogNotice, "cli") + << "Notifying umbrella process (PID " << l_UmbrellaPid << ") about the config loading success"; + + (void)kill(l_UmbrellaPid, SIGUSR2); + + Log(LogNotice, "cli") + << "Waiting for the umbrella process to let us doing the actual work"; + + if (closeConsoleLog) { + CloseStdIO(stderrFile); + Logger::DisableConsoleLog(); + } + + while (!l_AllowedToWork.load()) { + Utility::Sleep(0.2); + } + + Log(LogNotice, "cli") + << "The umbrella process let us continuing"; +#endif /* _WIN32 */ + + /* restore the previous program state */ + try { + ConfigObject::RestoreObjects(Configuration::StatePath); + } catch (const std::exception& ex) { Log(LogCritical, "cli") - << "Failed to send signal to \"" << vm["reload-internal"].as() << "\" with " << strerror(errno); + << "Failed to restore state file: " << DiagnosticInformation(ex); return EXIT_FAILURE; } - double start = Utility::GetTime(); - while (kill(vm["reload-internal"].as(), SIGCHLD) == 0) - Utility::Sleep(0.2); - - Log(LogNotice, "cli") - << "Waited for " << Utility::FormatDuration(Utility::GetTime() - start) << " on old process to exit."; - } -#endif /* _WIN32 */ - - if (vm.count("daemonize")) { - if (!vm.count("reload-internal")) { - // no additional fork neccessary on reload - try { - Daemonize(); - } catch (std::exception&) { - Log(LogCritical, "cli", "Daemonize failed. Exiting."); - return EXIT_FAILURE; - } - } - } - - /* restore the previous program state */ - try { - ConfigObject::RestoreObjects(Application::GetStatePath()); - } catch (const std::exception& ex) { - Log(LogCritical, "cli") - << "Failed to restore state file: " << DiagnosticInformation(ex); - return EXIT_FAILURE; - } - - { - WorkQueue upq(25000, Application::GetConcurrency()); + WorkQueue upq(25000, Configuration::Concurrency); upq.SetName("DaemonCommand::Run"); // activate config only after daemonization: it starts threads and that is not compatible with fork() @@ -307,26 +254,536 @@ int DaemonCommand::Run(const po::variables_map& vm, const std::vector(); - - SetDaemonIO(errorLog); - Logger::DisableConsoleLog(); - } + /* Create the internal API object storage. Do this here too with setups without API. */ + ConfigObjectUtility::CreateStorage(); /* Remove ignored Downtime/Comment objects. */ - ConfigItem::RemoveIgnoredItems(ConfigObjectUtility::GetConfigDir()); - -#ifndef _WIN32 - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = &SigHupHandler; - sigaction(SIGHUP, &sa, nullptr); -#endif /* _WIN32 */ + try { + String configDir = ConfigObjectUtility::GetConfigDir(); + ConfigItem::RemoveIgnoredItems(configDir); + } catch (const std::exception& ex) { + Log(LogNotice, "cli") + << "Cannot clean ignored downtimes/comments: " << ex.what(); + } ApiListener::UpdateObjectAuthority(); return Application::GetInstance()->Run(); } + +#ifndef _WIN32 +/** + * The possible states of a seemless worker being started by StartUnixWorker(). + */ +enum class UnixWorkerState : uint_fast8_t +{ + Pending, + LoadedConfig, + Failed +}; + +// The signals to block temporarily in StartUnixWorker(). +static const sigset_t l_UnixWorkerSignals = ([]() -> sigset_t { + sigset_t s; + + (void)sigemptyset(&s); + (void)sigaddset(&s, SIGCHLD); + (void)sigaddset(&s, SIGUSR1); + (void)sigaddset(&s, SIGUSR2); + (void)sigaddset(&s, SIGINT); + (void)sigaddset(&s, SIGTERM); + (void)sigaddset(&s, SIGHUP); + + return s; +})(); + +// The PID of the seemless worker currently being started by StartUnixWorker() +static Atomic l_CurrentlyStartingUnixWorkerPid (-1); + +// The state of the seemless worker currently being started by StartUnixWorker() +static Atomic l_CurrentlyStartingUnixWorkerState (UnixWorkerState::Pending); + +// The last temination signal we received +static Atomic l_TermSignal (-1); + +// Whether someone requested to re-load config (and we didn't handle that request, yet) +static Atomic l_RequestedReload (false); + +// Whether someone requested to re-open logs (and we didn't handle that request, yet) +static Atomic l_RequestedReopenLogs (false); + +/** + * Umbrella process' signal handlers + */ +static void UmbrellaSignalHandler(int num, siginfo_t *info, void*) +{ + switch (num) { + case SIGUSR1: + // Someone requested to re-open logs + l_RequestedReopenLogs.store(true); + break; + case SIGUSR2: + if (l_CurrentlyStartingUnixWorkerState.load() == UnixWorkerState::Pending + && info->si_pid == l_CurrentlyStartingUnixWorkerPid.load()) { + // The seemless worker currently being started by StartUnixWorker() successfully loaded its config + l_CurrentlyStartingUnixWorkerState.store(UnixWorkerState::LoadedConfig); + } + break; + case SIGCHLD: + if (l_CurrentlyStartingUnixWorkerState.load() == UnixWorkerState::Pending + && info->si_pid == l_CurrentlyStartingUnixWorkerPid.load()) { + // The seemless worker currently being started by StartUnixWorker() failed + l_CurrentlyStartingUnixWorkerState.store(UnixWorkerState::Failed); + } + break; + case SIGINT: + case SIGTERM: + // Someone requested our termination + + { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + + sa.sa_handler = SIG_DFL; + + (void)sigaction(num, &sa, nullptr); + } + + l_TermSignal.store(num); + break; + case SIGHUP: + // Someone requested to re-load config + l_RequestedReload.store(true); + break; + default: + // Programming error (or someone has broken the userspace) + VERIFY(!"Caught unexpected signal"); + } +} + +/** + * Seemless worker's signal handlers + */ +static void WorkerSignalHandler(int num, siginfo_t *info, void*) +{ + switch (num) { + case SIGUSR2: + if (info->si_pid == l_UmbrellaPid) { + // The umbrella process allowed us to continue working beyond config validation + l_AllowedToWork.store(true); + } + break; + case SIGINT: + case SIGTERM: + if (info->si_pid == l_UmbrellaPid) { + // The umbrella process requested our termination + Application::RequestShutdown(); + } + break; + default: + // Programming error (or someone has broken the userspace) + VERIFY(!"Caught unexpected signal"); + } +} + +#ifdef HAVE_SYSTEMD +// When we last notified the watchdog. +static Atomic l_LastNotifiedWatchdog (0); + +/** + * Notify the watchdog if not notified during the last 2.5s. + */ +static void NotifyWatchdog() +{ + double now = Utility::GetTime(); + + if (now - l_LastNotifiedWatchdog.load() >= 2.5) { + sd_notify(0, "WATCHDOG=1"); + l_LastNotifiedWatchdog.store(now); + } +} +#endif /* HAVE_SYSTEMD */ + +/** + * Starts seemless worker process doing the actual work (config loading, ...) + * + * @param configs Files to read config from + * @param closeConsoleLog Whether to close the console log after config loading + * @param stderrFile Where to log errors + * + * @return The worker's PID on success, -1 on failure (if the worker couldn't load its config) + */ +static pid_t StartUnixWorker(const std::vector& configs, bool closeConsoleLog = false, const String& stderrFile = String()) +{ + Log(LogNotice, "cli") + << "Spawning seemless worker process doing the actual work"; + + try { + Application::UninitializeBase(); + } catch (const std::exception& ex) { + Log(LogCritical, "cli") + << "Failed to stop thread pool before forking, unexpected error: " << DiagnosticInformation(ex); + exit(EXIT_FAILURE); + } + + /* Block the signal handlers we'd like to change in the child process until we changed them. + * Block SIGUSR2 and SIGCHLD handlers until we've set l_CurrentlyStartingUnixWorkerPid. + */ + (void)sigprocmask(SIG_BLOCK, &l_UnixWorkerSignals, nullptr); + + pid_t pid = fork(); + + switch (pid) { + case -1: + Log(LogCritical, "cli") + << "fork() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; + exit(EXIT_FAILURE); + + case 0: + try { + { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + + sa.sa_handler = SIG_DFL; + + (void)sigaction(SIGCHLD, &sa, nullptr); + (void)sigaction(SIGUSR1, &sa, nullptr); + (void)sigaction(SIGHUP, &sa, nullptr); + } + + { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + + sa.sa_sigaction = &WorkerSignalHandler; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + + (void)sigaction(SIGUSR2, &sa, nullptr); + (void)sigaction(SIGINT, &sa, nullptr); + (void)sigaction(SIGTERM, &sa, nullptr); + } + + (void)sigprocmask(SIG_UNBLOCK, &l_UnixWorkerSignals, nullptr); + + try { + Application::InitializeBase(); + } catch (const std::exception& ex) { + Log(LogCritical, "cli") + << "Failed to re-initialize thread pool after forking (child): " << DiagnosticInformation(ex); + _exit(EXIT_FAILURE); + } + + _exit(RunWorker(configs, closeConsoleLog, stderrFile)); + } catch (...) { + _exit(EXIT_FAILURE); + } + + default: + l_CurrentlyStartingUnixWorkerPid.store(pid); + (void)sigprocmask(SIG_UNBLOCK, &l_UnixWorkerSignals, nullptr); + + Log(LogNotice, "cli") + << "Spawned worker process (PID " << pid << "), waiting for it to load its config"; + + // Wait for the newly spawned process to either load its config or fail. + for (;;) { +#ifdef HAVE_SYSTEMD + NotifyWatchdog(); +#endif /* HAVE_SYSTEMD */ + + switch (l_CurrentlyStartingUnixWorkerState.load()) { + case UnixWorkerState::LoadedConfig: + Log(LogNotice, "cli") + << "Worker process successfully loaded its config"; + break; + case UnixWorkerState::Failed: + Log(LogNotice, "cli") + << "Worker process couldn't load its config"; + + while (waitpid(pid, nullptr, 0) == -1 && errno == EINTR) { +#ifdef HAVE_SYSTEMD + NotifyWatchdog(); +#endif /* HAVE_SYSTEMD */ + } + pid = -1; + break; + default: + Utility::Sleep(0.2); + continue; + } + + break; + } + + // Reset flags for the next time + l_CurrentlyStartingUnixWorkerPid.store(-1); + l_CurrentlyStartingUnixWorkerState.store(UnixWorkerState::Pending); + + try { + Application::InitializeBase(); + } catch (const std::exception& ex) { + Log(LogCritical, "cli") + << "Failed to re-initialize thread pool after forking (parent): " << DiagnosticInformation(ex); + exit(EXIT_FAILURE); + } + } + + return pid; +} + +/** + * Workaround to instantiate Application (which is abstract) in DaemonCommand#Run() + */ +class PidFileManagementApp : public Application +{ +public: + inline int Main() override + { + return EXIT_FAILURE; + } +}; +#endif /* _WIN32 */ + +/** + * The entry point for the "daemon" CLI command. + * + * @returns An exit status. + */ +int DaemonCommand::Run(const po::variables_map& vm, const std::vector& ap) const +{ +#ifdef _WIN32 + SetConsoleOutputCP(65001); +#endif /* _WIN32 */ + + Logger::EnableTimestamp(); + + Log(LogInformation, "cli") + << "Icinga application loader (version: " << Application::GetAppVersion() +#ifdef I2_DEBUG + << "; debug" +#endif /* I2_DEBUG */ + << ")"; + + std::vector configs; + if (vm.count("config") > 0) + configs = vm["config"].as >(); + else if (!vm.count("no-config")) { + /* The implicit string assignment is needed for Windows builds. */ + String configDir = Configuration::ConfigDir; + configs.push_back(configDir + "/icinga2.conf"); + } + + if (vm.count("validate")) { + Log(LogInformation, "cli", "Loading configuration file(s)."); + + std::vector newItems; + + if (!DaemonUtility::LoadConfigFiles(configs, newItems, Configuration::ObjectsPath, Configuration::VarsPath)) { + Log(LogCritical, "cli", "Config validation failed. Re-run with 'icinga2 daemon -C' after fixing the config."); + return EXIT_FAILURE; + } + + Log(LogInformation, "cli", "Finished validating the configuration file(s)."); + return EXIT_SUCCESS; + } + + { + pid_t runningpid = Application::ReadPidFile(Configuration::PidPath); + if (runningpid > 0) { + Log(LogCritical, "cli") + << "Another instance of Icinga already running with PID " << runningpid; + return EXIT_FAILURE; + } + } + + if (vm.count("daemonize")) { + // this subroutine either succeeds, or logs an error + // and terminates the process (does not return). + Daemonize(); + } + +#ifndef _WIN32 + /* The Application manages the PID file, + * but on *nix this process doesn't load any config + * so there's no central Application instance. + */ + PidFileManagementApp app; + + try { + app.UpdatePidFile(Configuration::PidPath); + } catch (const std::exception&) { + Log(LogCritical, "Application") + << "Cannot update PID file '" << Configuration::PidPath << "'. Aborting."; + return EXIT_FAILURE; + } + + Defer closePidFile ([&app]() { + app.ClosePidFile(true); + }); +#endif /* _WIN32 */ + + if (vm.count("daemonize")) { + // After disabling the console log, any further errors will go to the configured log only. + // Let's try to make this clear and say good bye. + Log(LogInformation, "cli", "Closing console log."); + + String errorLog; + if (vm.count("errorlog")) + errorLog = vm["errorlog"].as(); + + CloseStdIO(errorLog); + Logger::DisableConsoleLog(); + } + +#ifdef _WIN32 + return RunWorker(configs); +#else /* _WIN32 */ + l_UmbrellaPid = getpid(); + Application::SetUmbrellaProcess(l_UmbrellaPid); + + { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + + sa.sa_sigaction = &UmbrellaSignalHandler; + sa.sa_flags = SA_NOCLDSTOP | SA_RESTART | SA_SIGINFO; + + (void)sigaction(SIGCHLD, &sa, nullptr); + (void)sigaction(SIGUSR1, &sa, nullptr); + (void)sigaction(SIGUSR2, &sa, nullptr); + (void)sigaction(SIGINT, &sa, nullptr); + (void)sigaction(SIGTERM, &sa, nullptr); + (void)sigaction(SIGHUP, &sa, nullptr); + } + + bool closeConsoleLog = !vm.count("daemonize") && vm.count("close-stdio"); + + String errorLog; + if (vm.count("errorlog")) + errorLog = vm["errorlog"].as(); + + // The PID of the current seemless worker + pid_t currentWorker = StartUnixWorker(configs, closeConsoleLog, errorLog); + + if (currentWorker == -1) { + return EXIT_FAILURE; + } + + if (closeConsoleLog) { + // After disabling the console log, any further errors will go to the configured log only. + // Let's try to make this clear and say good bye. + Log(LogInformation, "cli", "Closing console log."); + + CloseStdIO(errorLog); + Logger::DisableConsoleLog(); + } + + // Immediately allow the first (non-reload) worker to continue working beyond config validation + (void)kill(currentWorker, SIGUSR2); + +#ifdef HAVE_SYSTEMD + sd_notify(0, "READY=1"); +#endif /* HAVE_SYSTEMD */ + + // Whether we already forwarded a termination signal to the seemless worker + bool requestedTermination = false; + + // Whether we already notified systemd about our termination + bool notifiedTermination = false; + + for (;;) { +#ifdef HAVE_SYSTEMD + NotifyWatchdog(); +#endif /* HAVE_SYSTEMD */ + + if (!requestedTermination) { + int termSig = l_TermSignal.load(); + if (termSig != -1) { + Log(LogNotice, "cli") + << "Got signal " << termSig << ", forwarding to seemless worker (PID " << currentWorker << ")"; + + (void)kill(currentWorker, termSig); + requestedTermination = true; + +#ifdef HAVE_SYSTEMD + if (!notifiedTermination) { + notifiedTermination = true; + sd_notify(0, "STOPPING=1"); + } +#endif /* HAVE_SYSTEMD */ + } + } + + if (l_RequestedReload.exchange(false)) { + Log(LogInformation, "Application") + << "Got reload command: Starting new instance."; + +#ifdef HAVE_SYSTEMD + sd_notify(0, "RELOADING=1"); +#endif /* HAVE_SYSTEMD */ + + pid_t nextWorker = StartUnixWorker(configs); + + if (nextWorker == -1) { + Log(LogCritical, "Application", "Found error in config: reloading aborted"); + } else { + Log(LogInformation, "Application") + << "Reload done, old process shutting down. Child process with PID '" << nextWorker << "' is taking over."; + + (void)kill(currentWorker, SIGTERM); + + { + double start = Utility::GetTime(); + + while (waitpid(currentWorker, nullptr, 0) == -1 && errno == EINTR) { +#ifdef HAVE_SYSTEMD + NotifyWatchdog(); +#endif /* HAVE_SYSTEMD */ + } + + Log(LogNotice, "cli") + << "Waited for " << Utility::FormatDuration(Utility::GetTime() - start) << " on old process to exit."; + } + + // Old instance shut down, allow the new one to continue working beyond config validation + (void)kill(nextWorker, SIGUSR2); + + currentWorker = nextWorker; + } + +#ifdef HAVE_SYSTEMD + sd_notify(0, "READY=1"); +#endif /* HAVE_SYSTEMD */ + + } + + if (l_RequestedReopenLogs.exchange(false)) { + Log(LogNotice, "cli") + << "Got signal " << SIGUSR1 << ", forwarding to seemless worker (PID " << currentWorker << ")"; + + (void)kill(currentWorker, SIGUSR1); + } + + { + int status; + if (waitpid(currentWorker, &status, WNOHANG) > 0) { + Log(LogNotice, "cli") + << "Seemless worker (PID " << currentWorker << ") stopped, stopping as well"; + +#ifdef HAVE_SYSTEMD + if (!notifiedTermination) { + notifiedTermination = true; + sd_notify(0, "STOPPING=1"); + } +#endif /* HAVE_SYSTEMD */ + + // If killed by signal, forward it via the exit code (to be as seemless as possible) + return WIFSIGNALED(status) ? 128 + WTERMSIG(status) : WEXITSTATUS(status); + } + } + + Utility::Sleep(0.2); + } +#endif /* _WIN32 */ +} diff --git a/lib/cli/daemoncommand.hpp b/lib/cli/daemoncommand.hpp index 7fafe6d10..da8a34bd3 100644 --- a/lib/cli/daemoncommand.hpp +++ b/lib/cli/daemoncommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DAEMONCOMMAND_H #define DAEMONCOMMAND_H diff --git a/lib/cli/daemonutility.cpp b/lib/cli/daemonutility.cpp index 432fe3d45..d5bb2cf1e 100644 --- a/lib/cli/daemonutility.cpp +++ b/lib/cli/daemonutility.cpp @@ -1,31 +1,14 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/daemonutility.hpp" #include "base/utility.hpp" #include "base/logger.hpp" #include "base/application.hpp" +#include "base/scriptglobal.hpp" #include "config/configcompiler.hpp" #include "config/configcompilercontext.hpp" #include "config/configitembuilder.hpp" - using namespace icinga; static bool ExecuteExpression(Expression *expression) @@ -48,6 +31,15 @@ static void IncludeZoneDirRecursive(const String& path, const String& package, b { String zoneName = Utility::BaseName(path); + /* We don't have an activated zone object yet. We may forcefully guess from configitems + * to not include this specific synced zones directory. + */ + if(!ConfigItem::GetByTypeAndName(Type::GetByName("Zone"), zoneName)) { + Log(LogWarning, "config") + << "Ignoring directory '" << path << "' for unknown zone '" << zoneName << "'."; + return; + } + /* register this zone path for cluster config sync */ ConfigCompiler::RegisterZoneDir("_etc", path, zoneName); @@ -65,6 +57,15 @@ static void IncludeNonLocalZone(const String& zonePath, const String& package, b String zoneName = Utility::BaseName(zonePath); + /* We don't have an activated zone object yet. We may forcefully guess from configitems + * to not include this specific synced zones directory. + */ + if(!ConfigItem::GetByTypeAndName(Type::GetByName("Zone"), zoneName)) { + Log(LogWarning, "config") + << "Ignoring directory '" << zonePath << "' for unknown zone '" << zoneName << "'."; + return; + } + /* Check whether this node already has an authoritative config version * from zones.d in etc or api package directory, or a local marker file) */ @@ -99,6 +100,10 @@ static void IncludePackage(const String& packagePath, bool& success) bool DaemonUtility::ValidateConfigFiles(const std::vector& configs, const String& objectsFile) { bool success; + + Namespace::Ptr systemNS = ScriptGlobal::Get("System"); + VERIFY(systemNS); + if (!objectsFile.IsEmpty()) ConfigCompilerContext::GetInstance()->OpenObjectsFile(objectsFile); @@ -121,31 +126,48 @@ bool DaemonUtility::ValidateConfigFiles(const std::vector& configs, * unfortunately moving it there is somewhat non-trivial. */ success = true; - String zonesEtcDir = Application::GetZonesDir(); - if (!zonesEtcDir.IsEmpty() && Utility::PathExists(zonesEtcDir)) - Utility::Glob(zonesEtcDir + "/*", std::bind(&IncludeZoneDirRecursive, _1, "_etc", std::ref(success)), GlobDirectory); + /* Only load zone directory if we're not in staging validation. */ + if (!systemNS->Contains("ZonesStageVarDir")) { + String zonesEtcDir = Configuration::ZonesDir; + if (!zonesEtcDir.IsEmpty() && Utility::PathExists(zonesEtcDir)) + Utility::Glob(zonesEtcDir + "/*", std::bind(&IncludeZoneDirRecursive, _1, "_etc", std::ref(success)), GlobDirectory); - if (!success) - return false; + if (!success) + return false; + } /* Load package config files - they may contain additional zones which * are authoritative on this node and are checked in HasZoneConfigAuthority(). */ - String packagesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/packages"; + String packagesVarDir = Configuration::DataDir + "/api/packages"; if (Utility::PathExists(packagesVarDir)) Utility::Glob(packagesVarDir + "/*", std::bind(&IncludePackage, _1, std::ref(success)), GlobDirectory); if (!success) return false; - /* Load cluster synchronized configuration files */ - String zonesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones"; + /* Load cluster synchronized configuration files. This can be overridden for staged sync validations. */ + String zonesVarDir = Configuration::DataDir + "/api/zones"; + + /* Cluster config sync stage validation needs this. */ + if (systemNS->Contains("ZonesStageVarDir")) { + zonesVarDir = systemNS->Get("ZonesStageVarDir"); + + Log(LogNotice, "DaemonUtility") + << "Overriding zones var directory with '" << zonesVarDir << "' for cluster config sync staging."; + } + + if (Utility::PathExists(zonesVarDir)) Utility::Glob(zonesVarDir + "/*", std::bind(&IncludeNonLocalZone, _1, "_cluster", std::ref(success)), GlobDirectory); if (!success) return false; - Type::Ptr appType = Type::GetByName(ScriptGlobal::Get("ApplicationType", &Empty)); + /* This is initialized inside the IcingaApplication class. */ + Value vAppType; + VERIFY(systemNS->Get("ApplicationType", &vAppType)); + + Type::Ptr appType = Type::GetByName(vAppType); if (ConfigItem::GetItems(appType).empty()) { ConfigItemBuilder builder; @@ -170,7 +192,7 @@ bool DaemonUtility::LoadConfigFiles(const std::vector& configs, return false; } - WorkQueue upq(25000, Application::GetConcurrency()); + WorkQueue upq(25000, Configuration::Concurrency); upq.SetName("DaemonUtility::LoadConfigFiles"); bool result = ConfigItem::CommitItems(ascope.GetContext(), upq, newItems); diff --git a/lib/cli/daemonutility.hpp b/lib/cli/daemonutility.hpp index ffd8f04b3..963bfba74 100644 --- a/lib/cli/daemonutility.hpp +++ b/lib/cli/daemonutility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DAEMONUTILITY_H #define DAEMONUTILITY_H diff --git a/lib/cli/editline.hpp b/lib/cli/editline.hpp index f284c207b..f97525edb 100644 --- a/lib/cli/editline.hpp +++ b/lib/cli/editline.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef EDITLINE_H #define EDITLINE_H @@ -24,6 +7,7 @@ extern "C" { char *readline(const char *prompt); int add_history(const char *line); +void rl_deprep_terminal(); typedef char *ELFunction(const char *, int); diff --git a/lib/cli/featuredisablecommand.cpp b/lib/cli/featuredisablecommand.cpp index 54a702c3d..95a4a26d7 100644 --- a/lib/cli/featuredisablecommand.cpp +++ b/lib/cli/featuredisablecommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/featuredisablecommand.hpp" #include "cli/featureutility.hpp" @@ -53,7 +36,7 @@ int FeatureDisableCommand::GetMaxArguments() const ImpersonationLevel FeatureDisableCommand::GetImpersonationLevel() const { - return ImpersonateRoot; + return ImpersonateIcinga; } /** diff --git a/lib/cli/featuredisablecommand.hpp b/lib/cli/featuredisablecommand.hpp index 7dc5465d2..b24655de6 100644 --- a/lib/cli/featuredisablecommand.hpp +++ b/lib/cli/featuredisablecommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef FEATUREDISABLECOMMAND_H #define FEATUREDISABLECOMMAND_H diff --git a/lib/cli/featureenablecommand.cpp b/lib/cli/featureenablecommand.cpp index 244bbdb95..0cf906648 100644 --- a/lib/cli/featureenablecommand.cpp +++ b/lib/cli/featureenablecommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/featureenablecommand.hpp" #include "cli/featureutility.hpp" @@ -53,7 +36,7 @@ int FeatureEnableCommand::GetMaxArguments() const ImpersonationLevel FeatureEnableCommand::GetImpersonationLevel() const { - return ImpersonateRoot; + return ImpersonateIcinga; } /** diff --git a/lib/cli/featureenablecommand.hpp b/lib/cli/featureenablecommand.hpp index a81ecc2c9..fc917787d 100644 --- a/lib/cli/featureenablecommand.hpp +++ b/lib/cli/featureenablecommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef FEATUREENABLECOMMAND_H #define FEATUREENABLECOMMAND_H diff --git a/lib/cli/featurelistcommand.cpp b/lib/cli/featurelistcommand.cpp index 33e4e96ad..2aad4a9cd 100644 --- a/lib/cli/featurelistcommand.cpp +++ b/lib/cli/featurelistcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/featurelistcommand.hpp" #include "cli/featureutility.hpp" diff --git a/lib/cli/featurelistcommand.hpp b/lib/cli/featurelistcommand.hpp index 46f357c75..cae1d740b 100644 --- a/lib/cli/featurelistcommand.hpp +++ b/lib/cli/featurelistcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef FEATURELISTCOMMAND_H #define FEATURELISTCOMMAND_H diff --git a/lib/cli/featureutility.cpp b/lib/cli/featureutility.cpp index 9056b60fe..a882104f6 100644 --- a/lib/cli/featureutility.cpp +++ b/lib/cli/featureutility.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/featureutility.hpp" #include "base/logger.hpp" @@ -31,12 +14,12 @@ using namespace icinga; String FeatureUtility::GetFeaturesAvailablePath() { - return Application::GetSysconfDir() + "/icinga2/features-available"; + return Configuration::ConfigDir + "/features-available"; } String FeatureUtility::GetFeaturesEnabledPath() { - return Application::GetSysconfDir() + "/icinga2/features-enabled"; + return Configuration::ConfigDir + "/features-enabled"; } std::vector FeatureUtility::GetFieldCompletionSuggestions(const String& word, bool enable) diff --git a/lib/cli/featureutility.hpp b/lib/cli/featureutility.hpp index 38f91cce0..9cb2128c6 100644 --- a/lib/cli/featureutility.hpp +++ b/lib/cli/featureutility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef FEATUREUTILITY_H #define FEATUREUTILITY_H diff --git a/lib/cli/i2-cli.hpp b/lib/cli/i2-cli.hpp index c23e2b875..86e5ddd14 100644 --- a/lib/cli/i2-cli.hpp +++ b/lib/cli/i2-cli.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef I2CLI_H #define I2CLI_H diff --git a/lib/cli/internalsignalcommand.cpp b/lib/cli/internalsignalcommand.cpp index 3e196728d..b09796569 100644 --- a/lib/cli/internalsignalcommand.cpp +++ b/lib/cli/internalsignalcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/internalsignalcommand.hpp" #include "base/logger.hpp" diff --git a/lib/cli/internalsignalcommand.hpp b/lib/cli/internalsignalcommand.hpp index 44830ccd9..d599b80cd 100644 --- a/lib/cli/internalsignalcommand.hpp +++ b/lib/cli/internalsignalcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef INTERNALSIGNALCOMMAND_H #define INTERNALSIGNALCOMMAND_H diff --git a/lib/cli/nodesetupcommand.cpp b/lib/cli/nodesetupcommand.cpp index fa188e7f0..ad21f1d72 100644 --- a/lib/cli/nodesetupcommand.cpp +++ b/lib/cli/nodesetupcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/nodesetupcommand.hpp" #include "cli/nodeutility.hpp" @@ -88,7 +71,7 @@ std::vector NodeSetupCommand::GetArgumentSuggestions(const String& argum ImpersonationLevel NodeSetupCommand::GetImpersonationLevel() const { - return ImpersonateRoot; + return ImpersonateIcinga; } /** @@ -121,12 +104,6 @@ int NodeSetupCommand::SetupMaster(const boost::program_options::variables_map& v if (vm.count("trustedcert")) Log(LogWarning, "cli", "Master for Node setup: Ignoring --trustedcert"); - if (vm.count("accept-config")) - Log(LogWarning, "cli", "Master for Node setup: Ignoring --accept-config"); - - if (vm.count("accept-commands")) - Log(LogWarning, "cli", "Master for Node setup: Ignoring --accept-commands"); - String cn = Utility::GetFQDN(); if (vm.count("cn")) @@ -176,7 +153,7 @@ int NodeSetupCommand::SetupMaster(const boost::program_options::variables_map& v if (vm.count("global_zones")) setupGlobalZones = vm["global_zones"].as >(); - for (int i = 0; i < setupGlobalZones.size(); i++) { + for (decltype(setupGlobalZones.size()) i = 0; i < setupGlobalZones.size(); i++) { if (std::find(globalZones.begin(), globalZones.end(), setupGlobalZones[i]) != globalZones.end()) { Log(LogCritical, "cli") << "The global zone '" << setupGlobalZones[i] << "' is already specified."; @@ -212,31 +189,34 @@ int NodeSetupCommand::SetupMaster(const boost::program_options::variables_map& v fp << " bind_port = " << tokens[1] << "\n"; } + fp << "\n"; + + if (vm.count("accept-config")) + fp << " accept_config = true\n"; + else + fp << " accept_config = false\n"; + + if (vm.count("accept-commands")) + fp << " accept_commands = true\n"; + else + fp << " accept_commands = false\n"; + fp << "\n" << " ticket_salt = TicketSalt\n" << "}\n"; fp.close(); -#ifdef _WIN32 - _unlink(apipath.CStr()); -#endif /* _WIN32 */ - - if (rename(tempApiPath.CStr(), apipath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempApiPath)); - } + Utility::RenameFile(tempApiPath, apipath); /* update constants.conf with NodeName = CN + TicketSalt = random value */ - if (cn != Utility::GetFQDN()) { + if (endpointName != Utility::GetFQDN()) { Log(LogWarning, "cli") - << "CN '" << cn << "' does not match the default FQDN '" << Utility::GetFQDN() << "'. Requires update for NodeName constant in constants.conf!"; + << "CN/Endpoint name '" << endpointName << "' does not match the default FQDN '" << Utility::GetFQDN() << "'. Requires update for NodeName constant in constants.conf!"; } - NodeUtility::UpdateConstant("NodeName", cn); - NodeUtility::UpdateConstant("ZoneName", cn); + NodeUtility::UpdateConstant("NodeName", endpointName); + NodeUtility::UpdateConstant("ZoneName", zoneName); String salt = RandomString(16); @@ -262,7 +242,7 @@ int NodeSetupCommand::SetupMaster(const boost::program_options::variables_map& v NodeUtility::UpdateConfiguration("\"conf.d/api-users.conf\"", true, false); } else { Log(LogWarning, "cli") - << "Included file dosen't exist " << apiUsersFilePath; + << "Included file doesn't exist " << apiUsersFilePath; } } @@ -353,8 +333,8 @@ int NodeSetupCommand::SetupNode(const boost::program_options::variables_map& vm, String certsDir = ApiListener::GetCertsDir(); Utility::MkDirP(certsDir, 0700); - String user = ScriptGlobal::Get("RunAsUser"); - String group = ScriptGlobal::Get("RunAsGroup"); + String user = Configuration::RunAsUser; + String group = Configuration::RunAsGroup; if (!Utility::SetFileOwnership(certsDir, user, group)) { Log(LogWarning, "cli") @@ -410,7 +390,7 @@ int NodeSetupCommand::SetupNode(const boost::program_options::variables_map& vm, } else { /* We cannot retrieve the parent certificate. * Tell the user to manually copy the ca.crt file - * into LocalStateDir + "/lib/icinga2/certs" + * into DataDir + "/certs" */ Log(LogWarning, "cli") << "\nNo connection to the parent node was specified.\n\n" @@ -483,17 +463,7 @@ int NodeSetupCommand::SetupNode(const boost::program_options::variables_map& vm, fp.close(); -#ifdef _WIN32 - _unlink(apipath.CStr()); -#endif /* _WIN32 */ - - if (rename(tempApiPath.CStr(), apipath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempApiPath)); - } - + Utility::RenameFile(tempApiPath, apipath); /* Generate zones configuration. */ Log(LogInformation, "cli", "Generating zone and object configuration."); @@ -516,7 +486,7 @@ int NodeSetupCommand::SetupNode(const boost::program_options::variables_map& vm, if (vm.count("global_zones")) setupGlobalZones = vm["global_zones"].as >(); - for (int i = 0; i < setupGlobalZones.size(); i++) { + for (decltype(setupGlobalZones.size()) i = 0; i < setupGlobalZones.size(); i++) { if (std::find(globalZones.begin(), globalZones.end(), setupGlobalZones[i]) != globalZones.end()) { Log(LogCritical, "cli") << "The global zone '" << setupGlobalZones[i] << "' is already specified."; @@ -530,13 +500,14 @@ int NodeSetupCommand::SetupNode(const boost::program_options::variables_map& vm, NodeUtility::GenerateNodeIcingaConfig(endpointName, zoneName, parentZoneName, vm["endpoint"].as >(), globalZones); /* update constants.conf with NodeName = CN */ - if (cn != Utility::GetFQDN()) { + if (endpointName != Utility::GetFQDN()) { Log(LogWarning, "cli") - << "CN '" << cn << "' does not match the default FQDN '" << Utility::GetFQDN() << "'. Requires an update for the NodeName constant in constants.conf!"; + << "CN/Endpoint name '" << endpointName << "' does not match the default FQDN '" + << Utility::GetFQDN() << "'. Requires an update for the NodeName constant in constants.conf!"; } - NodeUtility::UpdateConstant("NodeName", cn); - NodeUtility::UpdateConstant("ZoneName", vm["zone"].as()); + NodeUtility::UpdateConstant("NodeName", endpointName); + NodeUtility::UpdateConstant("ZoneName", zoneName); if (!ticket.IsEmpty()) { String ticketPath = ApiListener::GetCertsDir() + "/ticket"; @@ -554,16 +525,7 @@ int NodeSetupCommand::SetupNode(const boost::program_options::variables_map& vm, fp.close(); -#ifdef _WIN32 - _unlink(ticketPath.CStr()); -#endif /* _WIN32 */ - - if (rename(tempTicketPath.CStr(), ticketPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempTicketPath)); - } + Utility::RenameFile(tempTicketPath, ticketPath); } /* If no parent connection was made, the user must supply the ca.crt before restarting Icinga 2.*/ diff --git a/lib/cli/nodesetupcommand.hpp b/lib/cli/nodesetupcommand.hpp index 1e3560a3b..d25d21e11 100644 --- a/lib/cli/nodesetupcommand.hpp +++ b/lib/cli/nodesetupcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef NODESETUPCOMMAND_H #define NODESETUPCOMMAND_H diff --git a/lib/cli/nodeutility.cpp b/lib/cli/nodeutility.cpp index eb9080b97..bd343ab4c 100644 --- a/lib/cli/nodeutility.cpp +++ b/lib/cli/nodeutility.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/nodeutility.hpp" #include "cli/clicommand.hpp" @@ -43,12 +26,12 @@ using namespace icinga; String NodeUtility::GetConstantsConfPath() { - return Application::GetSysconfDir() + "/icinga2/constants.conf"; + return Configuration::ConfigDir + "/constants.conf"; } String NodeUtility::GetZonesConfPath() { - return Application::GetSysconfDir() + "/icinga2/zones.conf"; + return Configuration::ConfigDir + "/zones.conf"; } /* @@ -170,8 +153,8 @@ bool NodeUtility::WriteNodeConfigObjects(const String& filename, const Array::Pt Utility::MkDirP(path, 0755); - String user = ScriptGlobal::Get("RunAsUser"); - String group = ScriptGlobal::Get("RunAsGroup"); + String user = Configuration::RunAsUser; + String group = Configuration::RunAsGroup; if (!Utility::SetFileOwnership(path, user, group)) { Log(LogWarning, "cli") @@ -198,16 +181,7 @@ bool NodeUtility::WriteNodeConfigObjects(const String& filename, const Array::Pt fp << std::endl; fp.close(); -#ifdef _WIN32 - _unlink(filename.CStr()); -#endif /* _WIN32 */ - - if (rename(tempFilename.CStr(), filename.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempFilename)); - } + Utility::RenameFile(tempFilename, filename); return true; } @@ -265,6 +239,49 @@ void NodeUtility::SerializeObject(std::ostream& fp, const Dictionary::Ptr& objec fp << "}\n\n"; } +/* +* Returns true if the include is found, otherwise false +*/ +bool NodeUtility::GetConfigurationIncludeState(const String& value, bool recursive) { + String configurationFile = Configuration::ConfigDir + "/icinga2.conf"; + + Log(LogInformation, "cli") + << "Reading '" << configurationFile << "'."; + + std::ifstream ifp(configurationFile.CStr()); + + String affectedInclude = value; + + if (recursive) + affectedInclude = "include_recursive " + affectedInclude; + else + affectedInclude = "include " + affectedInclude; + + bool isIncluded = false; + + std::string line; + + while(std::getline(ifp, line)) { + /* + * Trying to find if the inclusion is enabled. + * First hit breaks out of the loop. + */ + + if (line.compare(0, affectedInclude.GetLength(), affectedInclude) == 0) { + isIncluded = true; + + /* + * We can safely break out here, since an enabled include always win. + */ + break; + } + } + + ifp.close(); + + return isIncluded; +} + /* * include = false, will comment out the include statement * include = true, will add an include statement or uncomment a statement if one is existing @@ -274,7 +291,7 @@ void NodeUtility::SerializeObject(std::ostream& fp, const Dictionary::Ptr& objec */ bool NodeUtility::UpdateConfiguration(const String& value, bool include, bool recursive) { - String configurationFile = Application::GetSysconfDir() + "/icinga2/icinga2.conf"; + String configurationFile = Configuration::ConfigDir + "/icinga2.conf"; Log(LogInformation, "cli") << "Updating '" << value << "' include in '" << configurationFile << "'."; @@ -334,16 +351,7 @@ bool NodeUtility::UpdateConfiguration(const String& value, bool include, bool re ifp.close(); ofp.close(); -#ifdef _WIN32 - _unlink(configurationFile.CStr()); -#endif /* _WIN32 */ - - if (rename(tempFile.CStr(), configurationFile.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(configurationFile)); - } + Utility::RenameFile(tempFile, configurationFile); return (found || include); } @@ -378,14 +386,5 @@ void NodeUtility::UpdateConstant(const String& name, const String& value) ifp.close(); ofp.close(); -#ifdef _WIN32 - _unlink(constantsConfPath.CStr()); -#endif /* _WIN32 */ - - if (rename(tempFile.CStr(), constantsConfPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(constantsConfPath)); - } + Utility::RenameFile(tempFile, constantsConfPath); } diff --git a/lib/cli/nodeutility.hpp b/lib/cli/nodeutility.hpp index 3017a8ead..7016b6bae 100644 --- a/lib/cli/nodeutility.hpp +++ b/lib/cli/nodeutility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef NODEUTILITY_H #define NODEUTILITY_H @@ -44,6 +27,7 @@ public: static bool WriteNodeConfigObjects(const String& filename, const Array::Ptr& objects); + static bool GetConfigurationIncludeState(const String& value, bool recursive); static bool UpdateConfiguration(const String& value, bool include, bool recursive); static void UpdateConstant(const String& name, const String& value); diff --git a/lib/cli/nodewizardcommand.cpp b/lib/cli/nodewizardcommand.cpp index b5e143730..abf87d87e 100644 --- a/lib/cli/nodewizardcommand.cpp +++ b/lib/cli/nodewizardcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/nodewizardcommand.hpp" #include "cli/nodeutility.hpp" @@ -54,7 +37,7 @@ String NodeWizardCommand::GetShortDescription() const ImpersonationLevel NodeWizardCommand::GetImpersonationLevel() const { - return ImpersonateRoot; + return ImpersonateIcinga; } int NodeWizardCommand::GetMaxArguments() const @@ -103,15 +86,15 @@ int NodeWizardCommand::Run(const boost::program_options::variables_map& vm, * 8. copy key information to /var/lib/icinga2/certs * 9. enable ApiListener feature * 10. generate zones.conf with endpoints and zone objects - * 11. set NodeName = cn in constants.conf + * 11. set NodeName = cn and ZoneName in constants.conf * 12. disable conf.d directory? * 13. reload icinga2, or tell the user to */ std::string answer; - /* master or satellite/client setup */ + /* master or satellite/agent setup */ std::cout << ConsoleColorTag(Console_Bold) - << "Please specify if this is a satellite/client setup " + << "Please specify if this is an agent/satellite setup " << "('n' installs a master setup)" << ConsoleColorTag(Console_Normal) << " [Y/n]: "; std::getline (std::cin, answer); @@ -127,7 +110,7 @@ int NodeWizardCommand::Run(const boost::program_options::variables_map& vm, if (choice.Contains("n")) res = MasterSetup(); else - res = ClientSetup(); + res = AgentSatelliteSetup(); if (res != 0) return res; @@ -144,13 +127,13 @@ int NodeWizardCommand::Run(const boost::program_options::variables_map& vm, return 0; } -int NodeWizardCommand::ClientSetup() const +int NodeWizardCommand::AgentSatelliteSetup() const { std::string answer; String choice; bool connectToParent = false; - std::cout << "Starting the Client/Satellite setup routine...\n\n"; + std::cout << "Starting the Agent/Satellite setup routine...\n\n"; /* CN */ std::cout << ConsoleColorTag(Console_Bold) @@ -272,8 +255,8 @@ wizard_endpoint_loop_start: String certsDir = ApiListener::GetCertsDir(); Utility::MkDirP(certsDir, 0700); - String user = ScriptGlobal::Get("RunAsUser"); - String group = ScriptGlobal::Get("RunAsGroup"); + String user = Configuration::RunAsUser; + String group = Configuration::RunAsGroup; if (!Utility::SetFileOwnership(certsDir, user, group)) { Log(LogWarning, "cli") @@ -392,7 +375,7 @@ wizard_ticket: } else { /* We cannot retrieve the parent certificate. * Tell the user to manually copy the ca.crt file - * into LocalStateDir + "/lib/icinga2/certs" + * into DataDir + "/certs" */ std::cout << ConsoleColorTag(Console_Bold) @@ -456,7 +439,7 @@ wizard_ticket: << "Reconfiguring Icinga...\n" << ConsoleColorTag(Console_Normal); - /* disable the notifications feature on client nodes */ + /* disable the notifications feature on agent/satellite nodes */ Log(LogInformation, "cli", "Disabling the Notification feature."); FeatureUtility::DisableFeatures({ "notification" }); @@ -487,16 +470,7 @@ wizard_ticket: fp.close(); -#ifdef _WIN32 - _unlink(apiConfPath.CStr()); -#endif /* _WIN32 */ - - if (rename(tempApiConfPath.CStr(), apiConfPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempApiConfPath)); - } + Utility::RenameFile(tempApiConfPath, apiConfPath); /* Zones configuration. */ Log(LogInformation, "cli", "Generating local zones.conf."); @@ -570,14 +544,14 @@ wizard_global_zone_loop_start: /* Generate node configuration. */ NodeUtility::GenerateNodeIcingaConfig(endpointName, zoneName, parentZoneName, endpoints, globalZones); - if (cn != Utility::GetFQDN()) { + if (endpointName != Utility::GetFQDN()) { Log(LogWarning, "cli") - << "CN '" << cn << "' does not match the default FQDN '" + << "CN/Endpoint name '" << endpointName << "' does not match the default FQDN '" << Utility::GetFQDN() << "'. Requires update for NodeName constant in constants.conf!"; } - NodeUtility::UpdateConstant("NodeName", cn); - NodeUtility::UpdateConstant("ZoneName", cn); + NodeUtility::UpdateConstant("NodeName", endpointName); + NodeUtility::UpdateConstant("ZoneName", zoneName); if (!ticket.IsEmpty()) { String ticketPath = ApiListener::GetCertsDir() + "/ticket"; @@ -595,16 +569,7 @@ wizard_global_zone_loop_start: fp.close(); -#ifdef _WIN32 - _unlink(ticketPath.CStr()); -#endif /* _WIN32 */ - - if (rename(tempTicketPath.CStr(), ticketPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempTicketPath)); - } + Utility::RenameFile(tempTicketPath, ticketPath); } /* If no parent connection was made, the user must supply the ca.crt before restarting Icinga 2.*/ @@ -638,7 +603,7 @@ wizard_global_zone_loop_start: << ConsoleColorTag(Console_Normal); } - /* Satellite/Clients should not include the api-users.conf file. + /* Satellite/Agents should not include the api-users.conf file. * The configuration should instead be managed via config sync or automation tools. */ } @@ -799,16 +764,7 @@ wizard_global_zone_loop_start: fp.close(); -#ifdef _WIN32 - _unlink(apiConfPath.CStr()); -#endif /* _WIN32 */ - - if (rename(tempApiConfPath.CStr(), apiConfPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempApiConfPath)); - } + Utility::RenameFile(tempApiConfPath, apiConfPath); /* update constants.conf with NodeName = CN + TicketSalt = random value */ if (cn != Utility::GetFQDN()) { @@ -821,8 +777,8 @@ wizard_global_zone_loop_start: NodeUtility::CreateBackupFile(NodeUtility::GetConstantsConfPath()); - NodeUtility::UpdateConstant("NodeName", cn); - NodeUtility::UpdateConstant("ZoneName", cn); + NodeUtility::UpdateConstant("NodeName", endpointName); + NodeUtility::UpdateConstant("ZoneName", zoneName); String salt = RandomString(16); @@ -850,7 +806,7 @@ wizard_global_zone_loop_start: } /* Include api-users.conf */ - String apiUsersFilePath = Application::GetSysconfDir() + "/icinga2/conf.d/api-users.conf"; + String apiUsersFilePath = Configuration::ConfigDir + "/conf.d/api-users.conf"; std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundGreen) << "Checking if the api-users.conf file exists...\n" diff --git a/lib/cli/nodewizardcommand.hpp b/lib/cli/nodewizardcommand.hpp index 5cc6033c0..dfda70c8a 100644 --- a/lib/cli/nodewizardcommand.hpp +++ b/lib/cli/nodewizardcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef NODEWIZARDCOMMAND_H #define NODEWIZARDCOMMAND_H @@ -44,7 +27,7 @@ public: boost::program_options::options_description& hiddenDesc) const override; private: - int ClientSetup() const; + int AgentSatelliteSetup() const; int MasterSetup() const; }; diff --git a/lib/cli/objectlistcommand.cpp b/lib/cli/objectlistcommand.cpp index f5e46448c..f931d8c25 100644 --- a/lib/cli/objectlistcommand.cpp +++ b/lib/cli/objectlistcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/objectlistcommand.hpp" #include "cli/objectlistutility.hpp" @@ -67,11 +50,11 @@ void ObjectListCommand::InitParameters(boost::program_options::options_descripti */ int ObjectListCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const { - String objectfile = Application::GetObjectsPath(); + String objectfile = Configuration::ObjectsPath; if (!Utility::PathExists(objectfile)) { Log(LogCritical, "cli") - << "Cannot open objects file '" << Application::GetObjectsPath() << "'."; + << "Cannot open objects file '" << Configuration::ObjectsPath << "'."; Log(LogCritical, "cli", "Run 'icinga2 daemon -C' to validate config and generate the cache file."); return 1; } diff --git a/lib/cli/objectlistcommand.hpp b/lib/cli/objectlistcommand.hpp index 2220e40c8..bafe3ec71 100644 --- a/lib/cli/objectlistcommand.hpp +++ b/lib/cli/objectlistcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef OBJECTLISTCOMMAND_H #define OBJECTLISTCOMMAND_H diff --git a/lib/cli/objectlistutility.cpp b/lib/cli/objectlistutility.cpp index 3f5d9c0a3..a8135d98a 100644 --- a/lib/cli/objectlistutility.cpp +++ b/lib/cli/objectlistutility.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/objectlistutility.hpp" #include "base/json.hpp" diff --git a/lib/cli/objectlistutility.hpp b/lib/cli/objectlistutility.hpp index 1b80f7dc2..ee1b97c4d 100644 --- a/lib/cli/objectlistutility.hpp +++ b/lib/cli/objectlistutility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef OBJECTLISTUTILITY_H #define OBJECTLISTUTILITY_H diff --git a/lib/cli/pkinewcacommand.cpp b/lib/cli/pkinewcacommand.cpp index c9c1e172e..eba08c69f 100644 --- a/lib/cli/pkinewcacommand.cpp +++ b/lib/cli/pkinewcacommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/pkinewcacommand.hpp" #include "remote/pkiutility.hpp" diff --git a/lib/cli/pkinewcacommand.hpp b/lib/cli/pkinewcacommand.hpp index b9d362a9a..5b1bff645 100644 --- a/lib/cli/pkinewcacommand.hpp +++ b/lib/cli/pkinewcacommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PKINEWCACOMMAND_H #define PKINEWCACOMMAND_H diff --git a/lib/cli/pkinewcertcommand.cpp b/lib/cli/pkinewcertcommand.cpp index 61215bd0a..5201d9239 100644 --- a/lib/cli/pkinewcertcommand.cpp +++ b/lib/cli/pkinewcertcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/pkinewcertcommand.hpp" #include "remote/pkiutility.hpp" diff --git a/lib/cli/pkinewcertcommand.hpp b/lib/cli/pkinewcertcommand.hpp index c3b639c23..0c39bb6fb 100644 --- a/lib/cli/pkinewcertcommand.hpp +++ b/lib/cli/pkinewcertcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PKINEWCERTCOMMAND_H #define PKINEWCERTCOMMAND_H diff --git a/lib/cli/pkirequestcommand.cpp b/lib/cli/pkirequestcommand.cpp index c14fb7b21..d2b79f0a9 100644 --- a/lib/cli/pkirequestcommand.cpp +++ b/lib/cli/pkirequestcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/pkirequestcommand.hpp" #include "remote/pkiutility.hpp" diff --git a/lib/cli/pkirequestcommand.hpp b/lib/cli/pkirequestcommand.hpp index 71b9ca3a9..6e2a39334 100644 --- a/lib/cli/pkirequestcommand.hpp +++ b/lib/cli/pkirequestcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PKIREQUESTCOMMAND_H #define PKIREQUESTCOMMAND_H diff --git a/lib/cli/pkisavecertcommand.cpp b/lib/cli/pkisavecertcommand.cpp index 1cfd463e1..dbde44339 100644 --- a/lib/cli/pkisavecertcommand.cpp +++ b/lib/cli/pkisavecertcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/pkisavecertcommand.hpp" #include "remote/pkiutility.hpp" diff --git a/lib/cli/pkisavecertcommand.hpp b/lib/cli/pkisavecertcommand.hpp index 0fb0c4847..c552eefc2 100644 --- a/lib/cli/pkisavecertcommand.hpp +++ b/lib/cli/pkisavecertcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PKISAVECERTCOMMAND_H #define PKISAVECERTCOMMAND_H diff --git a/lib/cli/pkisigncsrcommand.cpp b/lib/cli/pkisigncsrcommand.cpp index e7de7aca8..ce1427bcd 100644 --- a/lib/cli/pkisigncsrcommand.cpp +++ b/lib/cli/pkisigncsrcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/pkisigncsrcommand.hpp" #include "remote/pkiutility.hpp" diff --git a/lib/cli/pkisigncsrcommand.hpp b/lib/cli/pkisigncsrcommand.hpp index da9e1c8e7..a66fd3935 100644 --- a/lib/cli/pkisigncsrcommand.hpp +++ b/lib/cli/pkisigncsrcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PKISIGNCSRCOMMAND_H #define PKISIGNCSRCOMMAND_H diff --git a/lib/cli/pkiticketcommand.cpp b/lib/cli/pkiticketcommand.cpp index 8ba088488..82f3586f0 100644 --- a/lib/cli/pkiticketcommand.cpp +++ b/lib/cli/pkiticketcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/pkiticketcommand.hpp" #include "remote/pkiutility.hpp" diff --git a/lib/cli/pkiticketcommand.hpp b/lib/cli/pkiticketcommand.hpp index 0ada10ea5..500ce864c 100644 --- a/lib/cli/pkiticketcommand.hpp +++ b/lib/cli/pkiticketcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PKITICKETCOMMAND_H #define PKITICKETCOMMAND_H diff --git a/lib/cli/troubleshootcommand.cpp b/lib/cli/troubleshootcommand.cpp deleted file mode 100644 index aabd9e41a..000000000 --- a/lib/cli/troubleshootcommand.cpp +++ /dev/null @@ -1,691 +0,0 @@ -/***************************************************************************** -* Icinga 2 * -* Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * -* * -* This program is free software; you can redistribute it and/or * -* modify it under the terms of the GNU General Public License * -* as published by the Free Software Foundation; either version 2 * -* of the License, or (at your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU General Public License for more details. * -* * -* You should have received a copy of the GNU General Public License * -* along with this program; if not, write to the Free Software Foundation * -* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * -******************************************************************************/ - -#include "base/application.hpp" -#include "base/console.hpp" -#include "base/convert.hpp" -#include "base/json.hpp" -#include "base/netstring.hpp" -#include "base/objectlock.hpp" -#include "base/stdiostream.hpp" -#include "cli/daemonutility.hpp" -#include "cli/featureutility.hpp" -#include "cli/objectlistutility.hpp" -#include "cli/troubleshootcommand.hpp" -#include "cli/variableutility.hpp" -#include "config/configitembuilder.hpp" - -#include -#include -#include - -#include -#include - -using namespace icinga; -namespace po = boost::program_options; - -REGISTER_CLICOMMAND("troubleshoot", TroubleshootCommand); - -String TroubleshootCommand::GetDescription() const -{ - return "Collect logs and other relevant information for troubleshooting purposes."; -} - -String TroubleshootCommand::GetShortDescription() const -{ - return "collect information for troubleshooting"; -} - -class TroubleshootCommand::InfoLog -{ -public: - InfoLog(const String& path, const bool cons) - { - m_Console = cons; - m_ConsoleType = Console_Dumb; - if (m_Console) { - m_Stream = new std::ostream(std::cout.rdbuf()); -#ifndef _WIN32 - m_ConsoleType = Console_VT100; -#else /*_WIN32*/ - m_ConsoleType = Console_Windows; -#endif /*_WIN32*/ - } - else { - auto *ofs = new std::ofstream(); - ofs->open(path.CStr(), std::ios::out | std::ios::trunc); - m_Stream = ofs; - } - } - - ~InfoLog() - { - delete m_Stream; - } - - void WriteLine(const LogSeverity sev, const int color, const String& str) - { - if (!m_Console) - Log(sev, "troubleshoot", str); - - if (sev == LogWarning) { - *m_Stream - << '\n' << ConsoleColorTag(Console_ForegroundYellow, m_ConsoleType) << std::string(24, '#') << '\n' - << ConsoleColorTag(Console_Normal, m_ConsoleType) << str - << ConsoleColorTag(Console_ForegroundYellow, m_ConsoleType) << std::string(24, '#') << "\n\n" - << ConsoleColorTag(Console_Normal, m_ConsoleType); - } else if (sev == LogCritical) { - *m_Stream - << '\n' << ConsoleColorTag(Console_ForegroundRed, m_ConsoleType) << std::string(24, '#') << '\n' - << ConsoleColorTag(Console_Normal, m_ConsoleType) << str - << ConsoleColorTag(Console_ForegroundRed, m_ConsoleType) << std::string(24, '#') << "\n\n" - << ConsoleColorTag(Console_Normal, m_ConsoleType); - } else - *m_Stream - << ConsoleColorTag(color, m_ConsoleType) << str - << ConsoleColorTag(Console_Normal, m_ConsoleType); - } - - bool GetStreamHealth() const - { - return m_Stream->good(); - } - -private: - bool m_Console; - ConsoleType m_ConsoleType; - std::ostream *m_Stream; -}; - -class TroubleshootCommand::InfoLogLine -{ -public: - InfoLogLine(InfoLog& log, int col = Console_Normal, LogSeverity sev = LogInformation) - : m_Log(log), m_Color(col), m_Sev(sev) {} - - ~InfoLogLine() - { - m_Log.WriteLine(m_Sev, m_Color, m_String.str()); - } - - template - InfoLogLine& operator<<(const T& info) - { - m_String << info; - return *this; - } - -private: - std::ostringstream m_String; - InfoLog& m_Log; - int m_Color; - LogSeverity m_Sev; -}; - - -bool TroubleshootCommand::GeneralInfo(InfoLog& log, const boost::program_options::variables_map& vm) -{ - InfoLogLine(log, Console_ForegroundBlue) - << std::string(14, '=') << " GENERAL INFORMATION " << std::string(14, '=') << "\n\n"; - - //Application::DisplayInfoMessage() but formatted - InfoLogLine(log) - << "\tApplication version: " << Application::GetAppVersion() << '\n' - << "\tInstallation root: " << Application::GetPrefixDir() << '\n' - << "\tSysconf directory: " << Application::GetSysconfDir() << '\n' - << "\tRun directory: " << Application::GetRunDir() << '\n' - << "\tLocal state directory: " << Application::GetLocalStateDir() << '\n' - << "\tPackage data directory: " << Application::GetPkgDataDir() << '\n' - << "\tState path: " << Application::GetStatePath() << '\n' - << "\tObjects path: " << Application::GetObjectsPath() << '\n' - << "\tVars path: " << Application::GetVarsPath() << '\n' - << "\tPID path: " << Application::GetPidPath() << '\n'; - - InfoLogLine(log) - << '\n'; - - return true; -} - -bool TroubleshootCommand::FeatureInfo(InfoLog& log, const boost::program_options::variables_map& vm) -{ - TroubleshootCommand::CheckFeatures(log); - //TODO Check whether active features are operational. - return true; -} - -bool TroubleshootCommand::ObjectInfo(InfoLog& log, const boost::program_options::variables_map& vm, Dictionary::Ptr& logs, const String& path) -{ - InfoLogLine(log, Console_ForegroundBlue) - << std::string(14, '=') << " OBJECT INFORMATION " << std::string(14, '=') << "\n\n"; - - String objectfile = Application::GetObjectsPath(); - std::set configs; - - if (!Utility::PathExists(objectfile)) { - InfoLogLine(log, 0, LogCritical) - << "Cannot open object file '" << objectfile << "'.\n" - << "FAILED: This probably means you have a fault configuration.\n"; - return false; - } else { - InfoLog *OFile = nullptr; - bool OConsole = false; - if (vm.count("include-objects")) { - if (vm.count("console")) - OConsole = true; - else { - OFile = new InfoLog(path+"-objects", false); - if (!OFile->GetStreamHealth()) { - InfoLogLine(log, 0, LogWarning) - << "Failed to open Object-write-stream, not printing objects\n\n"; - delete OFile; - OFile = nullptr; - } else - InfoLogLine(log) - << "Printing all objects to " << path+"-objects\n"; - } - } - CheckObjectFile(objectfile, log, OFile, OConsole, logs, configs); - delete OFile; - } - - if (vm.count("include-vars")) { - if (vm.count("console")) { - InfoLogLine(log, Console_ForegroundBlue) - << "\n[begin: varsfile]\n"; - if (!PrintVarsFile(path, true)) - InfoLogLine(log, 0, LogWarning) - << "Failed to print vars file\n"; - InfoLogLine(log, Console_ForegroundBlue) - << "[end: varsfile]\n"; - } else { - if (PrintVarsFile(path, false)) - InfoLogLine(log) - << "Successfully printed all variables to " << path+"-vars\n"; - else - InfoLogLine(log, 0, LogWarning) - << "Failed to print vars to " << path+"-vars\n"; - } - } - - InfoLogLine(log) - << '\n'; - - return true; -} - -bool TroubleshootCommand::ReportInfo(InfoLog& log, const boost::program_options::variables_map& vm, Dictionary::Ptr& logs) -{ - InfoLogLine(log, Console_ForegroundBlue) - << std::string(14, '=') << " LOGS AND CRASH REPORTS " << std::string(14, '=') << "\n\n"; - PrintLoggers(log, logs); - PrintCrashReports(log); - - InfoLogLine(log) - << '\n'; - - return true; -} - -bool TroubleshootCommand::ConfigInfo(InfoLog& log, const boost::program_options::variables_map& vm) -{ - InfoLogLine(log, Console_ForegroundBlue) - << std::string(14, '=') << " CONFIGURATION FILES " << std::string(14, '=') << "\n\n"; - - InfoLogLine(log) - << "A collection of important configuration files follows, please make sure to remove any sensitive data such as credentials, internal company names, etc\n"; - - if (!PrintFile(log, Application::GetSysconfDir() + "/icinga2/icinga2.conf")) { - InfoLogLine(log, 0, LogWarning) - << "icinga2.conf not found, therefore skipping validation.\n" - << "If you are using an icinga2.conf somewhere but the default path please validate it via 'icinga2 daemon -C -c \"path\to/icinga2.conf\"'\n" - << "and provide it with your support request.\n"; - } - - if (!PrintFile(log, Application::GetSysconfDir() + "/icinga2/zones.conf")) { - InfoLogLine(log, 0, LogWarning) - << "zones.conf not found.\n" - << "If you are using a zones.conf somewhere but the default path please provide it with your support request\n"; - } - - InfoLogLine(log) - << '\n'; - - return true; -} - -/*Print the last *numLines* of *file* to *os* */ -int TroubleshootCommand::Tail(const String& file, int numLines, InfoLog& log) -{ - boost::circular_buffer ringBuf(numLines); - std::ifstream text; - text.open(file.CStr(), std::ifstream::in); - if (!text.good()) - return 0; - - std::string line; - int lines = 0; - - while (std::getline(text, line)) { - ringBuf.push_back(line); - lines++; - } - - if (lines < numLines) - numLines = lines; - - InfoLogLine(log, Console_ForegroundCyan) - << "[begin: '" << file << "' line: " << lines-numLines << "]\n"; - - for (int k = 0; k < numLines; k++) { - InfoLogLine(log, Console_ForegroundCyan) - << "# "; - InfoLogLine(log) - << ringBuf[k] << '\n'; - } - - text.close(); - - InfoLogLine(log, Console_ForegroundCyan) - << "[end: '" << file << "' line: " << lines << "]\n\n"; - - return numLines; -} - -bool TroubleshootCommand::CheckFeatures(InfoLog& log) -{ - Dictionary::Ptr features = new Dictionary; - std::vector disabled_features; - std::vector enabled_features; - - if (!FeatureUtility::GetFeatures(disabled_features, true) || - !FeatureUtility::GetFeatures(enabled_features, false)) { - InfoLogLine(log, 0, LogCritical) - << "Failed to collect enabled and/or disabled features. Check\n" - << FeatureUtility::GetFeaturesAvailablePath() << '\n' - << FeatureUtility::GetFeaturesEnabledPath() << '\n'; - return false; - } - - for (const String& feature : disabled_features) - features->Set(feature, false); - for (const String& feature : enabled_features) - features->Set(feature, true); - - InfoLogLine(log) - << "Enabled features:\n"; - InfoLogLine(log, Console_ForegroundGreen) - << '\t' << boost::algorithm::join(enabled_features, " ") << '\n'; - InfoLogLine(log) - << "Disabled features:\n"; - InfoLogLine(log, Console_ForegroundRed) - << '\t' << boost::algorithm::join(disabled_features, " ") << '\n'; - - if (!features->Get("checker").ToBool()) - InfoLogLine(log, 0, LogWarning) - << "checker is disabled, no checks can be run from this instance\n"; - if (!features->Get("mainlog").ToBool()) - InfoLogLine(log, 0, LogWarning) - << "mainlog is disabled, please activate it and rerun icinga2\n"; - if (!features->Get("debuglog").ToBool()) - InfoLogLine(log, 0, LogWarning) - << "debuglog is disabled, please activate it and rerun icinga2\n"; - - return true; -} - -void TroubleshootCommand::GetLatestReport(const String& filename, time_t& bestTimestamp, String& bestFilename) -{ -#ifdef _WIN32 - struct _stat buf; - if (_stat(filename.CStr(), &buf)) - return; -#else - struct stat buf; - if (stat(filename.CStr(), &buf)) - return; -#endif /*_WIN32*/ - if (buf.st_mtime > bestTimestamp) { - bestTimestamp = buf.st_mtime; - bestFilename = filename; - } -} - -bool TroubleshootCommand::PrintCrashReports(InfoLog& log) -{ - String spath = Application::GetLocalStateDir() + "/log/icinga2/crash/report.*"; - time_t bestTimestamp = 0; - String bestFilename; - - try { - Utility::Glob(spath, std::bind(&GetLatestReport, _1, std::ref(bestTimestamp), - std::ref(bestFilename)), GlobFile); - } -#ifdef _WIN32 - catch (win32_error &ex) { - if (int const * err = boost::get_error_info(ex)) { - if (*err != 3) {//Error code for path does not exist - InfoLogLine(log, 0, LogWarning) - << Application::GetLocalStateDir() << "/log/icinga2/crash/ does not exist\n"; - - return false; - } - } - InfoLogLine(log, 0, LogWarning) - << "Error printing crash reports\n"; - - return false; - } -#else - catch (...) { - InfoLogLine(log, 0, LogWarning) << "Error printing crash reports.\n" - << "Does " << Application::GetLocalStateDir() << "/log/icinga2/crash/ exist?\n"; - - return false; - } -#endif /*_WIN32*/ - - if (!bestTimestamp) - InfoLogLine(log, Console_ForegroundYellow) - << "No crash logs found in " << Application::GetLocalStateDir().CStr() << "/log/icinga2/crash/\n\n"; - else { - InfoLogLine(log) - << "Latest crash report is from " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", Utility::GetTime()) << '\n' - << "File: " << bestFilename << "\n\n"; - PrintFile(log, bestFilename); - InfoLogLine(log) - << '\n'; - } - - return true; -} - -bool TroubleshootCommand::PrintFile(InfoLog& log, const String& path) -{ - std::ifstream text; - text.open(path.CStr(), std::ifstream::in); - if (!text.is_open()) - return false; - - std::string line; - - InfoLogLine(log, Console_ForegroundCyan) - << "[begin: '" << path << "']\n"; - - while (std::getline(text, line)) { - InfoLogLine(log, Console_ForegroundCyan) - << "# "; - InfoLogLine(log) - << line << '\n'; - } - - InfoLogLine(log, Console_ForegroundCyan) - << "[end: '" << path << "']\n"; - - return true; -} - -bool TroubleshootCommand::CheckConfig() -{ - return DaemonUtility::ValidateConfigFiles({ Application::GetSysconfDir() + "/icinga2/icinga2.conf" }, Application::GetObjectsPath()); -} - -//print is supposed allow the user to print the object file -void TroubleshootCommand::CheckObjectFile(const String& objectfile, InfoLog& log, InfoLog *OFile, const bool objectConsole, - Dictionary::Ptr& logs, std::set& configs) -{ - InfoLogLine(log) - << "Checking object file from " << objectfile << '\n'; - - std::fstream fp; - fp.open(objectfile.CStr(), std::ios_base::in); - - if (!fp.is_open()) { - InfoLogLine(log, 0, LogWarning) - << "Could not open object file.\n"; - return; - } - - StdioStream::Ptr sfp = new StdioStream(&fp, false); - String::SizeType typeL = 0, countTotal = 0; - - String message; - StreamReadContext src; - StreamReadStatus srs; - std::map type_count; - bool first = true; - - std::stringstream sStream; - - if (objectConsole) - InfoLogLine(log, Console_ForegroundBlue) - << "\n[begin: objectfile]\n"; - - while ((srs = NetString::ReadStringFromStream(sfp, &message, src)) != StatusEof) { - if (srs != StatusNewItem) - continue; - - if (objectConsole) { - ObjectListUtility::PrintObject(std::cout, first, message, type_count, "", ""); - } - else { - ObjectListUtility::PrintObject(sStream, first, message, type_count, "", ""); - if (OFile) { - InfoLogLine(*OFile) - << sStream.str(); - sStream.flush(); - } - } - - Dictionary::Ptr object = JsonDecode(message); - Dictionary::Ptr properties = object->Get("properties"); - - String name = object->Get("name"); - String type = object->Get("type"); - - //Find longest typename for padding - typeL = type.GetLength() > typeL ? type.GetLength() : typeL; - countTotal++; - - Array::Ptr debug_info = object->Get("debug_info"); - - if (debug_info) - configs.insert(debug_info->Get(0)); - - if (Utility::Match(type, "FileLogger")) { - Dictionary::Ptr debug_hints = object->Get("debug_hints"); - Dictionary::Ptr properties = object->Get("properties"); - - ObjectLock olock(properties); - for (const Dictionary::Pair& kv : properties) { - if (Utility::Match(kv.first, "path")) - logs->Set(name, kv.second); - } - } - } - - if (objectConsole) - InfoLogLine(log, Console_ForegroundBlue) - << "\n[end: objectfile]\n"; - - if (!countTotal) { - InfoLogLine(log, 0, LogCritical) - << "No objects found in objectfile.\n"; - return; - } - - //Print objects with count - InfoLogLine(log) - << "Found the " << countTotal << " objects:\n" - << " Type" << std::string(typeL-4, ' ') << " : Count\n"; - - for (const Dictionary::Pair& kv : type_count) { - InfoLogLine(log) - << " " << kv.first << std::string(typeL - kv.first.GetLength(), ' ') - << " : " << kv.second << '\n'; - } - - InfoLogLine(log) - << '\n'; - - TroubleshootCommand::PrintObjectOrigin(log, configs); -} - -bool TroubleshootCommand::PrintVarsFile(const String& path, const bool console) { - if (!console) { - auto *ofs = new std::ofstream(); - ofs->open((path+"-vars").CStr(), std::ios::out | std::ios::trunc); - if (!ofs->is_open()) - return false; - else - VariableUtility::PrintVariables(*ofs); - ofs->close(); - } else - VariableUtility::PrintVariables(std::cout); - - return true; -} - -void TroubleshootCommand::PrintLoggers(InfoLog& log, Dictionary::Ptr& logs) -{ - if (!logs->GetLength()) { - InfoLogLine(log, 0, LogWarning) - << "No loggers found, check whether you enabled any logging features\n"; - } else { - InfoLogLine(log) - << "Getting the last 20 lines of " << logs->GetLength() << " FileLogger objects.\n"; - - ObjectLock ulock(logs); - for (const Dictionary::Pair& kv : logs) { - InfoLogLine(log) - << "Logger " << kv.first << " at path: " << kv.second << '\n'; - - if (!Tail(kv.second, 20, log)) { - InfoLogLine(log, 0, LogWarning) - << kv.second << " either does not exist or is empty\n"; - } - } - } -} - -void TroubleshootCommand::PrintObjectOrigin(InfoLog& log, const std::set& configSet) -{ - InfoLogLine(log) - << "The objects origins are:\n"; - - for (const String& config : configSet) { - InfoLogLine(log) - << " " << config << '\n'; - } -} - -void TroubleshootCommand::InitParameters(boost::program_options::options_description& visibleDesc, - boost::program_options::options_description& hiddenDesc) const -{ - visibleDesc.add_options() - ("console,c", "print to console instead of file") - ("output,o", boost::program_options::value(), "path to output file") - ("include-objects", "Print the whole objectfile (like `object list`)") - ("include-vars", "Print all Variables (like `variable list`)") - ; -} - -int TroubleshootCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const -{ -#ifdef _WIN32 //Dislikes ':' in filenames - String path = Application::GetLocalStateDir() + "/log/icinga2/troubleshooting-" - + Utility::FormatDateTime("%Y-%m-%d_%H-%M-%S", Utility::GetTime()) + ".log"; -#else - String path = Application::GetLocalStateDir() + "/log/icinga2/troubleshooting-" - + Utility::FormatDateTime("%Y-%m-%d_%H:%M:%S", Utility::GetTime()) + ".log"; -#endif /*_WIN32*/ - - InfoLog *log; - Logger::SetConsoleLogSeverity(LogWarning); - - if (vm.count("output")) - path = vm["output"].as(); - - if (vm.count("console")) { - log = new InfoLog("", true); - } else { - log = new InfoLog(path, false); - if (!log->GetStreamHealth()) { - Log(LogCritical, "troubleshoot", "Failed to open file to write: " + path); - delete log; - return 3; - } - } - - String appName = Utility::BaseName(Application::GetArgV()[0]); - double goTime = Utility::GetTime(); - - InfoLogLine(*log) - << appName << " -- Troubleshooting help:\n" - << "Should you run into problems with Icinga please add this file to your help request\n" - << "Started collection at " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", goTime) << "\n"; - - InfoLogLine(*log, Console_ForegroundMagenta) - << std::string(52, '=') << "\n\n"; - - if (appName.GetLength() > 3 && appName.SubStr(0, 3) == "lt-") - appName = appName.SubStr(3, appName.GetLength() - 3); - - Dictionary::Ptr logs = new Dictionary; - - if (!GeneralInfo(*log, vm) || - !FeatureInfo(*log, vm) || - !ObjectInfo(*log, vm, logs, path) || - !ReportInfo(*log, vm, logs) || - !ConfigInfo(*log, vm)) { - InfoLogLine(*log, 0, LogCritical) - << "Could not recover from critical failure, exiting.\n"; - - delete log; - return 3; - } - - double endTime = Utility::GetTime(); - - InfoLogLine(*log, Console_ForegroundMagenta) - << std::string(52, '=') << '\n'; - InfoLogLine(*log, Console_ForegroundGreen) - << "Finished collection at " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", endTime) - << "\nTook " << Convert::ToString(endTime - goTime) << " seconds\n"; - - if (!vm.count("console")) { - std::cout << "Started collection at " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", goTime) << "\n" - << "Finished collection at " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", endTime) - << "\nTook " << Convert::ToString(endTime - goTime) << " seconds\n\n"; - - std::cout << "General log file: '" << path << "'\n"; - - if (vm.count("include-vars")) - std::cout << "Vars log file: '" << path << "-vars'\n"; - if (vm.count("include-objects")) - std::cout << "Objects log file: '" << path << "-objects'\n"; - - std::cout << "\nPlease compress the files before uploading them,, for example:\n" - << " # tar czf troubleshoot.tar.gz " << path << "*\n"; - } - - delete log; - return 0; -} diff --git a/lib/cli/troubleshootcommand.hpp b/lib/cli/troubleshootcommand.hpp deleted file mode 100644 index dbe5fba5e..000000000 --- a/lib/cli/troubleshootcommand.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#ifndef TROUBLESHOOTCOMMAND_H -#define TROUBLESHOOTCOMMAND_H - -#include "cli/clicommand.hpp" -#include "base/i2-base.hpp" -#include "base/dictionary.hpp" - -namespace icinga -{ - -/** - * The "troubleshoot" command. - * - * @ingroup cli - */ -class TroubleshootCommand final : public CLICommand -{ -public: - DECLARE_PTR_TYPEDEFS(TroubleshootCommand); - - String GetDescription() const override; - String GetShortDescription() const override; - int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const override; - void InitParameters(boost::program_options::options_description& visibleDesc, - boost::program_options::options_description& hiddenDesc) const override; - -private: - class InfoLog; - class InfoLogLine; - static bool GeneralInfo(InfoLog& log, const boost::program_options::variables_map& vm); - static bool FeatureInfo(InfoLog& log, const boost::program_options::variables_map& vm); - static bool ObjectInfo(InfoLog& log, const boost::program_options::variables_map& vm, - Dictionary::Ptr& logs, const String& path); - static bool ReportInfo(InfoLog& log, const boost::program_options::variables_map& vm, - Dictionary::Ptr& logs); - static bool ConfigInfo(InfoLog& log, const boost::program_options::variables_map& vm); - - static int Tail(const String& file, const int numLines, InfoLog& log); - static bool CheckFeatures(InfoLog& log); - static void GetLatestReport(const String& filename, time_t& bestTimestamp, String& bestFilename); - static bool PrintCrashReports(InfoLog& log); - static bool PrintFile(InfoLog& log, const String& path); - static bool CheckConfig(); - static void CheckObjectFile(const String& objectfile, InfoLog& log, InfoLog *OFile, const bool objectConsole, - Dictionary::Ptr& logs, std::set& configs); - static bool PrintVarsFile(const String& path, const bool console); - static void PrintLoggers(InfoLog& log, Dictionary::Ptr& logs); - static void PrintObjectOrigin(InfoLog& log, const std::set& configSet); -}; - -} -#endif /* TROUBLESHOOTCOMMAND_H */ diff --git a/lib/cli/variablegetcommand.cpp b/lib/cli/variablegetcommand.cpp index 76fa634aa..c05ac965a 100644 --- a/lib/cli/variablegetcommand.cpp +++ b/lib/cli/variablegetcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/variablegetcommand.hpp" #include "cli/variableutility.hpp" @@ -75,7 +58,7 @@ int VariableGetCommand::Run(const boost::program_options::variables_map& vm, con return 0; } - String varsfile = Application::GetVarsPath(); + String varsfile = Configuration::VarsPath; if (!Utility::PathExists(varsfile)) { Log(LogCritical, "cli") diff --git a/lib/cli/variablegetcommand.hpp b/lib/cli/variablegetcommand.hpp index 84f63df5b..9479b3a43 100644 --- a/lib/cli/variablegetcommand.hpp +++ b/lib/cli/variablegetcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef VARIABLEGETCOMMAND_H #define VARIABLEGETCOMMAND_H diff --git a/lib/cli/variablelistcommand.cpp b/lib/cli/variablelistcommand.cpp index 9aff1cbea..b7ba1be3f 100644 --- a/lib/cli/variablelistcommand.cpp +++ b/lib/cli/variablelistcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/variablelistcommand.hpp" #include "cli/variableutility.hpp" @@ -53,7 +36,7 @@ String VariableListCommand::GetShortDescription() const */ int VariableListCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const { - String varsfile = Application::GetVarsPath(); + String varsfile = Configuration::VarsPath; if (!Utility::PathExists(varsfile)) { Log(LogCritical, "cli") diff --git a/lib/cli/variablelistcommand.hpp b/lib/cli/variablelistcommand.hpp index d5502dca1..909d9eb74 100644 --- a/lib/cli/variablelistcommand.hpp +++ b/lib/cli/variablelistcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef VARIABLELISTCOMMAND_H #define VARIABLELISTCOMMAND_H diff --git a/lib/cli/variableutility.cpp b/lib/cli/variableutility.cpp index 9044a9e9b..398c9a0e5 100644 --- a/lib/cli/variableutility.cpp +++ b/lib/cli/variableutility.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/variableutility.hpp" #include "base/logger.hpp" @@ -31,7 +14,7 @@ using namespace icinga; Value VariableUtility::GetVariable(const String& name) { - String varsfile = Application::GetVarsPath(); + String varsfile = Configuration::VarsPath; std::fstream fp; fp.open(varsfile.CStr(), std::ios_base::in); @@ -61,7 +44,7 @@ Value VariableUtility::GetVariable(const String& name) void VariableUtility::PrintVariables(std::ostream& outfp) { - String varsfile = Application::GetVarsPath(); + String varsfile = Configuration::VarsPath; std::fstream fp; fp.open(varsfile.CStr(), std::ios_base::in); diff --git a/lib/cli/variableutility.hpp b/lib/cli/variableutility.hpp index af1f3fb8e..69869b2b6 100644 --- a/lib/cli/variableutility.hpp +++ b/lib/cli/variableutility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef VARIABLEUTILITY_H #define VARIABLEUTILITY_H diff --git a/lib/compat/CMakeLists.txt b/lib/compat/CMakeLists.txt index df4a3e0eb..84225c4f8 100644 --- a/lib/compat/CMakeLists.txt +++ b/lib/compat/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ mkclass_target(checkresultreader.ti checkresultreader-ti.cpp checkresultreader-ti.hpp) mkclass_target(compatlogger.ti compatlogger-ti.cpp compatlogger-ti.hpp) @@ -42,21 +27,21 @@ set_target_properties ( install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/command.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/compatlog.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/statusdata.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/log/icinga2/compat/archives\")") -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/spool/icinga2\")") -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_RUNDIR}/icinga2/cmd\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_LOGDIR}/compat/archives\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_SPOOLDIR}\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_INITRUNDIR}/cmd\")") set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) diff --git a/lib/compat/checkresultreader.cpp b/lib/compat/checkresultreader.cpp index 016248765..2b3f05ccd 100644 --- a/lib/compat/checkresultreader.cpp +++ b/lib/compat/checkresultreader.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/compatutility.hpp" #include "compat/checkresultreader.hpp" @@ -62,7 +45,7 @@ void CheckResultReader::Start(bool runtimeCreated) << "'" << GetName() << "' started."; Log(LogWarning, "CheckResultReader") - << "The CheckResultReader feature is DEPRECATED and will be removed in Icinga v2.11."; + << "This feature is DEPRECATED and will be removed in future releases. Check the roadmap at https://github.com/Icinga/icinga2/milestones"; #ifndef _WIN32 m_ReadTimer = new Timer(); @@ -124,17 +107,9 @@ void CheckResultReader::ProcessCheckResultFile(const String& path) const } /* Remove the checkresult files. */ - if (unlink(path.CStr()) < 0) - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("unlink") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(path)); + Utility::Remove(path); - if (unlink(crfile.CStr()) < 0) - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("unlink") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(crfile)); + Utility::Remove(crfile); Checkable::Ptr checkable; diff --git a/lib/compat/checkresultreader.hpp b/lib/compat/checkresultreader.hpp index 445f8354c..6cd28e318 100644 --- a/lib/compat/checkresultreader.hpp +++ b/lib/compat/checkresultreader.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CHECKRESULTREADER_H #define CHECKRESULTREADER_H diff --git a/lib/compat/checkresultreader.ti b/lib/compat/checkresultreader.ti index 18b3d0ac4..0132818cf 100644 --- a/lib/compat/checkresultreader.ti +++ b/lib/compat/checkresultreader.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" #include "base/application.hpp" @@ -30,7 +13,7 @@ class CheckResultReader : ConfigObject activation_priority 100; [config] String spool_dir { - default {{{ return Application::GetLocalStateDir() + "/lib/icinga2/spool/checkresults/"; }}} + default {{{ return Configuration::DataDir + "/spool/checkresults/"; }}} }; }; diff --git a/lib/compat/compatlogger.cpp b/lib/compat/compatlogger.cpp index 5e6d2c752..75e94dc95 100644 --- a/lib/compat/compatlogger.cpp +++ b/lib/compat/compatlogger.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "compat/compatlogger.hpp" #include "compat/compatlogger-ti.cpp" @@ -64,10 +47,10 @@ void CompatLogger::Start(bool runtimeCreated) << "'" << GetName() << "' started."; Log(LogWarning, "CompatLogger") - << "The CompatLogger feature is DEPRECATED and will be removed in Icinga v2.11."; + << "This feature is DEPRECATED and will be removed in future releases. Check the roadmap at https://github.com/Icinga/icinga2/milestones"; Checkable::OnNewCheckResult.connect(std::bind(&CompatLogger::CheckResultHandler, this, _1, _2)); - Checkable::OnNotificationSentToUser.connect(std::bind(&CompatLogger::NotificationSentHandler, this, _1, _2, _3, _4, _5, _6, _7, _8)); + Checkable::OnNotificationSentToUser.connect(std::bind(&CompatLogger::NotificationSentHandler, this, _1, _2, _3, _4, _5, _6, _7, _8, _9)); Downtime::OnDowntimeTriggered.connect(std::bind(&CompatLogger::TriggerDowntimeHandler, this, _1)); Downtime::OnDowntimeRemoved.connect(std::bind(&CompatLogger::RemoveDowntimeHandler, this, _1)); Checkable::OnEventCommandExecuted.connect(std::bind(&CompatLogger::EventCommandHandler, this, _1)); @@ -247,14 +230,14 @@ void CompatLogger::RemoveDowntimeHandler(const Downtime::Ptr& downtime) * @threadsafety Always. */ void CompatLogger::NotificationSentHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, - const User::Ptr& user, NotificationType notification_type, CheckResult::Ptr const& cr, + const User::Ptr& user, NotificationType notification_type, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, const String& author, const String& comment_text, const String& command_name) { Host::Ptr host; Service::Ptr service; tie(host, service) = GetHostService(checkable); - String notification_type_str = Notification::NotificationTypeToString(notification_type); + String notification_type_str = Notification::NotificationTypeToStringCompat(notification_type); /* override problem notifications with their current state string */ if (notification_type == NotificationProblem) { diff --git a/lib/compat/compatlogger.hpp b/lib/compat/compatlogger.hpp index fecab1f99..748bb90a9 100644 --- a/lib/compat/compatlogger.hpp +++ b/lib/compat/compatlogger.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef COMPATLOGGER_H #define COMPATLOGGER_H @@ -53,8 +36,8 @@ private: void CheckResultHandler(const Checkable::Ptr& service, const CheckResult::Ptr& cr); void NotificationSentHandler(const Notification::Ptr& notification, const Checkable::Ptr& service, - const User::Ptr& user, NotificationType notification_type, CheckResult::Ptr const& cr, - const String& author, const String& comment_text, const String& command_name); + const User::Ptr& user, NotificationType notification_type, const CheckResult::Ptr& cr, + const NotificationResult::Ptr& nr, const String& author, const String& comment_text, const String& command_name); void FlappingChangedHandler(const Checkable::Ptr& checkable); void EnableFlappingChangedHandler(const Checkable::Ptr& checkable); void TriggerDowntimeHandler(const Downtime::Ptr& downtime); diff --git a/lib/compat/compatlogger.ti b/lib/compat/compatlogger.ti index 322236cd8..56431eccd 100644 --- a/lib/compat/compatlogger.ti +++ b/lib/compat/compatlogger.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" #include "base/application.hpp" @@ -30,7 +13,7 @@ class CompatLogger : ConfigObject activation_priority 100; [config] String log_dir { - default {{{ return Application::GetLocalStateDir() + "/log/icinga2/compat"; }}} + default {{{ return Configuration::LogDir + "/compat"; }}} }; [config] String rotation_method { default {{{ return "HOURLY"; }}} diff --git a/lib/compat/externalcommandlistener.cpp b/lib/compat/externalcommandlistener.cpp index bb9402c79..d5bdd63b5 100644 --- a/lib/compat/externalcommandlistener.cpp +++ b/lib/compat/externalcommandlistener.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "compat/externalcommandlistener.hpp" #include "compat/externalcommandlistener-ti.cpp" @@ -53,6 +36,8 @@ void ExternalCommandListener::Start(bool runtimeCreated) Log(LogInformation, "ExternalCommandListener") << "'" << GetName() << "' started."; + Log(LogWarning, "ExternalCommandListener") + << "This feature is DEPRECATED and will be removed in future releases. Check the roadmap at https://github.com/Icinga/icinga2/milestones"; #ifndef _WIN32 m_CommandThread = std::thread(std::bind(&ExternalCommandListener::CommandPipeThread, this, GetCommandPath())); m_CommandThread.detach(); @@ -82,12 +67,7 @@ void ExternalCommandListener::CommandPipeThread(const String& commandPath) if (S_ISFIFO(statbuf.st_mode) && access(commandPath.CStr(), R_OK) >= 0) { fifo_ok = true; } else { - if (unlink(commandPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("unlink") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(commandPath)); - } + Utility::Remove(commandPath); } } diff --git a/lib/compat/externalcommandlistener.hpp b/lib/compat/externalcommandlistener.hpp index a2adc47ef..895531f78 100644 --- a/lib/compat/externalcommandlistener.hpp +++ b/lib/compat/externalcommandlistener.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef EXTERNALCOMMANDLISTENER_H #define EXTERNALCOMMANDLISTENER_H diff --git a/lib/compat/externalcommandlistener.ti b/lib/compat/externalcommandlistener.ti index baffb7f51..5b529442e 100644 --- a/lib/compat/externalcommandlistener.ti +++ b/lib/compat/externalcommandlistener.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" #include "base/application.hpp" @@ -30,7 +13,7 @@ class ExternalCommandListener : ConfigObject activation_priority 100; [config] String command_path { - default {{{ return Application::GetRunDir() + "/icinga2/cmd/icinga2.cmd"; }}} + default {{{ return Configuration::InitRunDir + "/cmd/icinga2.cmd"; }}} }; }; diff --git a/lib/compat/statusdatawriter.cpp b/lib/compat/statusdatawriter.cpp index 6b1610b0e..5a0f136bf 100644 --- a/lib/compat/statusdatawriter.cpp +++ b/lib/compat/statusdatawriter.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "compat/statusdatawriter.hpp" #include "compat/statusdatawriter-ti.cpp" @@ -39,7 +22,6 @@ #include "base/application.hpp" #include "base/context.hpp" #include "base/statsfunction.hpp" -#include #include #include #include @@ -78,7 +60,7 @@ void StatusDataWriter::Start(bool runtimeCreated) << "'" << GetName() << "' started."; Log(LogWarning, "StatusDataWriter") - << "The StatusDataWriter feature is DEPRECATED and will be removed in Icinga v2.11."; + << "This feature is DEPRECATED and will be removed in future releases. Check the roadmap at https://github.com/Icinga/icinga2/milestones"; m_ObjectsCacheOutdated = true; @@ -394,7 +376,7 @@ void StatusDataWriter::DumpCheckableStatusAttrs(std::ostream& fp, const Checkabl "\t" "last_state_change=" << static_cast(checkable->GetLastStateChange()) << "\n" "\t" "last_hard_state_change=" << static_cast(checkable->GetLastHardStateChange()) << "\n" "\t" "last_update=" << static_cast(Utility::GetTime()) << "\n" - "\t" "notifications_enabled" "\t" << Convert::ToLong(checkable->GetEnableNotifications()) << "\n" + "\t" "notifications_enabled=" << Convert::ToLong(checkable->GetEnableNotifications()) << "\n" "\t" "active_checks_enabled=" << Convert::ToLong(checkable->GetEnableActiveChecks()) << "\n" "\t" "passive_checks_enabled=" << Convert::ToLong(checkable->GetEnablePassiveChecks()) << "\n" "\t" "flap_detection_enabled=" << Convert::ToLong(checkable->GetEnableFlapping()) << "\n" @@ -559,6 +541,7 @@ void StatusDataWriter::UpdateObjectsCache() { CONTEXT("Writing objects.cache file"); + /* Use the compat path here from the .ti generated class. */ String objectsPath = GetObjectsPath(); std::fstream objectfp; @@ -779,16 +762,7 @@ void StatusDataWriter::UpdateObjectsCache() objectfp.close(); -#ifdef _WIN32 - _unlink(objectsPath.CStr()); -#endif /* _WIN32 */ - - if (rename(tempObjectsPath.CStr(), objectsPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempObjectsPath)); - } + Utility::RenameFile(tempObjectsPath, objectsPath); } /** @@ -861,16 +835,7 @@ void StatusDataWriter::StatusTimerHandler() statusfp.close(); -#ifdef _WIN32 - _unlink(statusPath.CStr()); -#endif /* _WIN32 */ - - if (rename(tempStatusPath.CStr(), statusPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempStatusPath)); - } + Utility::RenameFile(tempStatusPath, statusPath); Log(LogNotice, "StatusDataWriter") << "Writing status.dat file took " << Utility::FormatDuration(Utility::GetTime() - start); diff --git a/lib/compat/statusdatawriter.hpp b/lib/compat/statusdatawriter.hpp index b8fb71d33..31a5efe54 100644 --- a/lib/compat/statusdatawriter.hpp +++ b/lib/compat/statusdatawriter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef STATUSDATAWRITER_H #define STATUSDATAWRITER_H diff --git a/lib/compat/statusdatawriter.ti b/lib/compat/statusdatawriter.ti index 5ac2969a9..cc7eb113f 100644 --- a/lib/compat/statusdatawriter.ti +++ b/lib/compat/statusdatawriter.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" #include "base/application.hpp" @@ -30,10 +13,10 @@ class StatusDataWriter : ConfigObject activation_priority 100; [config] String status_path { - default {{{ return Application::GetLocalStateDir() + "/cache/icinga2/status.dat"; }}} + default {{{ return Configuration::CacheDir + "/status.dat"; }}} }; [config] String objects_path { - default {{{ return Application::GetLocalStateDir() + "/cache/icinga2/objects.cache"; }}} + default {{{ return Configuration::CacheDir + "/objects.cache"; }}} }; [config] double update_interval { default {{{ return 15; }}} diff --git a/lib/config/CMakeLists.txt b/lib/config/CMakeLists.txt index 471384cbb..042668dc3 100644 --- a/lib/config/CMakeLists.txt +++ b/lib/config/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ find_package(BISON 2.3.0 REQUIRED) find_package(FLEX 2.5.31 REQUIRED) diff --git a/lib/config/activationcontext.cpp b/lib/config/activationcontext.cpp index c22f41caf..d05087552 100644 --- a/lib/config/activationcontext.cpp +++ b/lib/config/activationcontext.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/activationcontext.hpp" #include "base/exception.hpp" diff --git a/lib/config/activationcontext.hpp b/lib/config/activationcontext.hpp index c5a275631..3fe5d09ab 100644 --- a/lib/config/activationcontext.hpp +++ b/lib/config/activationcontext.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ACTIVATIONCONTEXT_H #define ACTIVATIONCONTEXT_H diff --git a/lib/config/applyrule.cpp b/lib/config/applyrule.cpp index c8f012e23..f7b7cbdd2 100644 --- a/lib/config/applyrule.cpp +++ b/lib/config/applyrule.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/applyrule.hpp" #include "base/logger.hpp" @@ -158,11 +141,11 @@ std::vector& ApplyRule::GetRules(const String& type) return it->second; } -void ApplyRule::CheckMatches() +void ApplyRule::CheckMatches(bool silent) { for (const RuleMap::value_type& kv : m_Rules) { for (const ApplyRule& rule : kv.second) { - if (!rule.HasMatches()) + if (!rule.HasMatches() && !silent) Log(LogWarning, "ApplyRule") << "Apply rule '" << rule.GetName() << "' (" << rule.GetDebugInfo() << ") for type '" << kv.first << "' does not match anywhere!"; } diff --git a/lib/config/applyrule.hpp b/lib/config/applyrule.hpp index 91623df75..bc4868b9e 100644 --- a/lib/config/applyrule.hpp +++ b/lib/config/applyrule.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef APPLYRULE_H #define APPLYRULE_H @@ -62,7 +45,7 @@ public: static bool IsValidTargetType(const String& sourceType, const String& targetType); static std::vector GetTargetTypes(const String& sourceType); - static void CheckMatches(); + static void CheckMatches(bool silent); private: String m_TargetType; diff --git a/lib/config/config_lexer.ll b/lib/config/config_lexer.ll index 6a2d84027..71f1b96c7 100644 --- a/lib/config/config_lexer.ll +++ b/lib/config/config_lexer.ll @@ -1,22 +1,5 @@ %{ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/configcompiler.hpp" #include "config/expression.hpp" @@ -180,7 +163,7 @@ this return T_THIS; globals return T_GLOBALS; locals return T_LOCALS; use return T_USE; -__using return T_USING; +using return T_USING; apply return T_APPLY; default return T_DEFAULT; to return T_TO; @@ -203,6 +186,7 @@ ignore_on_error return T_IGNORE_ON_ERROR; current_filename return T_CURRENT_FILENAME; current_line return T_CURRENT_LINE; debugger return T_DEBUGGER; +namespace return T_NAMESPACE; =\> return T_FOLLOWS; \<\< return T_SHIFT_LEFT; \>\> return T_SHIFT_RIGHT; diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 70f309bd3..4e0d536cc 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -1,24 +1,7 @@ %{ #define YYDEBUG 1 - -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ + +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/i2-config.hpp" #include "config/configcompiler.hpp" @@ -149,8 +132,9 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig %token T_CURRENT_FILENAME "current_filename (T_CURRENT_FILENAME)" %token T_CURRENT_LINE "current_line (T_CURRENT_LINE)" %token T_DEBUGGER "debugger (T_DEBUGGER)" +%token T_NAMESPACE "namespace (T_NAMESPACE)" %token T_USE "use (T_USE)" -%token T_USING "__using (T_USING)" +%token T_USING "using (T_USING)" %token T_OBJECT "object (T_OBJECT)" %token T_TEMPLATE "template (T_TEMPLATE)" %token T_INCLUDE "include (T_INCLUDE)" @@ -227,6 +211,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig %left T_PLUS T_MINUS %left T_MULTIPLY T_DIVIDE_OP T_MODULO %left UNARY_MINUS UNARY_PLUS +%right REF_OP DEREF_OP %right '!' '~' %left '.' '(' '[' %left T_VAR T_THIS T_GLOBALS T_LOCALS @@ -469,6 +454,10 @@ combined_set_op: T_SET | T_SET_BINARY_OR ; +optional_var: /* empty */ + | T_VAR + ; + lterm: T_LIBRARY rterm { $$ = new LibraryExpression(std::unique_ptr($2), @$); @@ -597,13 +586,27 @@ lterm: T_LIBRARY rterm { $$ = new BreakpointExpression(@$); } + | T_NAMESPACE rterm + { + BeginFlowControlBlock(context, FlowControlReturn, false); + } + rterm_scope_require_side_effect + { + EndFlowControlBlock(context); + + std::unique_ptr expr{$2}; + BindToScope(expr, ScopeGlobal); + $$ = new SetExpression(std::move(expr), OpSetLiteral, std::unique_ptr(new NamespaceExpression(std::unique_ptr($4), @$)), @$); + } | T_USING rterm { - $$ = new UsingExpression(std::unique_ptr($2), @$); + std::shared_ptr expr{$2}; + context->AddImport(expr); + $$ = MakeLiteralRaw(); } | apply | object - | T_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')' + | T_FOR '(' optional_var identifier T_FOLLOWS optional_var identifier T_IN rterm ')' { BeginFlowControlBlock(context, FlowControlContinue | FlowControlBreak, true); } @@ -611,11 +614,11 @@ lterm: T_LIBRARY rterm { EndFlowControlBlock(context); - $$ = new ForExpression(*$3, *$5, std::unique_ptr($7), std::unique_ptr($10), @$); - delete $3; - delete $5; + $$ = new ForExpression(*$4, *$7, std::unique_ptr($9), std::unique_ptr($12), @$); + delete $4; + delete $7; } - | T_FOR '(' identifier T_IN rterm ')' + | T_FOR '(' optional_var identifier T_IN rterm ')' { BeginFlowControlBlock(context, FlowControlContinue | FlowControlBreak, true); } @@ -623,8 +626,8 @@ lterm: T_LIBRARY rterm { EndFlowControlBlock(context); - $$ = new ForExpression(*$3, "", std::unique_ptr($5), std::unique_ptr($8), @$); - delete $3; + $$ = new ForExpression(*$4, "", std::unique_ptr($6), std::unique_ptr($9), @$); + delete $4; } | T_FUNCTION identifier '(' identifier_items ')' use_specifier { @@ -643,7 +646,7 @@ lterm: T_LIBRARY rterm } | T_CONST T_IDENTIFIER T_SET rterm { - $$ = new SetExpression(MakeIndexer(ScopeGlobal, *$2), OpSetLiteral, std::unique_ptr($4)); + $$ = new SetConstExpression(*$2, std::unique_ptr($4), @$); delete $2; } | T_VAR rterm @@ -874,9 +877,17 @@ rterm_no_side_effect_no_dict: T_STRING } | T_IDENTIFIER { - $$ = new VariableExpression(*$1, @1); + $$ = new VariableExpression(*$1, context->GetImports(), @1); delete $1; } + | T_MULTIPLY rterm %prec DEREF_OP + { + $$ = new DerefExpression(std::unique_ptr($2), @$); + } + | T_BINARY_AND rterm %prec REF_OP + { + $$ = new RefExpression(std::unique_ptr($2), @$); + } | '!' rterm { $$ = new LogicalNegateExpression(std::unique_ptr($2), @$); @@ -1092,7 +1103,7 @@ use_specifier_items: use_specifier_item use_specifier_item: identifier { - $$ = new std::pair >(*$1, std::unique_ptr(new VariableExpression(*$1, @1))); + $$ = new std::pair >(*$1, std::unique_ptr(new VariableExpression(*$1, context->GetImports(), @1))); delete $1; } | identifier T_SET rterm @@ -1103,24 +1114,24 @@ use_specifier_item: identifier ; apply_for_specifier: /* empty */ - | T_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')' + | T_FOR '(' optional_var identifier T_FOLLOWS optional_var identifier T_IN rterm ')' { - context->m_FKVar.top() = *$3; - delete $3; + context->m_FKVar.top() = *$4; + delete $4; - context->m_FVVar.top() = *$5; - delete $5; + context->m_FVVar.top() = *$7; + delete $7; - context->m_FTerm.top() = $7; + context->m_FTerm.top() = $9; } - | T_FOR '(' identifier T_IN rterm ')' + | T_FOR '(' optional_var identifier T_IN rterm ')' { - context->m_FKVar.top() = *$3; - delete $3; + context->m_FKVar.top() = *$4; + delete $4; context->m_FVVar.top() = ""; - context->m_FTerm.top() = $5; + context->m_FTerm.top() = $6; } ; diff --git a/lib/config/configcompiler.cpp b/lib/config/configcompiler.cpp index f8b754bb9..8f450994f 100644 --- a/lib/config/configcompiler.cpp +++ b/lib/config/configcompiler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/configcompiler.hpp" #include "config/configitem.hpp" @@ -340,7 +323,8 @@ bool ConfigCompiler::HasZoneConfigAuthority(const String& zoneName) if (!empty) { std::vector paths; paths.reserve(zoneDirs.size()); -for (const ZoneFragment& zf : zoneDirs) { + + for (const ZoneFragment& zf : zoneDirs) { paths.push_back(zf.Path); } @@ -361,3 +345,12 @@ bool ConfigCompiler::IsAbsolutePath(const String& path) #endif /* _WIN32 */ } +void ConfigCompiler::AddImport(const std::shared_ptr& import) +{ + m_Imports.push_back(import); +} + +std::vector > ConfigCompiler::GetImports() const +{ + return m_Imports; +} diff --git a/lib/config/configcompiler.hpp b/lib/config/configcompiler.hpp index d4a03681e..b89fdf05c 100644 --- a/lib/config/configcompiler.hpp +++ b/lib/config/configcompiler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGCOMPILER_H #define CONFIGCOMPILER_H @@ -109,6 +92,9 @@ public: void SetPackage(const String& package); String GetPackage() const; + void AddImport(const std::shared_ptr& import); + std::vector > GetImports() const; + static void CollectIncludes(std::vector >& expressions, const String& file, const String& zone, const String& package); @@ -134,6 +120,7 @@ private: std::istream *m_Input; String m_Zone; String m_Package; + std::vector > m_Imports; void *m_Scanner; diff --git a/lib/config/configcompilercontext.cpp b/lib/config/configcompilercontext.cpp index b298a33ec..7be89fff6 100644 --- a/lib/config/configcompilercontext.cpp +++ b/lib/config/configcompilercontext.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/configcompilercontext.hpp" #include "base/singleton.hpp" @@ -23,6 +6,7 @@ #include "base/netstring.hpp" #include "base/exception.hpp" #include "base/application.hpp" +#include "base/utility.hpp" using namespace icinga; @@ -79,15 +63,6 @@ void ConfigCompilerContext::FinishObjectsFile() delete m_ObjectsFP; m_ObjectsFP = nullptr; -#ifdef _WIN32 - _unlink(m_ObjectsPath.CStr()); -#endif /* _WIN32 */ - - if (rename(m_ObjectsTempFile.CStr(), m_ObjectsPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(m_ObjectsTempFile)); - } + Utility::RenameFile(m_ObjectsTempFile, m_ObjectsPath); } diff --git a/lib/config/configcompilercontext.hpp b/lib/config/configcompilercontext.hpp index 7a7fb54c8..58c55d539 100644 --- a/lib/config/configcompilercontext.hpp +++ b/lib/config/configcompilercontext.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGCOMPILERCONTEXT_H #define CONFIGCOMPILERCONTEXT_H diff --git a/lib/config/configfragment.hpp b/lib/config/configfragment.hpp index bd3c5939b..55038f8bf 100644 --- a/lib/config/configfragment.hpp +++ b/lib/config/configfragment.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGFRAGMENT_H #define CONFIGFRAGMENT_H @@ -26,6 +9,7 @@ #include "base/exception.hpp" #include "base/application.hpp" +/* Ensure that the priority is lower than the basic namespace initialization in scriptframe.cpp. */ #define REGISTER_CONFIG_FRAGMENT(name, fragment) \ INITIALIZE_ONCE_WITH_PRIORITY([]() { \ std::unique_ptr expression = icinga::ConfigCompiler::CompileText(name, fragment); \ diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index c790f8794..5c9f0dd55 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/configitem.hpp" #include "config/configcompilercontext.hpp" @@ -36,8 +19,12 @@ #include "base/json.hpp" #include "base/exception.hpp" #include "base/function.hpp" +#include "base/utility.hpp" +#include #include #include +#include +#include using namespace icinga; @@ -47,7 +34,7 @@ ConfigItem::TypeMap ConfigItem::m_DefaultTemplates; ConfigItem::ItemList ConfigItem::m_UnnamedItems; ConfigItem::IgnoredItemList ConfigItem::m_IgnoredItems; -REGISTER_SCRIPTFUNCTION_NS(Internal, run_with_activation_context, &ConfigItem::RunWithActivationContext, "func"); +REGISTER_FUNCTION(Internal, run_with_activation_context, &ConfigItem::RunWithActivationContext, "func"); /** * Constructor for the ConfigItem class. @@ -289,6 +276,14 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard) throw; } + Value serializedObject; + + try { + serializedObject = Serialize(dobj, FAConfig); + } catch (const CircularReferenceError& ex) { + BOOST_THROW_EXCEPTION(ValidationError(dobj, ex.GetPath(), "Circular references are not allowed")); + } + Dictionary::Ptr persistentItem = new Dictionary({ { "type", type->GetName() }, { "name", GetName() }, @@ -428,27 +423,26 @@ bool ConfigItem::CommitNewItems(const ActivationContext::Ptr& context, WorkQueue if (items.empty()) return true; + // Shuffle all items to evenly distribute them over the threads of the workqueue. This increases perfomance + // noticably in environments with lots of objects and available threads. + std::shuffle(std::begin(items), std::end(items), std::default_random_engine {}); + +#ifdef I2_DEBUG + Log(LogDebug, "configitem") + << "Committing " << items.size() << " new items."; +#endif /* I2_DEBUG */ + for (const auto& ip : items) newItems.push_back(ip.first); - upq.ParallelFor(items, [](const ItemPair& ip) { - ip.first->Commit(ip.second); - }); - - upq.Join(); - - if (upq.HasExceptions()) - return false; - std::set types; + std::set completed_types; for (const Type::Ptr& type : Type::GetAllTypes()) { if (ConfigObject::TypeInstance->IsAssignableFrom(type)) types.insert(type); } - std::set completed_types; - while (types.size() != completed_types.size()) { for (const Type::Ptr& type : types) { if (completed_types.find(type) != completed_types.end()) @@ -468,7 +462,60 @@ bool ConfigItem::CommitNewItems(const ActivationContext::Ptr& context, WorkQueue if (unresolved_dep) continue; - upq.ParallelFor(items, [&type](const ItemPair& ip) { + int committed_items = 0; + upq.ParallelFor(items, [&type, &committed_items](const ItemPair& ip) { + const ConfigItem::Ptr& item = ip.first; + + if (item->m_Type != type) + return; + + ip.first->Commit(ip.second); + committed_items++; + }); + + upq.Join(); + + completed_types.insert(type); + +#ifdef I2_DEBUG + if (committed_items > 0) + Log(LogDebug, "configitem") + << "Committed " << committed_items << " items of type '" << type->GetName() << "'."; +#endif /* I2_DEBUG */ + + if (upq.HasExceptions()) + return false; + } + } + +#ifdef I2_DEBUG + Log(LogDebug, "configitem") + << "Committed " << items.size() << " items."; +#endif /* I2_DEBUG */ + + completed_types.clear(); + + while (types.size() != completed_types.size()) { + for (const Type::Ptr& type : types) { + if (completed_types.find(type) != completed_types.end()) + continue; + + bool unresolved_dep = false; + + /* skip this type (for now) if there are unresolved load dependencies */ + for (const String& loadDep : type->GetLoadDependencies()) { + Type::Ptr pLoadDep = Type::GetByName(loadDep); + if (types.find(pLoadDep) != types.end() && completed_types.find(pLoadDep) == completed_types.end()) { + unresolved_dep = true; + break; + } + } + + if (unresolved_dep) + continue; + + int notified_items = 0; + upq.ParallelFor(items, [&type, ¬ified_items](const ItemPair& ip) { const ConfigItem::Ptr& item = ip.first; if (!item->m_Object || item->m_Type != type) @@ -476,6 +523,7 @@ bool ConfigItem::CommitNewItems(const ActivationContext::Ptr& context, WorkQueue try { item->m_Object->OnAllConfigLoaded(); + notified_items++; } catch (const std::exception& ex) { if (!item->m_IgnoreOnError) throw; @@ -496,11 +544,18 @@ bool ConfigItem::CommitNewItems(const ActivationContext::Ptr& context, WorkQueue upq.Join(); +#ifdef I2_DEBUG + if (notified_items > 0) + Log(LogDebug, "configitem") + << "Sent OnAllConfigLoaded to " << notified_items << " items of type '" << type->GetName() << "'."; +#endif /* I2_DEBUG */ + if (upq.HasExceptions()) return false; + notified_items = 0; for (const String& loadDep : type->GetLoadDependencies()) { - upq.ParallelFor(items, [loadDep, &type](const ItemPair& ip) { + upq.ParallelFor(items, [loadDep, &type, ¬ified_items](const ItemPair& ip) { const ConfigItem::Ptr& item = ip.first; if (!item->m_Object || item->m_Type->GetName() != loadDep) @@ -508,14 +563,22 @@ bool ConfigItem::CommitNewItems(const ActivationContext::Ptr& context, WorkQueue ActivationScope ascope(item->m_ActivationContext); item->m_Object->CreateChildObjects(type); + notified_items++; }); } upq.Join(); +#ifdef I2_DEBUG + if (notified_items > 0) + Log(LogDebug, "configitem") + << "Sent CreateChildObjects to " << notified_items << " items of type '" << type->GetName() << "'."; +#endif /* I2_DEBUG */ + if (upq.HasExceptions()) return false; + // Make sure to activate any additionally generated items if (!CommitNewItems(context, upq, newItems)) return false; } @@ -539,7 +602,7 @@ bool ConfigItem::CommitItems(const ActivationContext::Ptr& context, WorkQueue& u return false; } - ApplyRule::CheckMatches(); + ApplyRule::CheckMatches(silent); if (!silent) { /* log stats for external parsers */ @@ -561,15 +624,16 @@ bool ConfigItem::CommitItems(const ActivationContext::Ptr& context, WorkQueue& u return true; } -bool ConfigItem::ActivateItems(WorkQueue& upq, const std::vector& newItems, bool runtimeCreated, bool silent, bool withModAttrs) +bool ConfigItem::ActivateItems(WorkQueue& upq, const std::vector& newItems, bool runtimeCreated, + bool silent, bool withModAttrs, const Value& cookie) { static boost::mutex mtx; boost::mutex::scoped_lock lock(mtx); if (withModAttrs) { /* restore modified attributes */ - if (Utility::PathExists(Application::GetModAttrPath())) { - std::unique_ptr expression = ConfigCompiler::CompileFile(Application::GetModAttrPath()); + if (Utility::PathExists(Configuration::ModAttrPath)) { + std::unique_ptr expression = ConfigCompiler::CompileFile(Configuration::ModAttrPath); if (expression) { try { @@ -625,11 +689,11 @@ bool ConfigItem::ActivateItems(WorkQueue& upq, const std::vectorGetName() << "' of type '" - << objectType->GetName() << "' with priority '" + << objectType->GetName() << "' with priority " << objectType->GetActivationPriority(); #endif /* I2_DEBUG */ - object->Activate(runtimeCreated); + object->Activate(runtimeCreated, cookie); } } @@ -666,7 +730,7 @@ bool ConfigItem::RunWithActivationContext(const Function::Ptr& function) function->Invoke(); - WorkQueue upq(25000, Application::GetConcurrency()); + WorkQueue upq(25000, Configuration::Concurrency); upq.SetName("ConfigItem::RunWithActivationContext"); std::vector newItems; diff --git a/lib/config/configitem.hpp b/lib/config/configitem.hpp index f560fa64d..0b7ee86a3 100644 --- a/lib/config/configitem.hpp +++ b/lib/config/configitem.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGITEM_H #define CONFIGITEM_H @@ -70,7 +53,8 @@ public: const String& name); static bool CommitItems(const ActivationContext::Ptr& context, WorkQueue& upq, std::vector& newItems, bool silent = false); - static bool ActivateItems(WorkQueue& upq, const std::vector& newItems, bool runtimeCreated = false, bool silent = false, bool withModAttrs = false); + static bool ActivateItems(WorkQueue& upq, const std::vector& newItems, bool runtimeCreated = false, + bool silent = false, bool withModAttrs = false, const Value& cookie = Empty); static bool RunWithActivationContext(const Function::Ptr& function); diff --git a/lib/config/configitembuilder.cpp b/lib/config/configitembuilder.cpp index d81b595d2..6a64c76a8 100644 --- a/lib/config/configitembuilder.cpp +++ b/lib/config/configitembuilder.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/configitembuilder.hpp" #include "base/configtype.hpp" @@ -31,6 +14,7 @@ ConfigItemBuilder::ConfigItemBuilder(const DebugInfo& debugInfo) void ConfigItemBuilder::SetType(const Type::Ptr& type) { + ASSERT(type); m_Type = type; } diff --git a/lib/config/configitembuilder.hpp b/lib/config/configitembuilder.hpp index 2144bdbe2..792e351b7 100644 --- a/lib/config/configitembuilder.hpp +++ b/lib/config/configitembuilder.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGITEMBUILDER_H #define CONFIGITEMBUILDER_H diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index 938deb69b..b02f2b73c 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/expression.hpp" #include "config/configitem.hpp" @@ -28,6 +11,8 @@ #include "base/exception.hpp" #include "base/scriptglobal.hpp" #include "base/loader.hpp" +#include "base/reference.hpp" +#include "base/namespace.hpp" #include #include @@ -115,6 +100,15 @@ const DebugInfo& DebuggableExpression::GetDebugInfo() const return m_DebugInfo; } +VariableExpression::VariableExpression(String variable, std::vector > imports, const DebugInfo& debugInfo) + : DebuggableExpression(debugInfo), m_Variable(std::move(variable)), m_Imports(std::move(imports)) +{ + m_Imports.push_back(MakeIndexer(ScopeGlobal, "System")); + m_Imports.push_back(std::unique_ptr(new IndexerExpression(MakeIndexer(ScopeGlobal, "System"), MakeLiteral("Configuration")))); + m_Imports.push_back(MakeIndexer(ScopeGlobal, "Types")); + m_Imports.push_back(MakeIndexer(ScopeGlobal, "Icinga")); +} + ExpressionResult VariableExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { Value value; @@ -123,7 +117,7 @@ ExpressionResult VariableExpression::DoEvaluate(ScriptFrame& frame, DebugHint *d return value; else if (frame.Self.IsObject() && frame.Locals != frame.Self.Get() && frame.Self.Get()->GetOwnField(m_Variable, &value)) return value; - else if (VMOps::FindVarImport(frame, m_Variable, &value, m_DebugInfo)) + else if (VMOps::FindVarImport(frame, m_Imports, m_Variable, &value, m_DebugInfo)) return value; else return ScriptGlobal::Get(m_Variable); @@ -143,7 +137,7 @@ bool VariableExpression::GetReference(ScriptFrame& frame, bool init_dict, Value if (dhint && *dhint) *dhint = new DebugHint((*dhint)->GetChild(m_Variable)); - } else if (VMOps::FindVarImportRef(frame, m_Variable, parent, m_DebugInfo)) { + } else if (VMOps::FindVarImportRef(frame, m_Imports, m_Variable, parent, m_DebugInfo)) { return true; } else if (ScriptGlobal::Exists(m_Variable)) { *parent = ScriptGlobal::GetGlobals(); @@ -156,6 +150,47 @@ bool VariableExpression::GetReference(ScriptFrame& frame, bool init_dict, Value return true; } +ExpressionResult RefExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + Value parent; + String index; + + if (!m_Operand->GetReference(frame, false, &parent, &index, &dhint)) + BOOST_THROW_EXCEPTION(ScriptError("Cannot obtain reference for expression.", m_DebugInfo)); + + if (!parent.IsObject()) + BOOST_THROW_EXCEPTION(ScriptError("Cannot obtain reference for expression because parent is not an object.", m_DebugInfo)); + + return new Reference(parent, index); +} + +ExpressionResult DerefExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand = m_Operand->Evaluate(frame); + CHECK_RESULT(operand); + + Object::Ptr obj = operand.GetValue(); + Reference::Ptr ref = dynamic_pointer_cast(obj); + + if (!ref) + BOOST_THROW_EXCEPTION(ScriptError("Invalid reference specified.", GetDebugInfo())); + + return ref->Get(); +} + +bool DerefExpression::GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const +{ + ExpressionResult operand = m_Operand->Evaluate(frame); + if (operand.GetCode() != ResultOK) + return false; + + Reference::Ptr ref = operand.GetValue(); + + *parent = ref->GetParent(); + *index = ref->GetIndex(); + return true; +} + ExpressionResult NegateExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { ExpressionResult operand = m_Operand->Evaluate(frame); @@ -517,6 +552,58 @@ ExpressionResult GetScopeExpression::DoEvaluate(ScriptFrame& frame, DebugHint *d VERIFY(!"Invalid scope."); } +static inline +void WarnOnImplicitlySetGlobalVar(const std::unique_ptr& setLhs, const Value& setLhsParent, CombinedSetOp setOp, const DebugInfo& debug) +{ + auto var (dynamic_cast(setLhs.get())); + + if (var && setLhsParent.IsObject()) { + auto ns (dynamic_pointer_cast(setLhsParent.Get())); + + if (ns && ns == ScriptGlobal::GetGlobals()) { + const char *opStr = nullptr; + + switch (setOp) { + case OpSetLiteral: + opStr = "="; + break; + case OpSetAdd: + opStr = "+="; + break; + case OpSetSubtract: + opStr = "-="; + break; + case OpSetMultiply: + opStr = "*="; + break; + case OpSetDivide: + opStr = "/="; + break; + case OpSetModulo: + opStr = "%="; + break; + case OpSetXor: + opStr = "^="; + break; + case OpSetBinaryAnd: + opStr = "&="; + break; + case OpSetBinaryOr: + opStr = "|="; + break; + default: + VERIFY(!"Invalid opcode."); + } + + auto varName (var->GetVariable()); + + Log(LogWarning, "config") + << "Global variable '" << varName << "' has been set implicitly via '" << varName << ' ' << opStr << " ...' " << debug << "." + " Please set it explicitly via 'globals." << varName << ' ' << opStr << " ...' instead."; + } + } +} + ExpressionResult SetExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { if (frame.Sandboxed) @@ -566,7 +653,7 @@ ExpressionResult SetExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) } } - VMOps::SetField(parent, index, operand2.GetValue(), m_DebugInfo); + VMOps::SetField(parent, index, operand2.GetValue(), m_OverrideFrozen, m_DebugInfo); if (psdhint) { psdhint->AddMessage("=", m_DebugInfo); @@ -575,6 +662,35 @@ ExpressionResult SetExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) delete psdhint; } + WarnOnImplicitlySetGlobalVar(m_Operand1, parent, m_Op, m_DebugInfo); + + return Empty; +} + +void SetExpression::SetOverrideFrozen() +{ + m_OverrideFrozen = true; +} + +ExpressionResult SetConstExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + auto globals = ScriptGlobal::GetGlobals(); + + auto attr = globals->GetAttribute(m_Name); + + if (dynamic_pointer_cast(attr)) { + std::ostringstream msgbuf; + msgbuf << "Value for constant '" << m_Name << "' was modified. This behaviour is deprecated.\n"; + ShowCodeLocation(msgbuf, GetDebugInfo(), false); + Log(LogWarning, msgbuf.str()); + } + + ExpressionResult operandres = m_Operand->Evaluate(frame); + CHECK_RESULT(operandres); + Value operand = operandres.GetValue(); + + globals->SetAttribute(m_Name, std::make_shared(operand)); + return Empty; } @@ -654,10 +770,19 @@ bool IndexerExpression::GetReference(ScriptFrame& frame, bool init_dict, Value * if (m_Operand1->GetReference(frame, init_dict, &vparent, &vindex, &psdhint)) { if (init_dict) { - Value old_value = VMOps::GetField(vparent, vindex, frame.Sandboxed, m_Operand1->GetDebugInfo()); + Value old_value; + bool has_field = true; + + if (vparent.IsObject()) { + Object::Ptr oparent = vparent; + has_field = oparent->HasOwnField(vindex); + } + + if (has_field) + old_value = VMOps::GetField(vparent, vindex, frame.Sandboxed, m_Operand1->GetDebugInfo()); if (old_value.IsEmpty() && !old_value.IsString()) - VMOps::SetField(vparent, vindex, new Dictionary(), m_Operand1->GetDebugInfo()); + VMOps::SetField(vparent, vindex, new Dictionary(), m_OverrideFrozen, m_Operand1->GetDebugInfo()); } *parent = VMOps::GetField(vparent, vindex, frame.Sandboxed, m_DebugInfo); @@ -683,6 +808,11 @@ bool IndexerExpression::GetReference(ScriptFrame& frame, bool init_dict, Value * return true; } +void IndexerExpression::SetOverrideFrozen() +{ + m_OverrideFrozen = true; +} + void icinga::BindToScope(std::unique_ptr& expr, ScopeSpecifier scopeSpec) { auto *dexpr = dynamic_cast(expr.get()); @@ -799,6 +929,17 @@ ExpressionResult ApplyExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhin m_Package, m_FKVar, m_FVVar, m_FTerm, m_ClosedVars, m_IgnoreOnError, m_Expression, m_DebugInfo); } +ExpressionResult NamespaceExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + Namespace::Ptr ns = new Namespace(new ConstNamespaceBehavior()); + + ScriptFrame innerFrame(true, ns); + ExpressionResult result = m_Expression->Evaluate(innerFrame); + CHECK_RESULT(result); + + return ns; +} + ExpressionResult ObjectExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { if (frame.Sandboxed) @@ -922,23 +1063,6 @@ ExpressionResult BreakpointExpression::DoEvaluate(ScriptFrame& frame, DebugHint return Empty; } -ExpressionResult UsingExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const -{ - if (frame.Sandboxed) - BOOST_THROW_EXCEPTION(ScriptError("Using directives are not allowed in sandbox mode.", m_DebugInfo)); - - ExpressionResult importres = m_Name->Evaluate(frame); - CHECK_RESULT(importres); - Value import = importres.GetValue(); - - if (!import.IsObjectType()) - BOOST_THROW_EXCEPTION(ScriptError("The parameter must resolve to an object of type 'Dictionary'", m_DebugInfo)); - - ScriptFrame::AddImport(import); - - return Empty; -} - ExpressionResult TryExceptExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { try { diff --git a/lib/config/expression.hpp b/lib/config/expression.hpp index ce3f438e6..6c36f06e7 100644 --- a/lib/config/expression.hpp +++ b/lib/config/expression.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef EXPRESSION_H #define EXPRESSION_H @@ -305,9 +288,7 @@ protected: class VariableExpression final : public DebuggableExpression { public: - VariableExpression(String variable, const DebugInfo& debugInfo = DebugInfo()) - : DebuggableExpression(debugInfo), m_Variable(std::move(variable)) - { } + VariableExpression(String variable, std::vector > imports, const DebugInfo& debugInfo = DebugInfo()); String GetVariable() const { @@ -320,10 +301,34 @@ protected: private: String m_Variable; + std::vector > m_Imports; friend void BindToScope(std::unique_ptr& expr, ScopeSpecifier scopeSpec); }; +class DerefExpression final : public UnaryExpression +{ +public: + DerefExpression(std::unique_ptr operand, const DebugInfo& debugInfo = DebugInfo()) + : UnaryExpression(std::move(operand), debugInfo) + { } + +protected: + ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; + bool GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const override; +}; + +class RefExpression final : public UnaryExpression +{ +public: + RefExpression(std::unique_ptr operand, const DebugInfo& debugInfo = DebugInfo()) + : UnaryExpression(std::move(operand), debugInfo) + { } + +protected: + ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; +}; + class NegateExpression final : public UnaryExpression { public: @@ -614,6 +619,19 @@ private: friend void BindToScope(std::unique_ptr& expr, ScopeSpecifier scopeSpec); }; +class SetConstExpression final : public UnaryExpression +{ +public: + SetConstExpression(const String& name, std::unique_ptr operand, const DebugInfo& debugInfo = DebugInfo()) + : UnaryExpression(std::move(operand), debugInfo), m_Name(name) + { } + +protected: + String m_Name; + + ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; +}; + class SetExpression final : public BinaryExpression { public: @@ -621,11 +639,14 @@ public: : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo), m_Op(op) { } + void SetOverrideFrozen(); + protected: ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; private: CombinedSetOp m_Op; + bool m_OverrideFrozen{false}; friend void BindToScope(std::unique_ptr& expr, ScopeSpecifier scopeSpec); }; @@ -716,7 +737,11 @@ public: : BinaryExpression(std::move(operand1), std::move(operand2), debugInfo) { } + void SetOverrideFrozen(); + protected: + bool m_OverrideFrozen{false}; + ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; bool GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const override; @@ -813,6 +838,20 @@ private: std::shared_ptr m_Expression; }; +class NamespaceExpression final : public DebuggableExpression +{ +public: + NamespaceExpression(std::unique_ptr expression, const DebugInfo& debugInfo = DebugInfo()) + : DebuggableExpression(debugInfo), m_Expression(std::move(expression)) + { } + +protected: + ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; + +private: + std::shared_ptr m_Expression; +}; + class ObjectExpression final : public DebuggableExpression { public: @@ -909,20 +948,6 @@ protected: ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; }; -class UsingExpression final : public DebuggableExpression -{ -public: - UsingExpression(std::unique_ptr name, const DebugInfo& debugInfo = DebugInfo()) - : DebuggableExpression(debugInfo), m_Name(std::move(name)) - { } - -protected: - ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; - -private: - std::unique_ptr m_Name; -}; - class TryExceptExpression final : public DebuggableExpression { public: diff --git a/lib/config/i2-config.hpp b/lib/config/i2-config.hpp index 5ec9145c3..8c26287f9 100644 --- a/lib/config/i2-config.hpp +++ b/lib/config/i2-config.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef I2CONFIG_H #define I2CONFIG_H diff --git a/lib/config/objectrule.cpp b/lib/config/objectrule.cpp index ce6c8d409..6a74a40b9 100644 --- a/lib/config/objectrule.cpp +++ b/lib/config/objectrule.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/objectrule.hpp" #include diff --git a/lib/config/objectrule.hpp b/lib/config/objectrule.hpp index 1e20f346c..d093c9f5f 100644 --- a/lib/config/objectrule.hpp +++ b/lib/config/objectrule.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef OBJECTRULE_H #define OBJECTRULE_H diff --git a/lib/config/vmops.hpp b/lib/config/vmops.hpp index 503381730..8bd456d40 100644 --- a/lib/config/vmops.hpp +++ b/lib/config/vmops.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef VMOPS_H #define VMOPS_H @@ -28,6 +11,7 @@ #include "base/debuginfo.hpp" #include "base/array.hpp" #include "base/dictionary.hpp" +#include "base/namespace.hpp" #include "base/function.hpp" #include "base/scriptglobal.hpp" #include "base/exception.hpp" @@ -42,15 +26,13 @@ namespace icinga class VMOps { public: - static inline bool FindVarImportRef(ScriptFrame& frame, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo()) + static inline bool FindVarImportRef(ScriptFrame& frame, const std::vector >& imports, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo()) { - Array::Ptr imports = ScriptFrame::GetImports(); - - ObjectLock olock(imports); - for (const Value& import : imports) { - Object::Ptr obj = import; + for (const auto& import : imports) { + ExpressionResult res = import->Evaluate(frame); + Object::Ptr obj = res.GetValue(); if (obj->HasOwnField(name)) { - *result = import; + *result = obj; return true; } } @@ -58,11 +40,11 @@ public: return false; } - static inline bool FindVarImport(ScriptFrame& frame, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo()) + static inline bool FindVarImport(ScriptFrame& frame, const std::vector >& imports, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo()) { Value parent; - if (FindVarImportRef(frame, name, &parent, debugInfo)) { + if (FindVarImportRef(frame, imports, name, &parent, debugInfo)) { *result = GetField(parent, name, frame.Sandboxed, debugInfo); return true; } @@ -225,6 +207,26 @@ public: ExpressionResult res = expression->Evaluate(frame); CHECK_RESULT_LOOP(res); } + } else if (value.IsObjectType()) { + if (fvvar.IsEmpty()) + BOOST_THROW_EXCEPTION(ScriptError("Cannot use array iterator for namespace.", debugInfo)); + + Namespace::Ptr ns = value; + std::vector keys; + + { + ObjectLock olock(ns); + for (const Namespace::Pair& kv : ns) { + keys.push_back(kv.first); + } + } + + for (const String& key : keys) { + frame.Locals->Set(fkvar, key); + frame.Locals->Set(fvvar, ns->Get(key)); + ExpressionResult res = expression->Evaluate(frame); + CHECK_RESULT_LOOP(res); + } } else BOOST_THROW_EXCEPTION(ScriptError("Invalid type in for expression: " + value.GetTypeName(), debugInfo)); @@ -233,10 +235,10 @@ public: static inline Value GetField(const Value& context, const String& field, bool sandboxed = false, const DebugInfo& debugInfo = DebugInfo()) { - if (unlikely(context.IsEmpty() && !context.IsString())) + if (BOOST_UNLIKELY(context.IsEmpty() && !context.IsString())) return Empty; - if (unlikely(!context.IsObject())) + if (BOOST_UNLIKELY(!context.IsObject())) return GetPrototypeField(context, field, true, debugInfo); Object::Ptr object = context; @@ -244,12 +246,12 @@ public: return object->GetFieldByName(field, sandboxed, debugInfo); } - static inline void SetField(const Object::Ptr& context, const String& field, const Value& value, const DebugInfo& debugInfo = DebugInfo()) + static inline void SetField(const Object::Ptr& context, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo = DebugInfo()) { if (!context) BOOST_THROW_EXCEPTION(ScriptError("Cannot set field '" + field + "' on a value that is not an object.", debugInfo)); - return context->SetFieldByName(field, value, debugInfo); + return context->SetFieldByName(field, value, overrideFrozen, debugInfo); } private: diff --git a/lib/db_ido/CMakeLists.txt b/lib/db_ido/CMakeLists.txt index 6b94a7b9d..7a97d276e 100644 --- a/lib/db_ido/CMakeLists.txt +++ b/lib/db_ido/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ mkclass_target(dbconnection.ti dbconnection-ti.cpp dbconnection-ti.hpp) diff --git a/lib/db_ido/commanddbobject.cpp b/lib/db_ido/commanddbobject.cpp index b097bed33..2ac167aab 100644 --- a/lib/db_ido/commanddbobject.cpp +++ b/lib/db_ido/commanddbobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/commanddbobject.hpp" #include "db_ido/dbtype.hpp" diff --git a/lib/db_ido/commanddbobject.hpp b/lib/db_ido/commanddbobject.hpp index 18534e423..6d227472b 100644 --- a/lib/db_ido/commanddbobject.hpp +++ b/lib/db_ido/commanddbobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef COMMANDDBOBJECT_H #define COMMANDDBOBJECT_H diff --git a/lib/db_ido/db_ido-itl.conf b/lib/db_ido/db_ido-itl.conf index e6958234a..e2c42c3d0 100644 --- a/lib/db_ido/db_ido-itl.conf +++ b/lib/db_ido/db_ido-itl.conf @@ -1,27 +1,8 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ System.assert(Internal.run_with_activation_context(function() { - var _Internal = Internal.clone() - - template CheckCommand "ido-check-command" use (_Internal) { - execute = _Internal.IdoCheck + template CheckCommand "ido-check-command" use (checkFunc = Internal.IdoCheck) { + execute = checkFunc } object CheckCommand "ido" { diff --git a/lib/db_ido/dbconnection.cpp b/lib/db_ido/dbconnection.cpp index 30f602d56..0ae9a80ca 100644 --- a/lib/db_ido/dbconnection.cpp +++ b/lib/db_ido/dbconnection.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/dbconnection.hpp" #include "db_ido/dbconnection-ti.cpp" @@ -149,6 +132,11 @@ void DbConnection::InsertRuntimeVariable(const String& key, const Value& value) void DbConnection::UpdateProgramStatus() { + IcingaApplication::Ptr icingaApplication = IcingaApplication::GetInstance(); + + if (!icingaApplication) + return; + Log(LogNotice, "DbConnection") << "Updating programstatus table."; @@ -166,18 +154,18 @@ void DbConnection::UpdateProgramStatus() { "status_update_time", DbValue::FromTimestamp(Utility::GetTime()) }, { "program_start_time", DbValue::FromTimestamp(Application::GetStartTime()) }, { "is_currently_running", 1 }, - { "endpoint_name", IcingaApplication::GetInstance()->GetNodeName() }, + { "endpoint_name", icingaApplication->GetNodeName() }, { "process_id", Utility::GetPid() }, { "daemon_mode", 1 }, { "last_command_check", DbValue::FromTimestamp(Utility::GetTime()) }, - { "notifications_enabled", (IcingaApplication::GetInstance()->GetEnableNotifications() ? 1 : 0) }, - { "active_host_checks_enabled", (IcingaApplication::GetInstance()->GetEnableHostChecks() ? 1 : 0) }, + { "notifications_enabled", (icingaApplication->GetEnableNotifications() ? 1 : 0) }, + { "active_host_checks_enabled", (icingaApplication->GetEnableHostChecks() ? 1 : 0) }, { "passive_host_checks_enabled", 1 }, - { "active_service_checks_enabled", (IcingaApplication::GetInstance()->GetEnableServiceChecks() ? 1 : 0) }, + { "active_service_checks_enabled", (icingaApplication->GetEnableServiceChecks() ? 1 : 0) }, { "passive_service_checks_enabled", 1 }, - { "event_handlers_enabled", (IcingaApplication::GetInstance()->GetEnableEventHandlers() ? 1 : 0) }, - { "flap_detection_enabled", (IcingaApplication::GetInstance()->GetEnableFlapping() ? 1 : 0) }, - { "process_performance_data", (IcingaApplication::GetInstance()->GetEnablePerfdata() ? 1 : 0) } + { "event_handlers_enabled", (icingaApplication->GetEnableEventHandlers() ? 1 : 0) }, + { "flap_detection_enabled", (icingaApplication->GetEnableFlapping() ? 1 : 0) }, + { "process_performance_data", (icingaApplication->GetEnablePerfdata() ? 1 : 0) } }); query1.WhereCriteria = new Dictionary({ @@ -392,7 +380,26 @@ bool DbConnection::GetStatusUpdate(const DbObject::Ptr& dbobj) const void DbConnection::UpdateObject(const ConfigObject::Ptr& object) { - if (!GetConnected() || Application::IsShuttingDown()) + bool isShuttingDown = Application::IsShuttingDown(); + bool isRestarting = Application::IsRestarting(); + +#ifdef I2_DEBUG + if (isShuttingDown || isRestarting) { + //Log(LogDebug, "DbConnection") + // << "Updating object '" << object->GetName() << "' \t\t active '" << Convert::ToLong(object->IsActive()) + // << "' shutting down '" << Convert::ToLong(isShuttingDown) << "' restarting '" << Convert::ToLong(isRestarting) << "'."; + } +#endif /* I2_DEBUG */ + + /* Wait until a database connection is established on reconnect. */ + if (!GetConnected()) + return; + + /* Don't update inactive objects during shutdown/reload/restart. + * They would be marked as deleted. This gets triggered with ConfigObject::StopObjects(). + * During startup/reconnect this is fine, the handler is not active there. + */ + if (isShuttingDown || isRestarting) return; DbObject::Ptr dbobj = DbObject::GetOrCreateByObject(object); @@ -419,7 +426,10 @@ void DbConnection::UpdateObject(const ConfigObject::Ptr& object) dbobj->SendConfigUpdateLight(); } } else if (!active) { - /* Deactivate the deleted object no matter + /* This may happen on reload/restart actions too + * and is blocked above already. + * + * Deactivate the deleted object no matter * which state it had in the database. */ DeactivateObject(dbobj); @@ -452,8 +462,8 @@ void DbConnection::ValidateFailoverTimeout(const Lazy& lvalue, const Val { ObjectImpl::ValidateFailoverTimeout(lvalue, utils); - if (lvalue() < 60) - BOOST_THROW_EXCEPTION(ValidationError(this, { "failover_timeout" }, "Failover timeout minimum is 60s.")); + if (lvalue() < 30) + BOOST_THROW_EXCEPTION(ValidationError(this, { "failover_timeout" }, "Failover timeout minimum is 30s.")); } void DbConnection::ValidateCategories(const Lazy& lvalue, const ValidationUtils& utils) diff --git a/lib/db_ido/dbconnection.hpp b/lib/db_ido/dbconnection.hpp index 20ea9324f..3cb049f64 100644 --- a/lib/db_ido/dbconnection.hpp +++ b/lib/db_ido/dbconnection.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DBCONNECTION_H #define DBCONNECTION_H diff --git a/lib/db_ido/dbconnection.ti b/lib/db_ido/dbconnection.ti index 2f6e60d02..ad02b4094 100644 --- a/lib/db_ido/dbconnection.ti +++ b/lib/db_ido/dbconnection.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/dbquery.hpp" #include "base/configobject.hpp" @@ -59,9 +42,11 @@ abstract class DbConnection : ConfigObject }; [config] double failover_timeout { - default {{{ return 60; }}} + default {{{ return 30; }}} }; + [state, no_user_modify] double last_failover; + [no_user_modify] String schema_version; [no_user_modify] bool connected; [no_user_modify] bool should_connect { diff --git a/lib/db_ido/dbevents.cpp b/lib/db_ido/dbevents.cpp index 97709793b..68bef4f8a 100644 --- a/lib/db_ido/dbevents.cpp +++ b/lib/db_ido/dbevents.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/dbevents.hpp" #include "db_ido/dbtype.hpp" @@ -73,7 +56,7 @@ void DbEvents::StaticInitialize() Checkable::OnStateChange.connect(std::bind(&DbEvents::AddStateChangeHistory, _1, _2, _3)); Checkable::OnNewCheckResult.connect(std::bind(&DbEvents::AddCheckResultLogHistory, _1, _2)); - Checkable::OnNotificationSentToUser.connect(std::bind(&DbEvents::AddNotificationSentLogHistory, _1, _2, _3, _4, _5, _6, _7)); + Checkable::OnNotificationSentToUser.connect(std::bind(&DbEvents::AddNotificationSentLogHistory, _1, _2, _3, _4, _5, _6, _7, _8)); Checkable::OnFlappingChanged.connect(std::bind(&DbEvents::AddFlappingChangedLogHistory, _1)); Checkable::OnEnableFlappingChanged.connect(std::bind(&DbEvents::AddEnableFlappingChangedLogHistory, _1)); Downtime::OnDowntimeTriggered.connect(std::bind(&DbEvents::AddTriggerDowntimeLogHistory, _1)); @@ -197,13 +180,7 @@ void DbEvents::ReachabilityChangedHandler(const Checkable::Ptr& checkable, const if (cr->GetState() == ServiceOK) is_reachable = 1; - Log(LogDebug, "DbEvents") - << "Updating reachability for checkable '" << checkable->GetName() << "': " << (is_reachable ? "" : "not" ) << " reachable for " << children.size() << " children."; - for (const Checkable::Ptr& child : children) { - Log(LogDebug, "DbEvents") - << "Updating reachability for checkable '" << child->GetName() << "': " << (is_reachable ? "" : "not" ) << " reachable."; - Host::Ptr host; Service::Ptr service; tie(host, service) = GetHostService(child); @@ -339,7 +316,6 @@ void DbEvents::AddCommentInternal(std::vector& queries, const Comment:: else if (checkable->GetReflectionType() == Service::TypeInstance) commentType = 1; else { - Log(LogDebug, "DbEvents", "unknown object type for adding comment."); return; } @@ -475,7 +451,6 @@ void DbEvents::AddDowntimeInternal(std::vector& queries, const Downtime else if (checkable->GetReflectionType() == Service::TypeInstance) downtimeType = 1; else { - Log(LogDebug, "DbEvents", "unknown object type for adding downtime."); return; } @@ -731,9 +706,6 @@ void DbEvents::TriggerDowntime(const Downtime::Ptr& downtime) void DbEvents::AddAcknowledgementHistory(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, bool notify, double expiry) { - Log(LogDebug, "DbEvents") - << "add acknowledgement history for '" << checkable->GetName() << "'"; - std::pair timeBag = ConvertTimestamp(Utility::GetTime()); DbQuery query1; @@ -775,17 +747,11 @@ void DbEvents::AddAcknowledgementHistory(const Checkable::Ptr& checkable, const void DbEvents::AddAcknowledgement(const Checkable::Ptr& checkable, AcknowledgementType type) { - Log(LogDebug, "DbEvents") - << "add acknowledgement for '" << checkable->GetName() << "'"; - AddAcknowledgementInternal(checkable, type, true); } void DbEvents::RemoveAcknowledgement(const Checkable::Ptr& checkable) { - Log(LogDebug, "DbEvents") - << "remove acknowledgement for '" << checkable->GetName() << "'"; - AddAcknowledgementInternal(checkable, AcknowledgementNone, false); } @@ -825,9 +791,6 @@ void DbEvents::AddAcknowledgementInternal(const Checkable::Ptr& checkable, Ackno void DbEvents::AddNotificationHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set& users, NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text) { - Log(LogDebug, "DbEvents") - << "add notification history for '" << checkable->GetName() << "'"; - /* start and end happen at the same time */ std::pair timeBag = ConvertTimestamp(Utility::GetTime()); @@ -875,9 +838,6 @@ void DbEvents::AddNotificationHistory(const Notification::Ptr& notification, con std::vector queries; for (const User::Ptr& user : users) { - Log(LogDebug, "DbEvents") - << "add contact notification history for service '" << checkable->GetName() << "' and user '" << user->GetName() << "'."; - DbQuery query2; query2.Table = "contactnotifications"; query2.Type = DbQueryInsert; @@ -902,9 +862,6 @@ void DbEvents::AddNotificationHistory(const Notification::Ptr& notification, con /* statehistory */ void DbEvents::AddStateChangeHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type) { - Log(LogDebug, "DbEvents") - << "add state change history for '" << checkable->GetName() << "'"; - double ts = cr->GetExecutionEnd(); std::pair timeBag = ConvertTimestamp(ts); @@ -1104,7 +1061,7 @@ void DbEvents::AddRemoveDowntimeLogHistory(const Downtime::Ptr& downtime) } void DbEvents::AddNotificationSentLogHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const User::Ptr& user, - NotificationType notification_type, const CheckResult::Ptr& cr, + NotificationType notification_type, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, const String& author, const String& comment_text) { CheckCommand::Ptr commandObj = checkable->GetCheckCommand(); @@ -1114,7 +1071,7 @@ void DbEvents::AddNotificationSentLogHistory(const Notification::Ptr& notificati if (commandObj) checkCommandName = commandObj->GetName(); - String notificationTypeStr = Notification::NotificationTypeToString(notification_type); + String notificationTypeStr = Notification::NotificationTypeToStringCompat(notification_type); //TODO: Change that to our own types. String author_comment = ""; if (notification_type == NotificationCustom || notification_type == NotificationAcknowledgement) { @@ -1227,9 +1184,6 @@ void DbEvents::AddEnableFlappingChangedLogHistory(const Checkable::Ptr& checkabl void DbEvents::AddLogHistory(const Checkable::Ptr& checkable, const String& buffer, LogEntryType type) { - Log(LogDebug, "DbEvents") - << "add log entry history for '" << checkable->GetName() << "'"; - std::pair timeBag = ConvertTimestamp(Utility::GetTime()); DbQuery query1; @@ -1260,9 +1214,6 @@ void DbEvents::AddLogHistory(const Checkable::Ptr& checkable, const String& buff /* flappinghistory */ void DbEvents::AddFlappingChangedHistory(const Checkable::Ptr& checkable) { - Log(LogDebug, "DbEvents") - << "add flapping history for '" << checkable->GetName() << "'"; - std::pair timeBag = ConvertTimestamp(Utility::GetTime()); DbQuery query1; @@ -1308,9 +1259,6 @@ void DbEvents::AddEnableFlappingChangedHistory(const Checkable::Ptr& checkable) if (!checkable->GetEnableFlapping()) return; - Log(LogDebug, "DbEvents") - << "add flapping history for '" << checkable->GetName() << "'"; - std::pair timeBag = ConvertTimestamp(Utility::GetTime()); DbQuery query1; @@ -1352,9 +1300,6 @@ void DbEvents::AddCheckableCheckHistory(const Checkable::Ptr& checkable, const C if (!cr) return; - Log(LogDebug, "DbEvents") - << "add checkable check history for '" << checkable->GetName() << "'"; - Host::Ptr host; Service::Ptr service; tie(host, service) = GetHostService(checkable); @@ -1412,9 +1357,6 @@ void DbEvents::AddCheckableCheckHistory(const Checkable::Ptr& checkable, const C /* eventhandlers */ void DbEvents::AddEventHandlerHistory(const Checkable::Ptr& checkable) { - Log(LogDebug, "DbEvents") - << "add eventhandler history for '" << checkable->GetName() << "'"; - DbQuery query1; query1.Table = "eventhandlers"; query1.Type = DbQueryInsert; @@ -1458,8 +1400,6 @@ void DbEvents::AddEventHandlerHistory(const Checkable::Ptr& checkable) /* externalcommands */ void DbEvents::AddExternalCommandHistory(double time, const String& command, const std::vector& arguments) { - Log(LogDebug, "DbEvents", "add external command history"); - DbQuery query1; query1.Table = "externalcommands"; query1.Type = DbQueryInsert; diff --git a/lib/db_ido/dbevents.hpp b/lib/db_ido/dbevents.hpp index 3e27d2960..6e620ddfa 100644 --- a/lib/db_ido/dbevents.hpp +++ b/lib/db_ido/dbevents.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DBEVENTS_H #define DBEVENTS_H @@ -111,8 +94,8 @@ public: static void AddTriggerDowntimeLogHistory(const Downtime::Ptr& downtime); static void AddRemoveDowntimeLogHistory(const Downtime::Ptr& downtime); static void AddNotificationSentLogHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable, - const User::Ptr& user, NotificationType notification_type, const CheckResult::Ptr& cr, const String& author, - const String& comment_text); + const User::Ptr& user, NotificationType notification_type, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, + const String& author, const String& comment_text); static void AddFlappingChangedLogHistory(const Checkable::Ptr& checkable); static void AddEnableFlappingChangedLogHistory(const Checkable::Ptr& checkable); diff --git a/lib/db_ido/dbobject.cpp b/lib/db_ido/dbobject.cpp index 9ab72ec1d..c48739ede 100644 --- a/lib/db_ido/dbobject.cpp +++ b/lib/db_ido/dbobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/dbobject.hpp" #include "db_ido/dbtype.hpp" diff --git a/lib/db_ido/dbobject.hpp b/lib/db_ido/dbobject.hpp index a90170d74..21c23504c 100644 --- a/lib/db_ido/dbobject.hpp +++ b/lib/db_ido/dbobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DBOBJECT_H #define DBOBJECT_H diff --git a/lib/db_ido/dbquery.cpp b/lib/db_ido/dbquery.cpp index cd3656c1f..01196a537 100644 --- a/lib/db_ido/dbquery.cpp +++ b/lib/db_ido/dbquery.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/dbquery.hpp" #include "base/initialize.hpp" @@ -29,22 +12,22 @@ std::map DbQuery::m_CategoryFilterMap; void DbQuery::StaticInitialize() { - ScriptGlobal::Set("DbCatConfig", DbCatConfig); - ScriptGlobal::Set("DbCatState", DbCatState); - ScriptGlobal::Set("DbCatAcknowledgement", DbCatAcknowledgement); - ScriptGlobal::Set("DbCatComment", DbCatComment); - ScriptGlobal::Set("DbCatDowntime", DbCatDowntime); - ScriptGlobal::Set("DbCatEventHandler", DbCatEventHandler); - ScriptGlobal::Set("DbCatExternalCommand", DbCatExternalCommand); - ScriptGlobal::Set("DbCatFlapping", DbCatFlapping); - ScriptGlobal::Set("DbCatCheck", DbCatCheck); - ScriptGlobal::Set("DbCatLog", DbCatLog); - ScriptGlobal::Set("DbCatNotification", DbCatNotification); - ScriptGlobal::Set("DbCatProgramStatus", DbCatProgramStatus); - ScriptGlobal::Set("DbCatRetention", DbCatRetention); - ScriptGlobal::Set("DbCatStateHistory", DbCatStateHistory); + ScriptGlobal::Set("Icinga.DbCatConfig", DbCatConfig, true); + ScriptGlobal::Set("Icinga.DbCatState", DbCatState, true); + ScriptGlobal::Set("Icinga.DbCatAcknowledgement", DbCatAcknowledgement, true); + ScriptGlobal::Set("Icinga.DbCatComment", DbCatComment, true); + ScriptGlobal::Set("Icinga.DbCatDowntime", DbCatDowntime, true); + ScriptGlobal::Set("Icinga.DbCatEventHandler", DbCatEventHandler, true); + ScriptGlobal::Set("Icinga.DbCatExternalCommand", DbCatExternalCommand, true); + ScriptGlobal::Set("Icinga.DbCatFlapping", DbCatFlapping, true); + ScriptGlobal::Set("Icinga.DbCatCheck", DbCatCheck, true); + ScriptGlobal::Set("Icinga.DbCatLog", DbCatLog, true); + ScriptGlobal::Set("Icinga.DbCatNotification", DbCatNotification, true); + ScriptGlobal::Set("Icinga.DbCatProgramStatus", DbCatProgramStatus, true); + ScriptGlobal::Set("Icinga.DbCatRetention", DbCatRetention, true); + ScriptGlobal::Set("Icinga.DbCatStateHistory", DbCatStateHistory, true); - ScriptGlobal::Set("DbCatEverything", DbCatEverything); + ScriptGlobal::Set("Icinga.DbCatEverything", DbCatEverything, true); m_CategoryFilterMap["DbCatConfig"] = DbCatConfig; m_CategoryFilterMap["DbCatState"] = DbCatState; diff --git a/lib/db_ido/dbquery.hpp b/lib/db_ido/dbquery.hpp index fe1220681..fecb2e346 100644 --- a/lib/db_ido/dbquery.hpp +++ b/lib/db_ido/dbquery.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DBQUERY_H #define DBQUERY_H diff --git a/lib/db_ido/dbreference.cpp b/lib/db_ido/dbreference.cpp index f2eba39bf..e8f13c0d7 100644 --- a/lib/db_ido/dbreference.cpp +++ b/lib/db_ido/dbreference.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "dbreference.hpp" diff --git a/lib/db_ido/dbreference.hpp b/lib/db_ido/dbreference.hpp index eef22157a..70edf9a90 100644 --- a/lib/db_ido/dbreference.hpp +++ b/lib/db_ido/dbreference.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DBREFERENCE_H #define DBREFERENCE_H diff --git a/lib/db_ido/dbtype.cpp b/lib/db_ido/dbtype.cpp index 65b56896b..0870490cb 100644 --- a/lib/db_ido/dbtype.cpp +++ b/lib/db_ido/dbtype.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/dbtype.hpp" #include "db_ido/dbconnection.hpp" diff --git a/lib/db_ido/dbtype.hpp b/lib/db_ido/dbtype.hpp index ad6f3f957..a667ecc6e 100644 --- a/lib/db_ido/dbtype.hpp +++ b/lib/db_ido/dbtype.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DBTYPE_H #define DBTYPE_H diff --git a/lib/db_ido/dbvalue.cpp b/lib/db_ido/dbvalue.cpp index a626e59fe..e1e3e6c69 100644 --- a/lib/db_ido/dbvalue.cpp +++ b/lib/db_ido/dbvalue.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/dbvalue.hpp" @@ -33,11 +16,6 @@ Value DbValue::FromTimestamp(const Value& ts) return new DbValue(DbValueTimestamp, ts); } -Value DbValue::FromTimestampNow() -{ - return new DbValue(DbValueTimestampNow, Empty); -} - Value DbValue::FromValue(const Value& value) { return value; @@ -57,15 +35,6 @@ bool DbValue::IsTimestamp(const Value& value) return dbv->GetType() == DbValueTimestamp; } -bool DbValue::IsTimestampNow(const Value& value) -{ - if (!value.IsObjectType()) - return false; - - DbValue::Ptr dbv = value; - return dbv->GetType() == DbValueTimestampNow; -} - bool DbValue::IsObjectInsertID(const Value& value) { if (!value.IsObjectType()) diff --git a/lib/db_ido/dbvalue.hpp b/lib/db_ido/dbvalue.hpp index fcb985409..cb59e3ad4 100644 --- a/lib/db_ido/dbvalue.hpp +++ b/lib/db_ido/dbvalue.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DBVALUE_H #define DBVALUE_H @@ -30,7 +13,6 @@ namespace icinga enum DbValueType { DbValueTimestamp, - DbValueTimestampNow, DbValueObjectInsertID }; @@ -47,12 +29,10 @@ public: DbValue(DbValueType type, Value value); static Value FromTimestamp(const Value& ts); - static Value FromTimestampNow(); static Value FromValue(const Value& value); static Value FromObjectInsertID(const Value& value); static bool IsTimestamp(const Value& value); - static bool IsTimestampNow(const Value& value); static bool IsObjectInsertID(const Value& value); static Value ExtractValue(const Value& value); diff --git a/lib/db_ido/endpointdbobject.cpp b/lib/db_ido/endpointdbobject.cpp index ed9b27fb3..ad343cfa0 100644 --- a/lib/db_ido/endpointdbobject.cpp +++ b/lib/db_ido/endpointdbobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/endpointdbobject.hpp" #include "db_ido/dbtype.hpp" diff --git a/lib/db_ido/endpointdbobject.hpp b/lib/db_ido/endpointdbobject.hpp index e2edb4b6f..e4fba360f 100644 --- a/lib/db_ido/endpointdbobject.hpp +++ b/lib/db_ido/endpointdbobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ENDPOINTDBOBJECT_H #define ENDPOINTDBOBJECT_H diff --git a/lib/db_ido/hostdbobject.cpp b/lib/db_ido/hostdbobject.cpp index 808ecf640..18be0bd52 100644 --- a/lib/db_ido/hostdbobject.cpp +++ b/lib/db_ido/hostdbobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/hostdbobject.hpp" #include "db_ido/hostgroupdbobject.hpp" @@ -107,7 +90,7 @@ Dictionary::Ptr HostDbObject::GetStatusFields() const int currentState = host->GetState(); - if (currentState != HostUp && !host->IsReachable()) + if (currentState != HostUp && !host->GetLastReachable()) currentState = 2; /* hardcoded compat state */ fields->Set("current_state", currentState); @@ -139,7 +122,7 @@ Dictionary::Ptr HostDbObject::GetStatusFields() const fields->Set("normal_check_interval", host->GetCheckInterval() / 60.0); fields->Set("retry_check_interval", host->GetRetryInterval() / 60.0); fields->Set("check_timeperiod_object_id", host->GetCheckPeriod()); - fields->Set("is_reachable", host->IsReachable()); + fields->Set("is_reachable", host->GetLastReachable()); fields->Set("original_attributes", JsonEncode(host->GetOriginalAttributes())); fields->Set("current_notification_number", CompatUtility::GetCheckableNotificationNotificationNumber(host)); diff --git a/lib/db_ido/hostdbobject.hpp b/lib/db_ido/hostdbobject.hpp index 6d5afc7ac..9fff10ad5 100644 --- a/lib/db_ido/hostdbobject.hpp +++ b/lib/db_ido/hostdbobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef HOSTDBOBJECT_H #define HOSTDBOBJECT_H diff --git a/lib/db_ido/hostgroupdbobject.cpp b/lib/db_ido/hostgroupdbobject.cpp index 13ee82783..cef6aa2c5 100644 --- a/lib/db_ido/hostgroupdbobject.cpp +++ b/lib/db_ido/hostgroupdbobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/hostgroupdbobject.hpp" #include "db_ido/dbtype.hpp" diff --git a/lib/db_ido/hostgroupdbobject.hpp b/lib/db_ido/hostgroupdbobject.hpp index dc7016959..9c48f2995 100644 --- a/lib/db_ido/hostgroupdbobject.hpp +++ b/lib/db_ido/hostgroupdbobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef HOSTGROUPDBOBJECT_H #define HOSTGROUPDBOBJECT_H diff --git a/lib/db_ido/i2-db_ido.hpp b/lib/db_ido/i2-db_ido.hpp index 959fb9fbc..1da9fdcf8 100644 --- a/lib/db_ido/i2-db_ido.hpp +++ b/lib/db_ido/i2-db_ido.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef I2DB_IDO_H #define I2DB_IDO_H diff --git a/lib/db_ido/idochecktask.cpp b/lib/db_ido/idochecktask.cpp index 544c2cefb..8a9536dc3 100644 --- a/lib/db_ido/idochecktask.cpp +++ b/lib/db_ido/idochecktask.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/idochecktask.hpp" #include "icinga/host.hpp" @@ -32,7 +15,7 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, IdoCheck, &IdoCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, IdoCheck, &IdoCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) @@ -156,6 +139,12 @@ void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult cr->SetState(ServiceOK); } + if (conn->GetEnableHa()) { + double failoverTs = conn->GetLastFailover(); + + msgbuf << " Last failover: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", failoverTs) << "."; + } + /* Check whether the thresholds have been defined and match. */ if (missingQueriesCritical.IsEmpty() && qps < queriesCritical) { msgbuf << " " << qps << " queries/s lower than critical threshold (" << queriesCritical << " queries/s)."; diff --git a/lib/db_ido/idochecktask.hpp b/lib/db_ido/idochecktask.hpp index 8f73b36cd..5868c3867 100644 --- a/lib/db_ido/idochecktask.hpp +++ b/lib/db_ido/idochecktask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef IDOCHECKTASK_H #define IDOCHECKTASK_H diff --git a/lib/db_ido/servicedbobject.cpp b/lib/db_ido/servicedbobject.cpp index 2d80f526b..ac1f78897 100644 --- a/lib/db_ido/servicedbobject.cpp +++ b/lib/db_ido/servicedbobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/servicedbobject.hpp" #include "db_ido/servicegroupdbobject.hpp" @@ -136,7 +119,7 @@ Dictionary::Ptr ServiceDbObject::GetStatusFields() const fields->Set("normal_check_interval", service->GetCheckInterval() / 60.0); fields->Set("retry_check_interval", service->GetRetryInterval() / 60.0); fields->Set("check_timeperiod_object_id", service->GetCheckPeriod()); - fields->Set("is_reachable", service->IsReachable()); + fields->Set("is_reachable", service->GetLastReachable()); fields->Set("original_attributes", JsonEncode(service->GetOriginalAttributes())); fields->Set("current_notification_number", CompatUtility::GetCheckableNotificationNotificationNumber(service)); @@ -200,9 +183,6 @@ void ServiceDbObject::OnConfigUpdateHeavy() DbObject::OnMultipleQueries(queries); /* service dependencies */ - Log(LogDebug, "ServiceDbObject") - << "service dependencies for '" << service->GetName() << "'"; - queries.clear(); DbQuery query2; @@ -250,9 +230,6 @@ void ServiceDbObject::OnConfigUpdateHeavy() DbObject::OnMultipleQueries(queries); /* service contacts, contactgroups */ - Log(LogDebug, "ServiceDbObject") - << "service contacts: " << service->GetName(); - queries.clear(); DbQuery query3; @@ -265,9 +242,6 @@ void ServiceDbObject::OnConfigUpdateHeavy() queries.emplace_back(std::move(query3)); for (const User::Ptr& user : CompatUtility::GetCheckableNotificationUsers(service)) { - Log(LogDebug, "ServiceDbObject") - << "service contacts: " << user->GetName(); - DbQuery query_contact; query_contact.Table = GetType()->GetTable() + "_contacts"; query_contact.Type = DbQueryInsert; @@ -283,9 +257,6 @@ void ServiceDbObject::OnConfigUpdateHeavy() DbObject::OnMultipleQueries(queries); - Log(LogDebug, "ServiceDbObject") - << "service contactgroups: " << service->GetName(); - queries.clear(); DbQuery query4; @@ -298,9 +269,6 @@ void ServiceDbObject::OnConfigUpdateHeavy() queries.emplace_back(std::move(query4)); for (const UserGroup::Ptr& usergroup : CompatUtility::GetCheckableNotificationUserGroups(service)) { - Log(LogDebug, "ServiceDbObject") - << "service contactgroups: " << usergroup->GetName(); - DbQuery query_contact; query_contact.Table = GetType()->GetTable() + "_contactgroups"; query_contact.Type = DbQueryInsert; diff --git a/lib/db_ido/servicedbobject.hpp b/lib/db_ido/servicedbobject.hpp index b88bbbd86..19824be70 100644 --- a/lib/db_ido/servicedbobject.hpp +++ b/lib/db_ido/servicedbobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SERVICEDBOBJECT_H #define SERVICEDBOBJECT_H diff --git a/lib/db_ido/servicegroupdbobject.cpp b/lib/db_ido/servicegroupdbobject.cpp index 085e76f7f..ea4d40c32 100644 --- a/lib/db_ido/servicegroupdbobject.cpp +++ b/lib/db_ido/servicegroupdbobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/servicegroupdbobject.hpp" #include "db_ido/dbtype.hpp" diff --git a/lib/db_ido/servicegroupdbobject.hpp b/lib/db_ido/servicegroupdbobject.hpp index c62516352..7f0d6c15a 100644 --- a/lib/db_ido/servicegroupdbobject.hpp +++ b/lib/db_ido/servicegroupdbobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SERVICEGROUPDBOBJECT_H #define SERVICEGROUPDBOBJECT_H diff --git a/lib/db_ido/timeperioddbobject.cpp b/lib/db_ido/timeperioddbobject.cpp index a53ae5c2a..98997f527 100644 --- a/lib/db_ido/timeperioddbobject.cpp +++ b/lib/db_ido/timeperioddbobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/timeperioddbobject.hpp" #include "db_ido/dbtype.hpp" diff --git a/lib/db_ido/timeperioddbobject.hpp b/lib/db_ido/timeperioddbobject.hpp index f40a024f3..e3cc13cb7 100644 --- a/lib/db_ido/timeperioddbobject.hpp +++ b/lib/db_ido/timeperioddbobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef TIMEPERIODDBOBJECT_H #define TIMEPERIODDBOBJECT_H diff --git a/lib/db_ido/userdbobject.cpp b/lib/db_ido/userdbobject.cpp index 2078d57cc..f0d80b604 100644 --- a/lib/db_ido/userdbobject.cpp +++ b/lib/db_ido/userdbobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/userdbobject.hpp" #include "db_ido/usergroupdbobject.hpp" diff --git a/lib/db_ido/userdbobject.hpp b/lib/db_ido/userdbobject.hpp index fa2be5198..e0f36c5fb 100644 --- a/lib/db_ido/userdbobject.hpp +++ b/lib/db_ido/userdbobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef USERDBOBJECT_H #define USERDBOBJECT_H diff --git a/lib/db_ido/usergroupdbobject.cpp b/lib/db_ido/usergroupdbobject.cpp index 2a8458f94..23b3581a7 100644 --- a/lib/db_ido/usergroupdbobject.cpp +++ b/lib/db_ido/usergroupdbobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/usergroupdbobject.hpp" #include "db_ido/dbtype.hpp" diff --git a/lib/db_ido/usergroupdbobject.hpp b/lib/db_ido/usergroupdbobject.hpp index ec2330627..9469823ab 100644 --- a/lib/db_ido/usergroupdbobject.hpp +++ b/lib/db_ido/usergroupdbobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef USERGROUPDBOBJECT_H #define USERGROUPDBOBJECT_H diff --git a/lib/db_ido/zonedbobject.cpp b/lib/db_ido/zonedbobject.cpp index 1c462d7e9..b8ad0c196 100644 --- a/lib/db_ido/zonedbobject.cpp +++ b/lib/db_ido/zonedbobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/zonedbobject.hpp" #include "db_ido/dbtype.hpp" diff --git a/lib/db_ido/zonedbobject.hpp b/lib/db_ido/zonedbobject.hpp index ac051ab6c..3901c81b3 100644 --- a/lib/db_ido/zonedbobject.hpp +++ b/lib/db_ido/zonedbobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ZONEDBOBJECT_H #define ZONEDBOBJECT_H diff --git a/lib/db_ido_mysql/CMakeLists.txt b/lib/db_ido_mysql/CMakeLists.txt index 1587cbf19..70cb90db1 100644 --- a/lib/db_ido_mysql/CMakeLists.txt +++ b/lib/db_ido_mysql/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ mkclass_target(idomysqlconnection.ti idomysqlconnection-ti.cpp idomysqlconnection-ti.hpp) @@ -38,7 +23,7 @@ set_target_properties ( install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/ido-mysql.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) install( diff --git a/lib/db_ido_mysql/idomysqlconnection.cpp b/lib/db_ido_mysql/idomysqlconnection.cpp index 9c8fd7b41..5dbaba071 100644 --- a/lib/db_ido_mysql/idomysqlconnection.cpp +++ b/lib/db_ido_mysql/idomysqlconnection.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido_mysql/idomysqlconnection.hpp" #include "db_ido_mysql/idomysqlconnection-ti.cpp" @@ -30,7 +13,6 @@ #include "base/configtype.hpp" #include "base/exception.hpp" #include "base/statsfunction.hpp" -#include #include using namespace icinga; @@ -82,8 +64,6 @@ void IdoMysqlConnection::StatsFunc(const Dictionary::Ptr& status, const Array::P void IdoMysqlConnection::Resume() { - DbConnection::Resume(); - Log(LogInformation, "IdoMysqlConnection") << "'" << GetName() << "' resumed."; @@ -91,6 +71,9 @@ void IdoMysqlConnection::Resume() m_QueryQueue.SetExceptionCallback(std::bind(&IdoMysqlConnection::ExceptionHandler, this, _1)); + /* Immediately try to connect on Resume() without timer. */ + m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::Reconnect, this), PriorityImmediate); + m_TxTimer = new Timer(); m_TxTimer->SetInterval(1); m_TxTimer->OnTimerExpired.connect(std::bind(&IdoMysqlConnection::TxTimerHandler, this)); @@ -100,27 +83,35 @@ void IdoMysqlConnection::Resume() m_ReconnectTimer->SetInterval(10); m_ReconnectTimer->OnTimerExpired.connect(std::bind(&IdoMysqlConnection::ReconnectTimerHandler, this)); m_ReconnectTimer->Start(); - m_ReconnectTimer->Reschedule(0); + + /* Start with queries after connect. */ + DbConnection::Resume(); ASSERT(m_Mysql->thread_safe()); } void IdoMysqlConnection::Pause() { - Log(LogInformation, "IdoMysqlConnection") - << "'" << GetName() << "' paused."; - - m_ReconnectTimer.reset(); + Log(LogDebug, "IdoMysqlConnection") + << "Attempting to pause '" << GetName() << "'."; DbConnection::Pause(); + m_ReconnectTimer.reset(); + #ifdef I2_DEBUG /* I2_DEBUG */ Log(LogDebug, "IdoMysqlConnection") << "Rescheduling disconnect task."; #endif /* I2_DEBUG */ - m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::Disconnect, this), PriorityHigh); + m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::Disconnect, this), PriorityLow); + + /* Work on remaining tasks but never delete the threads, for HA resuming later. */ m_QueryQueue.Join(); + + Log(LogInformation, "IdoMysqlConnection") + << "'" << GetName() << "' paused."; + } void IdoMysqlConnection::ExceptionHandler(boost::exception_ptr exp) @@ -153,6 +144,9 @@ void IdoMysqlConnection::Disconnect() m_Mysql->close(&m_Connection); SetConnected(false); + + Log(LogInformation, "IdoMysqlConnection") + << "Disconnected from '" << GetName() << "' database '" << GetDatabase() << "'."; } void IdoMysqlConnection::TxTimerHandler() @@ -162,13 +156,16 @@ void IdoMysqlConnection::TxTimerHandler() void IdoMysqlConnection::NewTransaction() { + if (IsPaused()) + return; + #ifdef I2_DEBUG /* I2_DEBUG */ Log(LogDebug, "IdoMysqlConnection") << "Scheduling new transaction and finishing async queries."; #endif /* I2_DEBUG */ - m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalNewTransaction, this), PriorityHigh); - m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::FinishAsyncQueries, this), PriorityHigh); + m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalNewTransaction, this), PriorityNormal); + m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::FinishAsyncQueries, this), PriorityNormal); } void IdoMysqlConnection::InternalNewTransaction() @@ -189,7 +186,8 @@ void IdoMysqlConnection::ReconnectTimerHandler() << "Scheduling reconnect task."; #endif /* I2_DEBUG */ - m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::Reconnect, this), PriorityLow); + /* Only allow Reconnect events with high priority. */ + m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::Reconnect, this), PriorityImmediate); } void IdoMysqlConnection::Reconnect() @@ -207,6 +205,7 @@ void IdoMysqlConnection::Reconnect() bool reconnect = false; + /* Ensure to close old connections first. */ if (GetConnected()) { /* Check if we're really still connected */ if (m_Mysql->ping(&m_Connection) == 0) @@ -217,6 +216,9 @@ void IdoMysqlConnection::Reconnect() reconnect = true; } + Log(LogDebug, "IdoMysqlConnection") + << "Reconnect: Clearing ID cache."; + ClearIDCache(); String ihost, isocket_path, iuser, ipasswd, idb; @@ -271,6 +273,9 @@ void IdoMysqlConnection::Reconnect() BOOST_THROW_EXCEPTION(std::runtime_error(m_Mysql->error(&m_Connection))); } + Log(LogNotice, "IdoMysqlConnection") + << "Reconnect: '" << GetName() << "' is now connected to database '" << GetDatabase() << "'."; + SetConnected(true); IdoMysqlResult result = Query("SELECT @@global.max_allowed_packet AS max_allowed_packet"); @@ -311,7 +316,7 @@ void IdoMysqlConnection::Reconnect() Log(LogCritical, "IdoMysqlConnection") << "Schema version '" << version << "' does not match the required version '" << IDO_COMPAT_SCHEMA_VERSION << "' (or newer)! Please check the upgrade documentation at " - << "https://docs.icinga.com/icinga2/latest/doc/module/icinga2/chapter/upgrading-icinga-2#upgrading-mysql-db"; + << "https://icinga.com/docs/icinga2/latest/doc/16-upgrading-icinga-2/#upgrading-mysql-db"; BOOST_THROW_EXCEPTION(std::runtime_error("Schema version mismatch.")); } @@ -356,12 +361,16 @@ void IdoMysqlConnection::Reconnect() else status_update_time = 0; - double status_update_age = Utility::GetTime() - status_update_time; + double now = Utility::GetTime(); - Log(LogNotice, "IdoMysqlConnection") - << "Last update by '" << endpoint_name << "' was " << status_update_age << "s ago."; + double status_update_age = now - status_update_time; + double failoverTimeout = GetFailoverTimeout(); + + if (status_update_age < failoverTimeout) { + Log(LogInformation, "IdoMysqlConnection") + << "Last update by endpoint '" << endpoint_name << "' was " + << status_update_age << "s ago (< failover timeout of " << failoverTimeout << "s). Retrying."; - if (status_update_age < GetFailoverTimeout()) { m_Mysql->close(&m_Connection); SetConnected(false); SetShouldConnect(false); @@ -379,9 +388,15 @@ void IdoMysqlConnection::Reconnect() return; } + + SetLastFailover(now); + + Log(LogInformation, "IdoMysqlConnection") + << "Last update by endpoint '" << endpoint_name << "' was " + << status_update_age << "s ago. Taking over '" << GetName() << "' in HA zone '" << Zone::GetLocalZone()->GetName() << "'."; } - Log(LogNotice, "IdoMysqlConnection", "Enabling IDO connection."); + Log(LogNotice, "IdoMysqlConnection", "Enabling IDO connection in HA zone."); } Log(LogInformation, "IdoMysqlConnection") @@ -448,9 +463,9 @@ void IdoMysqlConnection::Reconnect() << "Scheduling session table clear and finish connect task."; #endif /* I2_DEBUG */ - m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::ClearTablesBySession, this), PriorityLow); + m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::ClearTablesBySession, this), PriorityNormal); - m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::FinishConnect, this, startTime), PriorityLow); + m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::FinishConnect, this, startTime), PriorityNormal); } void IdoMysqlConnection::FinishConnect(double startTime) @@ -463,7 +478,8 @@ void IdoMysqlConnection::FinishConnect(double startTime) FinishAsyncQueries(); Log(LogInformation, "IdoMysqlConnection") - << "Finished reconnecting to MySQL IDO database in " << std::setw(2) << Utility::GetTime() - startTime << " second(s)."; + << "Finished reconnecting to '" << GetName() << "' database '" << GetDatabase() << "' in " + << std::setw(2) << Utility::GetTime() - startTime << " second(s)."; Query("COMMIT"); Query("BEGIN"); @@ -519,11 +535,12 @@ void IdoMysqlConnection::FinishAsyncQueries() size_t size_query = aq.Query.GetLength() + 1; - if (num_bytes + size_query > m_MaxPacketSize - 512) - break; + if (count > 0) { + if (num_bytes + size_query > m_MaxPacketSize - 512) + break; - if (count > 0) querybuf << ";"; + } IncreaseQueryCount(); count++; @@ -714,18 +731,24 @@ void IdoMysqlConnection::DiscardRows(const IdoMysqlResult& result) void IdoMysqlConnection::ActivateObject(const DbObject::Ptr& dbobj) { + if (IsPaused()) + return; + #ifdef I2_DEBUG /* I2_DEBUG */ Log(LogDebug, "IdoMysqlConnection") << "Scheduling object activation task for '" << dbobj->GetName1() << "!" << dbobj->GetName2() << "'."; #endif /* I2_DEBUG */ - m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalActivateObject, this, dbobj), PriorityLow); + m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalActivateObject, this, dbobj), PriorityNormal); } void IdoMysqlConnection::InternalActivateObject(const DbObject::Ptr& dbobj) { AssertOnWorkQueue(); + if (IsPaused()) + return; + if (!GetConnected()) return; @@ -753,18 +776,24 @@ void IdoMysqlConnection::InternalActivateObject(const DbObject::Ptr& dbobj) void IdoMysqlConnection::DeactivateObject(const DbObject::Ptr& dbobj) { + if (IsPaused()) + return; + #ifdef I2_DEBUG /* I2_DEBUG */ Log(LogDebug, "IdoMysqlConnection") << "Scheduling object deactivation task for '" << dbobj->GetName1() << "!" << dbobj->GetName2() << "'."; #endif /* I2_DEBUG */ - m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalDeactivateObject, this, dbobj), PriorityLow); + m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalDeactivateObject, this, dbobj), PriorityNormal); } void IdoMysqlConnection::InternalDeactivateObject(const DbObject::Ptr& dbobj) { AssertOnWorkQueue(); + if (IsPaused()) + return; + if (!GetConnected()) return; @@ -830,8 +859,6 @@ bool IdoMysqlConnection::FieldToEscapedString(const String& key, const Value& va std::ostringstream msgbuf; msgbuf << "FROM_UNIXTIME(" << ts << ")"; *result = Value(msgbuf.str()); - } else if (DbValue::IsTimestampNow(value)) { - *result = "NOW()"; } else if (DbValue::IsObjectInsertID(value)) { auto id = static_cast(rawvalue); @@ -856,6 +883,9 @@ bool IdoMysqlConnection::FieldToEscapedString(const String& key, const Value& va void IdoMysqlConnection::ExecuteQuery(const DbQuery& query) { + if (IsPaused()) + return; + ASSERT(query.Category != DbCatInvalid); #ifdef I2_DEBUG /* I2_DEBUG */ @@ -868,6 +898,9 @@ void IdoMysqlConnection::ExecuteQuery(const DbQuery& query) void IdoMysqlConnection::ExecuteMultipleQueries(const std::vector& queries) { + if (IsPaused()) + return; + if (queries.empty()) return; @@ -915,6 +948,9 @@ void IdoMysqlConnection::InternalExecuteMultipleQueries(const std::vector entry_time -- instance_id -CREATE INDEX systemcommands_i_id_idx on icinga_systemcommands(instance_id); CREATE INDEX servicechecks_i_id_idx on icinga_servicechecks(instance_id); CREATE INDEX hostchecks_i_id_idx on icinga_hostchecks(instance_id); -CREATE INDEX eventhandlers_i_id_idx on icinga_eventhandlers(instance_id); CREATE INDEX externalcommands_i_id_idx on icinga_externalcommands(instance_id); -- time @@ -1481,40 +1477,22 @@ CREATE INDEX externalcommands_time_id_idx on icinga_externalcommands(entry_time) -- instance_id only -- realtime data -CREATE INDEX programstatus_i_id_idx on icinga_programstatus(instance_id); CREATE INDEX hoststatus_i_id_idx on icinga_hoststatus(instance_id); CREATE INDEX servicestatus_i_id_idx on icinga_servicestatus(instance_id); CREATE INDEX contactstatus_i_id_idx on icinga_contactstatus(instance_id); -CREATE INDEX comments_i_id_idx on icinga_comments(instance_id); -CREATE INDEX scheduleddowntime_i_id_idx on icinga_scheduleddowntime(instance_id); -CREATE INDEX runtimevariables_i_id_idx on icinga_runtimevariables(instance_id); CREATE INDEX customvariablestatus_i_id_idx on icinga_customvariablestatus(instance_id); -- config data -CREATE INDEX configfiles_i_id_idx on icinga_configfiles(instance_id); CREATE INDEX configfilevariables_i_id_idx on icinga_configfilevariables(instance_id); CREATE INDEX customvariables_i_id_idx on icinga_customvariables(instance_id); -CREATE INDEX commands_i_id_idx on icinga_commands(instance_id); -CREATE INDEX timeperiods_i_id_idx on icinga_timeperiods(instance_id); CREATE INDEX timeperiod_timeranges_i_id_idx on icinga_timeperiod_timeranges(instance_id); -CREATE INDEX contactgroups_i_id_idx on icinga_contactgroups(instance_id); CREATE INDEX contactgroup_members_i_id_idx on icinga_contactgroup_members(instance_id); -CREATE INDEX hostgroups_i_id_idx on icinga_hostgroups(instance_id); CREATE INDEX hostgroup_members_i_id_idx on icinga_hostgroup_members(instance_id); -CREATE INDEX servicegroups_i_id_idx on icinga_servicegroups(instance_id); CREATE INDEX servicegroup_members_i_id_idx on icinga_servicegroup_members(instance_id); -CREATE INDEX hostesc_i_id_idx on icinga_hostescalations(instance_id); -CREATE INDEX hostesc_contacts_i_id_idx on icinga_hostescalation_contacts(instance_id); -CREATE INDEX serviceesc_i_id_idx on icinga_serviceescalations(instance_id); -CREATE INDEX serviceesc_contacts_i_id_idx on icinga_serviceescalation_contacts(instance_id); -CREATE INDEX hostdependencies_i_id_idx on icinga_hostdependencies(instance_id); -CREATE INDEX contacts_i_id_idx on icinga_contacts(instance_id); CREATE INDEX contact_addresses_i_id_idx on icinga_contact_addresses(instance_id); CREATE INDEX contact_notifcommands_i_id_idx on icinga_contact_notificationcommands(instance_id); -CREATE INDEX hosts_i_id_idx on icinga_hosts(instance_id); CREATE INDEX host_parenthosts_i_id_idx on icinga_host_parenthosts(instance_id); CREATE INDEX host_contacts_i_id_idx on icinga_host_contacts(instance_id); -CREATE INDEX services_i_id_idx on icinga_services(instance_id); CREATE INDEX service_contacts_i_id_idx on icinga_service_contacts(instance_id); CREATE INDEX service_contactgroups_i_id_idx on icinga_service_contactgroups(instance_id); CREATE INDEX host_contactgroups_i_id_idx on icinga_host_contactgroups(instance_id); @@ -1574,7 +1552,6 @@ CREATE INDEX hostchks_h_obj_id_idx on icinga_hostchecks(host_object_id); CREATE INDEX servicechks_s_obj_id_idx on icinga_servicechecks(service_object_id); -- objects -CREATE INDEX objects_objtype_id_idx ON icinga_objects(objecttype_id); CREATE INDEX objects_name1_idx ON icinga_objects(name1); CREATE INDEX objects_name2_idx ON icinga_objects(name2); CREATE INDEX objects_inst_id_idx ON icinga_objects(instance_id); @@ -1643,10 +1620,6 @@ CREATE INDEX sla_idx_obj ON icinga_objects (objecttype_id, is_active, name1); -- #4985 CREATE INDEX commenthistory_delete_idx ON icinga_commenthistory (instance_id, comment_time, internal_comment_id); --- #10070 -CREATE INDEX idx_comments_object_id on icinga_comments(object_id); -CREATE INDEX idx_scheduleddowntime_object_id on icinga_scheduleddowntime(object_id); - -- #10066 CREATE INDEX idx_endpoints_object_id on icinga_endpoints(endpoint_object_id); CREATE INDEX idx_endpointstatus_object_id on icinga_endpointstatus(endpoint_object_id); @@ -1668,7 +1641,6 @@ CREATE INDEX idx_downtimes_session_del ON icinga_scheduleddowntime (instance_id, CREATE INDEX idx_statehistory_cleanup on icinga_statehistory(instance_id, state_time); -- #12435 -CREATE INDEX idx_customvariables_object_id on icinga_customvariables(object_id); CREATE INDEX idx_contactgroup_members_object_id on icinga_contactgroup_members(contact_object_id); CREATE INDEX idx_hostgroup_members_object_id on icinga_hostgroup_members(host_object_id); CREATE INDEX idx_servicegroup_members_object_id on icinga_servicegroup_members(service_object_id); diff --git a/lib/db_ido_mysql/schema/upgrade/2.0.2.sql b/lib/db_ido_mysql/schema/upgrade/2.0.2.sql index fb8834a22..c622ae9a6 100644 --- a/lib/db_ido_mysql/schema/upgrade/2.0.2.sql +++ b/lib/db_ido_mysql/schema/upgrade/2.0.2.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.0.2 -- -- ----------------------------------------- --- Copyright (c) 2014 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_mysql/schema/upgrade/2.1.0.sql b/lib/db_ido_mysql/schema/upgrade/2.1.0.sql index 244463829..7bbed721b 100644 --- a/lib/db_ido_mysql/schema/upgrade/2.1.0.sql +++ b/lib/db_ido_mysql/schema/upgrade/2.1.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.1.0 -- -- ----------------------------------------- --- Copyright (c) 2014 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_mysql/schema/upgrade/2.11.0.sql b/lib/db_ido_mysql/schema/upgrade/2.11.0.sql new file mode 100644 index 000000000..bafa93f7a --- /dev/null +++ b/lib/db_ido_mysql/schema/upgrade/2.11.0.sql @@ -0,0 +1,89 @@ +-- ----------------------------------------- +-- upgrade path for Icinga 2.11.0 +-- +-- ----------------------------------------- +-- Copyright (c) 2019 Icinga Development Team (https://icinga.com/) +-- +-- Please check https://docs.icinga.com for upgrading information! +-- ----------------------------------------- + +SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; + +-- -------------------------------------------------------- +-- Helper functions and procedures for DROP INDEX IF EXISTS +-- -------------------------------------------------------- + +DELIMITER // +DROP FUNCTION IF EXISTS ido_index_exists // +CREATE FUNCTION ido_index_exists( + f_table_name varchar(64), + f_index_name varchar(64) +) + RETURNS BOOL + DETERMINISTIC + READS SQL DATA + BEGIN + DECLARE index_exists BOOL DEFAULT FALSE; + SELECT EXISTS ( + SELECT 1 + FROM information_schema.statistics + WHERE table_schema = SCHEMA() + AND table_name = f_table_name + AND index_name = f_index_name + ) INTO index_exists; + RETURN index_exists; + END // + +DROP PROCEDURE IF EXISTS ido_drop_index_if_exists // +CREATE PROCEDURE ido_drop_index_if_exists ( + IN p_table_name varchar(64), + IN p_index_name varchar(64) +) + DETERMINISTIC + MODIFIES SQL DATA + BEGIN + IF ido_index_exists(p_table_name, p_index_name) + THEN + SET @ido_drop_index_sql = CONCAT('ALTER TABLE `', SCHEMA(), '`.`', p_table_name, '` DROP INDEX `', p_index_name, '`'); + PREPARE stmt FROM @ido_drop_index_sql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + SET @ido_drop_index_sql = NULL; + END IF; + END // +DELIMITER ; + +CALL ido_drop_index_if_exists('icinga_commands', 'commands_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_comments', 'idx_comments_object_id'); +CALL ido_drop_index_if_exists('icinga_comments', 'comments_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_configfiles', 'configfiles_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_contactgroups', 'contactgroups_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_contacts', 'contacts_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_customvariables', 'idx_customvariables_object_id'); +CALL ido_drop_index_if_exists('icinga_eventhandlers', 'eventhandlers_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_hostdependencies', 'hostdependencies_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_hostescalations', 'hostesc_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_hostescalation_contacts', 'hostesc_contacts_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_hostgroups', 'hostgroups_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_hosts', 'host_object_id'); +CALL ido_drop_index_if_exists('icinga_hosts', 'hosts_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_objects', 'objects_objtype_id_idx'); +CALL ido_drop_index_if_exists('icinga_programstatus', 'programstatus_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_runtimevariables', 'runtimevariables_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_scheduleddowntime', 'scheduleddowntime_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_scheduleddowntime', 'idx_scheduleddowntime_object_id'); +CALL ido_drop_index_if_exists('icinga_serviceescalations', 'serviceesc_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_serviceescalation_contacts', 'serviceesc_contacts_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_servicegroups', 'servicegroups_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_services', 'services_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_services', 'service_object_id'); +CALL ido_drop_index_if_exists('icinga_systemcommands', 'systemcommands_i_id_idx'); +CALL ido_drop_index_if_exists('icinga_timeperiods', 'timeperiods_i_id_idx'); + +DROP FUNCTION ido_index_exists; +DROP PROCEDURE ido_drop_index_if_exists; + +-- ----------------------------------------- +-- set dbversion (same as 2.11.0) +-- ----------------------------------------- +INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.15.0', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.15.0', modify_time=NOW(); diff --git a/lib/db_ido_mysql/schema/upgrade/2.2.0.sql b/lib/db_ido_mysql/schema/upgrade/2.2.0.sql index 040c6ee3d..22a611531 100644 --- a/lib/db_ido_mysql/schema/upgrade/2.2.0.sql +++ b/lib/db_ido_mysql/schema/upgrade/2.2.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.2.0 -- -- ----------------------------------------- --- Copyright (c) 2014 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_mysql/schema/upgrade/2.3.0.sql b/lib/db_ido_mysql/schema/upgrade/2.3.0.sql index 2c43d5844..f2fe463b1 100644 --- a/lib/db_ido_mysql/schema/upgrade/2.3.0.sql +++ b/lib/db_ido_mysql/schema/upgrade/2.3.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.3.0 -- -- ----------------------------------------- --- Copyright (c) 2015 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_mysql/schema/upgrade/2.4.0.sql b/lib/db_ido_mysql/schema/upgrade/2.4.0.sql index 1064b7088..f6803f715 100644 --- a/lib/db_ido_mysql/schema/upgrade/2.4.0.sql +++ b/lib/db_ido_mysql/schema/upgrade/2.4.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.4.0 -- -- ----------------------------------------- --- Copyright (c) 2015 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_mysql/schema/upgrade/2.5.0.sql b/lib/db_ido_mysql/schema/upgrade/2.5.0.sql index 79090cf64..d5714a0cb 100644 --- a/lib/db_ido_mysql/schema/upgrade/2.5.0.sql +++ b/lib/db_ido_mysql/schema/upgrade/2.5.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.5.0 -- -- ----------------------------------------- --- Copyright (c) 2016 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_mysql/schema/upgrade/2.6.0.sql b/lib/db_ido_mysql/schema/upgrade/2.6.0.sql index fbf354452..33dd780b3 100644 --- a/lib/db_ido_mysql/schema/upgrade/2.6.0.sql +++ b/lib/db_ido_mysql/schema/upgrade/2.6.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.6.0 -- -- ----------------------------------------- --- Copyright (c) 2016 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_mysql/schema/upgrade/2.8.0.sql b/lib/db_ido_mysql/schema/upgrade/2.8.0.sql index db546d007..8d511a757 100644 --- a/lib/db_ido_mysql/schema/upgrade/2.8.0.sql +++ b/lib/db_ido_mysql/schema/upgrade/2.8.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.8.0 -- -- ----------------------------------------- --- Copyright (c) 2017 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_mysql/schema/upgrade/2.8.1.sql b/lib/db_ido_mysql/schema/upgrade/2.8.1.sql index 4bf943e10..98f851143 100644 --- a/lib/db_ido_mysql/schema/upgrade/2.8.1.sql +++ b/lib/db_ido_mysql/schema/upgrade/2.8.1.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.8.1 (fix for fresh 2.8.0 installation only) -- -- ----------------------------------------- --- Copyright (c) 2018 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_pgsql/CMakeLists.txt b/lib/db_ido_pgsql/CMakeLists.txt index 35f15fab5..e081a6278 100644 --- a/lib/db_ido_pgsql/CMakeLists.txt +++ b/lib/db_ido_pgsql/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ mkclass_target(idopgsqlconnection.ti idopgsqlconnection-ti.cpp idopgsqlconnection-ti.hpp) @@ -38,7 +23,7 @@ set_target_properties ( install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/ido-pgsql.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) install( diff --git a/lib/db_ido_pgsql/idopgsqlconnection.cpp b/lib/db_ido_pgsql/idopgsqlconnection.cpp index 9771d4027..e458e0d63 100644 --- a/lib/db_ido_pgsql/idopgsqlconnection.cpp +++ b/lib/db_ido_pgsql/idopgsqlconnection.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido_pgsql/idopgsqlconnection.hpp" #include "db_ido_pgsql/idopgsqlconnection-ti.cpp" @@ -31,7 +14,6 @@ #include "base/exception.hpp" #include "base/context.hpp" #include "base/statsfunction.hpp" -#include #include using namespace icinga; @@ -89,8 +71,6 @@ void IdoPgsqlConnection::StatsFunc(const Dictionary::Ptr& status, const Array::P void IdoPgsqlConnection::Resume() { - DbConnection::Resume(); - Log(LogInformation, "IdoPgsqlConnection") << "'" << GetName() << "' resumed."; @@ -98,6 +78,9 @@ void IdoPgsqlConnection::Resume() m_QueryQueue.SetExceptionCallback(std::bind(&IdoPgsqlConnection::ExceptionHandler, this, _1)); + /* Immediately try to connect on Resume() without timer. */ + m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::Reconnect, this), PriorityImmediate); + m_TxTimer = new Timer(); m_TxTimer->SetInterval(1); m_TxTimer->OnTimerExpired.connect(std::bind(&IdoPgsqlConnection::TxTimerHandler, this)); @@ -107,22 +90,26 @@ void IdoPgsqlConnection::Resume() m_ReconnectTimer->SetInterval(10); m_ReconnectTimer->OnTimerExpired.connect(std::bind(&IdoPgsqlConnection::ReconnectTimerHandler, this)); m_ReconnectTimer->Start(); - m_ReconnectTimer->Reschedule(0); + + /* Start with queries after connect. */ + DbConnection::Resume(); ASSERT(m_Pgsql->isthreadsafe()); } void IdoPgsqlConnection::Pause() { - Log(LogInformation, "IdoPgsqlConnection") - << "'" << GetName() << "' paused."; + DbConnection::Pause(); m_ReconnectTimer.reset(); - DbConnection::Pause(); + m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::Disconnect, this), PriorityLow); - m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::Disconnect, this), PriorityHigh); + /* Work on remaining tasks but never delete the threads, for HA resuming later. */ m_QueryQueue.Join(); + + Log(LogInformation, "IdoPgsqlConnection") + << "'" << GetName() << "' paused."; } void IdoPgsqlConnection::ExceptionHandler(boost::exception_ptr exp) @@ -154,6 +141,9 @@ void IdoPgsqlConnection::Disconnect() m_Pgsql->finish(m_Connection); SetConnected(false); + + Log(LogInformation, "IdoPgsqlConnection") + << "Disconnected from '" << GetName() << "' database '" << GetDatabase() << "'."; } void IdoPgsqlConnection::TxTimerHandler() @@ -163,7 +153,10 @@ void IdoPgsqlConnection::TxTimerHandler() void IdoPgsqlConnection::NewTransaction() { - m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalNewTransaction, this), PriorityHigh, true); + if (IsPaused()) + return; + + m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalNewTransaction, this), PriorityNormal, true); } void IdoPgsqlConnection::InternalNewTransaction() @@ -179,7 +172,8 @@ void IdoPgsqlConnection::InternalNewTransaction() void IdoPgsqlConnection::ReconnectTimerHandler() { - m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::Reconnect, this), PriorityLow); + /* Only allow Reconnect events with high priority. */ + m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::Reconnect, this), PriorityHigh); } void IdoPgsqlConnection::Reconnect() @@ -294,7 +288,7 @@ void IdoPgsqlConnection::Reconnect() Log(LogCritical, "IdoPgsqlConnection") << "Schema version '" << version << "' does not match the required version '" << IDO_COMPAT_SCHEMA_VERSION << "' (or newer)! Please check the upgrade documentation at " - << "https://docs.icinga.com/icinga2/latest/doc/module/icinga2/chapter/upgrading-icinga-2#upgrading-postgresql-db"; + << "https://icinga.com/docs/icinga2/latest/doc/16-upgrading-icinga-2/#upgrading-postgresql-db"; BOOST_THROW_EXCEPTION(std::runtime_error("Schema version mismatch.")); } @@ -336,12 +330,16 @@ void IdoPgsqlConnection::Reconnect() else status_update_time = 0; - double status_update_age = Utility::GetTime() - status_update_time; + double now = Utility::GetTime(); - Log(LogNotice, "IdoPgsqlConnection") - << "Last update by '" << endpoint_name << "' was " << status_update_age << "s ago."; + double status_update_age = now - status_update_time; + double failoverTimeout = GetFailoverTimeout(); if (status_update_age < GetFailoverTimeout()) { + Log(LogInformation, "IdoPgsqlConnection") + << "Last update by endpoint '" << endpoint_name << "' was " + << status_update_age << "s ago (< failover timeout of " << failoverTimeout << "s). Retrying."; + m_Pgsql->finish(m_Connection); SetConnected(false); SetShouldConnect(false); @@ -359,6 +357,12 @@ void IdoPgsqlConnection::Reconnect() return; } + + SetLastFailover(now); + + Log(LogInformation, "IdoPgsqlConnection") + << "Last update by endpoint '" << endpoint_name << "' was " + << status_update_age << "s ago. Taking over '" << GetName() << "' in HA zone '" << Zone::GetLocalZone()->GetName() << "'."; } Log(LogNotice, "IdoPgsqlConnection", "Enabling IDO connection."); @@ -422,9 +426,9 @@ void IdoPgsqlConnection::Reconnect() UpdateAllObjects(); - m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::ClearTablesBySession, this), PriorityLow); + m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::ClearTablesBySession, this), PriorityNormal); - m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::FinishConnect, this, startTime), PriorityLow); + m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::FinishConnect, this, startTime), PriorityNormal); } void IdoPgsqlConnection::FinishConnect(double startTime) @@ -435,7 +439,8 @@ void IdoPgsqlConnection::FinishConnect(double startTime) return; Log(LogInformation, "IdoPgsqlConnection") - << "Finished reconnecting to PostgreSQL IDO database in " << std::setw(2) << Utility::GetTime() - startTime << " second(s)."; + << "Finished reconnecting to '" << GetName() << "' database '" << GetDatabase() << "' in " + << std::setw(2) << Utility::GetTime() - startTime << " second(s)."; Query("COMMIT"); Query("BEGIN"); @@ -569,7 +574,10 @@ Dictionary::Ptr IdoPgsqlConnection::FetchRow(const IdoPgsqlResult& result, int r void IdoPgsqlConnection::ActivateObject(const DbObject::Ptr& dbobj) { - m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalActivateObject, this, dbobj), PriorityLow); + if (IsPaused()) + return; + + m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalActivateObject, this, dbobj), PriorityNormal); } void IdoPgsqlConnection::InternalActivateObject(const DbObject::Ptr& dbobj) @@ -603,7 +611,10 @@ void IdoPgsqlConnection::InternalActivateObject(const DbObject::Ptr& dbobj) void IdoPgsqlConnection::DeactivateObject(const DbObject::Ptr& dbobj) { - m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalDeactivateObject, this, dbobj), PriorityLow); + if (IsPaused()) + return; + + m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalDeactivateObject, this, dbobj), PriorityNormal); } void IdoPgsqlConnection::InternalDeactivateObject(const DbObject::Ptr& dbobj) @@ -675,8 +686,6 @@ bool IdoPgsqlConnection::FieldToEscapedString(const String& key, const Value& va std::ostringstream msgbuf; msgbuf << "TO_TIMESTAMP(" << ts << ") AT TIME ZONE 'UTC'"; *result = Value(msgbuf.str()); - } else if (DbValue::IsTimestampNow(value)) { - *result = "NOW()"; } else if (DbValue::IsObjectInsertID(value)) { auto id = static_cast(rawvalue); @@ -701,6 +710,9 @@ bool IdoPgsqlConnection::FieldToEscapedString(const String& key, const Value& va void IdoPgsqlConnection::ExecuteQuery(const DbQuery& query) { + if (IsPaused()) + return; + ASSERT(query.Category != DbCatInvalid); m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalExecuteQuery, this, query, -1), query.Priority, true); @@ -708,6 +720,9 @@ void IdoPgsqlConnection::ExecuteQuery(const DbQuery& query) void IdoPgsqlConnection::ExecuteMultipleQueries(const std::vector& queries) { + if (IsPaused()) + return; + if (queries.empty()) return; @@ -750,6 +765,9 @@ void IdoPgsqlConnection::InternalExecuteMultipleQueries(const std::vector(max_age)) + ")"); + " < TO_TIMESTAMP(" + Convert::ToString(static_cast(max_age)) + ") AT TIME ZONE 'UTC'"); } void IdoPgsqlConnection::FillIDCache(const DbType::Ptr& type) diff --git a/lib/db_ido_pgsql/idopgsqlconnection.hpp b/lib/db_ido_pgsql/idopgsqlconnection.hpp index 3b1dc2649..83e4d3f9f 100644 --- a/lib/db_ido_pgsql/idopgsqlconnection.hpp +++ b/lib/db_ido_pgsql/idopgsqlconnection.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef IDOPGSQLCONNECTION_H #define IDOPGSQLCONNECTION_H diff --git a/lib/db_ido_pgsql/idopgsqlconnection.ti b/lib/db_ido_pgsql/idopgsqlconnection.ti index ef7cf6ccb..90ca84ecd 100644 --- a/lib/db_ido_pgsql/idopgsqlconnection.ti +++ b/lib/db_ido_pgsql/idopgsqlconnection.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "db_ido/dbconnection.hpp" diff --git a/lib/db_ido_pgsql/schema/pgsql.sql b/lib/db_ido_pgsql/schema/pgsql.sql index d491051ed..242b6db5c 100644 --- a/lib/db_ido_pgsql/schema/pgsql.sql +++ b/lib/db_ido_pgsql/schema/pgsql.sql @@ -2,7 +2,7 @@ -- pgsql.sql -- DB definition for IDO Postgresql -- --- Copyright (c) 2009-2018 Icinga Development Team (https://www.icinga.com/) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- -------------------------------------------------------- diff --git a/lib/db_ido_pgsql/schema/upgrade/2.0.2.sql b/lib/db_ido_pgsql/schema/upgrade/2.0.2.sql index 39d738650..60710efd3 100644 --- a/lib/db_ido_pgsql/schema/upgrade/2.0.2.sql +++ b/lib/db_ido_pgsql/schema/upgrade/2.0.2.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.0.2 -- -- ----------------------------------------- --- Copyright (c) 2014 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_pgsql/schema/upgrade/2.1.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.1.0.sql index 8efc9cc81..a32ecea51 100644 --- a/lib/db_ido_pgsql/schema/upgrade/2.1.0.sql +++ b/lib/db_ido_pgsql/schema/upgrade/2.1.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.1.0 -- -- ----------------------------------------- --- Copyright (c) 2014 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_pgsql/schema/upgrade/2.2.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.2.0.sql index f9d5501a5..d105a34be 100644 --- a/lib/db_ido_pgsql/schema/upgrade/2.2.0.sql +++ b/lib/db_ido_pgsql/schema/upgrade/2.2.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.2.0 -- -- ----------------------------------------- --- Copyright (c) 2014 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_pgsql/schema/upgrade/2.3.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.3.0.sql index 445deda23..91764de64 100644 --- a/lib/db_ido_pgsql/schema/upgrade/2.3.0.sql +++ b/lib/db_ido_pgsql/schema/upgrade/2.3.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.3.0 -- -- ----------------------------------------- --- Copyright (c) 2015 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_pgsql/schema/upgrade/2.4.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.4.0.sql index 35d8dad2f..4a6e45eb9 100644 --- a/lib/db_ido_pgsql/schema/upgrade/2.4.0.sql +++ b/lib/db_ido_pgsql/schema/upgrade/2.4.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.4.0 -- -- ----------------------------------------- --- Copyright (c) 2015 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_pgsql/schema/upgrade/2.5.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.5.0.sql index c9d4c2f7e..063a812d6 100644 --- a/lib/db_ido_pgsql/schema/upgrade/2.5.0.sql +++ b/lib/db_ido_pgsql/schema/upgrade/2.5.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.5.0 -- -- ----------------------------------------- --- Copyright (c) 2016 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_pgsql/schema/upgrade/2.6.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.6.0.sql index 229e9f106..aa538a62d 100644 --- a/lib/db_ido_pgsql/schema/upgrade/2.6.0.sql +++ b/lib/db_ido_pgsql/schema/upgrade/2.6.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.6.0 -- -- ----------------------------------------- --- Copyright (c) 2016 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_pgsql/schema/upgrade/2.8.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.8.0.sql index cae9b6699..31ab3246b 100644 --- a/lib/db_ido_pgsql/schema/upgrade/2.8.0.sql +++ b/lib/db_ido_pgsql/schema/upgrade/2.8.0.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.8.0 -- -- ----------------------------------------- --- Copyright (c) 2017 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/db_ido_pgsql/schema/upgrade/2.8.1.sql b/lib/db_ido_pgsql/schema/upgrade/2.8.1.sql index 0ee92f659..05202c03d 100644 --- a/lib/db_ido_pgsql/schema/upgrade/2.8.1.sql +++ b/lib/db_ido_pgsql/schema/upgrade/2.8.1.sql @@ -2,7 +2,7 @@ -- upgrade path for Icinga 2.8.1 (fix for fresh 2.8.0 installation only) -- -- ----------------------------------------- --- Copyright (c) 2018 Icinga Development Team (https://www.icinga.com) +-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ -- -- Please check https://docs.icinga.com for upgrading information! -- ----------------------------------------- diff --git a/lib/icinga/CMakeLists.txt b/lib/icinga/CMakeLists.txt index 6f9f14d7e..00951609b 100644 --- a/lib/icinga/CMakeLists.txt +++ b/lib/icinga/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ mkclass_target(checkable.ti checkable-ti.cpp checkable-ti.hpp) mkclass_target(checkcommand.ti checkcommand-ti.cpp checkcommand-ti.hpp) @@ -28,6 +13,7 @@ mkclass_target(host.ti host-ti.cpp host-ti.hpp) mkclass_target(icingaapplication.ti icingaapplication-ti.cpp icingaapplication-ti.hpp) mkclass_target(customvarobject.ti customvarobject-ti.cpp customvarobject-ti.hpp) mkclass_target(notificationcommand.ti notificationcommand-ti.cpp notificationcommand-ti.hpp) +mkclass_target(notificationresult.ti notificationresult-ti.cpp notificationresult-ti.hpp) mkclass_target(notification.ti notification-ti.cpp notification-ti.hpp) mkclass_target(scheduleddowntime.ti scheduleddowntime-ti.cpp scheduleddowntime-ti.hpp) mkclass_target(servicegroup.ti servicegroup-ti.cpp servicegroup-ti.hpp) @@ -66,6 +52,7 @@ set(icinga_SOURCES macroresolver.hpp notification.cpp notification.hpp notification-ti.hpp notification-apply.cpp notificationcommand.cpp notificationcommand.hpp notificationcommand-ti.hpp + notificationresult.cpp notificationresult.hpp notificationresult-ti.hpp objectutils.cpp objectutils.hpp pluginutility.cpp pluginutility.hpp scheduleddowntime.cpp scheduleddowntime.hpp scheduleddowntime-ti.hpp scheduleddowntime-apply.cpp diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 2a373eb1c..757b37eca 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/apiactions.hpp" #include "icinga/service.hpp" @@ -258,7 +241,7 @@ Dictionary::Ptr ApiActions::RemoveAcknowledgement(const ConfigObject::Ptr& objec if (!checkable) return ApiActions::CreateResult(404, - "Cannot remove acknowlegement for non-existent checkable object " + "Cannot remove acknowledgement for non-existent checkable object " + object->GetName() + "."); checkable->ClearAcknowledgement(); @@ -355,6 +338,19 @@ Dictionary::Ptr ApiActions::ScheduleDowntime(const ConfigObject::Ptr& object, double startTime = HttpUtility::GetLastParameter(params, "start_time"); double endTime = HttpUtility::GetLastParameter(params, "end_time"); + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + DowntimeChildOptions childOptions = DowntimeNoChildren; + if (params->Contains("child_options")) { + try { + childOptions = Downtime::ChildOptionsFromValue(HttpUtility::GetLastParameter(params, "child_options")); + } catch (const std::exception&) { + return ApiActions::CreateResult(400, "Option 'child_options' provided an invalid value."); + } + } + String downtimeName = Downtime::AddDowntime(checkable, author, comment, startTime, endTime, fixed, triggerName, duration); @@ -365,39 +361,90 @@ Dictionary::Ptr ApiActions::ScheduleDowntime(const ConfigObject::Ptr& object, { "legacy_id", downtime->GetLegacyId() } }); - /* Schedule downtime for all child objects. */ - int childOptions = 0; - if (params->Contains("child_options")) - childOptions = HttpUtility::GetLastParameter(params, "child_options"); + /* Schedule downtime for all services for the host type. */ + bool allServices = false; - if (childOptions > 0) { - /* '1' schedules child downtimes triggered by the parent downtime. - * '2' schedules non-triggered downtimes for all children. + if (params->Contains("all_services")) + allServices = HttpUtility::GetLastParameter(params, "all_services"); + + if (allServices && !service) { + ArrayData serviceDowntimes; + + for (const Service::Ptr& hostService : host->GetServices()) { + Log(LogNotice, "ApiActions") + << "Creating downtime for service " << hostService->GetName() << " on host " << host->GetName(); + + String serviceDowntimeName = Downtime::AddDowntime(hostService, author, comment, startTime, endTime, + fixed, triggerName, duration); + + Downtime::Ptr serviceDowntime = Downtime::GetByName(serviceDowntimeName); + + serviceDowntimes.push_back(new Dictionary({ + { "name", serviceDowntimeName }, + { "legacy_id", serviceDowntime->GetLegacyId() } + })); + } + + additional->Set("service_downtimes", new Array(std::move(serviceDowntimes))); + } + + /* Schedule downtime for all child objects. */ + if (childOptions != DowntimeNoChildren) { + /* 'DowntimeTriggeredChildren' schedules child downtimes triggered by the parent downtime. + * 'DowntimeNonTriggeredChildren' schedules non-triggered downtimes for all children. */ - if (childOptions == 1) + if (childOptions == DowntimeTriggeredChildren) triggerName = downtimeName; - Log(LogCritical, "ApiActions") + Log(LogNotice, "ApiActions") << "Processing child options " << childOptions << " for downtime " << downtimeName; ArrayData childDowntimes; for (const Checkable::Ptr& child : checkable->GetAllChildren()) { - Log(LogCritical, "ApiActions") + Log(LogNotice, "ApiActions") << "Scheduling downtime for child object " << child->GetName(); String childDowntimeName = Downtime::AddDowntime(child, author, comment, startTime, endTime, fixed, triggerName, duration); - Log(LogCritical, "ApiActions") + Log(LogNotice, "ApiActions") << "Add child downtime '" << childDowntimeName << "'."; Downtime::Ptr childDowntime = Downtime::GetByName(childDowntimeName); - childDowntimes.push_back(new Dictionary({ + Dictionary::Ptr childAdditional = new Dictionary({ { "name", childDowntimeName }, { "legacy_id", childDowntime->GetLegacyId() } - })); + }); + + /* For a host, also schedule all service downtimes if requested. */ + Host::Ptr childHost; + Service::Ptr childService; + tie(childHost, childService) = GetHostService(child); + + if (allServices && !childService) { + ArrayData childServiceDowntimes; + + for (const Service::Ptr& hostService : host->GetServices()) { + Log(LogNotice, "ApiActions") + << "Creating downtime for service " << hostService->GetName() << " on child host " << host->GetName(); + + String serviceDowntimeName = Downtime::AddDowntime(hostService, author, comment, startTime, endTime, + fixed, triggerName, duration); + + Downtime::Ptr serviceDowntime = Downtime::GetByName(serviceDowntimeName); + + childServiceDowntimes.push_back(new Dictionary({ + { "name", serviceDowntimeName }, + { "legacy_id", serviceDowntime->GetLegacyId() } + })); + } + + childAdditional->Set("service_downtimes", new Array(std::move(childServiceDowntimes))); + } + + childDowntimes.push_back(childAdditional); } additional->Set("child_downtimes", new Array(std::move(childDowntimes))); diff --git a/lib/icinga/apiactions.hpp b/lib/icinga/apiactions.hpp index 0b13bb546..c2c8b6188 100644 --- a/lib/icinga/apiactions.hpp +++ b/lib/icinga/apiactions.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef APIACTIONS_H #define APIACTIONS_H diff --git a/lib/icinga/apievents.cpp b/lib/icinga/apievents.cpp index 7041890df..73eef3e48 100644 --- a/lib/icinga/apievents.cpp +++ b/lib/icinga/apievents.cpp @@ -1,24 +1,8 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/apievents.hpp" #include "icinga/service.hpp" +#include "icinga/notificationcommand.hpp" #include "remote/eventqueue.hpp" #include "base/initialize.hpp" #include "base/serializer.hpp" @@ -51,8 +35,9 @@ void ApiEvents::StaticInitialize() void ApiEvents::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const MessageOrigin::Ptr& origin) { std::vector queues = EventQueue::GetQueuesForType("CheckResult"); + auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::CheckResult)); - if (queues.empty()) + if (queues.empty() && !inboxes) return; Log(LogDebug, "ApiEvents", "Processing event type 'CheckResult'."); @@ -74,13 +59,16 @@ void ApiEvents::CheckResultHandler(const Checkable::Ptr& checkable, const CheckR for (const EventQueue::Ptr& queue : queues) { queue->ProcessEvent(result); } + + inboxes.Push(std::move(result)); } void ApiEvents::StateChangeHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type, const MessageOrigin::Ptr& origin) { std::vector queues = EventQueue::GetQueuesForType("StateChange"); + auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::StateChange)); - if (queues.empty()) + if (queues.empty() && !inboxes) return; Log(LogDebug, "ApiEvents", "Processing event type 'StateChange'."); @@ -104,6 +92,8 @@ void ApiEvents::StateChangeHandler(const Checkable::Ptr& checkable, const CheckR for (const EventQueue::Ptr& queue : queues) { queue->ProcessEvent(result); } + + inboxes.Push(std::move(result)); } void ApiEvents::NotificationSentToAllUsersHandler(const Notification::Ptr& notification, @@ -111,8 +101,9 @@ void ApiEvents::NotificationSentToAllUsersHandler(const Notification::Ptr& notif const CheckResult::Ptr& cr, const String& author, const String& text, const MessageOrigin::Ptr& origin) { std::vector queues = EventQueue::GetQueuesForType("Notification"); + auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::Notification)); - if (queues.empty()) + if (queues.empty() && !inboxes) return; Log(LogDebug, "ApiEvents", "Processing event type 'Notification'."); @@ -129,6 +120,11 @@ void ApiEvents::NotificationSentToAllUsersHandler(const Notification::Ptr& notif if (service) result->Set("service", service->GetShortName()); + NotificationCommand::Ptr command = notification->GetCommand(); + + if (command) + result->Set("command", command->GetName()); + ArrayData userNames; for (const User::Ptr& user : users) { @@ -136,7 +132,7 @@ void ApiEvents::NotificationSentToAllUsersHandler(const Notification::Ptr& notif } result->Set("users", new Array(std::move(userNames))); - result->Set("notification_type", Notification::NotificationTypeToString(type)); + result->Set("notification_type", Notification::NotificationTypeToStringCompat(type)); //TODO: Change this to our own types. result->Set("author", author); result->Set("text", text); result->Set("check_result", Serialize(cr)); @@ -144,13 +140,16 @@ void ApiEvents::NotificationSentToAllUsersHandler(const Notification::Ptr& notif for (const EventQueue::Ptr& queue : queues) { queue->ProcessEvent(result); } + + inboxes.Push(std::move(result)); } void ApiEvents::FlappingChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin) { std::vector queues = EventQueue::GetQueuesForType("Flapping"); + auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::Flapping)); - if (queues.empty()) + if (queues.empty() && !inboxes) return; Log(LogDebug, "ApiEvents", "Processing event type 'Flapping'."); @@ -177,6 +176,8 @@ void ApiEvents::FlappingChangedHandler(const Checkable::Ptr& checkable, const Me for (const EventQueue::Ptr& queue : queues) { queue->ProcessEvent(result); } + + inboxes.Push(std::move(result)); } void ApiEvents::AcknowledgementSetHandler(const Checkable::Ptr& checkable, @@ -184,8 +185,9 @@ void ApiEvents::AcknowledgementSetHandler(const Checkable::Ptr& checkable, bool notify, bool persistent, double expiry, const MessageOrigin::Ptr& origin) { std::vector queues = EventQueue::GetQueuesForType("AcknowledgementSet"); + auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::AcknowledgementSet)); - if (queues.empty()) + if (queues.empty() && !inboxes) return; Log(LogDebug, "ApiEvents", "Processing event type 'AcknowledgementSet'."); @@ -215,13 +217,16 @@ void ApiEvents::AcknowledgementSetHandler(const Checkable::Ptr& checkable, for (const EventQueue::Ptr& queue : queues) { queue->ProcessEvent(result); } + + inboxes.Push(std::move(result)); } void ApiEvents::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin) { std::vector queues = EventQueue::GetQueuesForType("AcknowledgementCleared"); + auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::AcknowledgementCleared)); - if (queues.empty()) + if (queues.empty() && !inboxes) return; Log(LogDebug, "ApiEvents", "Processing event type 'AcknowledgementCleared'."); @@ -246,13 +251,16 @@ void ApiEvents::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, c } result->Set("acknowledgement_type", AcknowledgementNone); + + inboxes.Push(std::move(result)); } void ApiEvents::CommentAddedHandler(const Comment::Ptr& comment) { std::vector queues = EventQueue::GetQueuesForType("CommentAdded"); + auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::CommentAdded)); - if (queues.empty()) + if (queues.empty() && !inboxes) return; Log(LogDebug, "ApiEvents", "Processing event type 'CommentAdded'."); @@ -266,13 +274,16 @@ void ApiEvents::CommentAddedHandler(const Comment::Ptr& comment) for (const EventQueue::Ptr& queue : queues) { queue->ProcessEvent(result); } + + inboxes.Push(std::move(result)); } void ApiEvents::CommentRemovedHandler(const Comment::Ptr& comment) { std::vector queues = EventQueue::GetQueuesForType("CommentRemoved"); + auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::CommentRemoved)); - if (queues.empty()) + if (queues.empty() && !inboxes) return; Log(LogDebug, "ApiEvents", "Processing event type 'CommentRemoved'."); @@ -286,13 +297,16 @@ void ApiEvents::CommentRemovedHandler(const Comment::Ptr& comment) for (const EventQueue::Ptr& queue : queues) { queue->ProcessEvent(result); } + + inboxes.Push(std::move(result)); } void ApiEvents::DowntimeAddedHandler(const Downtime::Ptr& downtime) { std::vector queues = EventQueue::GetQueuesForType("DowntimeAdded"); + auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::DowntimeAdded)); - if (queues.empty()) + if (queues.empty() && !inboxes) return; Log(LogDebug, "ApiEvents", "Processing event type 'DowntimeAdded'."); @@ -306,13 +320,16 @@ void ApiEvents::DowntimeAddedHandler(const Downtime::Ptr& downtime) for (const EventQueue::Ptr& queue : queues) { queue->ProcessEvent(result); } + + inboxes.Push(std::move(result)); } void ApiEvents::DowntimeRemovedHandler(const Downtime::Ptr& downtime) { std::vector queues = EventQueue::GetQueuesForType("DowntimeRemoved"); + auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::DowntimeRemoved)); - if (queues.empty()) + if (queues.empty() && !inboxes) return; Log(LogDebug, "ApiEvents", "Processing event type 'DowntimeRemoved'."); @@ -326,13 +343,16 @@ void ApiEvents::DowntimeRemovedHandler(const Downtime::Ptr& downtime) for (const EventQueue::Ptr& queue : queues) { queue->ProcessEvent(result); } + + inboxes.Push(std::move(result)); } void ApiEvents::DowntimeStartedHandler(const Downtime::Ptr& downtime) { std::vector queues = EventQueue::GetQueuesForType("DowntimeStarted"); + auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::DowntimeStarted)); - if (queues.empty()) + if (queues.empty() && !inboxes) return; Log(LogDebug, "ApiEvents", "Processing event type 'DowntimeStarted'."); @@ -346,13 +366,16 @@ void ApiEvents::DowntimeStartedHandler(const Downtime::Ptr& downtime) for (const EventQueue::Ptr& queue : queues) { queue->ProcessEvent(result); } + + inboxes.Push(std::move(result)); } void ApiEvents::DowntimeTriggeredHandler(const Downtime::Ptr& downtime) { std::vector queues = EventQueue::GetQueuesForType("DowntimeTriggered"); + auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::DowntimeTriggered)); - if (queues.empty()) + if (queues.empty() && !inboxes) return; Log(LogDebug, "ApiEvents", "Processing event type 'DowntimeTriggered'."); @@ -366,4 +389,6 @@ void ApiEvents::DowntimeTriggeredHandler(const Downtime::Ptr& downtime) for (const EventQueue::Ptr& queue : queues) { queue->ProcessEvent(result); } + + inboxes.Push(std::move(result)); } diff --git a/lib/icinga/apievents.hpp b/lib/icinga/apievents.hpp index 41c909a99..e4dcc7835 100644 --- a/lib/icinga/apievents.hpp +++ b/lib/icinga/apievents.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef APIEVENTS_H #define APIEVENTS_H diff --git a/lib/icinga/checkable-check.cpp b/lib/icinga/checkable-check.cpp index 719611aba..02bc8fc91 100644 --- a/lib/icinga/checkable-check.cpp +++ b/lib/icinga/checkable-check.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/checkable.hpp" #include "icinga/service.hpp" @@ -40,6 +23,8 @@ boost::signals2::signal Checkable::OnNotificationsRequested; boost::signals2::signal Checkable::OnNextCheckUpdated; +Atomic Checkable::CurrentConcurrentChecks (0); + boost::mutex Checkable::m_StatsMutex; int Checkable::m_PendingChecks = 0; boost::condition_variable Checkable::m_PendingChecksCV; @@ -79,9 +64,18 @@ void Checkable::UpdateNextCheck(const MessageOrigin::Ptr& origin) if (interval > 1) adj = fmod(now * 100 + GetSchedulingOffset(), interval * 100) / 100.0; - adj = std::min(0.5 + fmod(GetSchedulingOffset(), interval * 5) / 100.0, adj); + if (adj != 0.0) + adj = std::min(0.5 + fmod(GetSchedulingOffset(), interval * 5) / 100.0, adj); - SetNextCheck(now - adj + interval, false, origin); + double nextCheck = now - adj + interval; + double lastCheck = GetLastCheck(); + + Log(LogDebug, "Checkable") + << "Update checkable '" << GetName() << "' with check interval '" << GetCheckInterval() + << "' from last check time at " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", (lastCheck < 0 ? 0 : lastCheck)) + << " (" << GetLastCheck() << ") to next check time at " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", nextCheck) << " (" << nextCheck << ")."; + + SetNextCheck(nextCheck, false, origin); } bool Checkable::HasBeenChecked() const @@ -161,9 +155,34 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrig long old_attempt = GetCheckAttempt(); bool recovery = false; - /* Ignore check results older than the current one. */ - if (old_cr && cr->GetExecutionStart() < old_cr->GetExecutionStart()) - return; + /* When we have an check result already (not after fresh start), + * prevent to accept old check results and allow overrides for + * CRs happened in the future. + */ + if (old_cr) { + double currentCRTimestamp = old_cr->GetExecutionStart(); + double newCRTimestamp = cr->GetExecutionStart(); + + /* Our current timestamp may be from the future (wrong server time adjusted again). Allow overrides here. */ + if (currentCRTimestamp > now) { + /* our current CR is from the future, let the new CR override it. */ + Log(LogDebug, "Checkable") + << std::fixed << std::setprecision(6) << "Processing check result for checkable '" << GetName() << "' from " + << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", newCRTimestamp) << " (" << newCRTimestamp + << "). Overriding since ours is from the future at " + << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", currentCRTimestamp) << " (" << currentCRTimestamp << ")."; + } else { + /* Current timestamp is from the past, but the new timestamp is even more in the past. Skip it. */ + if (newCRTimestamp < currentCRTimestamp) { + Log(LogDebug, "Checkable") + << std::fixed << std::setprecision(6) << "Skipping check result for checkable '" << GetName() << "' from " + << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", newCRTimestamp) << " (" << newCRTimestamp + << "). It is in the past compared to ours at " + << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", currentCRTimestamp) << " (" << currentCRTimestamp << ")."; + return; + } + } + } /* The ExecuteCheck function already sets the old state, but we need to do it again * in case this was a passive check result. */ @@ -239,6 +258,9 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrig else stateChange = (Host::CalculateState(old_state) != Host::CalculateState(new_state)); + /* Store the current last state change for the next iteration. */ + SetPreviousStateChange(GetLastStateChange()); + if (stateChange) { SetLastStateChange(now); @@ -253,8 +275,13 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrig if (parent.get() == this) continue; - ObjectLock olock(parent); - parent->SetNextCheck(Utility::GetTime()); + if (!parent->GetEnableActiveChecks()) + continue; + + if (parent->GetNextCheck() >= now + parent->GetRetryInterval()) { + ObjectLock olock(parent); + parent->SetNextCheck(now); + } } } @@ -284,15 +311,14 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrig bool in_downtime = IsInDowntime(); bool send_notification = false; + bool suppress_notification = !notification_reachable || in_downtime || IsAcknowledged(); - if (notification_reachable && !in_downtime && !IsAcknowledged()) { - /* Send notifications whether when a hard state change occured. */ - if (hardChange && !(old_stateType == StateTypeSoft && IsStateOK(new_state))) - send_notification = true; - /* Or if the checkable is volatile and in a HARD state. */ - else if (is_volatile && GetStateType() == StateTypeHard) - send_notification = true; - } + /* Send notifications whether when a hard state change occurred. */ + if (hardChange && !(old_stateType == StateTypeSoft && IsStateOK(new_state))) + send_notification = true; + /* Or if the checkable is volatile and in a HARD state. */ + else if (is_volatile && GetStateType() == StateTypeHard) + send_notification = true; if (IsStateOK(old_state) && old_stateType == StateTypeSoft) send_notification = false; /* Don't send notifications for SOFT-OK -> HARD-OK. */ @@ -380,21 +406,33 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrig (is_volatile && !(IsStateOK(old_state) && IsStateOK(new_state)))) ExecuteEventHandler(); + int suppressed_types = 0; + /* Flapping start/end notifications */ - if (!in_downtime && !was_flapping && is_flapping) { + if (!was_flapping && is_flapping) { /* FlappingStart notifications happen on state changes, not in downtimes */ - if (!IsPaused()) - OnNotificationsRequested(this, NotificationFlappingStart, cr, "", "", nullptr); + if (!IsPaused()) { + if (in_downtime) { + suppressed_types |= NotificationFlappingStart; + } else { + OnNotificationsRequested(this, NotificationFlappingStart, cr, "", "", nullptr); + } + } Log(LogNotice, "Checkable") << "Flapping Start: Checkable '" << GetName() << "' started flapping (Current flapping value " << GetFlappingCurrent() << "% > high threshold " << GetFlappingThresholdHigh() << "%)."; NotifyFlapping(origin); - } else if (!in_downtime && was_flapping && !is_flapping) { + } else if (was_flapping && !is_flapping) { /* FlappingEnd notifications are independent from state changes, must not happen in downtine */ - if (!IsPaused()) - OnNotificationsRequested(this, NotificationFlappingEnd, cr, "", "", nullptr); + if (!IsPaused()) { + if (in_downtime) { + suppressed_types |= NotificationFlappingEnd; + } else { + OnNotificationsRequested(this, NotificationFlappingEnd, cr, "", "", nullptr); + } + } Log(LogNotice, "Checkable") << "Flapping Stop: Checkable '" << GetName() << "' stopped flapping (Current flapping value " @@ -404,8 +442,35 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrig } if (send_notification && !is_flapping) { - if (!IsPaused()) - OnNotificationsRequested(this, recovery ? NotificationRecovery : NotificationProblem, cr, "", "", nullptr); + if (!IsPaused()) { + if (suppress_notification) { + suppressed_types |= (recovery ? NotificationRecovery : NotificationProblem); + } else { + OnNotificationsRequested(this, recovery ? NotificationRecovery : NotificationProblem, cr, "", "", nullptr); + } + } + } + + if (suppressed_types) { + /* If some notifications were suppressed, but just because of e.g. a downtime, + * stash them into a notification types bitmask for maybe re-sending later. + */ + + ObjectLock olock (this); + int suppressed_types_before (GetSuppressedNotifications()); + int suppressed_types_after (suppressed_types_before | suppressed_types); + + for (int conflict : {NotificationProblem | NotificationRecovery, NotificationFlappingStart | NotificationFlappingEnd}) { + /* E.g. problem and recovery notifications neutralize each other. */ + + if ((suppressed_types_after & conflict) == conflict) { + suppressed_types_after &= ~conflict; + } + } + + if (suppressed_types_after != suppressed_types_before) { + SetSuppressedNotifications(suppressed_types_after); + } } } @@ -431,6 +496,12 @@ void Checkable::ExecuteCheck() double scheduled_start = GetNextCheck(); double before_check = Utility::GetTime(); + /* This calls SetNextCheck() which updates the CheckerComponent's idle/pending + * queues and ensures that checks are not fired multiple times. ProcessCheckResult() + * is called too late. See #6421. + */ + UpdateNextCheck(); + bool reachable = IsReachable(); { diff --git a/lib/icinga/checkable-comment.cpp b/lib/icinga/checkable-comment.cpp index 25b828ca3..b2184e1ff 100644 --- a/lib/icinga/checkable-comment.cpp +++ b/lib/icinga/checkable-comment.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/service.hpp" #include "remote/configobjectutility.hpp" diff --git a/lib/icinga/checkable-dependency.cpp b/lib/icinga/checkable-dependency.cpp index 2b6b50f91..10e39ab58 100644 --- a/lib/icinga/checkable-dependency.cpp +++ b/lib/icinga/checkable-dependency.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/service.hpp" #include "icinga/dependency.hpp" @@ -61,9 +44,12 @@ std::vector Checkable::GetReverseDependencies() const bool Checkable::IsReachable(DependencyType dt, Dependency::Ptr *failedDependency, int rstack) const { - if (rstack > 20) { + /* Anything greater than 256 causes recursion bus errors. */ + int limit = 256; + + if (rstack > limit) { Log(LogWarning, "Checkable") - << "Too many nested dependencies for service '" << GetName() << "': Dependency failed."; + << "Too many nested dependencies (>" << limit << ") for checkable '" << GetName() << "': Dependency failed."; return false; } diff --git a/lib/icinga/checkable-downtime.cpp b/lib/icinga/checkable-downtime.cpp index 3222986da..dcc1539a8 100644 --- a/lib/icinga/checkable-downtime.cpp +++ b/lib/icinga/checkable-downtime.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/service.hpp" #include "base/configtype.hpp" diff --git a/lib/icinga/checkable-event.cpp b/lib/icinga/checkable-event.cpp index 494b14cac..91b2a443d 100644 --- a/lib/icinga/checkable-event.cpp +++ b/lib/icinga/checkable-event.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/checkable.hpp" #include "icinga/eventcommand.hpp" diff --git a/lib/icinga/checkable-flapping.cpp b/lib/icinga/checkable-flapping.cpp index 9efd5f38e..d0c8f8ec0 100644 --- a/lib/icinga/checkable-flapping.cpp +++ b/lib/icinga/checkable-flapping.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/checkable.hpp" #include "icinga/icingaapplication.hpp" diff --git a/lib/icinga/checkable-notification.cpp b/lib/icinga/checkable-notification.cpp index 021b3f964..9aac0e7a4 100644 --- a/lib/icinga/checkable-notification.cpp +++ b/lib/icinga/checkable-notification.cpp @@ -1,29 +1,16 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/checkable.hpp" +#include "icinga/host.hpp" #include "icinga/icingaapplication.hpp" +#include "icinga/service.hpp" +#include "base/dictionary.hpp" #include "base/objectlock.hpp" #include "base/logger.hpp" #include "base/exception.hpp" #include "base/context.hpp" #include "base/convert.hpp" +#include "remote/apilistener.hpp" using namespace icinga; @@ -31,8 +18,8 @@ boost::signals2::signal Checkable::OnNotificationSentToAllUsers; boost::signals2::signal Checkable::OnNotificationSentToUser; + const NotificationType&, const CheckResult::Ptr&, const NotificationResult::Ptr&, const String&, + const String&, const String&, const MessageOrigin::Ptr&)> Checkable::OnNotificationSentToUser; void Checkable::ResetNotificationNumbers() { @@ -44,7 +31,9 @@ void Checkable::ResetNotificationNumbers() void Checkable::SendNotifications(NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text) { - CONTEXT("Sending notifications for object '" + GetName() + "'"); + String checkableName = GetName(); + + CONTEXT("Sending notifications for object '" + checkableName + "'"); bool force = GetForceNextNotification(); @@ -53,31 +42,70 @@ void Checkable::SendNotifications(NotificationType type, const CheckResult::Ptr& if (!IcingaApplication::GetInstance()->GetEnableNotifications() || !GetEnableNotifications()) { if (!force) { Log(LogInformation, "Checkable") - << "Notifications are disabled for service '" << GetName() << "'."; + << "Notifications are disabled for checkable '" << checkableName << "'."; return; } } - Log(LogInformation, "Checkable") - << "Checking for configured notifications for object '" << GetName() << "'"; - std::set notifications = GetNotifications(); - if (notifications.empty()) - Log(LogInformation, "Checkable") - << "Checkable '" << GetName() << "' does not have any notifications."; + String notificationTypeName = Notification::NotificationTypeToString(type); - Log(LogDebug, "Checkable") - << "Checkable '" << GetName() << "' has " << notifications.size() << " notification(s)."; + // Bail early if there are no notifications. + if (notifications.empty()) { + Log(LogNotice, "Checkable") + << "Skipping checkable '" << checkableName << "' which doesn't have any notification objects configured."; + return; + } + + Log(LogInformation, "Checkable") + << "Checkable '" << checkableName << "' has " << notifications.size() + << " notification(s). Checking filters for type '" << notificationTypeName << "', sends will be logged."; for (const Notification::Ptr& notification : notifications) { - try { - if (!notification->IsPaused()) - notification->BeginExecuteNotification(type, cr, force, false, author, text); - } catch (const std::exception& ex) { - Log(LogWarning, "Checkable") - << "Exception occured during notification for service '" - << GetName() << "': " << DiagnosticInformation(ex); + // Re-send stashed notifications from cold startup. + if (ApiListener::UpdatedObjectAuthority()) { + try { + if (!notification->IsPaused()) { + auto stashedNotifications (notification->GetStashedNotifications()); + + if (stashedNotifications->GetLength()) { + Log(LogNotice, "Notification") + << "Notification '" << notification->GetName() << "': there are some stashed notifications. Stashing notification to preserve order."; + + stashedNotifications->Add(new Dictionary({ + {"type", type}, + {"cr", cr}, + {"force", force}, + {"reminder", false}, + {"author", author}, + {"text", text} + })); + } else { + notification->BeginExecuteNotification(type, cr, force, false, author, text); + } + } else { + Log(LogNotice, "Notification") + << "Notification '" << notification->GetName() << "': HA cluster active, this endpoint does not have the authority (paused=true). Skipping."; + } + } catch (const std::exception& ex) { + Log(LogWarning, "Checkable") + << "Exception occurred during notification '" << notification->GetName() << "' for checkable '" + << GetName() << "': " << DiagnosticInformation(ex, false); + } + } else { + // Cold startup phase. Stash notification for later. + Log(LogNotice, "Notification") + << "Notification '" << notification->GetName() << "': object authority hasn't been updated, yet. Stashing notification."; + + notification->GetStashedNotifications()->Add(new Dictionary({ + {"type", type}, + {"cr", cr}, + {"force", force}, + {"reminder", false}, + {"author", author}, + {"text", text} + })); } } } @@ -99,3 +127,117 @@ void Checkable::UnregisterNotification(const Notification::Ptr& notification) boost::mutex::scoped_lock lock(m_NotificationMutex); m_Notifications.erase(notification); } + +static void FireSuppressedNotifications(Checkable* checkable) +{ + if (!checkable->IsActive()) + return; + + if (checkable->IsPaused()) + return; + + if (!checkable->GetEnableNotifications()) + return; + + int suppressed_types (checkable->GetSuppressedNotifications()); + if (!suppressed_types) + return; + + int subtract = 0; + + for (auto type : {NotificationProblem, NotificationRecovery, NotificationFlappingStart, NotificationFlappingEnd}) { + if (suppressed_types & type) { + bool still_applies; + auto cr (checkable->GetLastCheckResult()); + + switch (type) { + case NotificationProblem: + still_applies = cr && !checkable->IsStateOK(cr->GetState()) && checkable->GetStateType() == StateTypeHard; + break; + case NotificationRecovery: + still_applies = cr && checkable->IsStateOK(cr->GetState()); + break; + case NotificationFlappingStart: + still_applies = checkable->IsFlapping(); + break; + case NotificationFlappingEnd: + still_applies = !checkable->IsFlapping(); + break; + default: + break; + } + + if (still_applies) { + bool still_suppressed; + + switch (type) { + case NotificationProblem: + /* Fall through. */ + case NotificationRecovery: + still_suppressed = !checkable->IsReachable(DependencyNotification) || checkable->IsInDowntime() || checkable->IsAcknowledged(); + break; + case NotificationFlappingStart: + /* Fall through. */ + case NotificationFlappingEnd: + still_suppressed = checkable->IsInDowntime(); + break; + default: + break; + } + + if (!still_suppressed && checkable->GetEnableActiveChecks()) { + /* If e.g. the downtime just ended, but the service is still not ok, we would re-send the stashed problem notification. + * But if the next check result recovers the service soon, we would send a recovery notification soon after the problem one. + * This is not desired, especially for lots of services at once. + * Because of that if there's likely to be a check result soon, + * we delay the re-sending of the stashed notification until the next check. + * That check either doesn't change anything and we finally re-send the stashed problem notification + * or recovers the service and we drop the stashed notification. */ + + /* One minute unless the check interval is too short so the next check will always run during the next minute. */ + auto threshold (checkable->GetCheckInterval() - 10); + + if (threshold > 60) + threshold = 60; + else if (threshold < 0) + threshold = 0; + + still_suppressed = checkable->GetNextCheck() <= Utility::GetTime() + threshold; + } + + if (!still_suppressed) { + Checkable::OnNotificationsRequested(checkable, type, cr, "", "", nullptr); + + subtract |= type; + } + } else { + subtract |= type; + } + } + } + + if (subtract) { + ObjectLock olock (checkable); + + int suppressed_types_before (checkable->GetSuppressedNotifications()); + int suppressed_types_after (suppressed_types_before & ~subtract); + + if (suppressed_types_after != suppressed_types_before) { + checkable->SetSuppressedNotifications(suppressed_types_after); + } + } +} + +/** + * Re-sends all notifications previously suppressed by e.g. downtimes if the notification reason still applies. + */ +void Checkable::FireSuppressedNotifications(const Timer * const&) +{ + for (auto& host : ConfigType::GetObjectsByType()) { + ::FireSuppressedNotifications(host.get()); + } + + for (auto& service : ConfigType::GetObjectsByType()) { + ::FireSuppressedNotifications(service.get()); + } +} diff --git a/lib/icinga/checkable-script.cpp b/lib/icinga/checkable-script.cpp index 532fb58dd..4a0d1d805 100644 --- a/lib/icinga/checkable-script.cpp +++ b/lib/icinga/checkable-script.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/checkable.hpp" #include "base/configobject.hpp" diff --git a/lib/icinga/checkable.cpp b/lib/icinga/checkable.cpp index fe52e37ef..55bbfeb16 100644 --- a/lib/icinga/checkable.cpp +++ b/lib/icinga/checkable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/checkable.hpp" #include "icinga/checkable-ti.cpp" @@ -24,6 +7,8 @@ #include "base/objectlock.hpp" #include "base/utility.hpp" #include "base/exception.hpp" +#include "base/timer.hpp" +#include using namespace icinga; @@ -33,6 +18,8 @@ INITIALIZE_ONCE(&Checkable::StaticInitialize); boost::signals2::signal Checkable::OnAcknowledgementSet; boost::signals2::signal Checkable::OnAcknowledgementCleared; +static Timer::Ptr l_CheckablesFireSuppressedNotifications; + void Checkable::StaticInitialize() { /* fixed downtime start */ @@ -57,14 +44,16 @@ void Checkable::OnAllConfigLoaded() if (endpoint) { Zone::Ptr checkableZone = static_pointer_cast(GetZone()); - if (!checkableZone) - checkableZone = Zone::GetLocalZone(); + if (checkableZone) { + Zone::Ptr cmdZone = endpoint->GetZone(); - Zone::Ptr cmdZone = endpoint->GetZone(); - - if (checkableZone && cmdZone != checkableZone && cmdZone->GetParent() != checkableZone) { + if (cmdZone != checkableZone && cmdZone->GetParent() != checkableZone) { + BOOST_THROW_EXCEPTION(ValidationError(this, { "command_endpoint" }, + "Command endpoint must be in zone '" + checkableZone->GetName() + "' or in a direct child zone thereof.")); + } + } else { BOOST_THROW_EXCEPTION(ValidationError(this, { "command_endpoint" }, - "Command endpoint must be in zone '" + checkableZone->GetName() + "' or in a direct child zone thereof.")); + "Command endpoint must not be set.")); } } } @@ -80,6 +69,15 @@ void Checkable::Start(bool runtimeCreated) } ObjectImpl::Start(runtimeCreated); + + static boost::once_flag once = BOOST_ONCE_INIT; + + boost::call_once(once, []() { + l_CheckablesFireSuppressedNotifications = new Timer(); + l_CheckablesFireSuppressedNotifications->SetInterval(5); + l_CheckablesFireSuppressedNotifications->OnTimerExpired.connect(&Checkable::FireSuppressedNotifications); + l_CheckablesFireSuppressedNotifications->Start(); + }); } void Checkable::AddGroup(const String& name) @@ -132,6 +130,9 @@ void Checkable::AcknowledgeProblem(const String& author, const String& comment, if (notify && !IsPaused()) OnNotificationsRequested(this, NotificationAcknowledgement, GetLastCheckResult(), author, comment, nullptr); + Log(LogInformation, "Checkable") + << "Acknowledgement set for checkable '" << GetName() << "'."; + OnAcknowledgementSet(this, author, comment, type, notify, persistent, expiry, origin); } @@ -140,6 +141,9 @@ void Checkable::ClearAcknowledgement(const MessageOrigin::Ptr& origin) SetAcknowledgementRaw(AcknowledgementNone); SetAcknowledgementExpiry(0); + Log(LogInformation, "Checkable") + << "Acknowledgement cleared for checkable '" << GetName() << "'."; + OnAcknowledgementCleared(this, origin); } @@ -154,6 +158,16 @@ int Checkable::GetSeverity() const return 0; } +bool Checkable::GetProblem() const +{ + return !IsStateOK(GetStateRaw()); +} + +bool Checkable::GetHandled() const +{ + return GetProblem() && (IsInDowntime() || IsAcknowledged()); +} + void Checkable::NotifyFixedDowntimeStart(const Downtime::Ptr& downtime) { if (!downtime->GetFixed()) @@ -198,6 +212,14 @@ void Checkable::ValidateCheckInterval(const Lazy& lvalue, const Validati BOOST_THROW_EXCEPTION(ValidationError(this, { "check_interval" }, "Interval must be greater than 0.")); } +void Checkable::ValidateRetryInterval(const Lazy& lvalue, const ValidationUtils& utils) +{ + ObjectImpl::ValidateRetryInterval(lvalue, utils); + + if (lvalue() <= 0) + BOOST_THROW_EXCEPTION(ValidationError(this, { "retry_interval" }, "Interval must be greater than 0.")); +} + void Checkable::ValidateMaxCheckAttempts(const Lazy& lvalue, const ValidationUtils& utils) { ObjectImpl::ValidateMaxCheckAttempts(lvalue, utils); diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp index a71487e54..caddd63f4 100644 --- a/lib/icinga/checkable.hpp +++ b/lib/icinga/checkable.hpp @@ -1,25 +1,10 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CHECKABLE_H #define CHECKABLE_H +#include "base/atomic.hpp" +#include "base/timer.hpp" #include "icinga/i2-icinga.hpp" #include "icinga/checkable-ti.hpp" #include "icinga/timeperiod.hpp" @@ -28,6 +13,7 @@ #include "icinga/downtime.hpp" #include "remote/endpoint.hpp" #include "remote/messageorigin.hpp" +#include namespace icinga { @@ -62,6 +48,7 @@ enum SeverityFlag { SeverityFlagDowntime = 1, SeverityFlagAcknowledgement = 2, + SeverityFlagHostDown = 4, SeverityFlagUnhandled = 8, SeverityFlagPending = 16, SeverityFlagWarning = 32, @@ -102,6 +89,8 @@ public: void ClearAcknowledgement(const MessageOrigin::Ptr& origin = nullptr); int GetSeverity() const override; + bool GetProblem() const override; + bool GetHandled() const override; /* Checks */ intrusive_ptr GetCheckCommand() const; @@ -113,7 +102,7 @@ public: void UpdateNextCheck(const MessageOrigin::Ptr& origin = nullptr); bool HasBeenChecked() const; - virtual bool IsStateOK(ServiceState state) = 0; + virtual bool IsStateOK(ServiceState state) const = 0; double GetLastCheck() const final; @@ -133,8 +122,8 @@ public: static boost::signals2::signal OnNotificationsRequested; static boost::signals2::signal OnNotificationSentToUser; + const NotificationType&, const CheckResult::Ptr&, const NotificationResult::Ptr&, const String&, + const String&, const String&, const MessageOrigin::Ptr&)> OnNotificationSentToUser; static boost::signals2::signal&, const NotificationType&, const CheckResult::Ptr&, const String&, const String&, const MessageOrigin::Ptr&)> OnNotificationSentToAllUsers; @@ -144,6 +133,8 @@ public: static boost::signals2::signal OnNextCheckUpdated; static boost::signals2::signal OnEventCommandExecuted; + static Atomic CurrentConcurrentChecks; + /* Downtimes */ int GetDowntimeDepth() const final; @@ -192,6 +183,7 @@ public: std::vector > GetReverseDependencies() const; void ValidateCheckInterval(const Lazy& lvalue, const ValidationUtils& value) final; + void ValidateRetryInterval(const Lazy& lvalue, const ValidationUtils& value) final; void ValidateMaxCheckAttempts(const Lazy& lvalue, const ValidationUtils& value) final; static void IncreasePendingChecks(); @@ -224,6 +216,8 @@ private: static void NotifyDowntimeEnd(const Downtime::Ptr& downtime); + static void FireSuppressedNotifications(const Timer * const&); + /* Comments */ std::set m_Comments; mutable boost::mutex m_CommentMutex; diff --git a/lib/icinga/checkable.ti b/lib/icinga/checkable.ti index d9918e7f8..7969d6f46 100644 --- a/lib/icinga/checkable.ti +++ b/lib/icinga/checkable.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/icingaapplication.hpp" #include "icinga/customvarobject.hpp" @@ -137,9 +120,18 @@ abstract class Checkable : CustomVarObject }; [state] Timestamp last_state_unreachable; + [state] Timestamp previous_state_change { + default {{{ return Application::GetStartTime(); }}} + }; [no_storage] int severity { get; }; + [no_storage] bool problem { + get; + }; + [no_storage] bool handled { + get; + }; [state] bool force_next_check; [state] int acknowledgement (AcknowledgementRaw) { @@ -162,6 +154,9 @@ abstract class Checkable : CustomVarObject [state, no_user_view, no_user_modify] int flapping_buffer; [state, no_user_view, no_user_modify] int flapping_index; [state, protected] bool flapping; + [state, no_user_view, no_user_modify] int suppressed_notifications { + default {{{ return 0; }}} + }; [config, navigation] name(Endpoint) command_endpoint (CommandEndpointRaw) { navigate {{{ diff --git a/lib/icinga/checkcommand.cpp b/lib/icinga/checkcommand.cpp index e32af3220..e0da41547 100644 --- a/lib/icinga/checkcommand.cpp +++ b/lib/icinga/checkcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/checkcommand.hpp" #include "icinga/checkcommand-ti.cpp" diff --git a/lib/icinga/checkcommand.hpp b/lib/icinga/checkcommand.hpp index 471d6c2a1..6eb6119a3 100644 --- a/lib/icinga/checkcommand.hpp +++ b/lib/icinga/checkcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CHECKCOMMAND_H #define CHECKCOMMAND_H diff --git a/lib/icinga/checkcommand.ti b/lib/icinga/checkcommand.ti index bcc29526d..c211f0f68 100644 --- a/lib/icinga/checkcommand.ti +++ b/lib/icinga/checkcommand.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/command.hpp" diff --git a/lib/icinga/checkresult.cpp b/lib/icinga/checkresult.cpp index 52b1bb5e3..cb445af6c 100644 --- a/lib/icinga/checkresult.cpp +++ b/lib/icinga/checkresult.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/checkresult.hpp" #include "icinga/checkresult-ti.cpp" @@ -26,13 +9,13 @@ using namespace icinga; REGISTER_TYPE(CheckResult); INITIALIZE_ONCE([]() { - ScriptGlobal::Set("ServiceOK", ServiceOK); - ScriptGlobal::Set("ServiceWarning", ServiceWarning); - ScriptGlobal::Set("ServiceCritical", ServiceCritical); - ScriptGlobal::Set("ServiceUnknown", ServiceUnknown); + ScriptGlobal::Set("Icinga.ServiceOK", ServiceOK, true); + ScriptGlobal::Set("Icinga.ServiceWarning", ServiceWarning, true); + ScriptGlobal::Set("Icinga.ServiceCritical", ServiceCritical, true); + ScriptGlobal::Set("Icinga.ServiceUnknown", ServiceUnknown, true); - ScriptGlobal::Set("HostUp", HostUp); - ScriptGlobal::Set("HostDown", HostDown); + ScriptGlobal::Set("Icinga.HostUp", HostUp, true); + ScriptGlobal::Set("Icinga.HostDown", HostDown, true); }) double CheckResult::CalculateExecutionTime() const diff --git a/lib/icinga/checkresult.hpp b/lib/icinga/checkresult.hpp index 245b4ba32..ac54d6b0d 100644 --- a/lib/icinga/checkresult.hpp +++ b/lib/icinga/checkresult.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CHECKRESULT_H #define CHECKRESULT_H diff --git a/lib/icinga/checkresult.ti b/lib/icinga/checkresult.ti index f95a4ed64..6fe49ae94 100644 --- a/lib/icinga/checkresult.ti +++ b/lib/icinga/checkresult.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ library icinga; diff --git a/lib/icinga/cib.cpp b/lib/icinga/cib.cpp index e68df57ad..0cb6ef0d5 100644 --- a/lib/icinga/cib.cpp +++ b/lib/icinga/cib.cpp @@ -1,26 +1,10 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/cib.hpp" #include "icinga/host.hpp" #include "icinga/service.hpp" #include "icinga/clusterevents.hpp" +#include "base/application.hpp" #include "base/objectlock.hpp" #include "base/utility.hpp" #include "base/perfdatavalue.hpp" @@ -203,8 +187,6 @@ ServiceStatistics CIB::CalculateServiceStats() for (const Service::Ptr& service : ConfigType::GetObjectsByType()) { ObjectLock olock(service); - CheckResult::Ptr cr = service->GetLastCheckResult(); - if (service->GetState() == ServiceOK) ss.services_ok++; if (service->GetState() == ServiceWarning) @@ -214,8 +196,11 @@ ServiceStatistics CIB::CalculateServiceStats() if (service->GetState() == ServiceUnknown) ss.services_unknown++; + CheckResult::Ptr cr = service->GetLastCheckResult(); + if (!cr) ss.services_pending++; + if (!service->IsReachable()) ss.services_unreachable++; @@ -225,6 +210,11 @@ ServiceStatistics CIB::CalculateServiceStats() ss.services_in_downtime++; if (service->IsAcknowledged()) ss.services_acknowledged++; + + if (service->GetHandled()) + ss.services_handled++; + if (service->GetProblem()) + ss.services_problem++; } return ss; @@ -254,6 +244,11 @@ HostStatistics CIB::CalculateHostStats() hs.hosts_in_downtime++; if (host->IsAcknowledged()) hs.hosts_acknowledged++; + + if (host->GetHandled()) + hs.hosts_handled++; + if (host->GetProblem()) + hs.hosts_problem++; } return hs; @@ -268,13 +263,13 @@ std::pair CIB::GetFeatureStats() Dictionary::Ptr status = new Dictionary(); Array::Ptr perfdata = new Array(); - Dictionary::Ptr statsFunctions = ScriptGlobal::Get("StatsFunctions", &Empty); + Namespace::Ptr statsFunctions = ScriptGlobal::Get("StatsFunctions", &Empty); if (statsFunctions) { ObjectLock olock(statsFunctions); - for (const Dictionary::Pair& kv : statsFunctions) - static_cast(kv.second)->Invoke({ status, perfdata }); + for (const Namespace::Pair& kv : statsFunctions) + static_cast(kv.second->Get())->Invoke({ status, perfdata }); } return std::make_pair(status, perfdata); @@ -306,7 +301,10 @@ void CIB::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata) { status->Set("active_service_checks_15min", GetActiveServiceChecksStatistics(60 * 15)); status->Set("passive_service_checks_15min", GetPassiveServiceChecksStatistics(60 * 15)); + // Checker related stats status->Set("remote_check_queue", ClusterEvents::GetCheckRequestQueueSize()); + status->Set("current_pending_callbacks", Application::GetTP().GetPending()); + status->Set("current_concurrent_checks", Checkable::CurrentConcurrentChecks.load()); CheckableCheckStatistics scs = CalculateServiceCheckStats(); @@ -328,6 +326,8 @@ void CIB::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata) { status->Set("num_services_flapping", ss.services_flapping); status->Set("num_services_in_downtime", ss.services_in_downtime); status->Set("num_services_acknowledged", ss.services_acknowledged); + status->Set("num_services_handled", ss.services_handled); + status->Set("num_services_problem", ss.services_problem); double uptime = Utility::GetTime() - Application::GetStartTime(); status->Set("uptime", uptime); @@ -341,4 +341,6 @@ void CIB::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata) { status->Set("num_hosts_flapping", hs.hosts_flapping); status->Set("num_hosts_in_downtime", hs.hosts_in_downtime); status->Set("num_hosts_acknowledged", hs.hosts_acknowledged); + status->Set("num_hosts_handled", hs.hosts_handled); + status->Set("num_hosts_problem", hs.hosts_problem); } diff --git a/lib/icinga/cib.hpp b/lib/icinga/cib.hpp index 09bcba5e1..2abbb4600 100644 --- a/lib/icinga/cib.hpp +++ b/lib/icinga/cib.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CIB_H #define CIB_H @@ -47,6 +30,8 @@ struct ServiceStatistics { double services_flapping; double services_in_downtime; double services_acknowledged; + double services_handled; + double services_problem; }; struct HostStatistics { @@ -57,6 +42,8 @@ struct HostStatistics { double hosts_flapping; double hosts_in_downtime; double hosts_acknowledged; + double hosts_handled; + double hosts_problem; }; /** diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 58d983eb8..dec1b7c12 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -1,24 +1,9 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/clusterevents.hpp" +#include "icinga/icingaapplication.hpp" #include "remote/apilistener.hpp" +#include "base/configuration.hpp" #include "base/serializer.hpp" #include "base/exception.hpp" #include @@ -37,6 +22,8 @@ void ClusterEvents::RemoteCheckThreadProc() { Utility::SetThreadName("Remote Check Scheduler"); + int maxConcurrentChecks = IcingaApplication::GetInstance()->GetMaxConcurrentChecks(); + boost::mutex::scoped_lock lock(m_Mutex); for(;;) { @@ -44,7 +31,7 @@ void ClusterEvents::RemoteCheckThreadProc() break; lock.unlock(); - Checkable::AquirePendingCheckSlot(Application::GetMaxConcurrentChecks()); + Checkable::AquirePendingCheckSlot(maxConcurrentChecks); lock.lock(); auto callback = m_CheckRequestQueue.front(); @@ -179,7 +166,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons CheckResult::Ptr cr = new CheckResult(); cr->SetState(ServiceUnknown); - String output = "Exception occured while checking '" + host->GetName() + "': " + DiagnosticInformation(ex); + String output = "Exception occurred while checking '" + host->GetName() + "': " + DiagnosticInformation(ex); cr->SetOutput(output); double now = Utility::GetTime(); @@ -222,4 +209,4 @@ void ClusterEvents::LogRemoteCheckQueueInformation() { << m_ChecksExecutedDuringInterval * 6 * 15 << "/15min" << ");"; m_ChecksExecutedDuringInterval = 0; -} \ No newline at end of file +} diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index e892caabf..313adb1eb 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/clusterevents.hpp" #include "icinga/service.hpp" @@ -41,6 +24,7 @@ INITIALIZE_ONCE(&ClusterEvents::StaticInitialize); REGISTER_APIFUNCTION(CheckResult, event, &ClusterEvents::CheckResultAPIHandler); REGISTER_APIFUNCTION(SetNextCheck, event, &ClusterEvents::NextCheckChangedAPIHandler); +REGISTER_APIFUNCTION(SetSuppressedNotifications, event, &ClusterEvents::SuppressedNotificationsChangedAPIHandler); REGISTER_APIFUNCTION(SetNextNotification, event, &ClusterEvents::NextNotificationChangedAPIHandler); REGISTER_APIFUNCTION(SetForceNextCheck, event, &ClusterEvents::ForceNextCheckChangedAPIHandler); REGISTER_APIFUNCTION(SetForceNextNotification, event, &ClusterEvents::ForceNextNotificationChangedAPIHandler); @@ -55,6 +39,7 @@ void ClusterEvents::StaticInitialize() { Checkable::OnNewCheckResult.connect(&ClusterEvents::CheckResultHandler); Checkable::OnNextCheckChanged.connect(&ClusterEvents::NextCheckChangedHandler); + Checkable::OnSuppressedNotificationsChanged.connect(&ClusterEvents::SuppressedNotificationsChangedHandler); Notification::OnNextNotificationChanged.connect(&ClusterEvents::NextNotificationChangedHandler); Checkable::OnForceNextCheckChanged.connect(&ClusterEvents::ForceNextCheckChangedHandler); Checkable::OnForceNextNotificationChanged.connect(&ClusterEvents::ForceNextNotificationChangedHandler); @@ -249,6 +234,68 @@ Value ClusterEvents::NextCheckChangedAPIHandler(const MessageOrigin::Ptr& origin return Empty; } +void ClusterEvents::SuppressedNotificationsChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin) +{ + ApiListener::Ptr listener = ApiListener::GetInstance(); + + if (!listener) + return; + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + Dictionary::Ptr params = new Dictionary(); + params->Set("host", host->GetName()); + if (service) + params->Set("service", service->GetShortName()); + params->Set("suppressed_notifications", checkable->GetSuppressedNotifications()); + + Dictionary::Ptr message = new Dictionary(); + message->Set("jsonrpc", "2.0"); + message->Set("method", "event::SetSuppressedNotifications"); + message->Set("params", params); + + listener->RelayMessage(origin, checkable, message, true); +} + +Value ClusterEvents::SuppressedNotificationsChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) +{ + Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint(); + + if (!endpoint) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'suppressed notifications changed' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed)."; + return Empty; + } + + Host::Ptr host = Host::GetByName(params->Get("host")); + + if (!host) + return Empty; + + Checkable::Ptr checkable; + + if (params->Contains("service")) + checkable = host->GetServiceByShortName(params->Get("service")); + else + checkable = host; + + if (!checkable) + return Empty; + + if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'suppressed notifications changed' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; + return Empty; + } + + checkable->SetSuppressedNotifications(params->Get("suppressed_notifications"), false, origin); + + return Empty; +} + void ClusterEvents::NextNotificationChangedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin) { ApiListener::Ptr listener = ApiListener::GetInstance(); @@ -445,6 +492,7 @@ void ClusterEvents::AcknowledgementSetHandler(const Checkable::Ptr& checkable, params->Set("comment", comment); params->Set("acktype", type); params->Set("notify", notify); + params->Set("persistent", persistent); params->Set("expiry", expiry); Dictionary::Ptr message = new Dictionary(); @@ -640,7 +688,7 @@ Value ClusterEvents::SendNotificationsAPIHandler(const MessageOrigin::Ptr& origi } void ClusterEvents::NotificationSentUserHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const User::Ptr& user, - NotificationType notificationType, const CheckResult::Ptr& cr, const String& author, const String& commentText, const String& command, + NotificationType notificationType, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, const String& author, const String& commentText, const String& command, const MessageOrigin::Ptr& origin) { ApiListener::Ptr listener = ApiListener::GetInstance(); @@ -660,6 +708,7 @@ void ClusterEvents::NotificationSentUserHandler(const Notification::Ptr& notific params->Set("user", user->GetName()); params->Set("type", notificationType); params->Set("cr", Serialize(cr)); + params->Set("nr", Serialize(nr)); params->Set("author", author); params->Set("text", commentText); params->Set("command", command); @@ -721,6 +770,14 @@ Value ClusterEvents::NotificationSentUserAPIHandler(const MessageOrigin::Ptr& or } } + NotificationResult::Ptr nr; + if (params->Contains("nr")) { + nr = new NotificationResult(); + Dictionary::Ptr vnr = params->Get("nr"); + + Deserialize(nr, vnr, true); + } + NotificationType type = static_cast(static_cast(params->Get("type"))); String author = params->Get("author"); String text = params->Get("text"); @@ -737,7 +794,7 @@ Value ClusterEvents::NotificationSentUserAPIHandler(const MessageOrigin::Ptr& or String command = params->Get("command"); - Checkable::OnNotificationSentToUser(notification, checkable, user, type, cr, author, text, command, origin); + Checkable::OnNotificationSentToUser(notification, checkable, user, type, cr, nr, author, text, command, origin); return Empty; } diff --git a/lib/icinga/clusterevents.hpp b/lib/icinga/clusterevents.hpp index 5b8acf78a..8dc6f48b9 100644 --- a/lib/icinga/clusterevents.hpp +++ b/lib/icinga/clusterevents.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CLUSTEREVENTS_H #define CLUSTEREVENTS_H @@ -43,6 +26,9 @@ public: static void NextCheckChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin); static Value NextCheckChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); + static void SuppressedNotificationsChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin); + static Value SuppressedNotificationsChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); + static void NextNotificationChangedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin); static Value NextNotificationChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); @@ -68,7 +54,8 @@ public: static Value SendNotificationsAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static void NotificationSentUserHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const User::Ptr& user, - NotificationType notificationType, const CheckResult::Ptr& cr, const String& author, const String& commentText, const String& command, const MessageOrigin::Ptr& origin); + NotificationType notificationType, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, + const String& author, const String& commentText, const String& command, const MessageOrigin::Ptr& origin); static Value NotificationSentUserAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static void NotificationSentToAllUsersHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set& users, diff --git a/lib/icinga/command.cpp b/lib/icinga/command.cpp index 628ed301a..8e0f357e7 100644 --- a/lib/icinga/command.cpp +++ b/lib/icinga/command.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/command.hpp" #include "icinga/command-ti.cpp" diff --git a/lib/icinga/command.hpp b/lib/icinga/command.hpp index fa1aa5c03..19bb05013 100644 --- a/lib/icinga/command.hpp +++ b/lib/icinga/command.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef COMMAND_H #define COMMAND_H diff --git a/lib/icinga/command.ti b/lib/icinga/command.ti index 64b7482a8..5055ee3d5 100644 --- a/lib/icinga/command.ti +++ b/lib/icinga/command.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/customvarobject.hpp" #include "base/function.hpp" diff --git a/lib/icinga/comment.cpp b/lib/icinga/comment.cpp index 9931b42bd..0945724ea 100644 --- a/lib/icinga/comment.cpp +++ b/lib/icinga/comment.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/comment.hpp" #include "icinga/comment-ti.cpp" diff --git a/lib/icinga/comment.hpp b/lib/icinga/comment.hpp index d031b66de..166654d31 100644 --- a/lib/icinga/comment.hpp +++ b/lib/icinga/comment.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef COMMENT_H #define COMMENT_H diff --git a/lib/icinga/comment.ti b/lib/icinga/comment.ti index 003fe0ff5..4bf667f31 100644 --- a/lib/icinga/comment.ti +++ b/lib/icinga/comment.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" #include "base/utility.hpp" diff --git a/lib/icinga/compatutility.cpp b/lib/icinga/compatutility.cpp index 3eb0dc573..305d3b247 100644 --- a/lib/icinga/compatutility.cpp +++ b/lib/icinga/compatutility.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/compatutility.hpp" #include "icinga/checkcommand.hpp" diff --git a/lib/icinga/compatutility.hpp b/lib/icinga/compatutility.hpp index 388cdcb05..7b96fb308 100644 --- a/lib/icinga/compatutility.hpp +++ b/lib/icinga/compatutility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef COMPATUTILITY_H #define COMPATUTILITY_H diff --git a/lib/icinga/customvarobject.cpp b/lib/icinga/customvarobject.cpp index 2055dbd78..fc1fd27b4 100644 --- a/lib/icinga/customvarobject.cpp +++ b/lib/icinga/customvarobject.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/customvarobject.hpp" #include "icinga/customvarobject-ti.cpp" diff --git a/lib/icinga/customvarobject.hpp b/lib/icinga/customvarobject.hpp index 84328bd7b..e10ef3245 100644 --- a/lib/icinga/customvarobject.hpp +++ b/lib/icinga/customvarobject.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CUSTOMVAROBJECT_H #define CUSTOMVAROBJECT_H diff --git a/lib/icinga/customvarobject.ti b/lib/icinga/customvarobject.ti index 949c36498..18da48b00 100644 --- a/lib/icinga/customvarobject.ti +++ b/lib/icinga/customvarobject.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" diff --git a/lib/icinga/dependency-apply.cpp b/lib/icinga/dependency-apply.cpp index cc464aa7d..0f8eddc5b 100644 --- a/lib/icinga/dependency-apply.cpp +++ b/lib/icinga/dependency-apply.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/dependency.hpp" #include "icinga/service.hpp" diff --git a/lib/icinga/dependency.cpp b/lib/icinga/dependency.cpp index 328a20e76..d2261cfe1 100644 --- a/lib/icinga/dependency.cpp +++ b/lib/icinga/dependency.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/dependency.hpp" #include "icinga/dependency-ti.cpp" diff --git a/lib/icinga/dependency.hpp b/lib/icinga/dependency.hpp index b9db395c3..6a80d84f5 100644 --- a/lib/icinga/dependency.hpp +++ b/lib/icinga/dependency.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DEPENDENCY_H #define DEPENDENCY_H diff --git a/lib/icinga/dependency.ti b/lib/icinga/dependency.ti index 66c215bda..62e6d8b31 100644 --- a/lib/icinga/dependency.ti +++ b/lib/icinga/dependency.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/customvarobject.hpp" #include "icinga/checkable.hpp" diff --git a/lib/icinga/downtime.cpp b/lib/icinga/downtime.cpp index 8c3584a4b..f003bc746 100644 --- a/lib/icinga/downtime.cpp +++ b/lib/icinga/downtime.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/downtime.hpp" #include "icinga/downtime-ti.cpp" @@ -42,6 +25,15 @@ boost::signals2::signal Downtime::OnDowntimeTrigger REGISTER_TYPE(Downtime); +INITIALIZE_ONCE(&Downtime::StaticInitialize); + +void Downtime::StaticInitialize() +{ + ScriptGlobal::Set("Icinga.DowntimeNoChildren", "DowntimeNoChildren", true); + ScriptGlobal::Set("Icinga.DowntimeTriggeredChildren", "DowntimeTriggeredChildren", true); + ScriptGlobal::Set("Icinga.DowntimeNonTriggeredChildren", "DowntimeNonTriggeredChildren", true); +} + String DowntimeNameComposer::MakeName(const String& shortName, const Object::Ptr& context) const { Downtime::Ptr downtime = dynamic_pointer_cast(context); @@ -201,8 +193,12 @@ bool Downtime::IsExpired() const bool Downtime::HasValidConfigOwner() const { + if (!ScheduledDowntime::AllConfigIsLoaded()) { + return true; + } + String configOwner = GetConfigOwner(); - return configOwner.IsEmpty() || GetObject(configOwner); + return configOwner.IsEmpty() || Zone::GetByName(GetAuthoritativeZone()) != Zone::GetLocalZone() || GetObject(configOwner); } int Downtime::GetNextDowntimeID() @@ -238,6 +234,14 @@ String Downtime::AddDowntime(const Checkable::Ptr& checkable, const String& auth attrs->Set("config_owner", scheduledDowntime); attrs->Set("entry_time", Utility::GetTime()); + if (!scheduledDowntime.IsEmpty()) { + auto localZone (Zone::GetLocalZone()); + + if (localZone) { + attrs->Set("authoritative_zone", localZone->GetName()); + } + } + Host::Ptr host; Service::Ptr service; tie(host, service) = GetHostService(checkable); @@ -246,7 +250,23 @@ String Downtime::AddDowntime(const Checkable::Ptr& checkable, const String& auth if (service) attrs->Set("service_name", service->GetShortName()); - String zone = checkable->GetZoneName(); + String zone; + + if (!scheduledDowntime.IsEmpty()) { + auto sdt (ScheduledDowntime::GetByName(scheduledDowntime)); + + if (sdt) { + auto sdtZone (sdt->GetZone()); + + if (sdtZone) { + zone = sdtZone->GetName(); + } + } + } + + if (zone.IsEmpty()) { + zone = checkable->GetZoneName(); + } if (!zone.IsEmpty()) attrs->Set("zone", zone); @@ -278,10 +298,11 @@ String Downtime::AddDowntime(const Checkable::Ptr& checkable, const String& auth if (!downtime) BOOST_THROW_EXCEPTION(std::runtime_error("Could not create downtime object.")); - Log(LogNotice, "Downtime") + Log(LogInformation, "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) << "'."; + << "' and '" << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", endTime) << "', author: '" + << author << "', " << (fixed ? "fixed" : "flexible with " + Convert::ToString(duration) + "s duration"); return fullName; } @@ -303,9 +324,6 @@ void Downtime::RemoveDowntime(const String& id, bool cancelled, bool expired, co downtime->SetWasCancelled(cancelled); - Log(LogNotice, "Downtime") - << "Removed downtime '" << downtime->GetName() << "' from object '" << downtime->GetCheckable()->GetName() << "'."; - Array::Ptr errors = new Array(); if (!ConfigObjectUtility::DeleteObject(downtime, false, errors, nullptr)) { @@ -316,6 +334,21 @@ void Downtime::RemoveDowntime(const String& id, bool cancelled, bool expired, co BOOST_THROW_EXCEPTION(std::runtime_error("Could not remove downtime.")); } + + String reason; + + if (expired) { + reason = "expired at " + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", downtime->GetEndTime()); + } else if (cancelled) { + reason = "cancelled by user"; + } else { + reason = ""; + } + + Log(LogInformation, "Downtime") + << "Removed downtime '" << downtime->GetName() << "' from checkable '" + << downtime->GetCheckable()->GetName() << "' (Reason: " << reason << ")."; + } bool Downtime::CanBeTriggered() @@ -339,8 +372,10 @@ void Downtime::TriggerDowntime() if (!CanBeTriggered()) return; - Log(LogNotice, "Downtime") - << "Triggering downtime '" << GetName() << "'."; + Checkable::Ptr checkable = GetCheckable(); + + Log(LogInformation, "Downtime") + << "Triggering downtime '" << GetName() << "' for checkable '" << checkable->GetName() << "'."; if (GetTriggerTime() == 0) SetTriggerTime(Utility::GetTime()); @@ -420,3 +455,20 @@ void Downtime::ValidateEndTime(const Lazy& lvalue, const ValidationUt if (lvalue() <= 0) BOOST_THROW_EXCEPTION(ValidationError(this, { "end_time" }, "End time must be greater than 0.")); } + +DowntimeChildOptions Downtime::ChildOptionsFromValue(const Value& options) +{ + if (options == "DowntimeNoChildren") + return DowntimeNoChildren; + else if (options == "DowntimeTriggeredChildren") + return DowntimeTriggeredChildren; + else if (options == "DowntimeNonTriggeredChildren") + return DowntimeNonTriggeredChildren; + else if (options.IsNumber()) { + int number = options; + if (number >= 0 && number <= 2) + return static_cast(number); + } + + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid child option specified")); +} diff --git a/lib/icinga/downtime.hpp b/lib/icinga/downtime.hpp index 6d4ba0a21..c33d9017d 100644 --- a/lib/icinga/downtime.hpp +++ b/lib/icinga/downtime.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DOWNTIME_H #define DOWNTIME_H @@ -28,6 +11,13 @@ namespace icinga { +enum DowntimeChildOptions +{ + DowntimeNoChildren, + DowntimeTriggeredChildren, + DowntimeNonTriggeredChildren +}; + /** * A downtime. * @@ -51,6 +41,8 @@ public: bool IsExpired() const; bool HasValidConfigOwner() const; + static void StaticInitialize(); + static int GetNextDowntimeID(); static String AddDowntime(const intrusive_ptr& checkable, const String& author, @@ -65,6 +57,8 @@ public: static String GetDowntimeIDFromLegacyID(int id); + static DowntimeChildOptions ChildOptionsFromValue(const Value& options); + protected: void OnAllConfigLoaded() override; void Start(bool runtimeCreated) override; diff --git a/lib/icinga/downtime.ti b/lib/icinga/downtime.ti index 0cbc6738a..a55942fb0 100644 --- a/lib/icinga/downtime.ti +++ b/lib/icinga/downtime.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" #include "base/utility.hpp" @@ -37,6 +20,8 @@ public: class Downtime : ConfigObject < DowntimeNameComposer { + activation_priority -10; + load_after Host; load_after Service; @@ -84,6 +69,7 @@ class Downtime : ConfigObject < DowntimeNameComposer [state] int legacy_id; [state] bool was_cancelled; [config] String config_owner; + [config] String authoritative_zone; }; } diff --git a/lib/icinga/eventcommand.cpp b/lib/icinga/eventcommand.cpp index a2a1cdc79..f9ab3be19 100644 --- a/lib/icinga/eventcommand.cpp +++ b/lib/icinga/eventcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/eventcommand.hpp" #include "icinga/eventcommand-ti.cpp" diff --git a/lib/icinga/eventcommand.hpp b/lib/icinga/eventcommand.hpp index d971976a1..95bd1095a 100644 --- a/lib/icinga/eventcommand.hpp +++ b/lib/icinga/eventcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef EVENTCOMMAND_H #define EVENTCOMMAND_H diff --git a/lib/icinga/eventcommand.ti b/lib/icinga/eventcommand.ti index 10ee9d532..a166d1eb8 100644 --- a/lib/icinga/eventcommand.ti +++ b/lib/icinga/eventcommand.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/command.hpp" diff --git a/lib/icinga/externalcommandprocessor.cpp b/lib/icinga/externalcommandprocessor.cpp index f117743fa..9f088ae67 100644 --- a/lib/icinga/externalcommandprocessor.cpp +++ b/lib/icinga/externalcommandprocessor.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/externalcommandprocessor.hpp" #include "icinga/host.hpp" diff --git a/lib/icinga/externalcommandprocessor.hpp b/lib/icinga/externalcommandprocessor.hpp index 5aff98cb2..f7f0d75d6 100644 --- a/lib/icinga/externalcommandprocessor.hpp +++ b/lib/icinga/externalcommandprocessor.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef EXTERNALCOMMANDPROCESSOR_H #define EXTERNALCOMMANDPROCESSOR_H diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 89c4131e6..9744eed46 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/host.hpp" #include "icinga/host-ti.cpp" @@ -209,7 +192,7 @@ int Host::GetSeverity() const return severity; } -bool Host::IsStateOK(ServiceState state) +bool Host::IsStateOK(ServiceState state) const { return Host::CalculateState(state) == HostUp; } diff --git a/lib/icinga/host.hpp b/lib/icinga/host.hpp index cbd49d1db..237d8aaa1 100644 --- a/lib/icinga/host.hpp +++ b/lib/icinga/host.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef HOST_H #define HOST_H @@ -56,7 +39,7 @@ public: HostState GetLastHardState() const override; int GetSeverity() const override; - bool IsStateOK(ServiceState state) override; + bool IsStateOK(ServiceState state) const override; void SaveLastState(ServiceState state, double timestamp) override; static HostState StateFromString(const String& state); diff --git a/lib/icinga/host.ti b/lib/icinga/host.ti index 04fc8f90f..03c606228 100644 --- a/lib/icinga/host.ti +++ b/lib/icinga/host.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/checkable.hpp" #include "icinga/customvarobject.hpp" diff --git a/lib/icinga/hostgroup.cpp b/lib/icinga/hostgroup.cpp index d0ca7b323..02dc6d7c9 100644 --- a/lib/icinga/hostgroup.cpp +++ b/lib/icinga/hostgroup.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/hostgroup.hpp" #include "icinga/hostgroup-ti.cpp" diff --git a/lib/icinga/hostgroup.hpp b/lib/icinga/hostgroup.hpp index fb2564620..283930a23 100644 --- a/lib/icinga/hostgroup.hpp +++ b/lib/icinga/hostgroup.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef HOSTGROUP_H #define HOSTGROUP_H diff --git a/lib/icinga/hostgroup.ti b/lib/icinga/hostgroup.ti index 3ef1d0b91..a4404eafe 100644 --- a/lib/icinga/hostgroup.ti +++ b/lib/icinga/hostgroup.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/customvarobject.hpp" diff --git a/lib/icinga/i2-icinga.hpp b/lib/icinga/i2-icinga.hpp index 80469b4bd..7163822da 100644 --- a/lib/icinga/i2-icinga.hpp +++ b/lib/icinga/i2-icinga.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef I2ICINGA_H #define I2ICINGA_H diff --git a/lib/icinga/icinga-itl.conf b/lib/icinga/icinga-itl.conf index 6fcfaf518..22b688a7d 100644 --- a/lib/icinga/icinga-itl.conf +++ b/lib/icinga/icinga-itl.conf @@ -1,27 +1,8 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ System.assert(Internal.run_with_activation_context(function() { - var _Internal = Internal.clone() - - template TimePeriod "legacy-timeperiod" use (_Internal) default { - update = _Internal.LegacyTimePeriod + template TimePeriod "legacy-timeperiod" use (LegacyTimePeriod = Internal.LegacyTimePeriod) default { + update = LegacyTimePeriod } })) diff --git a/lib/icinga/icingaapplication.cpp b/lib/icinga/icingaapplication.cpp index 454c78d6d..451f009da 100644 --- a/lib/icinga/icingaapplication.cpp +++ b/lib/icinga/icingaapplication.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/icingaapplication.hpp" #include "icinga/icingaapplication-ti.cpp" @@ -41,10 +24,12 @@ using namespace icinga; static Timer::Ptr l_RetentionTimer; REGISTER_TYPE(IcingaApplication); -INITIALIZE_ONCE(&IcingaApplication::StaticInitialize); +/* Ensure that the priority is lower than the basic System namespace initialization in scriptframe.cpp. */ +INITIALIZE_ONCE_WITH_PRIORITY(&IcingaApplication::StaticInitialize, 50); void IcingaApplication::StaticInitialize() { + /* Pre-fill global constants, can be overridden with user input later in icinga-app/icinga.cpp. */ String node_name = Utility::GetFQDN(); if (node_name.IsEmpty()) { @@ -59,9 +44,23 @@ void IcingaApplication::StaticInitialize() ScriptGlobal::Set("NodeName", node_name); - ScriptGlobal::Set("ApplicationType", "IcingaApplication"); + ScriptGlobal::Set("ReloadTimeout", 300); + ScriptGlobal::Set("MaxConcurrentChecks", 512); - ScriptGlobal::Set("ApplicationVersion", Application::GetAppVersion()); + Namespace::Ptr systemNS = ScriptGlobal::Get("System"); + /* Ensure that the System namespace is already initialized. Otherwise this is a programming error. */ + VERIFY(systemNS); + + systemNS->Set("ApplicationType", "IcingaApplication", true); + systemNS->Set("ApplicationVersion", Application::GetAppVersion(), true); + + Namespace::Ptr globalNS = ScriptGlobal::GetGlobals(); + VERIFY(globalNS); + + auto icingaNSBehavior = new ConstNamespaceBehavior(); + icingaNSBehavior->Freeze(); + Namespace::Ptr icingaNS = new Namespace(icingaNSBehavior); + globalNS->SetAttribute("Icinga", std::make_shared(icingaNS)); } REGISTER_STATSFUNCTION(IcingaApplication, &IcingaApplication::StatsFunc); @@ -79,10 +78,10 @@ void IcingaApplication::StatsFunc(const Dictionary::Ptr& status, const Array::Pt { "enable_host_checks", icingaapplication->GetEnableHostChecks() }, { "enable_service_checks", icingaapplication->GetEnableServiceChecks() }, { "enable_perfdata", icingaapplication->GetEnablePerfdata() }, + { "environment", icingaapplication->GetEnvironment() }, { "pid", Utility::GetPid() }, { "program_start", Application::GetStartTime() }, - { "version", Application::GetAppVersion() }, - { "environment", ScriptGlobal::Get("Environment", &Empty) } + { "version", Application::GetAppVersion() } })); } @@ -156,13 +155,13 @@ static void PersistModAttrHelper(std::fstream& fp, ConfigObject::Ptr& previousOb void IcingaApplication::DumpProgramState() { - ConfigObject::DumpObjects(GetStatePath()); + ConfigObject::DumpObjects(Configuration::StatePath); DumpModifiedAttributes(); } void IcingaApplication::DumpModifiedAttributes() { - String path = GetModAttrPath(); + String path = Configuration::ModAttrPath; std::fstream fp; String tempFilename = Utility::CreateTempFile(path + ".XXXXXX", 0644, fp); @@ -179,16 +178,7 @@ void IcingaApplication::DumpModifiedAttributes() fp.close(); -#ifdef _WIN32 - _unlink(path.CStr()); -#endif /* _WIN32 */ - - if (rename(tempFilename.CStr(), path.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempFilename)); - } + Utility::RenameFile(tempFilename, path); } IcingaApplication::Ptr IcingaApplication::GetInstance() @@ -257,6 +247,12 @@ bool IcingaApplication::ResolveMacro(const String& macro, const CheckResult::Ptr } else if (macro == "num_services_acknowledged") { *result = ss.services_acknowledged; return true; + } else if (macro == "num_services_handled") { + *result = ss.services_handled; + return true; + } else if (macro == "num_services_problem") { + *result = ss.services_problem; + return true; } } else if (macro.Contains("num_hosts")) { @@ -283,6 +279,12 @@ bool IcingaApplication::ResolveMacro(const String& macro, const CheckResult::Ptr } else if (macro == "num_hosts_acknowledged") { *result = hs.hosts_acknowledged; return true; + } else if (macro == "num_hosts_handled") { + *result = hs.hosts_handled; + return true; + } else if (macro == "num_hosts_problem") { + *result = hs.hosts_problem; + return true; } } @@ -294,6 +296,22 @@ String IcingaApplication::GetNodeName() const return ScriptGlobal::Get("NodeName"); } +/* Intentionally kept here, since an agent may not have the CheckerComponent loaded. */ +int IcingaApplication::GetMaxConcurrentChecks() const +{ + return ScriptGlobal::Get("MaxConcurrentChecks"); +} + +String IcingaApplication::GetEnvironment() const +{ + return Application::GetAppEnvironment(); +} + +void IcingaApplication::SetEnvironment(const String& value, bool suppress_events, const Value& cookie) +{ + Application::SetAppEnvironment(value); +} + void IcingaApplication::ValidateVars(const Lazy& lvalue, const ValidationUtils& utils) { MacroProcessor::ValidateCustomVars(this, lvalue()); diff --git a/lib/icinga/icingaapplication.hpp b/lib/icinga/icingaapplication.hpp index 51216a02c..7888fa6ad 100644 --- a/lib/icinga/icingaapplication.hpp +++ b/lib/icinga/icingaapplication.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ICINGAAPPLICATION_H #define ICINGAAPPLICATION_H @@ -46,12 +29,15 @@ public: static IcingaApplication::Ptr GetInstance(); - String GetPidPath() const; - bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const override; String GetNodeName() const; + int GetMaxConcurrentChecks() const; + + String GetEnvironment() const override; + void SetEnvironment(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; + void ValidateVars(const Lazy& lvalue, const ValidationUtils& utils) override; private: diff --git a/lib/icinga/icingaapplication.ti b/lib/icinga/icingaapplication.ti index 61d4ecd4c..1cdef7406 100644 --- a/lib/icinga/icingaapplication.ti +++ b/lib/icinga/icingaapplication.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/application.hpp" @@ -26,6 +9,14 @@ namespace icinga class IcingaApplication : Application { + activation_priority -50; + + [config, no_storage, virtual] String environment { + get; + set; + default {{{ return Application::GetAppEnvironment(); }}} + }; + [config] bool enable_notifications { default {{{ return true; }}} }; diff --git a/lib/icinga/legacytimeperiod.cpp b/lib/icinga/legacytimeperiod.cpp index 5fe893035..560a588ec 100644 --- a/lib/icinga/legacytimeperiod.cpp +++ b/lib/icinga/legacytimeperiod.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/legacytimeperiod.hpp" #include "base/function.hpp" @@ -28,7 +11,7 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, LegacyTimePeriod, &LegacyTimePeriod::ScriptFunc, "tp:begin:end"); +REGISTER_FUNCTION_NONCONST(Internal, LegacyTimePeriod, &LegacyTimePeriod::ScriptFunc, "tp:begin:end"); bool LegacyTimePeriod::IsInTimeRange(tm *begin, tm *end, int stride, tm *reference) { @@ -42,7 +25,7 @@ bool LegacyTimePeriod::IsInTimeRange(tm *begin, tm *end, int stride, tm *referen int daynumber = (tsref - tsbegin) / (24 * 60 * 60); - if (stride > 1 && daynumber % stride == 0) + if (stride > 1 && daynumber % stride > 0) return false; return true; @@ -130,6 +113,13 @@ int LegacyTimePeriod::MonthFromString(const String& monthdef) return -1; } +boost::gregorian::date LegacyTimePeriod::GetEndOfMonthDay(int year, int month) +{ + boost::gregorian::date d(boost::gregorian::greg_year(year), boost::gregorian::greg_month(month), 1); + + return d.end_of_month(); +} + void LegacyTimePeriod::ParseTimeSpec(const String& timespec, tm *begin, tm *end, tm *reference) { /* Let mktime() figure out whether we're in DST or not. */ @@ -187,10 +177,17 @@ void LegacyTimePeriod::ParseTimeSpec(const String& timespec, tm *begin, tm *end, begin->tm_min = 0; begin->tm_sec = 0; - /* Negative days are relative to the next month. */ + /* day -X: Negative days are relative to the next month. */ if (mday < 0) { - begin->tm_mday = mday * -1 - 1; - begin->tm_mon++; + boost::gregorian::date d(GetEndOfMonthDay(reference->tm_year + 1900, mon + 1)); //TODO: Refactor this mess into full Boost.DateTime + + //Depending on the number, we need to substract specific days (counting starts at 0). + d = d - boost::gregorian::days(mday * -1 - 1); + + *begin = boost::gregorian::to_tm(d); + begin->tm_hour = 0; + begin->tm_min = 0; + begin->tm_sec = 0; } } @@ -202,10 +199,20 @@ void LegacyTimePeriod::ParseTimeSpec(const String& timespec, tm *begin, tm *end, end->tm_min = 0; end->tm_sec = 0; - /* Negative days are relative to the next month. */ + /* day -X: Negative days are relative to the next month. */ if (mday < 0) { - end->tm_mday = mday * -1 - 1; - end->tm_mon++; + boost::gregorian::date d(GetEndOfMonthDay(reference->tm_year + 1900, mon + 1)); //TODO: Refactor this mess into full Boost.DateTime + + //Depending on the number, we need to substract specific days (counting starts at 0). + d = d - boost::gregorian::days(mday * -1 - 1); + + // End date is one day in the future, starting 00:00:00 + d = d + boost::gregorian::days(1); + + *end = boost::gregorian::to_tm(d); + end->tm_hour = 0; + end->tm_min = 0; + end->tm_sec = 0; } } @@ -359,7 +366,7 @@ void LegacyTimePeriod::ProcessTimeRangeRaw(const String& timerange, tm *referenc if (begin->tm_hour * 3600 + begin->tm_min * 60 + begin->tm_sec >= end->tm_hour * 3600 + end->tm_min * 60 + end->tm_sec) - BOOST_THROW_EXCEPTION(std::invalid_argument("Time period segment ends before it begins")); + end->tm_hour += 24; } Dictionary::Ptr LegacyTimePeriod::ProcessTimeRange(const String& timestamp, tm *reference) @@ -388,6 +395,56 @@ void LegacyTimePeriod::ProcessTimeRanges(const String& timeranges, tm *reference } } +Dictionary::Ptr LegacyTimePeriod::FindRunningSegment(const String& daydef, const String& timeranges, tm *reference) +{ + tm begin, end, iter; + time_t tsend, tsiter, tsref; + int stride; + + tsref = mktime(reference); + + ParseTimeRange(daydef, &begin, &end, &stride, reference); + + iter = begin; + + tsend = mktime(&end); + + do { + if (IsInTimeRange(&begin, &end, stride, &iter)) { + Array::Ptr segments = new Array(); + ProcessTimeRanges(timeranges, &iter, segments); + + Dictionary::Ptr bestSegment; + double bestEnd = 0.0; + + ObjectLock olock(segments); + for (const Dictionary::Ptr& segment : segments) { + double begin = segment->Get("begin"); + double end = segment->Get("end"); + + if (begin >= tsref || end < tsref) + continue; + + if (!bestSegment || end > bestEnd) { + bestSegment = segment; + bestEnd = end; + } + } + + if (bestSegment) + return bestSegment; + } + + iter.tm_mday++; + iter.tm_hour = 0; + iter.tm_min = 0; + iter.tm_sec = 0; + tsiter = mktime(&iter); + } while (tsiter < tsend); + + return nullptr; +} + Dictionary::Ptr LegacyTimePeriod::FindNextSegment(const String& daydef, const String& timeranges, tm *reference) { tm begin, end, iter, ref; diff --git a/lib/icinga/legacytimeperiod.hpp b/lib/icinga/legacytimeperiod.hpp index 9bec5b68c..3f1a5cdb9 100644 --- a/lib/icinga/legacytimeperiod.hpp +++ b/lib/icinga/legacytimeperiod.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef LEGACYTIMEPERIOD_H #define LEGACYTIMEPERIOD_H @@ -23,6 +6,7 @@ #include "icinga/i2-icinga.hpp" #include "icinga/timeperiod.hpp" #include "base/dictionary.hpp" +#include namespace icinga { @@ -48,9 +32,12 @@ public: static Dictionary::Ptr ProcessTimeRange(const String& timerange, tm *reference); static void ProcessTimeRanges(const String& timeranges, tm *reference, const Array::Ptr& result); static Dictionary::Ptr FindNextSegment(const String& daydef, const String& timeranges, tm *reference); + static Dictionary::Ptr FindRunningSegment(const String& daydef, const String& timeranges, tm *reference); private: LegacyTimePeriod(); + + static boost::gregorian::date GetEndOfMonthDay(int year, int month); }; } diff --git a/lib/icinga/macroprocessor.cpp b/lib/icinga/macroprocessor.cpp index d99f7649d..df1e41d17 100644 --- a/lib/icinga/macroprocessor.cpp +++ b/lib/icinga/macroprocessor.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/macroprocessor.hpp" #include "icinga/macroresolver.hpp" diff --git a/lib/icinga/macroprocessor.hpp b/lib/icinga/macroprocessor.hpp index c7b3b5b36..1ff21efbf 100644 --- a/lib/icinga/macroprocessor.hpp +++ b/lib/icinga/macroprocessor.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef MACROPROCESSOR_H #define MACROPROCESSOR_H diff --git a/lib/icinga/macroresolver.hpp b/lib/icinga/macroresolver.hpp index 33d3a97ae..ea29ce1e4 100644 --- a/lib/icinga/macroresolver.hpp +++ b/lib/icinga/macroresolver.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef MACRORESOLVER_H #define MACRORESOLVER_H diff --git a/lib/icinga/notification-apply.cpp b/lib/icinga/notification-apply.cpp index 7f8f39dab..713bacea7 100644 --- a/lib/icinga/notification-apply.cpp +++ b/lib/icinga/notification-apply.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/notification.hpp" #include "icinga/service.hpp" diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp index 0e8d6dac2..df509c376 100644 --- a/lib/icinga/notification.cpp +++ b/lib/icinga/notification.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/notification.hpp" #include "icinga/notification-ti.cpp" @@ -29,6 +12,7 @@ #include "base/exception.hpp" #include "base/initialize.hpp" #include "base/scriptglobal.hpp" +#include using namespace icinga; @@ -39,6 +23,7 @@ std::map Notification::m_StateFilterMap; std::map Notification::m_TypeFilterMap; boost::signals2::signal Notification::OnNextNotificationChanged; +boost::signals2::signal Notification::OnNewNotificationResult; String NotificationNameComposer::MakeName(const String& shortName, const Object::Ptr& context) const { @@ -79,22 +64,22 @@ Dictionary::Ptr NotificationNameComposer::ParseName(const String& name) const void Notification::StaticInitialize() { - ScriptGlobal::Set("OK", "OK"); - ScriptGlobal::Set("Warning", "Warning"); - ScriptGlobal::Set("Critical", "Critical"); - ScriptGlobal::Set("Unknown", "Unknown"); - ScriptGlobal::Set("Up", "Up"); - ScriptGlobal::Set("Down", "Down"); + ScriptGlobal::Set("Icinga.OK", "OK", true); + ScriptGlobal::Set("Icinga.Warning", "Warning", true); + ScriptGlobal::Set("Icinga.Critical", "Critical", true); + ScriptGlobal::Set("Icinga.Unknown", "Unknown", true); + ScriptGlobal::Set("Icinga.Up", "Up", true); + ScriptGlobal::Set("Icinga.Down", "Down", true); - ScriptGlobal::Set("DowntimeStart", "DowntimeStart"); - ScriptGlobal::Set("DowntimeEnd", "DowntimeEnd"); - ScriptGlobal::Set("DowntimeRemoved", "DowntimeRemoved"); - ScriptGlobal::Set("Custom", "Custom"); - ScriptGlobal::Set("Acknowledgement", "Acknowledgement"); - ScriptGlobal::Set("Problem", "Problem"); - ScriptGlobal::Set("Recovery", "Recovery"); - ScriptGlobal::Set("FlappingStart", "FlappingStart"); - ScriptGlobal::Set("FlappingEnd", "FlappingEnd"); + ScriptGlobal::Set("Icinga.DowntimeStart", "DowntimeStart", true); + ScriptGlobal::Set("Icinga.DowntimeEnd", "DowntimeEnd", true); + ScriptGlobal::Set("Icinga.DowntimeRemoved", "DowntimeRemoved", true); + ScriptGlobal::Set("Icinga.Custom", "Custom", true); + ScriptGlobal::Set("Icinga.Acknowledgement", "Acknowledgement", true); + ScriptGlobal::Set("Icinga.Problem", "Problem", true); + ScriptGlobal::Set("Icinga.Recovery", "Recovery", true); + ScriptGlobal::Set("Icinga.FlappingStart", "FlappingStart", true); + ScriptGlobal::Set("Icinga.FlappingEnd", "FlappingEnd", true); m_StateFilterMap["OK"] = StateFilterOK; m_StateFilterMap["Warning"] = StateFilterWarning; @@ -231,37 +216,15 @@ void Notification::ResetNotificationNumber() SetNotificationNumber(0); } -/* the upper case string used in all interfaces */ -String Notification::NotificationTypeToString(NotificationType type) -{ - switch (type) { - case NotificationDowntimeStart: - return "DOWNTIMESTART"; - case NotificationDowntimeEnd: - return "DOWNTIMEEND"; - case NotificationDowntimeRemoved: - return "DOWNTIMECANCELLED"; - case NotificationCustom: - return "CUSTOM"; - case NotificationAcknowledgement: - return "ACKNOWLEDGEMENT"; - case NotificationProblem: - return "PROBLEM"; - case NotificationRecovery: - return "RECOVERY"; - case NotificationFlappingStart: - return "FLAPPINGSTART"; - case NotificationFlappingEnd: - return "FLAPPINGEND"; - default: - return "UNKNOWN_NOTIFICATION"; - } -} - void Notification::BeginExecuteNotification(NotificationType type, const CheckResult::Ptr& cr, bool force, bool reminder, const String& author, const String& text) { + String notificationName = GetName(); + String notificationTypeName = NotificationTypeToString(type); + Log(LogNotice, "Notification") - << "Attempting to send " << (reminder ? "reminder " : " ") << "notifications for notification object '" << GetName() << "'."; + << "Attempting to send " << (reminder ? "reminder " : "") + << "notifications of type '" << notificationTypeName + << "' for notification object '" << notificationName << "'."; Checkable::Ptr checkable = GetCheckable(); @@ -270,7 +233,7 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe if (tp && !tp->IsInside(Utility::GetTime())) { Log(LogNotice, "Notification") - << "Not sending " << (reminder ? "reminder " : " ") << "notifications for notification object '" << GetName() + << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '" << notificationName << "': not in timeperiod '" << tp->GetName() << "'"; return; } @@ -284,23 +247,21 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe if (timesBegin != Empty && timesBegin >= 0 && now < checkable->GetLastHardStateChange() + timesBegin) { Log(LogNotice, "Notification") - << "Not sending " << (reminder ? "reminder " : " ") << "notifications for notification object '" << GetName() - << "': before specified begin time (" << Utility::FormatDuration(timesBegin) << ")"; + << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '" + << notificationName << "': before specified begin time (" << Utility::FormatDuration(timesBegin) << ")"; /* we need to adjust the next notification time - * to now + begin delaying the first notification + * delaying the first notification */ - double nextProposedNotification = now + timesBegin + 1.0; - if (GetNextNotification() > nextProposedNotification) - SetNextNotification(nextProposedNotification); + SetNextNotification(checkable->GetLastHardStateChange() + timesBegin + 1.0); return; } if (timesEnd != Empty && timesEnd >= 0 && now > checkable->GetLastHardStateChange() + timesEnd) { Log(LogNotice, "Notification") - << "Not sending " << (reminder ? "reminder " : " ") << "notifications for notification object '" << GetName() - << "': after specified end time (" << Utility::FormatDuration(timesEnd) << ")"; + << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '" + << notificationName << "': after specified end time (" << Utility::FormatDuration(timesEnd) << ")"; return; } } @@ -308,15 +269,26 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe unsigned long ftype = type; Log(LogDebug, "Notification") - << "Type '" << NotificationTypeToStringInternal(type) + << "Type '" << NotificationTypeToString(type) << "', TypeFilter: " << NotificationFilterToString(GetTypeFilter(), GetTypeFilterMap()) << " (FType=" << ftype << ", TypeFilter=" << GetTypeFilter() << ")"; if (!(ftype & GetTypeFilter())) { Log(LogNotice, "Notification") - << "Not sending " << (reminder ? "reminder " : " ") << "notifications for notification object '" << GetName() << "': type '" - << NotificationTypeToStringInternal(type) << "' does not match type filter: " + << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '" + << notificationName << "': type '" + << NotificationTypeToString(type) << "' does not match type filter: " << NotificationFilterToString(GetTypeFilter(), GetTypeFilterMap()) << "."; + + /* Ensure to reset no_more_notifications on Recovery notifications, + * even if the admin did not configure them in the filter. + */ + { + ObjectLock olock(this); + if (type == NotificationRecovery && GetInterval() <= 0) + SetNoMoreNotifications(false); + } + return; } @@ -343,14 +315,16 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe if (!(fstate & GetStateFilter())) { Log(LogNotice, "Notification") - << "Not sending " << (reminder ? "reminder " : " ") << "notifications for notification object '" << GetName() << "': state '" << stateStr + << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '" + << notificationName << "': state '" << stateStr << "' does not match state filter: " << NotificationFilterToString(GetStateFilter(), GetStateFilterMap()) << "."; return; } } } else { Log(LogNotice, "Notification") - << "Not checking " << (reminder ? "reminder " : " ") << "notification filters for notification object '" << GetName() << "': Notification was forced."; + << "Not checking " << (reminder ? "reminder " : "") << "notification filters for notification object '" + << notificationName << "': Notification was forced."; } { @@ -390,28 +364,40 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe if (!user->GetEnableNotifications()) { Log(LogNotice, "Notification") - << "Disabled notifications for user '" << userName << "'. Not sending notification."; + << "Notification object '" << notificationName << "': Disabled notifications for user '" + << userName << "'. Not sending notification."; continue; } if (!CheckNotificationUserFilters(type, user, force, reminder)) { Log(LogNotice, "Notification") - << "Notification filters for user '" << userName << "' not matched. Not sending notification."; + << "Notification object '" << notificationName << "': Filters for user '" << userName << "' not matched. Not sending notification."; continue; } /* on recovery, check if user was notified before */ if (type == NotificationRecovery) { - if (!notifiedProblemUsers->Contains(userName)) { + if (!notifiedProblemUsers->Contains(userName) && (NotificationProblem & user->GetTypeFilter())) { Log(LogNotice, "Notification") - << "We did not notify user '" << userName << "' for a problem before. Not sending recovery notification."; + << "Notification object '" << notificationName << "': We did not notify user '" << userName + << "' (Problem types enabled) for a problem before. Not sending Recovery notification."; + continue; + } + } + + /* on acknowledgement, check if user was notified before */ + if (type == NotificationAcknowledgement) { + if (!notifiedProblemUsers->Contains(userName) && (NotificationProblem & user->GetTypeFilter())) { + Log(LogNotice, "Notification") + << "Notification object '" << notificationName << "': We did not notify user '" << userName + << "' (Problem types enabled) for a problem before. Not sending acknowledgement notification."; continue; } } Log(LogInformation, "Notification") - << "Sending " << (reminder ? "reminder " : "") << "'" << NotificationTypeToStringInternal(type) << "' notification '" - << GetName() << "' for user '" << userName << "'"; + << "Sending " << (reminder ? "reminder " : "") << "'" << NotificationTypeToString(type) << "' notification '" + << notificationName << "' for user '" << userName << "'"; Utility::QueueAsyncCallback(std::bind(&Notification::ExecuteNotificationHelper, this, type, user, cr, force, author, text)); @@ -433,13 +419,16 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe bool Notification::CheckNotificationUserFilters(NotificationType type, const User::Ptr& user, bool force, bool reminder) { + String notificationName = GetName(); + String userName = user->GetName(); + if (!force) { TimePeriod::Ptr tp = user->GetPeriod(); if (tp && !tp->IsInside(Utility::GetTime())) { Log(LogNotice, "Notification") - << "Not sending " << (reminder ? "reminder " : " ") << "notifications for notification object '" - << GetName() << " and user '" << user->GetName() + << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '" + << notificationName << " and user '" << userName << "': user period not in timeperiod '" << tp->GetName() << "'"; return false; } @@ -447,16 +436,17 @@ bool Notification::CheckNotificationUserFilters(NotificationType type, const Use unsigned long ftype = type; Log(LogDebug, "Notification") - << "User notification, Type '" << NotificationTypeToStringInternal(type) + << "User '" << userName << "' notification '" << notificationName + << "', Type '" << NotificationTypeToString(type) << "', TypeFilter: " << NotificationFilterToString(user->GetTypeFilter(), GetTypeFilterMap()) << " (FType=" << ftype << ", TypeFilter=" << GetTypeFilter() << ")"; if (!(ftype & user->GetTypeFilter())) { Log(LogNotice, "Notification") - << "Not sending " << (reminder ? "reminder " : " ") << "notifications for notification object '" - << GetName() << " and user '" << user->GetName() << "': type '" - << NotificationTypeToStringInternal(type) << "' does not match type filter: " + << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '" + << notificationName << " and user '" << userName << "': type '" + << NotificationTypeToString(type) << "' does not match type filter: " << NotificationFilterToString(user->GetTypeFilter(), GetTypeFilterMap()) << "."; return false; } @@ -480,22 +470,23 @@ bool Notification::CheckNotificationUserFilters(NotificationType type, const Use } Log(LogDebug, "Notification") - << "User notification, State '" << stateStr << "', StateFilter: " + << "User '" << userName << "' notification '" << notificationName + << "', State '" << stateStr << "', StateFilter: " << NotificationFilterToString(user->GetStateFilter(), GetStateFilterMap()) << " (FState=" << fstate << ", StateFilter=" << user->GetStateFilter() << ")"; if (!(fstate & user->GetStateFilter())) { Log(LogNotice, "Notification") - << "Not " << (reminder ? "reminder " : " ") << "sending notifications for notification object '" - << GetName() << " and user '" << user->GetName() << "': state '" << stateStr + << "Not " << (reminder ? "reminder " : "") << "sending notifications for notification object '" + << notificationName << " and user '" << userName << "': state '" << stateStr << "' does not match state filter: " << NotificationFilterToString(user->GetStateFilter(), GetStateFilterMap()) << "."; return false; } } } else { Log(LogNotice, "Notification") - << "Not checking " << (reminder ? "reminder " : " ") << "notification filters for notification object '" - << GetName() << "' and user '" << user->GetName() << "': Notification was forced."; + << "Not checking " << (reminder ? "reminder " : "") << "notification filters for notification object '" + << notificationName << "' and user '" << userName << "': Notification was forced."; } return true; @@ -503,32 +494,74 @@ bool Notification::CheckNotificationUserFilters(NotificationType type, const Use void Notification::ExecuteNotificationHelper(NotificationType type, const User::Ptr& user, const CheckResult::Ptr& cr, bool force, const String& author, const String& text) { + String notificationName = GetName(); + String userName = user->GetName(); + String checkableName = GetCheckable()->GetName(); + + NotificationCommand::Ptr command = GetCommand(); + + if (!command) { + Log(LogDebug, "Notification") + << "No command found for notification '" << notificationName << "'. Skipping execution."; + return; + } + + String commandName = command->GetName(); + try { - NotificationCommand::Ptr command = GetCommand(); + NotificationResult::Ptr nr = new NotificationResult(); - if (!command) { - Log(LogDebug, "Notification") - << "No command found for notification '" << GetName() << "'. Skipping execution."; - return; - } + nr->SetExecutionStart(Utility::GetTime()); - command->Execute(this, user, cr, type, author, text); + command->Execute(this, user, cr, nr, type, author, text); /* required by compatlogger */ - Service::OnNotificationSentToUser(this, GetCheckable(), user, type, cr, author, text, command->GetName(), nullptr); + Checkable::OnNotificationSentToUser(this, GetCheckable(), user, type, cr, nr, author, text, command->GetName(), nullptr); Log(LogInformation, "Notification") - << "Completed sending '" << NotificationTypeToStringInternal(type) - << "' notification '" << GetName() - << "' for checkable '" << GetCheckable()->GetName() - << "' and user '" << user->GetName() << "'."; + << "Completed sending '" << NotificationTypeToString(type) + << "' notification '" << notificationName + << "' for checkable '" << checkableName + << "' and user '" << userName << "' using command '" << commandName << "'."; } catch (const std::exception& ex) { Log(LogWarning, "Notification") - << "Exception occured during notification for checkable '" - << GetCheckable()->GetName() << "': " << DiagnosticInformation(ex); + << "Exception occurred during notification '" << notificationName + << "' for checkable '" << checkableName + << "' and user '" << userName << "' using command '" << commandName << "': " + << DiagnosticInformation(ex, false); } } +void Notification::ProcessNotificationResult(const NotificationResult::Ptr& nr, const MessageOrigin::Ptr& origin) +{ + if (!nr) + return; + + double now = Utility::GetTime(); + + if (nr->GetExecutionStart() == 0) + nr->SetExecutionStart(now); + + if (nr->GetExecutionEnd() == 0) + nr->SetExecutionEnd(now); + + /* Determine the execution endpoint from a locally executed check. */ + if (!origin || origin->IsLocal()) + nr->SetExecutionEndpoint(IcingaApplication::GetInstance()->GetNodeName()); + + if (!IsActive()) + return; + + { + ObjectLock olock(this); + + SetLastNotificationResult(nr); + } + + /* Notify cluster, API and feature events. */ + OnNewNotificationResult(this, nr, origin); +} + int icinga::ServiceStateToFilter(ServiceState state) { switch (state) { @@ -570,30 +603,51 @@ String Notification::NotificationFilterToString(int filter, const std::map& p) { + return p.second == type; + }); + + if (it == typeMap.end()) + return Empty; + + return it->first; +} + + +/* + * Compat interface used in external features. + */ +String Notification::NotificationTypeToStringCompat(NotificationType type) { switch (type) { case NotificationDowntimeStart: - return "DowntimeStart"; + return "DOWNTIMESTART"; case NotificationDowntimeEnd: - return "DowntimeEnd"; + return "DOWNTIMEEND"; case NotificationDowntimeRemoved: - return "DowntimeRemoved"; + return "DOWNTIMECANCELLED"; case NotificationCustom: - return "Custom"; + return "CUSTOM"; case NotificationAcknowledgement: - return "Acknowledgement"; + return "ACKNOWLEDGEMENT"; case NotificationProblem: - return "Problem"; + return "PROBLEM"; case NotificationRecovery: - return "Recovery"; + return "RECOVERY"; case NotificationFlappingStart: - return "FlappingStart"; + return "FLAPPINGSTART"; case NotificationFlappingEnd: - return "FlappingEnd"; + return "FLAPPINGEND"; default: - return Empty; + return "UNKNOWN_NOTIFICATION"; } } @@ -664,6 +718,35 @@ void Notification::ValidateTypes(const Lazy& lvalue, const Validatio BOOST_THROW_EXCEPTION(ValidationError(this, { "types" }, "Type filter is invalid.")); } +void Notification::ValidateTimes(const Lazy& lvalue, const ValidationUtils& utils) +{ + ObjectImpl::ValidateTimes(lvalue, utils); + + Dictionary::Ptr times = lvalue(); + + if (!times) + return; + + double begin; + double end; + + try { + begin = Convert::ToDouble(times->Get("begin")); + } catch (const std::exception&) { + BOOST_THROW_EXCEPTION(ValidationError(this, { "times" }, "'begin' is invalid, must be duration or number." )); + } + + try { + end = Convert::ToDouble(times->Get("end")); + } catch (const std::exception&) { + BOOST_THROW_EXCEPTION(ValidationError(this, { "times" }, "'end' is invalid, must be duration or number." )); + } + + /* Also solve logical errors where begin > end. */ + if (begin > 0 && end > 0 && begin > end) + BOOST_THROW_EXCEPTION(ValidationError(this, { "times" }, "'begin' must be smaller than 'end'.")); +} + Endpoint::Ptr Notification::GetCommandEndpoint() const { return Endpoint::GetByName(GetCommandEndpointRaw()); diff --git a/lib/icinga/notification.hpp b/lib/icinga/notification.hpp index 47dd9d52d..5fcb6fd2a 100644 --- a/lib/icinga/notification.hpp +++ b/lib/icinga/notification.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef NOTIFICATION_H #define NOTIFICATION_H @@ -27,6 +10,7 @@ #include "icinga/usergroup.hpp" #include "icinga/timeperiod.hpp" #include "icinga/checkresult.hpp" +#include "icinga/notificationresult.hpp" #include "remote/endpoint.hpp" #include "remote/messageorigin.hpp" #include "base/array.hpp" @@ -99,15 +83,25 @@ public: Endpoint::Ptr GetCommandEndpoint() const; + void ProcessNotificationResult(const NotificationResult::Ptr& nr, const MessageOrigin::Ptr& origin = nullptr); + + // Logging, etc. static String NotificationTypeToString(NotificationType type); + // Compat, used for notifications, etc. + static String NotificationTypeToStringCompat(NotificationType type); static String NotificationFilterToString(int filter, const std::map& filterMap); + static String NotificationServiceStateToString(ServiceState state); + static String NotificationHostStateToString(HostState state); + static boost::signals2::signal OnNextNotificationChanged; + static boost::signals2::signal OnNewNotificationResult; void Validate(int types, const ValidationUtils& utils) override; void ValidateStates(const Lazy& lvalue, const ValidationUtils& utils) override; void ValidateTypes(const Lazy& lvalue, const ValidationUtils& utils) override; + void ValidateTimes(const Lazy& lvalue, const ValidationUtils& utils) override; static void EvaluateApplyRules(const intrusive_ptr& host); static void EvaluateApplyRules(const intrusive_ptr& service); @@ -131,10 +125,6 @@ private: static bool EvaluateApplyRuleInstance(const intrusive_ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule); static bool EvaluateApplyRule(const intrusive_ptr& checkable, const ApplyRule& rule); - static String NotificationTypeToStringInternal(NotificationType type); - static String NotificationServiceStateToString(ServiceState state); - static String NotificationHostStateToString(HostState state); - static std::map m_StateFilterMap; static std::map m_TypeFilterMap; }; diff --git a/lib/icinga/notification.ti b/lib/icinga/notification.ti index d11f1b377..0212a144a 100644 --- a/lib/icinga/notification.ti +++ b/lib/icinga/notification.ti @@ -1,23 +1,8 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/customvarobject.hpp" +#include "icinga/notificationresult.hpp" +#include "base/array.hpp" #impl_include "icinga/notificationcommand.hpp" #impl_include "icinga/service.hpp" @@ -33,6 +18,7 @@ public: virtual String MakeName(const String& shortName, const Object::Ptr& context) const; virtual Dictionary::Ptr ParseName(const String& name) const; }; + }}} class Notification : CustomVarObject < NotificationNameComposer @@ -94,10 +80,15 @@ class Notification : CustomVarObject < NotificationNameComposer default {{{ return false; }}} }; + [state, no_user_view, no_user_modify] Array::Ptr stashed_notifications { + default {{{ return new Array(); }}} + }; + [state] Timestamp last_notification; [state] Timestamp next_notification; [state] int notification_number; [state] Timestamp last_problem_notification; + [state] NotificationResult::Ptr last_notification_result; [config, navigation] name(Endpoint) command_endpoint (CommandEndpointRaw) { navigate {{{ diff --git a/lib/icinga/notificationcommand.cpp b/lib/icinga/notificationcommand.cpp index 46dc7c086..dc9edb4f3 100644 --- a/lib/icinga/notificationcommand.cpp +++ b/lib/icinga/notificationcommand.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/notificationcommand.hpp" #include "icinga/notificationcommand-ti.cpp" @@ -25,14 +8,15 @@ using namespace icinga; REGISTER_TYPE(NotificationCommand); Dictionary::Ptr NotificationCommand::Execute(const Notification::Ptr& notification, - const User::Ptr& user, const CheckResult::Ptr& cr, const NotificationType& type, - const String& author, const String& comment, const Dictionary::Ptr& resolvedMacros, - bool useResolvedMacros) + const User::Ptr& user, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, + const NotificationType& type, const String& author, const String& comment, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { return GetExecute()->Invoke({ notification, user, cr, + nr, type, author, comment, diff --git a/lib/icinga/notificationcommand.hpp b/lib/icinga/notificationcommand.hpp index 10079250e..0c76a0142 100644 --- a/lib/icinga/notificationcommand.hpp +++ b/lib/icinga/notificationcommand.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef NOTIFICATIONCOMMAND_H #define NOTIFICATIONCOMMAND_H @@ -40,8 +23,8 @@ public: DECLARE_OBJECTNAME(NotificationCommand); virtual Dictionary::Ptr Execute(const intrusive_ptr& notification, - const User::Ptr& user, const CheckResult::Ptr& cr, const NotificationType& type, - const String& author, const String& comment, + const User::Ptr& user, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, + const NotificationType& type, const String& author, const String& comment, const Dictionary::Ptr& resolvedMacros = nullptr, bool useResolvedMacros = false); }; diff --git a/lib/icinga/notificationcommand.ti b/lib/icinga/notificationcommand.ti index 3749c0613..51207a362 100644 --- a/lib/icinga/notificationcommand.ti +++ b/lib/icinga/notificationcommand.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/command.hpp" diff --git a/lib/icinga/notificationresult.cpp b/lib/icinga/notificationresult.cpp new file mode 100644 index 000000000..d57ad4bb0 --- /dev/null +++ b/lib/icinga/notificationresult.cpp @@ -0,0 +1,13 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "icinga/notificationresult.hpp" +#include "icinga/notificationresult-ti.cpp" + +using namespace icinga; + +REGISTER_TYPE(NotificationResult); + +double NotificationResult::CalculateExecutionTime() const +{ + return GetExecutionEnd() - GetExecutionStart(); +} diff --git a/lib/icinga/notificationresult.hpp b/lib/icinga/notificationresult.hpp new file mode 100644 index 000000000..bb7049f18 --- /dev/null +++ b/lib/icinga/notificationresult.hpp @@ -0,0 +1,27 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef NOTIFICATIONRESULT_H +#define NOTIFICATIONRESULT_H + +#include "icinga/i2-icinga.hpp" +#include "icinga/notificationresult-ti.hpp" + +namespace icinga +{ + +/** + * A notification result. + * + * @ingroup icinga + */ +class NotificationResult final : public ObjectImpl +{ +public: + DECLARE_OBJECT(NotificationResult); + + double CalculateExecutionTime() const; +}; + +} + +#endif /* NOTIFICATIONRESULT_H */ diff --git a/lib/icinga/notificationresult.ti b/lib/icinga/notificationresult.ti new file mode 100644 index 000000000..0b77c938f --- /dev/null +++ b/lib/icinga/notificationresult.ti @@ -0,0 +1,24 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +library icinga; + +namespace icinga +{ + +class NotificationResult +{ + [state] Timestamp execution_start; + [state] Timestamp execution_end; + + [state] Value command; + [state] int exit_status; + [state] String output; + + [state] bool active { + default {{{ return true; }}} + }; + + [state] String execution_endpoint; +}; + +} diff --git a/lib/icinga/objectutils.cpp b/lib/icinga/objectutils.cpp index f41143037..559ca4328 100644 --- a/lib/icinga/objectutils.cpp +++ b/lib/icinga/objectutils.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/objectutils.hpp" #include "icinga/host.hpp" @@ -29,17 +12,17 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(System, get_host, &Host::GetByName, "name"); -REGISTER_SCRIPTFUNCTION_NS(System, get_service, &ObjectUtils::GetService, "host:name"); -REGISTER_SCRIPTFUNCTION_NS(System, get_services, &ObjectUtils::GetServices, "host"); -REGISTER_SCRIPTFUNCTION_NS(System, get_user, &User::GetByName, "name"); -REGISTER_SCRIPTFUNCTION_NS(System, get_check_command, &CheckCommand::GetByName, "name"); -REGISTER_SCRIPTFUNCTION_NS(System, get_event_command, &EventCommand::GetByName, "name"); -REGISTER_SCRIPTFUNCTION_NS(System, get_notification_command, &NotificationCommand::GetByName, "name"); -REGISTER_SCRIPTFUNCTION_NS(System, get_host_group, &HostGroup::GetByName, "name"); -REGISTER_SCRIPTFUNCTION_NS(System, get_service_group, &ServiceGroup::GetByName, "name"); -REGISTER_SCRIPTFUNCTION_NS(System, get_user_group, &UserGroup::GetByName, "name"); -REGISTER_SCRIPTFUNCTION_NS(System, get_time_period, &TimePeriod::GetByName, "name"); +REGISTER_FUNCTION(Icinga, get_host, &Host::GetByName, "name"); +REGISTER_FUNCTION(Icinga, get_service, &ObjectUtils::GetService, "host:name"); +REGISTER_FUNCTION(Icinga, get_services, &ObjectUtils::GetServices, "host"); +REGISTER_FUNCTION(Icinga, get_user, &User::GetByName, "name"); +REGISTER_FUNCTION(Icinga, get_check_command, &CheckCommand::GetByName, "name"); +REGISTER_FUNCTION(Icinga, get_event_command, &EventCommand::GetByName, "name"); +REGISTER_FUNCTION(Icinga, get_notification_command, &NotificationCommand::GetByName, "name"); +REGISTER_FUNCTION(Icinga, get_host_group, &HostGroup::GetByName, "name"); +REGISTER_FUNCTION(Icinga, get_service_group, &ServiceGroup::GetByName, "name"); +REGISTER_FUNCTION(Icinga, get_user_group, &UserGroup::GetByName, "name"); +REGISTER_FUNCTION(Icinga, get_time_period, &TimePeriod::GetByName, "name"); Service::Ptr ObjectUtils::GetService(const Value& host, const String& name) { diff --git a/lib/icinga/objectutils.hpp b/lib/icinga/objectutils.hpp index 60a77646f..42e295342 100644 --- a/lib/icinga/objectutils.hpp +++ b/lib/icinga/objectutils.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef OBJECTUTILS_H #define OBJECTUTILS_H diff --git a/lib/icinga/pluginutility.cpp b/lib/icinga/pluginutility.cpp index 0dcc70997..d19399870 100644 --- a/lib/icinga/pluginutility.cpp +++ b/lib/icinga/pluginutility.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/pluginutility.hpp" #include "icinga/macroprocessor.hpp" @@ -32,7 +15,7 @@ using namespace icinga; void PluginUtility::ExecuteCommand(const Command::Ptr& commandObj, const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const MacroProcessor::ResolverList& macroResolvers, - const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros, int timeout, const std::function& callback) { Value raw_command = commandObj->GetCommandLine(); @@ -70,10 +53,17 @@ void PluginUtility::ExecuteCommand(const Command::Ptr& commandObj, const Checkab for (const Dictionary::Pair& kv : env) { String name = kv.second; + String missingMacro; Value value = MacroProcessor::ResolveMacros(name, macroResolvers, cr, - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, + &missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); +#ifdef I2_DEBUG + if (!missingMacro.IsEmpty()) + Log(LogDebug, "PluginUtility") + << "Macro '" << name << "' is not defined."; +#endif /* I2_DEBUG */ + if (value.IsObjectType()) value = Utility::Join(value, ';'); @@ -86,11 +76,7 @@ void PluginUtility::ExecuteCommand(const Command::Ptr& commandObj, const Checkab Process::Ptr process = new Process(Process::PrepareCommand(command), envMacros); - if (checkable->GetCheckTimeout().IsEmpty()) - process->SetTimeout(commandObj->GetTimeout()); - else - process->SetTimeout(checkable->GetCheckTimeout()); - + process->SetTimeout(timeout); process->SetAdjustPriority(true); process->Run(std::bind(callback, command, _1)); diff --git a/lib/icinga/pluginutility.hpp b/lib/icinga/pluginutility.hpp index 756ad9d97..0ea56e2c5 100644 --- a/lib/icinga/pluginutility.hpp +++ b/lib/icinga/pluginutility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PLUGINUTILITY_H #define PLUGINUTILITY_H @@ -41,7 +24,7 @@ class PluginUtility public: static void ExecuteCommand(const Command::Ptr& commandObj, const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const MacroProcessor::ResolverList& macroResolvers, - const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros, int timeout, const std::function& callback = std::function()); static ServiceState ExitStatusToState(int exitStatus); diff --git a/lib/icinga/scheduleddowntime-apply.cpp b/lib/icinga/scheduleddowntime-apply.cpp index 3909f6fc0..770688cd4 100644 --- a/lib/icinga/scheduleddowntime-apply.cpp +++ b/lib/icinga/scheduleddowntime-apply.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/scheduleddowntime.hpp" #include "icinga/service.hpp" diff --git a/lib/icinga/scheduleddowntime.cpp b/lib/icinga/scheduleddowntime.cpp index 6a715fc40..6642841f4 100644 --- a/lib/icinga/scheduleddowntime.cpp +++ b/lib/icinga/scheduleddowntime.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/scheduleddowntime.hpp" #include "icinga/scheduleddowntime-ti.cpp" @@ -80,6 +63,8 @@ void ScheduledDowntime::OnAllConfigLoaded() if (!GetCheckable()) BOOST_THROW_EXCEPTION(ScriptError("ScheduledDowntime '" + GetName() + "' references a host/service which doesn't exist.", GetDebugInfo())); + + m_AllConfigLoaded.store(true); } void ScheduledDowntime::Start(bool runtimeCreated) @@ -95,13 +80,14 @@ void ScheduledDowntime::Start(bool runtimeCreated) l_Timer->Start(); }); - Utility::QueueAsyncCallback(std::bind(&ScheduledDowntime::CreateNextDowntime, this)); + if (!IsPaused()) + Utility::QueueAsyncCallback(std::bind(&ScheduledDowntime::CreateNextDowntime, this)); } void ScheduledDowntime::TimerProc() { for (const ScheduledDowntime::Ptr& sd : ConfigType::GetObjectsByType()) { - if (sd->IsActive()) + if (sd->IsActive() && !sd->IsPaused()) sd->CreateNextDowntime(); } } @@ -116,6 +102,67 @@ Checkable::Ptr ScheduledDowntime::GetCheckable() const return host->GetServiceByShortName(GetServiceName()); } +std::pair ScheduledDowntime::FindRunningSegment(double minEnd) +{ + time_t refts = Utility::GetTime(); + tm reference = Utility::LocalTime(refts); + + Log(LogDebug, "ScheduledDowntime") + << "Finding running scheduled downtime segment for time " << refts + << " (minEnd " << (minEnd > 0 ? Utility::FormatDateTime("%c", minEnd) : "-") << ")"; + + Dictionary::Ptr ranges = GetRanges(); + + if (!ranges) + return std::make_pair(0, 0); + + Array::Ptr segments = new Array(); + + Dictionary::Ptr bestSegment; + double bestBegin = 0.0, bestEnd = 0.0; + double now = Utility::GetTime(); + + ObjectLock olock(ranges); + + /* Find the longest lasting (and longer than minEnd, if given) segment that's already running */ + for (const Dictionary::Pair& kv : ranges) { + Log(LogDebug, "ScheduledDowntime") + << "Evaluating (running?) segment: " << kv.first << ": " << kv.second; + + Dictionary::Ptr segment = LegacyTimePeriod::FindRunningSegment(kv.first, kv.second, &reference); + + if (!segment) + continue; + + double begin = segment->Get("begin"); + double end = segment->Get("end"); + + Log(LogDebug, "ScheduledDowntime") + << "Considering (running?) segment: " << Utility::FormatDateTime("%c", begin) << " -> " << Utility::FormatDateTime("%c", end); + + if (begin >= now || end < now) { + Log(LogDebug, "ScheduledDowntime") << "not running."; + continue; + } + if (minEnd && end <= minEnd) { + Log(LogDebug, "ScheduledDowntime") << "ending too early."; + continue; + } + + if (!bestSegment || end > bestEnd) { + Log(LogDebug, "ScheduledDowntime") << "(best match yet)"; + bestSegment = segment; + bestBegin = begin; + bestEnd = end; + } + } + + if (bestSegment) + return std::make_pair(bestBegin, bestEnd); + + return std::make_pair(0, 0); +} + std::pair ScheduledDowntime::FindNextSegment() { time_t refts = Utility::GetTime(); @@ -132,42 +179,62 @@ std::pair ScheduledDowntime::FindNextSegment() Array::Ptr segments = new Array(); Dictionary::Ptr bestSegment; - double bestBegin; + double bestBegin = 0.0, bestEnd = 0.0; double now = Utility::GetTime(); ObjectLock olock(ranges); + + /* Find the segment starting earliest */ for (const Dictionary::Pair& kv : ranges) { Log(LogDebug, "ScheduledDowntime") - << "Evaluating segment: " << kv.first << ": " << kv.second << " at "; + << "Evaluating segment: " << kv.first << ": " << kv.second; Dictionary::Ptr segment = LegacyTimePeriod::FindNextSegment(kv.first, kv.second, &reference); if (!segment) continue; - Log(LogDebug, "ScheduledDowntime") - << "Considering segment: " << Utility::FormatDateTime("%c", segment->Get("begin")) << " -> " << Utility::FormatDateTime("%c", segment->Get("end")); - double begin = segment->Get("begin"); + double end = segment->Get("end"); - if (begin < now) + Log(LogDebug, "ScheduledDowntime") + << "Considering segment: " << Utility::FormatDateTime("%c", begin) << " -> " << Utility::FormatDateTime("%c", end); + + if (begin < now) { + Log(LogDebug, "ScheduledDowntime") << "already running."; continue; + } if (!bestSegment || begin < bestBegin) { + Log(LogDebug, "ScheduledDowntime") << "(best match yet)"; bestSegment = segment; bestBegin = begin; + bestEnd = end; } } if (bestSegment) - return std::make_pair(bestSegment->Get("begin"), bestSegment->Get("end")); - else - return std::make_pair(0, 0); + return std::make_pair(bestBegin, bestEnd); + + return std::make_pair(0, 0); } void ScheduledDowntime::CreateNextDowntime() { + /* HA enabled zones. */ + if (IsActive() && IsPaused()) { + Log(LogNotice, "Checkable") + << "Skipping downtime creation for HA-paused Scheduled Downtime object '" << GetName() << "'"; + return; + } + + double minEnd = 0; + for (const Downtime::Ptr& downtime : GetCheckable()->GetDowntimes()) { + double end = downtime->GetEndTime(); + if (end > minEnd) + minEnd = end; + if (downtime->GetScheduledBy() != GetName() || downtime->GetStartTime() < Utility::GetTime()) continue; @@ -179,21 +246,42 @@ void ScheduledDowntime::CreateNextDowntime() Log(LogDebug, "ScheduledDowntime") << "Creating new Downtime for ScheduledDowntime \"" << GetName() << "\""; - std::pair segment = FindNextSegment(); - + std::pair segment = FindRunningSegment(minEnd); if (segment.first == 0 && segment.second == 0) { - tm reference = Utility::LocalTime(Utility::GetTime()); - reference.tm_mday++; - reference.tm_hour = 0; - reference.tm_min = 0; - reference.tm_sec = 0; - - return; + segment = FindNextSegment(); + if (segment.first == 0 && segment.second == 0) + return; } - Downtime::AddDowntime(GetCheckable(), GetAuthor(), GetComment(), + String downtimeName = Downtime::AddDowntime(GetCheckable(), GetAuthor(), GetComment(), segment.first, segment.second, GetFixed(), String(), GetDuration(), GetName(), GetName()); + + Downtime::Ptr downtime = Downtime::GetByName(downtimeName); + + int childOptions = Downtime::ChildOptionsFromValue(GetChildOptions()); + if (childOptions > 0) { + /* 'DowntimeTriggeredChildren' schedules child downtimes triggered by the parent downtime. + * 'DowntimeNonTriggeredChildren' schedules non-triggered downtimes for all children. + */ + String triggerName; + if (childOptions == 1) + triggerName = downtimeName; + + Log(LogNotice, "ScheduledDowntime") + << "Processing child options " << childOptions << " for downtime " << downtimeName; + + for (const Checkable::Ptr& child : GetCheckable()->GetAllChildren()) { + Log(LogNotice, "ScheduledDowntime") + << "Scheduling downtime for child object " << child->GetName(); + + String childDowntimeName = Downtime::AddDowntime(child, GetAuthor(), GetComment(), + segment.first, segment.second, GetFixed(), triggerName, GetDuration(), GetName(), GetName()); + + Log(LogNotice, "ScheduledDowntime") + << "Add child downtime '" << childDowntimeName << "'."; + } + } } void ScheduledDowntime::ValidateRanges(const Lazy& lvalue, const ValidationUtils& utils) @@ -226,3 +314,20 @@ void ScheduledDowntime::ValidateRanges(const Lazy& lvalue, cons } } +void ScheduledDowntime::ValidateChildOptions(const Lazy& lvalue, const ValidationUtils& utils) +{ + ObjectImpl::ValidateChildOptions(lvalue, utils); + + try { + Downtime::ChildOptionsFromValue(lvalue()); + } catch (const std::exception&) { + BOOST_THROW_EXCEPTION(ValidationError(this, { "child_options" }, "Invalid child_options specified")); + } +} + +bool ScheduledDowntime::AllConfigIsLoaded() +{ + return m_AllConfigLoaded.load(); +} + +std::atomic ScheduledDowntime::m_AllConfigLoaded (false); diff --git a/lib/icinga/scheduleddowntime.hpp b/lib/icinga/scheduleddowntime.hpp index 86138fa04..90e12d240 100644 --- a/lib/icinga/scheduleddowntime.hpp +++ b/lib/icinga/scheduleddowntime.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SCHEDULEDDOWNTIME_H #define SCHEDULEDDOWNTIME_H @@ -23,6 +6,7 @@ #include "icinga/i2-icinga.hpp" #include "icinga/scheduleddowntime-ti.hpp" #include "icinga/checkable.hpp" +#include namespace icinga { @@ -47,8 +31,10 @@ public: static void EvaluateApplyRules(const intrusive_ptr& host); static void EvaluateApplyRules(const intrusive_ptr& service); + static bool AllConfigIsLoaded(); void ValidateRanges(const Lazy& lvalue, const ValidationUtils& utils) override; + void ValidateChildOptions(const Lazy& lvalue, const ValidationUtils& utils) override; protected: void OnAllConfigLoaded() override; @@ -57,9 +43,12 @@ protected: private: static void TimerProc(); + std::pair FindRunningSegment(double minEnd = 0); std::pair FindNextSegment(); void CreateNextDowntime(); + static std::atomic m_AllConfigLoaded; + static bool EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule); static bool EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule); }; diff --git a/lib/icinga/scheduleddowntime.ti b/lib/icinga/scheduleddowntime.ti index e119eaa11..7dfb8bf39 100644 --- a/lib/icinga/scheduleddowntime.ti +++ b/lib/icinga/scheduleddowntime.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/customvarobject.hpp" #impl_include "icinga/service.hpp" @@ -77,6 +60,10 @@ class ScheduledDowntime : CustomVarObject < ScheduledDowntimeNameComposer default {{{ return true; }}} }; + [config] Value child_options { + default {{{ return "DowntimeNoChildren"; }}} + }; + [config, required] Dictionary::Ptr ranges; }; diff --git a/lib/icinga/service-apply.cpp b/lib/icinga/service-apply.cpp index 4e9f78a66..6479aac52 100644 --- a/lib/icinga/service-apply.cpp +++ b/lib/icinga/service-apply.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/service.hpp" #include "config/configitembuilder.hpp" diff --git a/lib/icinga/service.cpp b/lib/icinga/service.cpp index ffbbedd2d..e420b64c3 100644 --- a/lib/icinga/service.cpp +++ b/lib/icinga/service.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/service.hpp" #include "icinga/service-ti.cpp" @@ -142,6 +125,8 @@ int Service::GetSeverity() const severity |= SeverityFlagDowntime; else if (IsAcknowledged()) severity |= SeverityFlagAcknowledgement; + else if (m_Host && m_Host->GetProblem()) + severity |= SeverityFlagHostDown; else severity |= SeverityFlagUnhandled; @@ -150,7 +135,12 @@ int Service::GetSeverity() const return severity; } -bool Service::IsStateOK(ServiceState state) +bool Service::GetHandled() const +{ + return Checkable::GetHandled() || (m_Host && m_Host->GetProblem()); +} + +bool Service::IsStateOK(ServiceState state) const { return state == ServiceOK; } diff --git a/lib/icinga/service.hpp b/lib/icinga/service.hpp index 8fb45d684..0e6e4d8d3 100644 --- a/lib/icinga/service.hpp +++ b/lib/icinga/service.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SERVICE_H #define SERVICE_H @@ -46,10 +29,11 @@ public: Host::Ptr GetHost() const override; int GetSeverity() const override; + bool GetHandled() const override; bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const override; - bool IsStateOK(ServiceState state) override; + bool IsStateOK(ServiceState state) const override; void SaveLastState(ServiceState state, double timestamp) override; static ServiceState StateFromString(const String& state); diff --git a/lib/icinga/service.ti b/lib/icinga/service.ti index 0d30978ca..bab1ebc8f 100644 --- a/lib/icinga/service.ti +++ b/lib/icinga/service.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/checkable.hpp" #include "icinga/host.hpp" diff --git a/lib/icinga/servicegroup.cpp b/lib/icinga/servicegroup.cpp index 2b99f4214..45d953f63 100644 --- a/lib/icinga/servicegroup.cpp +++ b/lib/icinga/servicegroup.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/servicegroup.hpp" #include "icinga/servicegroup-ti.cpp" diff --git a/lib/icinga/servicegroup.hpp b/lib/icinga/servicegroup.hpp index 9a8d49a7b..a4fe132fb 100644 --- a/lib/icinga/servicegroup.hpp +++ b/lib/icinga/servicegroup.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SERVICEGROUP_H #define SERVICEGROUP_H diff --git a/lib/icinga/servicegroup.ti b/lib/icinga/servicegroup.ti index 1111c4b35..69f300508 100644 --- a/lib/icinga/servicegroup.ti +++ b/lib/icinga/servicegroup.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/customvarobject.hpp" diff --git a/lib/icinga/timeperiod.cpp b/lib/icinga/timeperiod.cpp index 97183223e..27b92d550 100644 --- a/lib/icinga/timeperiod.cpp +++ b/lib/icinga/timeperiod.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/timeperiod.hpp" #include "icinga/timeperiod-ti.cpp" @@ -142,12 +125,12 @@ void TimePeriod::RemoveSegment(double begin, double end) for (const Dictionary::Ptr& segment : segments) { /* Fully contained in the specified range? */ if (segment->Get("begin") >= begin && segment->Get("end") <= end) + // Don't add the old segment, because the segment is fully contained into our range continue; /* Not overlapping at all? */ if (segment->Get("end") < begin || segment->Get("begin") > end) { newSegments->Add(segment); - continue; } @@ -162,6 +145,8 @@ void TimePeriod::RemoveSegment(double begin, double end) { "begin", end }, { "end", segment->Get("end") } })); + // Don't add the old segment, because we have now two new segments and a gap between + continue; } /* Adjust the begin/end timestamps so as to not overlap with the specified range. */ @@ -235,7 +220,10 @@ void TimePeriod::Merge(const TimePeriod::Ptr& timeperiod, bool include) void TimePeriod::UpdateRegion(double begin, double end, bool clearExisting) { - if (!clearExisting) { + if (clearExisting) { + ObjectLock olock(this); + SetSegments(new Array()); + } else { if (begin < GetValidEnd()) begin = GetValidEnd(); @@ -359,6 +347,8 @@ void TimePeriod::UpdateTimerHandler() void TimePeriod::Dump() { + ObjectLock olock(this); + Array::Ptr segments = GetSegments(); Log(LogDebug, "TimePeriod") diff --git a/lib/icinga/timeperiod.hpp b/lib/icinga/timeperiod.hpp index a641d40cb..a5a2f73fa 100644 --- a/lib/icinga/timeperiod.hpp +++ b/lib/icinga/timeperiod.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef TIMEPERIOD_H #define TIMEPERIOD_H diff --git a/lib/icinga/timeperiod.ti b/lib/icinga/timeperiod.ti index f7e2576f4..27365988e 100644 --- a/lib/icinga/timeperiod.ti +++ b/lib/icinga/timeperiod.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/customvarobject.hpp" #include "base/function.hpp" diff --git a/lib/icinga/user.cpp b/lib/icinga/user.cpp index 2d72d7373..f55a36aaa 100644 --- a/lib/icinga/user.cpp +++ b/lib/icinga/user.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/user.hpp" #include "icinga/user-ti.cpp" diff --git a/lib/icinga/user.hpp b/lib/icinga/user.hpp index 57a007799..f91aefd08 100644 --- a/lib/icinga/user.hpp +++ b/lib/icinga/user.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef USER_H #define USER_H diff --git a/lib/icinga/user.ti b/lib/icinga/user.ti index 74034a212..450d95358 100644 --- a/lib/icinga/user.ti +++ b/lib/icinga/user.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/customvarobject.hpp" #include "base/array.hpp" diff --git a/lib/icinga/usergroup.cpp b/lib/icinga/usergroup.cpp index 8f2a4e7fc..913882d1c 100644 --- a/lib/icinga/usergroup.cpp +++ b/lib/icinga/usergroup.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/usergroup.hpp" #include "icinga/usergroup-ti.cpp" diff --git a/lib/icinga/usergroup.hpp b/lib/icinga/usergroup.hpp index 9bf02a4e8..169f29c10 100644 --- a/lib/icinga/usergroup.hpp +++ b/lib/icinga/usergroup.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef USERGROUP_H #define USERGROUP_H diff --git a/lib/icinga/usergroup.ti b/lib/icinga/usergroup.ti index d51b5e7e1..311932171 100644 --- a/lib/icinga/usergroup.ti +++ b/lib/icinga/usergroup.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/customvarobject.hpp" diff --git a/lib/livestatus/CMakeLists.txt b/lib/livestatus/CMakeLists.txt index e93e2624b..d49f9f5e1 100644 --- a/lib/livestatus/CMakeLists.txt +++ b/lib/livestatus/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ mkclass_target(livestatuslistener.ti livestatuslistener-ti.cpp livestatuslistener-ti.hpp) @@ -72,9 +57,9 @@ set_target_properties ( install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/livestatus.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_RUNDIR}/icinga2/cmd\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_INITRUNDIR}/cmd\")") set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) diff --git a/lib/livestatus/aggregator.cpp b/lib/livestatus/aggregator.cpp index 1cc4b9762..a809b073f 100644 --- a/lib/livestatus/aggregator.cpp +++ b/lib/livestatus/aggregator.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/aggregator.hpp" diff --git a/lib/livestatus/aggregator.hpp b/lib/livestatus/aggregator.hpp index 28aa60355..1c0f7781e 100644 --- a/lib/livestatus/aggregator.hpp +++ b/lib/livestatus/aggregator.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef AGGREGATOR_H #define AGGREGATOR_H diff --git a/lib/livestatus/andfilter.cpp b/lib/livestatus/andfilter.cpp index 66b46766b..98525806f 100644 --- a/lib/livestatus/andfilter.cpp +++ b/lib/livestatus/andfilter.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/andfilter.hpp" diff --git a/lib/livestatus/andfilter.hpp b/lib/livestatus/andfilter.hpp index cdbd21752..8192bf72e 100644 --- a/lib/livestatus/andfilter.hpp +++ b/lib/livestatus/andfilter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ANDFILTER_H #define ANDFILTER_H diff --git a/lib/livestatus/attributefilter.cpp b/lib/livestatus/attributefilter.cpp index 35ed40cf7..50d7244eb 100644 --- a/lib/livestatus/attributefilter.cpp +++ b/lib/livestatus/attributefilter.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/attributefilter.hpp" #include "base/convert.hpp" diff --git a/lib/livestatus/attributefilter.hpp b/lib/livestatus/attributefilter.hpp index f8220eaad..18bd8431e 100644 --- a/lib/livestatus/attributefilter.hpp +++ b/lib/livestatus/attributefilter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ATTRIBUTEFILTER_H #define ATTRIBUTEFILTER_H diff --git a/lib/livestatus/avgaggregator.cpp b/lib/livestatus/avgaggregator.cpp index 592aadb1c..35701f313 100644 --- a/lib/livestatus/avgaggregator.cpp +++ b/lib/livestatus/avgaggregator.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/avgaggregator.hpp" diff --git a/lib/livestatus/avgaggregator.hpp b/lib/livestatus/avgaggregator.hpp index 664134832..11bd9f380 100644 --- a/lib/livestatus/avgaggregator.hpp +++ b/lib/livestatus/avgaggregator.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef AVGAGGREGATOR_H #define AVGAGGREGATOR_H diff --git a/lib/livestatus/column.cpp b/lib/livestatus/column.cpp index 8cdd4eacf..c915b3df3 100644 --- a/lib/livestatus/column.cpp +++ b/lib/livestatus/column.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/column.hpp" diff --git a/lib/livestatus/column.hpp b/lib/livestatus/column.hpp index 4cc132ab7..264cca746 100644 --- a/lib/livestatus/column.hpp +++ b/lib/livestatus/column.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef COLUMN_H #define COLUMN_H diff --git a/lib/livestatus/combinerfilter.cpp b/lib/livestatus/combinerfilter.cpp index 0657b8397..36a8328b7 100644 --- a/lib/livestatus/combinerfilter.cpp +++ b/lib/livestatus/combinerfilter.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/combinerfilter.hpp" diff --git a/lib/livestatus/combinerfilter.hpp b/lib/livestatus/combinerfilter.hpp index 56b7e9b90..49b8b611d 100644 --- a/lib/livestatus/combinerfilter.hpp +++ b/lib/livestatus/combinerfilter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef COMBINERFILTER_H #define COMBINERFILTER_H diff --git a/lib/livestatus/commandstable.cpp b/lib/livestatus/commandstable.cpp index a26ae91a2..3a777d231 100644 --- a/lib/livestatus/commandstable.cpp +++ b/lib/livestatus/commandstable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/commandstable.hpp" #include "icinga/icingaapplication.hpp" diff --git a/lib/livestatus/commandstable.hpp b/lib/livestatus/commandstable.hpp index 3589f969f..cd2d91521 100644 --- a/lib/livestatus/commandstable.hpp +++ b/lib/livestatus/commandstable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef COMMANDSTABLE_H #define COMMANDSTABLE_H diff --git a/lib/livestatus/commentstable.cpp b/lib/livestatus/commentstable.cpp index ca75eb259..3676ac3ac 100644 --- a/lib/livestatus/commentstable.cpp +++ b/lib/livestatus/commentstable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/commentstable.hpp" #include "livestatus/hoststable.hpp" @@ -23,7 +6,6 @@ #include "icinga/service.hpp" #include "base/configtype.hpp" #include "base/objectlock.hpp" -#include using namespace icinga; diff --git a/lib/livestatus/commentstable.hpp b/lib/livestatus/commentstable.hpp index f6dbf7dc8..b46e155f3 100644 --- a/lib/livestatus/commentstable.hpp +++ b/lib/livestatus/commentstable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef COMMENTSTABLE_H #define COMMENTSTABLE_H diff --git a/lib/livestatus/contactgroupstable.cpp b/lib/livestatus/contactgroupstable.cpp index 2be78ffad..b4d6853d5 100644 --- a/lib/livestatus/contactgroupstable.cpp +++ b/lib/livestatus/contactgroupstable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/contactgroupstable.hpp" #include "icinga/usergroup.hpp" diff --git a/lib/livestatus/contactgroupstable.hpp b/lib/livestatus/contactgroupstable.hpp index f8e46390d..a57f5c3b3 100644 --- a/lib/livestatus/contactgroupstable.hpp +++ b/lib/livestatus/contactgroupstable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONTACTGROUPSTABLE_H #define CONTACTGROUPSTABLE_H diff --git a/lib/livestatus/contactstable.cpp b/lib/livestatus/contactstable.cpp index 62030aae6..d6a04c41d 100644 --- a/lib/livestatus/contactstable.cpp +++ b/lib/livestatus/contactstable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/contactstable.hpp" #include "icinga/user.hpp" @@ -25,7 +8,6 @@ #include "base/objectlock.hpp" #include "base/json.hpp" #include "base/utility.hpp" -#include using namespace icinga; diff --git a/lib/livestatus/contactstable.hpp b/lib/livestatus/contactstable.hpp index 4c4816c0c..0bd2679ed 100644 --- a/lib/livestatus/contactstable.hpp +++ b/lib/livestatus/contactstable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONTACTSTABLE_H #define CONTACTSTABLE_H diff --git a/lib/livestatus/countaggregator.cpp b/lib/livestatus/countaggregator.cpp index 11ceb96ff..b8a7238f3 100644 --- a/lib/livestatus/countaggregator.cpp +++ b/lib/livestatus/countaggregator.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/countaggregator.hpp" diff --git a/lib/livestatus/countaggregator.hpp b/lib/livestatus/countaggregator.hpp index 5b4d19588..22d498395 100644 --- a/lib/livestatus/countaggregator.hpp +++ b/lib/livestatus/countaggregator.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef COUNTAGGREGATOR_H #define COUNTAGGREGATOR_H diff --git a/lib/livestatus/downtimestable.cpp b/lib/livestatus/downtimestable.cpp index 008a391ac..b3eb121f9 100644 --- a/lib/livestatus/downtimestable.cpp +++ b/lib/livestatus/downtimestable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/downtimestable.hpp" #include "livestatus/hoststable.hpp" @@ -23,7 +6,6 @@ #include "icinga/service.hpp" #include "base/configtype.hpp" #include "base/objectlock.hpp" -#include using namespace icinga; diff --git a/lib/livestatus/downtimestable.hpp b/lib/livestatus/downtimestable.hpp index 9b3ca2a96..4b5c909f1 100644 --- a/lib/livestatus/downtimestable.hpp +++ b/lib/livestatus/downtimestable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DOWNTIMESTABLE_H #define DOWNTIMESTABLE_H diff --git a/lib/livestatus/endpointstable.cpp b/lib/livestatus/endpointstable.cpp index 260916fa6..3d407eb3e 100644 --- a/lib/livestatus/endpointstable.cpp +++ b/lib/livestatus/endpointstable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/endpointstable.hpp" #include "icinga/host.hpp" @@ -27,7 +10,6 @@ #include "base/objectlock.hpp" #include "base/convert.hpp" #include "base/utility.hpp" -#include #include using namespace icinga; diff --git a/lib/livestatus/endpointstable.hpp b/lib/livestatus/endpointstable.hpp index ced85cfe5..7d011eff6 100644 --- a/lib/livestatus/endpointstable.hpp +++ b/lib/livestatus/endpointstable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ENDPOINTSTABLE_H #define ENDPOINTSTABLE_H diff --git a/lib/livestatus/filter.hpp b/lib/livestatus/filter.hpp index 2febc8db3..b9a01c860 100644 --- a/lib/livestatus/filter.hpp +++ b/lib/livestatus/filter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef FILTER_H #define FILTER_H diff --git a/lib/livestatus/historytable.hpp b/lib/livestatus/historytable.hpp index 9d032e617..f1178573a 100644 --- a/lib/livestatus/historytable.hpp +++ b/lib/livestatus/historytable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef HISTORYTABLE_H #define HISTORYTABLE_H diff --git a/lib/livestatus/hostgroupstable.cpp b/lib/livestatus/hostgroupstable.cpp index a4ab713a3..984eddb08 100644 --- a/lib/livestatus/hostgroupstable.cpp +++ b/lib/livestatus/hostgroupstable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/hostgroupstable.hpp" #include "icinga/hostgroup.hpp" diff --git a/lib/livestatus/hostgroupstable.hpp b/lib/livestatus/hostgroupstable.hpp index 91d2847a1..cc5039fac 100644 --- a/lib/livestatus/hostgroupstable.hpp +++ b/lib/livestatus/hostgroupstable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef HOSTGROUPSTABLE_H #define HOSTGROUPSTABLE_H diff --git a/lib/livestatus/hoststable.cpp b/lib/livestatus/hoststable.cpp index d46b88535..9ecc45a40 100644 --- a/lib/livestatus/hoststable.cpp +++ b/lib/livestatus/hoststable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/hoststable.hpp" #include "livestatus/hostgroupstable.hpp" @@ -35,7 +18,6 @@ #include "base/json.hpp" #include "base/convert.hpp" #include "base/utility.hpp" -#include #include using namespace icinga; @@ -789,7 +771,7 @@ Value HostsTable::CheckIntervalAccessor(const Value& row) if (!host) return Empty; - return host->GetCheckInterval() / 60.0; + return host->GetCheckInterval() / LIVESTATUS_INTERVAL_LENGTH; } Value HostsTable::RetryIntervalAccessor(const Value& row) @@ -799,7 +781,7 @@ Value HostsTable::RetryIntervalAccessor(const Value& row) if (!host) return Empty; - return host->GetRetryInterval() / 60.0; + return host->GetRetryInterval() / LIVESTATUS_INTERVAL_LENGTH; } Value HostsTable::NotificationIntervalAccessor(const Value& row) diff --git a/lib/livestatus/hoststable.hpp b/lib/livestatus/hoststable.hpp index 9a08bec0c..9386183ce 100644 --- a/lib/livestatus/hoststable.hpp +++ b/lib/livestatus/hoststable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef HOSTSTABLE_H #define HOSTSTABLE_H diff --git a/lib/livestatus/i2-livestatus.hpp b/lib/livestatus/i2-livestatus.hpp index 7e7acf1fe..3375d9795 100644 --- a/lib/livestatus/i2-livestatus.hpp +++ b/lib/livestatus/i2-livestatus.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef I2LIVESTATUS_H #define I2LIVESTATUS_H diff --git a/lib/livestatus/invavgaggregator.cpp b/lib/livestatus/invavgaggregator.cpp index 82ed69f70..33cf85cd7 100644 --- a/lib/livestatus/invavgaggregator.cpp +++ b/lib/livestatus/invavgaggregator.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/invavgaggregator.hpp" diff --git a/lib/livestatus/invavgaggregator.hpp b/lib/livestatus/invavgaggregator.hpp index d8e43d6aa..9282b37de 100644 --- a/lib/livestatus/invavgaggregator.hpp +++ b/lib/livestatus/invavgaggregator.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef INVAVGAGGREGATOR_H #define INVAVGAGGREGATOR_H diff --git a/lib/livestatus/invsumaggregator.cpp b/lib/livestatus/invsumaggregator.cpp index dc6eac69c..c955667fc 100644 --- a/lib/livestatus/invsumaggregator.cpp +++ b/lib/livestatus/invsumaggregator.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/invsumaggregator.hpp" diff --git a/lib/livestatus/invsumaggregator.hpp b/lib/livestatus/invsumaggregator.hpp index 709928fd1..f7de7be82 100644 --- a/lib/livestatus/invsumaggregator.hpp +++ b/lib/livestatus/invsumaggregator.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef INVSUMAGGREGATOR_H #define INVSUMAGGREGATOR_H diff --git a/lib/livestatus/livestatuslistener.cpp b/lib/livestatus/livestatuslistener.cpp index 31cdebf4c..8344a187e 100644 --- a/lib/livestatus/livestatuslistener.cpp +++ b/lib/livestatus/livestatuslistener.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/livestatuslistener.hpp" #include "livestatus/livestatuslistener-ti.cpp" diff --git a/lib/livestatus/livestatuslistener.hpp b/lib/livestatus/livestatuslistener.hpp index 56fd96c65..dc739f6f1 100644 --- a/lib/livestatus/livestatuslistener.hpp +++ b/lib/livestatus/livestatuslistener.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef LIVESTATUSLISTENER_H #define LIVESTATUSLISTENER_H diff --git a/lib/livestatus/livestatuslistener.ti b/lib/livestatus/livestatuslistener.ti index 0742a6fb5..31482cff1 100644 --- a/lib/livestatus/livestatuslistener.ti +++ b/lib/livestatus/livestatuslistener.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" #include "base/application.hpp" @@ -32,7 +15,7 @@ class LivestatusListener : ConfigObject { default {{{ return "unix"; }}} }; [config] String socket_path { - default {{{ return Application::GetRunDir() + "/icinga2/cmd/livestatus"; }}} + default {{{ return Configuration::InitRunDir + "/cmd/livestatus"; }}} }; [config] String bind_host { default {{{ return "127.0.0.1"; }}} @@ -41,7 +24,7 @@ class LivestatusListener : ConfigObject { default {{{ return "6558"; }}} }; [config] String compat_log_path { - default {{{ return Application::GetLocalStateDir() + "/log/icinga2/compat"; }}} + default {{{ return Configuration::LogDir + "/compat"; }}} }; }; diff --git a/lib/livestatus/livestatuslogutility.cpp b/lib/livestatus/livestatuslogutility.cpp index 91db16253..f598d5852 100644 --- a/lib/livestatus/livestatuslogutility.cpp +++ b/lib/livestatus/livestatuslogutility.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/livestatuslogutility.hpp" #include "icinga/service.hpp" @@ -27,7 +10,6 @@ #include "base/utility.hpp" #include "base/convert.hpp" #include "base/logger.hpp" -#include #include #include #include diff --git a/lib/livestatus/livestatuslogutility.hpp b/lib/livestatus/livestatuslogutility.hpp index c2d934433..66d11548f 100644 --- a/lib/livestatus/livestatuslogutility.hpp +++ b/lib/livestatus/livestatuslogutility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef LIVESTATUSLOGUTILITY_H #define LIVESTATUSLOGUTILITY_H diff --git a/lib/livestatus/livestatusquery.cpp b/lib/livestatus/livestatusquery.cpp index 376176918..04d01f51f 100644 --- a/lib/livestatus/livestatusquery.cpp +++ b/lib/livestatus/livestatusquery.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/livestatusquery.hpp" #include "livestatus/countaggregator.hpp" diff --git a/lib/livestatus/livestatusquery.hpp b/lib/livestatus/livestatusquery.hpp index d8583eb40..910cc162e 100644 --- a/lib/livestatus/livestatusquery.hpp +++ b/lib/livestatus/livestatusquery.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef LIVESTATUSQUERY_H #define LIVESTATUSQUERY_H diff --git a/lib/livestatus/logtable.cpp b/lib/livestatus/logtable.cpp index 034dbb770..ed21b722e 100644 --- a/lib/livestatus/logtable.cpp +++ b/lib/livestatus/logtable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/logtable.hpp" #include "livestatus/livestatuslogutility.hpp" @@ -36,7 +19,6 @@ #include "base/logger.hpp" #include "base/application.hpp" #include "base/objectlock.hpp" -#include #include #include #include diff --git a/lib/livestatus/logtable.hpp b/lib/livestatus/logtable.hpp index aab3e1f0e..33c2e33e1 100644 --- a/lib/livestatus/logtable.hpp +++ b/lib/livestatus/logtable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef LOGTABLE_H #define LOGTABLE_H diff --git a/lib/livestatus/maxaggregator.cpp b/lib/livestatus/maxaggregator.cpp index a2265430b..375d24b60 100644 --- a/lib/livestatus/maxaggregator.cpp +++ b/lib/livestatus/maxaggregator.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/maxaggregator.hpp" diff --git a/lib/livestatus/maxaggregator.hpp b/lib/livestatus/maxaggregator.hpp index 3207159f1..5bff5f964 100644 --- a/lib/livestatus/maxaggregator.hpp +++ b/lib/livestatus/maxaggregator.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef MAXAGGREGATOR_H #define MAXAGGREGATOR_H diff --git a/lib/livestatus/minaggregator.cpp b/lib/livestatus/minaggregator.cpp index bf4390230..06cb76e22 100644 --- a/lib/livestatus/minaggregator.cpp +++ b/lib/livestatus/minaggregator.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/minaggregator.hpp" diff --git a/lib/livestatus/minaggregator.hpp b/lib/livestatus/minaggregator.hpp index 76be4d10e..71a9d89a0 100644 --- a/lib/livestatus/minaggregator.hpp +++ b/lib/livestatus/minaggregator.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef MINAGGREGATOR_H #define MINAGGREGATOR_H diff --git a/lib/livestatus/negatefilter.cpp b/lib/livestatus/negatefilter.cpp index 8a8f341c6..60202b4b1 100644 --- a/lib/livestatus/negatefilter.cpp +++ b/lib/livestatus/negatefilter.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/negatefilter.hpp" diff --git a/lib/livestatus/negatefilter.hpp b/lib/livestatus/negatefilter.hpp index 4a88f35e4..c08943ca8 100644 --- a/lib/livestatus/negatefilter.hpp +++ b/lib/livestatus/negatefilter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef NEGATEFILTER_H #define NEGATEFILTER_H diff --git a/lib/livestatus/orfilter.cpp b/lib/livestatus/orfilter.cpp index 14da7b7a2..6cc446cae 100644 --- a/lib/livestatus/orfilter.cpp +++ b/lib/livestatus/orfilter.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/orfilter.hpp" diff --git a/lib/livestatus/orfilter.hpp b/lib/livestatus/orfilter.hpp index d37f2e09f..df855c1a9 100644 --- a/lib/livestatus/orfilter.hpp +++ b/lib/livestatus/orfilter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ORFILTER_H #define ORFILTER_H diff --git a/lib/livestatus/servicegroupstable.cpp b/lib/livestatus/servicegroupstable.cpp index 1d98bfd50..38d6d0502 100644 --- a/lib/livestatus/servicegroupstable.cpp +++ b/lib/livestatus/servicegroupstable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/servicegroupstable.hpp" #include "icinga/servicegroup.hpp" diff --git a/lib/livestatus/servicegroupstable.hpp b/lib/livestatus/servicegroupstable.hpp index 4f97f8a4e..b3c60c493 100644 --- a/lib/livestatus/servicegroupstable.hpp +++ b/lib/livestatus/servicegroupstable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SERVICEGROUPSTABLE_H #define SERVICEGROUPSTABLE_H diff --git a/lib/livestatus/servicestable.cpp b/lib/livestatus/servicestable.cpp index dce09bf57..e8906f71a 100644 --- a/lib/livestatus/servicestable.cpp +++ b/lib/livestatus/servicestable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/servicestable.hpp" #include "livestatus/hoststable.hpp" @@ -37,7 +20,6 @@ #include "base/json.hpp" #include "base/convert.hpp" #include "base/utility.hpp" -#include #include using namespace icinga; @@ -810,7 +792,7 @@ Value ServicesTable::CheckIntervalAccessor(const Value& row) if (!service) return Empty; - return service->GetCheckInterval() / 60.0; + return service->GetCheckInterval() / LIVESTATUS_INTERVAL_LENGTH; } Value ServicesTable::RetryIntervalAccessor(const Value& row) @@ -820,7 +802,7 @@ Value ServicesTable::RetryIntervalAccessor(const Value& row) if (!service) return Empty; - return service->GetRetryInterval() / 60.0; + return service->GetRetryInterval() / LIVESTATUS_INTERVAL_LENGTH; } Value ServicesTable::NotificationIntervalAccessor(const Value& row) diff --git a/lib/livestatus/servicestable.hpp b/lib/livestatus/servicestable.hpp index 17ff49a3a..56d5ae541 100644 --- a/lib/livestatus/servicestable.hpp +++ b/lib/livestatus/servicestable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SERVICESTABLE_H #define SERVICESTABLE_H diff --git a/lib/livestatus/statehisttable.cpp b/lib/livestatus/statehisttable.cpp index bb402580c..e68d45dd9 100644 --- a/lib/livestatus/statehisttable.cpp +++ b/lib/livestatus/statehisttable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/statehisttable.hpp" #include "livestatus/livestatuslogutility.hpp" @@ -36,7 +19,6 @@ #include "base/logger.hpp" #include "base/application.hpp" #include "base/objectlock.hpp" -#include #include #include #include diff --git a/lib/livestatus/statehisttable.hpp b/lib/livestatus/statehisttable.hpp index 5ef4027e7..9233fd559 100644 --- a/lib/livestatus/statehisttable.hpp +++ b/lib/livestatus/statehisttable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef STATEHISTTABLE_H #define STATEHISTTABLE_H diff --git a/lib/livestatus/statustable.cpp b/lib/livestatus/statustable.cpp index ec0b62103..f6a6daf16 100644 --- a/lib/livestatus/statustable.cpp +++ b/lib/livestatus/statustable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/statustable.hpp" #include "livestatus/livestatuslistener.hpp" @@ -84,7 +67,7 @@ void StatusTable::AddColumns(Table *table, const String& prefix, table->AddColumn(prefix + "program_start", Column(&StatusTable::ProgramStartAccessor, objectAccessor)); table->AddColumn(prefix + "last_command_check", Column(&Table::ZeroAccessor, objectAccessor)); table->AddColumn(prefix + "last_log_rotation", Column(&Table::ZeroAccessor, objectAccessor)); - table->AddColumn(prefix + "interval_length", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "interval_length", Column(&StatusTable::IntervalLengthAccessor, objectAccessor)); table->AddColumn(prefix + "num_hosts", Column(&StatusTable::NumHostsAccessor, objectAccessor)); table->AddColumn(prefix + "num_services", Column(&StatusTable::NumServicesAccessor, objectAccessor)); table->AddColumn(prefix + "program_version", Column(&StatusTable::ProgramVersionAccessor, objectAccessor)); @@ -204,6 +187,11 @@ Value StatusTable::ProgramStartAccessor(const Value&) return static_cast(Application::GetStartTime()); } +Value StatusTable::IntervalLengthAccessor(const Value&) +{ + return LIVESTATUS_INTERVAL_LENGTH; +} + Value StatusTable::NumHostsAccessor(const Value&) { return ConfigType::Get()->GetObjectCount(); diff --git a/lib/livestatus/statustable.hpp b/lib/livestatus/statustable.hpp index 9edc9e3b8..2fba249ef 100644 --- a/lib/livestatus/statustable.hpp +++ b/lib/livestatus/statustable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef STATUSTABLE_H #define STATUSTABLE_H @@ -62,6 +45,7 @@ protected: static Value EnableFlapDetectionAccessor(const Value& row); static Value ProcessPerformanceDataAccessor(const Value& row); static Value ProgramStartAccessor(const Value& row); + static Value IntervalLengthAccessor(const Value& row); static Value NumHostsAccessor(const Value& row); static Value NumServicesAccessor(const Value& row); static Value ProgramVersionAccessor(const Value& row); diff --git a/lib/livestatus/stdaggregator.cpp b/lib/livestatus/stdaggregator.cpp index 8d3876e02..99c3a8e65 100644 --- a/lib/livestatus/stdaggregator.cpp +++ b/lib/livestatus/stdaggregator.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/stdaggregator.hpp" #include diff --git a/lib/livestatus/stdaggregator.hpp b/lib/livestatus/stdaggregator.hpp index e7b0509eb..3680fe734 100644 --- a/lib/livestatus/stdaggregator.hpp +++ b/lib/livestatus/stdaggregator.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef STDAGGREGATOR_H #define STDAGGREGATOR_H diff --git a/lib/livestatus/sumaggregator.cpp b/lib/livestatus/sumaggregator.cpp index 50d124f05..fc4b62e24 100644 --- a/lib/livestatus/sumaggregator.cpp +++ b/lib/livestatus/sumaggregator.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/sumaggregator.hpp" diff --git a/lib/livestatus/sumaggregator.hpp b/lib/livestatus/sumaggregator.hpp index 030d15761..23f22fb71 100644 --- a/lib/livestatus/sumaggregator.hpp +++ b/lib/livestatus/sumaggregator.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef SUMAGGREGATOR_H #define SUMAGGREGATOR_H diff --git a/lib/livestatus/table.cpp b/lib/livestatus/table.cpp index c1990f274..ac8d1745c 100644 --- a/lib/livestatus/table.cpp +++ b/lib/livestatus/table.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/table.hpp" #include "livestatus/statustable.hpp" @@ -37,7 +20,6 @@ #include "base/array.hpp" #include "base/dictionary.hpp" #include -#include using namespace icinga; diff --git a/lib/livestatus/table.hpp b/lib/livestatus/table.hpp index 5a3094e05..fa3fc2a65 100644 --- a/lib/livestatus/table.hpp +++ b/lib/livestatus/table.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef TABLE_H #define TABLE_H @@ -29,6 +12,9 @@ namespace icinga { +// Well, don't ask. +#define LIVESTATUS_INTERVAL_LENGTH 60.0 + struct LivestatusRowValue { Value Row; LivestatusGroupByType GroupByType; diff --git a/lib/livestatus/timeperiodstable.cpp b/lib/livestatus/timeperiodstable.cpp index bb1329511..5797d93ee 100644 --- a/lib/livestatus/timeperiodstable.cpp +++ b/lib/livestatus/timeperiodstable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/timeperiodstable.hpp" #include "icinga/icingaapplication.hpp" diff --git a/lib/livestatus/timeperiodstable.hpp b/lib/livestatus/timeperiodstable.hpp index ee05cc4e3..31cef93dc 100644 --- a/lib/livestatus/timeperiodstable.hpp +++ b/lib/livestatus/timeperiodstable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef TIMEPERIODSTABLE_H #define TIMEPERIODSTABLE_H diff --git a/lib/livestatus/zonestable.cpp b/lib/livestatus/zonestable.cpp index b755862be..d86cc7256 100644 --- a/lib/livestatus/zonestable.cpp +++ b/lib/livestatus/zonestable.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/zonestable.hpp" #include "remote/zone.hpp" diff --git a/lib/livestatus/zonestable.hpp b/lib/livestatus/zonestable.hpp index 7c223da48..fb03488fb 100644 --- a/lib/livestatus/zonestable.hpp +++ b/lib/livestatus/zonestable.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ZONESTABLE_H #define ZONESTABLE_H diff --git a/lib/methods/CMakeLists.txt b/lib/methods/CMakeLists.txt index 353189369..9fde9fddb 100644 --- a/lib/methods/CMakeLists.txt +++ b/lib/methods/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ mkembedconfig_target(methods-itl.conf methods-itl.cpp) @@ -31,6 +16,7 @@ set(methods_SOURCES pluginnotificationtask.cpp pluginnotificationtask.hpp randomchecktask.cpp randomchecktask.hpp timeperiodtask.cpp timeperiodtask.hpp + sleepchecktask.cpp sleepchecktask.hpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/methods/clusterchecktask.cpp b/lib/methods/clusterchecktask.cpp index 7473eb1ad..a9be0414d 100644 --- a/lib/methods/clusterchecktask.cpp +++ b/lib/methods/clusterchecktask.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "methods/clusterchecktask.hpp" #include "remote/apilistener.hpp" @@ -23,6 +6,7 @@ #include "icinga/cib.hpp" #include "icinga/service.hpp" #include "icinga/icingaapplication.hpp" +#include "icinga/checkcommand.hpp" #include "base/application.hpp" #include "base/objectlock.hpp" #include "base/convert.hpp" @@ -33,7 +17,7 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, ClusterCheck, &ClusterCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, ClusterCheck, &ClusterCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) @@ -44,6 +28,9 @@ void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRe if (resolvedMacros && !useResolvedMacros) return; + CheckCommand::Ptr command = checkable->GetCheckCommand(); + cr->SetCommand(command->GetName()); + ApiListener::Ptr listener = ApiListener::GetInstance(); if (!listener) { diff --git a/lib/methods/clusterchecktask.hpp b/lib/methods/clusterchecktask.hpp index ecdf5d6db..16ee8a53c 100644 --- a/lib/methods/clusterchecktask.hpp +++ b/lib/methods/clusterchecktask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CLUSTERCHECKTASK_H #define CLUSTERCHECKTASK_H diff --git a/lib/methods/clusterzonechecktask.cpp b/lib/methods/clusterzonechecktask.cpp index 76a6fb2b2..c955b7dd9 100644 --- a/lib/methods/clusterzonechecktask.cpp +++ b/lib/methods/clusterzonechecktask.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "methods/clusterzonechecktask.hpp" #include "icinga/checkcommand.hpp" @@ -29,7 +12,7 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, ClusterZoneCheck, &ClusterZoneCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, ClusterZoneCheck, &ClusterZoneCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) @@ -46,8 +29,8 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che return; } - CheckCommand::Ptr commandObj = checkable->GetCheckCommand(); - Value raw_command = commandObj->GetCommandLine(); + CheckCommand::Ptr command = checkable->GetCheckCommand(); + Value raw_command = command->GetCommandLine(); Host::Ptr host; Service::Ptr service; @@ -57,7 +40,7 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che if (service) resolvers.emplace_back("service", service); resolvers.emplace_back("host", host); - resolvers.emplace_back("command", commandObj); + resolvers.emplace_back("command", command); resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); String zoneName = MacroProcessor::ResolveMacros("$cluster_zone$", resolvers, checkable->GetLastCheckResult(), @@ -75,6 +58,8 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che if (resolvedMacros && !useResolvedMacros) return; + cr->SetCommand(command->GetName()); + if (zoneName.IsEmpty()) { cr->SetOutput("Macro 'cluster_zone' must be set."); cr->SetState(ServiceUnknown); @@ -122,23 +107,23 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che bytesReceivedPerSecond += endpoint->GetBytesReceivedPerSecond(); } - if (!connected) { - cr->SetState(ServiceCritical); - cr->SetOutput("Zone '" + zoneName + "' is not connected. Log lag: " + Utility::FormatDuration(zoneLag)); - } else { + if (connected) { cr->SetState(ServiceOK); cr->SetOutput("Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag)); - } - /* Check whether the thresholds have been resolved and compare them */ - if (missingLagCritical.IsEmpty() && zoneLag > lagCritical) { + /* Check whether the thresholds have been resolved and compare them */ + if (missingLagCritical.IsEmpty() && zoneLag > lagCritical) { + cr->SetState(ServiceCritical); + cr->SetOutput("Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag) + + " greater than critical threshold: " + Utility::FormatDuration(lagCritical)); + } else if (missingLagWarning.IsEmpty() && zoneLag > lagWarning) { + cr->SetState(ServiceWarning); + cr->SetOutput("Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag) + + " greater than warning threshold: " + Utility::FormatDuration(lagWarning)); + } + } else { cr->SetState(ServiceCritical); - cr->SetOutput("Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag) - + " greater than critical threshold: " + Utility::FormatDuration(lagCritical)); - } else if (missingLagWarning.IsEmpty() && zoneLag > lagWarning) { - cr->SetState(ServiceWarning); - cr->SetOutput("Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag) - + " greater than warning threshold: " + Utility::FormatDuration(lagWarning)); + cr->SetOutput("Zone '" + zoneName + "' is not connected. Log lag: " + Utility::FormatDuration(zoneLag)); } cr->SetPerformanceData(new Array({ diff --git a/lib/methods/clusterzonechecktask.hpp b/lib/methods/clusterzonechecktask.hpp index d3c2f392b..2af442c4c 100644 --- a/lib/methods/clusterzonechecktask.hpp +++ b/lib/methods/clusterzonechecktask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CLUSTERZONECHECKTASK_H #define CLUSTERZONECHECKTASK_H diff --git a/lib/methods/dummychecktask.cpp b/lib/methods/dummychecktask.cpp index 516cc23ea..e42754f31 100644 --- a/lib/methods/dummychecktask.cpp +++ b/lib/methods/dummychecktask.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef _WIN32 # include @@ -31,7 +14,7 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, DummyCheck, &DummyCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, DummyCheck, &DummyCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) @@ -39,7 +22,7 @@ void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); - CheckCommand::Ptr commandObj = checkable->GetCheckCommand(); + CheckCommand::Ptr command = checkable->GetCheckCommand(); Host::Ptr host; Service::Ptr service; @@ -49,7 +32,7 @@ void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu if (service) resolvers.emplace_back("service", service); resolvers.emplace_back("host", host); - resolvers.emplace_back("command", commandObj); + resolvers.emplace_back("command", command); resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); int dummyState = MacroProcessor::ResolveMacros("$dummy_state$", resolvers, checkable->GetLastCheckResult(), @@ -72,6 +55,7 @@ void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu cr->SetExitStatus(dummyState); cr->SetExecutionStart(now); cr->SetExecutionEnd(now); + cr->SetCommand(command->GetName()); checkable->ProcessCheckResult(cr); } diff --git a/lib/methods/dummychecktask.hpp b/lib/methods/dummychecktask.hpp index 83fd476cf..621bbfb9f 100644 --- a/lib/methods/dummychecktask.hpp +++ b/lib/methods/dummychecktask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DUMMYCHECKTASK_H #define DUMMYCHECKTASK_H diff --git a/lib/methods/exceptionchecktask.cpp b/lib/methods/exceptionchecktask.cpp index cb5ce76df..5ce3a9262 100644 --- a/lib/methods/exceptionchecktask.cpp +++ b/lib/methods/exceptionchecktask.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef _WIN32 # include @@ -29,7 +12,7 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, ExceptionCheck, &ExceptionCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, ExceptionCheck, &ExceptionCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void ExceptionCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) diff --git a/lib/methods/exceptionchecktask.hpp b/lib/methods/exceptionchecktask.hpp index 99fc0a5c2..09db1045f 100644 --- a/lib/methods/exceptionchecktask.hpp +++ b/lib/methods/exceptionchecktask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef EXCEPTIONCHECKTASK_H #define EXCEPTIONCHECKTASK_H diff --git a/lib/methods/i2-methods.hpp b/lib/methods/i2-methods.hpp index e3543bb04..ffd800259 100644 --- a/lib/methods/i2-methods.hpp +++ b/lib/methods/i2-methods.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef I2METHODS_H #define I2METHODS_H diff --git a/lib/methods/icingachecktask.cpp b/lib/methods/icingachecktask.cpp index 3f4d299da..3f46fba81 100644 --- a/lib/methods/icingachecktask.cpp +++ b/lib/methods/icingachecktask.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "methods/icingachecktask.hpp" #include "icinga/cib.hpp" @@ -25,6 +8,7 @@ #include "icinga/icingaapplication.hpp" #include "icinga/clusterevents.hpp" #include "icinga/checkable.hpp" +#include "remote/apilistener.hpp" #include "base/application.hpp" #include "base/objectlock.hpp" #include "base/utility.hpp" @@ -34,7 +18,7 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, IcingaCheck, &IcingaCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, IcingaCheck, &IcingaCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) @@ -42,7 +26,7 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); - CheckCommand::Ptr commandObj = checkable->GetCheckCommand(); + CheckCommand::Ptr command = checkable->GetCheckCommand(); Host::Ptr host; Service::Ptr service; @@ -52,7 +36,7 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (service) resolvers.emplace_back("service", service); resolvers.emplace_back("host", host); - resolvers.emplace_back("command", commandObj); + resolvers.emplace_back("command", command); resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); String missingIcingaMinVersion; @@ -91,7 +75,8 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes perfdata->Add(new PerfdataValue("active_service_checks_15min", CIB::GetActiveServiceChecksStatistics(60 * 15))); perfdata->Add(new PerfdataValue("passive_service_checks_15min", CIB::GetPassiveServiceChecksStatistics(60 * 15))); - perfdata->Add(new PerfdataValue("current_concurrent_checks", Checkable::GetPendingChecks())); + perfdata->Add(new PerfdataValue("current_pending_callbacks", Application::GetTP().GetPending())); + perfdata->Add(new PerfdataValue("current_concurrent_checks", Checkable::CurrentConcurrentChecks.load())); perfdata->Add(new PerfdataValue("remote_check_queue", ClusterEvents::GetCheckRequestQueueSize())); CheckableCheckStatistics scs = CIB::CalculateServiceCheckStats(); @@ -114,6 +99,8 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes perfdata->Add(new PerfdataValue("num_services_flapping", ss.services_flapping)); perfdata->Add(new PerfdataValue("num_services_in_downtime", ss.services_in_downtime)); perfdata->Add(new PerfdataValue("num_services_acknowledged", ss.services_acknowledged)); + perfdata->Add(new PerfdataValue("num_services_handled", ss.services_handled)); + perfdata->Add(new PerfdataValue("num_services_problem", ss.services_problem)); double uptime = Utility::GetTime() - Application::GetStartTime(); perfdata->Add(new PerfdataValue("uptime", uptime)); @@ -127,6 +114,8 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes perfdata->Add(new PerfdataValue("num_hosts_flapping", hs.hosts_flapping)); perfdata->Add(new PerfdataValue("num_hosts_in_downtime", hs.hosts_in_downtime)); perfdata->Add(new PerfdataValue("num_hosts_acknowledged", hs.hosts_acknowledged)); + perfdata->Add(new PerfdataValue("num_hosts_handled", hs.hosts_handled)); + perfdata->Add(new PerfdataValue("num_hosts_problem", hs.hosts_problem)); std::vector endpoints = ConfigType::GetObjectsByType(); @@ -174,15 +163,30 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes cr->SetState(ServiceWarning); } - /* Return an error if the version is less than specified (optional). */ - String parsedAppVersion = appVersion.SubStr(1,5); + /* Indicate a warning when the last synced config caused a stage validation error. */ + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (listener) { + Dictionary::Ptr validationResult = listener->GetLastFailedZonesStageValidation(); + + if (validationResult) { + output += "; Last zone sync stage validation failed at " + + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", validationResult->Get("ts")); + + cr->SetState(ServiceWarning); + } + } + + String parsedAppVersion = Utility::ParseVersion(appVersion); + + /* Return an error if the version is less than specified (optional). */ if (missingIcingaMinVersion.IsEmpty() && !icingaMinVersion.IsEmpty() && Utility::CompareVersion(icingaMinVersion, parsedAppVersion) < 0) { output += "; Minimum version " + icingaMinVersion + " is not installed."; cr->SetState(ServiceCritical); } cr->SetOutput(output); + cr->SetCommand(command->GetName()); checkable->ProcessCheckResult(cr); } diff --git a/lib/methods/icingachecktask.hpp b/lib/methods/icingachecktask.hpp index 24f9f232f..93def628d 100644 --- a/lib/methods/icingachecktask.hpp +++ b/lib/methods/icingachecktask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ICINGACHECKTASK_H #define ICINGACHECKTASK_H diff --git a/lib/methods/methods-itl.conf b/lib/methods/methods-itl.conf index a45439ccd..f9126f7ca 100644 --- a/lib/methods/methods-itl.conf +++ b/lib/methods/methods-itl.conf @@ -1,85 +1,64 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ System.assert(Internal.run_with_activation_context(function() { - var _Internal = Internal.clone() - - template CheckCommand "icinga-check-command" use (_Internal) { - execute = _Internal.IcingaCheck + template CheckCommand "icinga-check-command" use (IcingaCheck = Internal.IcingaCheck) { + execute = IcingaCheck vars.icinga_min_version = "" } - template CheckCommand "cluster-check-command" use (_Internal) { - execute = _Internal.ClusterCheck + template CheckCommand "cluster-check-command" use (ClusterCheck = Internal.ClusterCheck) { + execute = ClusterCheck } - template CheckCommand "cluster-zone-check-command" use (_Internal) { - execute = _Internal.ClusterZoneCheck + template CheckCommand "cluster-zone-check-command" use (ClusterZoneCheck = Internal.ClusterZoneCheck) { + execute = ClusterZoneCheck } - template CheckCommand "plugin-check-command" use (_Internal) default { - execute = _Internal.PluginCheck + template CheckCommand "plugin-check-command" use (PluginCheck = Internal.PluginCheck) default { + execute = PluginCheck } - template CheckCommand "clr-check-command" use (_Internal) { - if (_Internal.ClrCheck) { - execute = _Internal.ClrCheck - } else { - execute = _Internal.NullCheck - } + template NotificationCommand "plugin-notification-command" use (PluginNotification = Internal.PluginNotification) default { + execute = PluginNotification } - template NotificationCommand "plugin-notification-command" use (_Internal) default { - execute = _Internal.PluginNotification + template EventCommand "plugin-event-command" use (PluginEvent = Internal.PluginEvent) default { + execute = PluginEvent } - template EventCommand "plugin-event-command" use (_Internal) default { - execute = _Internal.PluginEvent + template CheckCommand "dummy-check-command" use (DummyCheck = Internal.DummyCheck) { + execute = DummyCheck } - template CheckCommand "dummy-check-command" use (_Internal) { - execute = _Internal.DummyCheck + template CheckCommand "random-check-command" use (RandomCheck = Internal.RandomCheck) { + execute = RandomCheck } - template CheckCommand "random-check-command" use (_Internal) { - execute = _Internal.RandomCheck + template CheckCommand "exception-check-command" use (ExceptionCheck = Internal.ExceptionCheck) { + execute = ExceptionCheck } - template CheckCommand "exception-check-command" use (_Internal) { - execute = _Internal.ExceptionCheck + template CheckCommand "null-check-command" use (NullCheck = Internal.NullCheck) { + execute = NullCheck } - template CheckCommand "null-check-command" use (_Internal) { - execute = _Internal.NullCheck + template EventCommand "null-event-command" use (NullEvent = Internal.NullEvent) { + execute = NullEvent } - template EventCommand "null-event-command" use (_Internal) { - execute = _Internal.NullEvent + template TimePeriod "empty-timeperiod" use (EmptyTimePeriod = Internal.EmptyTimePeriod) { + update = EmptyTimePeriod } - template TimePeriod "empty-timeperiod" use (_Internal) { - update = _Internal.EmptyTimePeriod + template TimePeriod "even-minutes-timeperiod" use (EvenMinutesTimePeriod = Internal.EvenMinutesTimePeriod) { + update = EvenMinutesTimePeriod } - template TimePeriod "even-minutes-timeperiod" use (_Internal) { - update = _Internal.EvenMinutesTimePeriod + template CheckCommand "sleep-check-command" use (SleepCheck = Internal.SleepCheck) { + execute = SleepCheck + + vars.sleep_time = 1s } })) @@ -97,7 +76,8 @@ var methods = [ "NullCheck", "NullEvent", "EmptyTimePeriod", - "EvenMinutesTimePeriod" + "EvenMinutesTimePeriod", + "SleepCheck" ] for (method in methods) { diff --git a/lib/methods/nullchecktask.cpp b/lib/methods/nullchecktask.cpp index df99c338e..b56087b2e 100644 --- a/lib/methods/nullchecktask.cpp +++ b/lib/methods/nullchecktask.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef _WIN32 # include @@ -30,7 +13,7 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, NullCheck, &NullCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, NullCheck, &NullCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void NullCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) diff --git a/lib/methods/nullchecktask.hpp b/lib/methods/nullchecktask.hpp index cf41c3e75..954cf8d61 100644 --- a/lib/methods/nullchecktask.hpp +++ b/lib/methods/nullchecktask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef NULLCHECKTASK_H #define NULLCHECKTASK_H diff --git a/lib/methods/nulleventtask.cpp b/lib/methods/nulleventtask.cpp index e515d31e8..0755a4d01 100644 --- a/lib/methods/nulleventtask.cpp +++ b/lib/methods/nulleventtask.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "methods/nulleventtask.hpp" #include "base/function.hpp" @@ -23,7 +6,7 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, NullEvent, &NullEventTask::ScriptFunc, "checkable:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, NullEvent, &NullEventTask::ScriptFunc, "checkable:resolvedMacros:useResolvedMacros"); void NullEventTask::ScriptFunc(const Checkable::Ptr& checkable, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { diff --git a/lib/methods/nulleventtask.hpp b/lib/methods/nulleventtask.hpp index aac040a6f..153470f3e 100644 --- a/lib/methods/nulleventtask.hpp +++ b/lib/methods/nulleventtask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef NULLEVENTTASK_H #define NULLEVENTTASK_H diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index b98eec4a7..212d7447c 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "methods/pluginchecktask.hpp" #include "icinga/pluginutility.hpp" @@ -31,7 +14,7 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, PluginCheck, &PluginCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, PluginCheck, &PluginCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) @@ -52,16 +35,24 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes resolvers.emplace_back("command", commandObj); resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); + int timeout = commandObj->GetTimeout(); + + if (!checkable->GetCheckTimeout().IsEmpty()) + timeout = checkable->GetCheckTimeout(); + PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), - resolvers, resolvedMacros, useResolvedMacros, + resolvers, resolvedMacros, useResolvedMacros, timeout, std::bind(&PluginCheckTask::ProcessFinishedHandler, checkable, cr, _1, _2)); - if (!resolvedMacros || useResolvedMacros) + if (!resolvedMacros || useResolvedMacros) { + Checkable::CurrentConcurrentChecks.fetch_add(1); Checkable::IncreasePendingChecks(); + } } void PluginCheckTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Value& commandLine, const ProcessResult& pr) { + Checkable::CurrentConcurrentChecks.fetch_sub(1); Checkable::DecreasePendingChecks(); if (pr.ExitStatus > 3) { diff --git a/lib/methods/pluginchecktask.hpp b/lib/methods/pluginchecktask.hpp index 19c10b4ff..a4fc3a385 100644 --- a/lib/methods/pluginchecktask.hpp +++ b/lib/methods/pluginchecktask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PLUGINCHECKTASK_H #define PLUGINCHECKTASK_H diff --git a/lib/methods/plugineventtask.cpp b/lib/methods/plugineventtask.cpp index 11774c7e2..b2bb466b8 100644 --- a/lib/methods/plugineventtask.cpp +++ b/lib/methods/plugineventtask.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "methods/plugineventtask.hpp" #include "icinga/eventcommand.hpp" @@ -31,7 +14,7 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, PluginEvent, &PluginEventTask::ScriptFunc, "checkable:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, PluginEvent, &PluginEventTask::ScriptFunc, "checkable:resolvedMacros:useResolvedMacros"); void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) @@ -51,8 +34,10 @@ void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable, resolvers.emplace_back("command", commandObj); resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); + int timeout = commandObj->GetTimeout(); + PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), - resolvers, resolvedMacros, useResolvedMacros, + resolvers, resolvedMacros, useResolvedMacros, timeout, std::bind(&PluginEventTask::ProcessFinishedHandler, checkable, _1, _2)); } diff --git a/lib/methods/plugineventtask.hpp b/lib/methods/plugineventtask.hpp index 0739bd13f..8908a82d3 100644 --- a/lib/methods/plugineventtask.hpp +++ b/lib/methods/plugineventtask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PLUGINEVENTTASK_H #define PLUGINEVENTTASK_H diff --git a/lib/methods/pluginnotificationtask.cpp b/lib/methods/pluginnotificationtask.cpp index c33d29e55..ab84e726a 100644 --- a/lib/methods/pluginnotificationtask.cpp +++ b/lib/methods/pluginnotificationtask.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "methods/pluginnotificationtask.hpp" #include "icinga/notification.hpp" @@ -32,12 +15,12 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, PluginNotification, &PluginNotificationTask::ScriptFunc, "notification:user:cr:itype:author:comment:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, PluginNotification, &PluginNotificationTask::ScriptFunc, "notification:user:cr:nr:itype:author:comment:resolvedMacros:useResolvedMacros"); void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, - const User::Ptr& user, const CheckResult::Ptr& cr, int itype, - const String& author, const String& comment, const Dictionary::Ptr& resolvedMacros, - bool useResolvedMacros) + const User::Ptr& user, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, + int itype, const String& author, const String& comment, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { REQUIRE_NOT_NULL(notification); REQUIRE_NOT_NULL(user); @@ -49,7 +32,7 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, Checkable::Ptr checkable = notification->GetCheckable(); Dictionary::Ptr notificationExtra = new Dictionary({ - { "type", Notification::NotificationTypeToString(type) }, + { "type", Notification::NotificationTypeToStringCompat(type) }, //TODO: Change that to our types. { "author", author }, { "comment", comment } }); @@ -68,18 +51,32 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, resolvers.emplace_back("command", commandObj); resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); + int timeout = commandObj->GetTimeout(); + PluginUtility::ExecuteCommand(commandObj, checkable, cr, resolvers, - resolvedMacros, useResolvedMacros, - std::bind(&PluginNotificationTask::ProcessFinishedHandler, checkable, _1, _2)); + resolvedMacros, useResolvedMacros, timeout, + std::bind(&PluginNotificationTask::ProcessFinishedHandler, checkable, notification, nr, _1, _2)); } -void PluginNotificationTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, const Value& commandLine, const ProcessResult& pr) +void PluginNotificationTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, + const Notification::Ptr& notification, const NotificationResult::Ptr& nr, const Value& commandLine, const ProcessResult& pr) { if (pr.ExitStatus != 0) { Process::Arguments parguments = Process::PrepareCommand(commandLine); Log(LogWarning, "PluginNotificationTask") - << "Notification command for object '" << checkable->GetName() << "' (PID: " << pr.PID + << "Notification command for checkable '" << checkable->GetName() + << "' and notification '" << notification->GetName() << "' (PID: " << pr.PID << ", arguments: " << Process::PrettyPrintArguments(parguments) << ") terminated with exit code " << pr.ExitStatus << ", output: " << pr.Output; } + + String output = pr.Output.Trim(); + + nr->SetCommand(commandLine); + nr->SetOutput(output); + nr->SetExitStatus(pr.ExitStatus); + nr->SetExecutionStart(pr.ExecutionStart); + nr->SetExecutionEnd(pr.ExecutionEnd); + + notification->ProcessNotificationResult(nr); } diff --git a/lib/methods/pluginnotificationtask.hpp b/lib/methods/pluginnotificationtask.hpp index 3fe7db757..6a741b73b 100644 --- a/lib/methods/pluginnotificationtask.hpp +++ b/lib/methods/pluginnotificationtask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PLUGINNOTIFICATIONTASK_H #define PLUGINNOTIFICATIONTASK_H @@ -37,14 +20,15 @@ class PluginNotificationTask { public: static void ScriptFunc(const Notification::Ptr& notification, - const User::Ptr& user, const CheckResult::Ptr& cr, int itype, - const String& author, const String& comment, + const User::Ptr& user, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, + int itype, const String& author, const String& comment, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); private: PluginNotificationTask(); static void ProcessFinishedHandler(const Checkable::Ptr& checkable, + const Notification::Ptr& notification, const NotificationResult::Ptr& nr, const Value& commandLine, const ProcessResult& pr); }; diff --git a/lib/methods/randomchecktask.cpp b/lib/methods/randomchecktask.cpp index bdf2d54d4..ddda9879b 100644 --- a/lib/methods/randomchecktask.cpp +++ b/lib/methods/randomchecktask.cpp @@ -1,27 +1,11 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef _WIN32 # include #endif /* _WIN32 */ #include "methods/randomchecktask.hpp" #include "icinga/icingaapplication.hpp" +#include "icinga/checkcommand.hpp" #include "base/utility.hpp" #include "base/perfdatavalue.hpp" #include "base/function.hpp" @@ -29,7 +13,7 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, RandomCheck, &RandomCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +REGISTER_FUNCTION_NONCONST(Internal, RandomCheck, &RandomCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); void RandomCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) @@ -61,5 +45,8 @@ void RandomCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes cr->SetState(static_cast(Utility::Random() % 4)); + CheckCommand::Ptr command = checkable->GetCheckCommand(); + cr->SetCommand(command->GetName()); + checkable->ProcessCheckResult(cr); } diff --git a/lib/methods/randomchecktask.hpp b/lib/methods/randomchecktask.hpp index 29b67cda3..00ce663dc 100644 --- a/lib/methods/randomchecktask.hpp +++ b/lib/methods/randomchecktask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef RANDOMCHECKTASK_H #define RANDOMCHECKTASK_H diff --git a/lib/methods/sleepchecktask.cpp b/lib/methods/sleepchecktask.cpp new file mode 100644 index 000000000..47f735c36 --- /dev/null +++ b/lib/methods/sleepchecktask.cpp @@ -0,0 +1,54 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "methods/sleepchecktask.hpp" +#include "icinga/icingaapplication.hpp" +#include "icinga/pluginutility.hpp" +#include "base/utility.hpp" +#include "base/convert.hpp" +#include "base/function.hpp" +#include "base/logger.hpp" + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, SleepCheck, &SleepCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); + +void SleepCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(checkable); + REQUIRE_NOT_NULL(cr); + + CheckCommand::Ptr commandObj = checkable->GetCheckCommand(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + MacroProcessor::ResolverList resolvers; + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("command", commandObj); + resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); + + double sleepTime = MacroProcessor::ResolveMacros("$sleep_time$", resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + + if (resolvedMacros && !useResolvedMacros) + return; + + Utility::Sleep(sleepTime); + + String output = "Slept for " + Convert::ToString(sleepTime) + " seconds."; + + double now = Utility::GetTime(); + + cr->SetOutput(output); + cr->SetExecutionStart(now); + cr->SetExecutionEnd(now); + + CheckCommand::Ptr command = checkable->GetCheckCommand(); + cr->SetCommand(command->GetName()); + + checkable->ProcessCheckResult(cr); +} \ No newline at end of file diff --git a/lib/methods/sleepchecktask.hpp b/lib/methods/sleepchecktask.hpp new file mode 100644 index 000000000..b104f60da --- /dev/null +++ b/lib/methods/sleepchecktask.hpp @@ -0,0 +1,30 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef SLEEPCHECKTASK_H +#define SLEEPCHECKTASK_H + +#include "methods/i2-methods.hpp" +#include "icinga/service.hpp" +#include "base/dictionary.hpp" + +namespace icinga +{ + +/** + * Test class for additional check types. Implements the "sleep" check type. + * + * @ingroup methods + */ +class SleepCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + SleepCheckTask(); +}; + +} + +#endif /* SLEEPCHECKTASK_H */ \ No newline at end of file diff --git a/lib/methods/timeperiodtask.cpp b/lib/methods/timeperiodtask.cpp index 152139e38..bb3f1bb00 100644 --- a/lib/methods/timeperiodtask.cpp +++ b/lib/methods/timeperiodtask.cpp @@ -1,29 +1,12 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "methods/timeperiodtask.hpp" #include "base/function.hpp" using namespace icinga; -REGISTER_SCRIPTFUNCTION_NS(Internal, EmptyTimePeriod, &TimePeriodTask::EmptyTimePeriodUpdate, "tp:begin:end"); -REGISTER_SCRIPTFUNCTION_NS(Internal, EvenMinutesTimePeriod, &TimePeriodTask::EvenMinutesTimePeriodUpdate, "tp:begin:end"); +REGISTER_FUNCTION_NONCONST(Internal, EmptyTimePeriod, &TimePeriodTask::EmptyTimePeriodUpdate, "tp:begin:end"); +REGISTER_FUNCTION_NONCONST(Internal, EvenMinutesTimePeriod, &TimePeriodTask::EvenMinutesTimePeriodUpdate, "tp:begin:end"); Array::Ptr TimePeriodTask::EmptyTimePeriodUpdate(const TimePeriod::Ptr& tp, double, double) { diff --git a/lib/methods/timeperiodtask.hpp b/lib/methods/timeperiodtask.hpp index 2bfe7eac4..0dff1c668 100644 --- a/lib/methods/timeperiodtask.hpp +++ b/lib/methods/timeperiodtask.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef TIMEPERIODTASK_H #define TIMEPERIODTASK_H diff --git a/lib/mysql_shim/CMakeLists.txt b/lib/mysql_shim/CMakeLists.txt index 1bc2db28f..fc7dbeec3 100644 --- a/lib/mysql_shim/CMakeLists.txt +++ b/lib/mysql_shim/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ include_directories(${MYSQL_INCLUDE_DIR}) diff --git a/lib/mysql_shim/mysqlinterface.cpp b/lib/mysql_shim/mysqlinterface.cpp index b9d9af1ab..aadefda47 100644 --- a/lib/mysql_shim/mysqlinterface.cpp +++ b/lib/mysql_shim/mysqlinterface.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "mysql_shim/mysqlinterface.hpp" @@ -109,7 +92,7 @@ struct MysqlInterfaceImpl final : public MysqlInterface return mysql_real_escape_string(mysql, to, from, length); } - my_bool ssl_set(MYSQL *mysql, const char *key, const char *cert, const char *ca, const char *capath, const char *cipher) const override + bool ssl_set(MYSQL *mysql, const char *key, const char *cert, const char *ca, const char *capath, const char *cipher) const override { return mysql_ssl_set(mysql, key, cert, ca, capath, cipher); } diff --git a/lib/mysql_shim/mysqlinterface.hpp b/lib/mysql_shim/mysqlinterface.hpp index dc2b3f109..1ac63868c 100644 --- a/lib/mysql_shim/mysqlinterface.hpp +++ b/lib/mysql_shim/mysqlinterface.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef MYSQLINTERFACE_H #define MYSQLINTERFACE_H @@ -52,7 +35,7 @@ struct MysqlInterface virtual MYSQL *real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag) const = 0; virtual unsigned long real_escape_string(MYSQL *mysql, char *to, const char *from, unsigned long length) const = 0; - virtual my_bool ssl_set(MYSQL *mysql, const char *key, const char *cert, const char *ca, const char *capath, const char *cipher) const = 0; + virtual bool ssl_set(MYSQL *mysql, const char *key, const char *cert, const char *ca, const char *capath, const char *cipher) const = 0; virtual MYSQL_RES *store_result(MYSQL *mysql) const = 0; virtual unsigned int thread_safe() const = 0; diff --git a/lib/notification/CMakeLists.txt b/lib/notification/CMakeLists.txt index 6c5940b9a..783b4fae9 100644 --- a/lib/notification/CMakeLists.txt +++ b/lib/notification/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ mkclass_target(notificationcomponent.ti notificationcomponent-ti.cpp notificationcomponent-ti.hpp) @@ -36,14 +21,14 @@ set_target_properties ( install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/notification.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) if(NOT WIN32) - install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled\")") - install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/notification.conf \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled/notification.conf\")") + install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_CONFIGDIR}/features-enabled\")") + install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/notification.conf \"\$ENV{DESTDIR}${ICINGA2_FULL_CONFIGDIR}/features-enabled/notification.conf\")") else() - install_if_not_exists(${PROJECT_SOURCE_DIR}/etc/icinga2/features-enabled/notification.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-enabled) + install_if_not_exists(${PROJECT_SOURCE_DIR}/etc/icinga2/features-enabled/notification.conf ${ICINGA2_CONFIGDIR}/features-enabled) endif() set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) diff --git a/lib/notification/notificationcomponent.cpp b/lib/notification/notificationcomponent.cpp index 3d7fbc60b..aa9601201 100644 --- a/lib/notification/notificationcomponent.cpp +++ b/lib/notification/notificationcomponent.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "notification/notificationcomponent.hpp" #include "notification/notificationcomponent-ti.cpp" @@ -27,6 +10,7 @@ #include "base/utility.hpp" #include "base/exception.hpp" #include "base/statsfunction.hpp" +#include "remote/apilistener.hpp" using namespace icinga; @@ -81,26 +65,87 @@ void NotificationComponent::NotificationTimerHandler() { double now = Utility::GetTime(); + /* Function already checks whether 'api' feature is enabled. */ + Endpoint::Ptr myEndpoint = Endpoint::GetLocalEndpoint(); + for (const Notification::Ptr& notification : ConfigType::GetObjectsByType()) { if (!notification->IsActive()) continue; - if (notification->IsPaused() && GetEnableHA()) - continue; + String notificationName = notification->GetName(); + bool updatedObjectAuthority = ApiListener::UpdatedObjectAuthority(); + + /* Skip notification if paused, in a cluster setup & HA feature is enabled. */ + if (notification->IsPaused()) { + if (updatedObjectAuthority) { + auto stashedNotifications (notification->GetStashedNotifications()); + ObjectLock olock(stashedNotifications); + + if (stashedNotifications->GetLength()) { + Log(LogNotice, "NotificationComponent") + << "Notification '" << notificationName << "': HA cluster active, this endpoint does not have the authority. Dropping all stashed notifications."; + + stashedNotifications->Clear(); + } + } + + if (myEndpoint && GetEnableHA()) { + Log(LogNotice, "NotificationComponent") + << "Reminder notification '" << notificationName << "': HA cluster active, this endpoint does not have the authority (paused=true). Skipping."; + continue; + } + } Checkable::Ptr checkable = notification->GetCheckable(); if (!IcingaApplication::GetInstance()->GetEnableNotifications() || !checkable->GetEnableNotifications()) continue; - if (notification->GetInterval() <= 0 && notification->GetNoMoreNotifications()) + bool reachable = checkable->IsReachable(DependencyNotification); + + if (reachable) { + Array::Ptr unstashedNotifications = new Array(); + + { + auto stashedNotifications (notification->GetStashedNotifications()); + ObjectLock olock(stashedNotifications); + + stashedNotifications->CopyTo(unstashedNotifications); + stashedNotifications->Clear(); + } + + ObjectLock olock(unstashedNotifications); + + for (Dictionary::Ptr unstashedNotification : unstashedNotifications) { + try { + Log(LogNotice, "NotificationComponent") + << "Attempting to send stashed notification '" << notificationName << "'."; + + notification->BeginExecuteNotification( + (NotificationType)(int)unstashedNotification->Get("type"), + (CheckResult::Ptr)unstashedNotification->Get("cr"), + (bool)unstashedNotification->Get("force"), + (bool)unstashedNotification->Get("reminder"), + (String)unstashedNotification->Get("author"), + (String)unstashedNotification->Get("text") + ); + } catch (const std::exception& ex) { + Log(LogWarning, "NotificationComponent") + << "Exception occurred during notification for object '" + << notificationName << "': " << DiagnosticInformation(ex, false); + } + } + } + + if (notification->GetInterval() <= 0 && notification->GetNoMoreNotifications()) { + Log(LogNotice, "NotificationComponent") + << "Reminder notification '" << notificationName << "': Notification was sent out once and interval=0 disables reminder notifications."; continue; + } if (notification->GetNextNotification() > now) continue; - bool reachable = checkable->IsReachable(DependencyNotification); - { ObjectLock olock(notification); notification->SetNextNotification(Utility::GetTime() + notification->GetInterval()); @@ -116,22 +161,24 @@ void NotificationComponent::NotificationTimerHandler() if (checkable->GetStateType() == StateTypeSoft) continue; + /* Don't send reminder notifications for OK/Up states. */ if ((service && service->GetState() == ServiceOK) || (!service && host->GetState() == HostUp)) continue; + /* Skip in runtime filters. */ if (!reachable || checkable->IsInDowntime() || checkable->IsAcknowledged() || checkable->IsFlapping()) continue; } try { Log(LogNotice, "NotificationComponent") - << "Attempting to send reminder notification '" << notification->GetName() << "'"; + << "Attempting to send reminder notification '" << notificationName << "'."; notification->BeginExecuteNotification(NotificationProblem, checkable->GetLastCheckResult(), false, true); } catch (const std::exception& ex) { Log(LogWarning, "NotificationComponent") - << "Exception occured during notification for object '" - << GetName() << "': " << DiagnosticInformation(ex); + << "Exception occurred during notification for object '" + << notificationName << "': " << DiagnosticInformation(ex, false); } } } diff --git a/lib/notification/notificationcomponent.hpp b/lib/notification/notificationcomponent.hpp index 39bb220f2..09434e21a 100644 --- a/lib/notification/notificationcomponent.hpp +++ b/lib/notification/notificationcomponent.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef NOTIFICATIONCOMPONENT_H #define NOTIFICATIONCOMPONENT_H diff --git a/lib/notification/notificationcomponent.ti b/lib/notification/notificationcomponent.ti index 547528d1e..13af13691 100644 --- a/lib/notification/notificationcomponent.ti +++ b/lib/notification/notificationcomponent.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" @@ -26,7 +9,7 @@ namespace icinga class NotificationComponent : ConfigObject { - activation_priority 100; + activation_priority 200; [config] bool enable_ha (EnableHA) { default {{{ return true; }}} diff --git a/lib/perfdata/CMakeLists.txt b/lib/perfdata/CMakeLists.txt index 7b0780414..c7fba5851 100644 --- a/lib/perfdata/CMakeLists.txt +++ b/lib/perfdata/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ mkclass_target(gelfwriter.ti gelfwriter-ti.cpp gelfwriter-ti.hpp) mkclass_target(graphitewriter.ti graphitewriter-ti.cpp graphitewriter-ti.hpp) @@ -46,35 +31,35 @@ set_target_properties ( install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/gelf.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/graphite.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/influxdb.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/elasticsearch.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/opentsdb.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) install_if_not_exists( ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/perfdata.conf - ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available + ${ICINGA2_CONFIGDIR}/features-available ) -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/spool/icinga2/perfdata\")") -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/spool/icinga2/tmp\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_SPOOLDIR}/perfdata\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_SPOOLDIR}/tmp\")") set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) diff --git a/lib/perfdata/elasticsearchwriter.cpp b/lib/perfdata/elasticsearchwriter.cpp index 2e8ffa515..7798ad8dc 100644 --- a/lib/perfdata/elasticsearchwriter.cpp +++ b/lib/perfdata/elasticsearchwriter.cpp @@ -1,30 +1,14 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "perfdata/elasticsearchwriter.hpp" #include "perfdata/elasticsearchwriter-ti.cpp" #include "remote/url.hpp" -#include "remote/httprequest.hpp" -#include "remote/httpresponse.hpp" #include "icinga/compatutility.hpp" #include "icinga/service.hpp" #include "icinga/checkcommand.hpp" +#include "base/application.hpp" +#include "base/defer.hpp" +#include "base/io-engine.hpp" #include "base/tcpsocket.hpp" #include "base/stream.hpp" #include "base/base64.hpp" @@ -35,7 +19,19 @@ #include "base/exception.hpp" #include "base/statsfunction.hpp" #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include #include using namespace icinga; @@ -49,6 +45,15 @@ void ElasticsearchWriter::OnConfigLoaded() ObjectImpl::OnConfigLoaded(); m_WorkQueue.SetName("ElasticsearchWriter, " + GetName()); + + if (!GetEnableHa()) { + Log(LogDebug, "ElasticsearchWriter") + << "HA functionality disabled. Won't pause connection: " << GetName(); + + SetHAMode(HARunEverywhere); + } else { + SetHAMode(HARunOnce); + } } void ElasticsearchWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata) @@ -71,14 +76,14 @@ void ElasticsearchWriter::StatsFunc(const Dictionary::Ptr& status, const Array:: status->Set("elasticsearchwriter", new Dictionary(std::move(nodes))); } -void ElasticsearchWriter::Start(bool runtimeCreated) +void ElasticsearchWriter::Resume() { - ObjectImpl::Start(runtimeCreated); + ObjectImpl::Resume(); m_EventPrefix = "icinga2.event."; Log(LogInformation, "ElasticsearchWriter") - << "'" << GetName() << "' started."; + << "'" << GetName() << "' resumed."; m_WorkQueue.SetExceptionCallback(std::bind(&ElasticsearchWriter::ExceptionHandler, this, _1)); @@ -95,14 +100,17 @@ void ElasticsearchWriter::Start(bool runtimeCreated) Checkable::OnNotificationSentToAllUsers.connect(std::bind(&ElasticsearchWriter::NotificationSentToAllUsersHandler, this, _1, _2, _3, _4, _5, _6, _7)); } -void ElasticsearchWriter::Stop(bool runtimeRemoved) +/* Pause is equivalent to Stop, but with HA capabilities to resume at runtime. */ +void ElasticsearchWriter::Pause() { - Log(LogInformation, "ElasticsearchWriter") - << "'" << GetName() << "' stopped."; - + Flush(); m_WorkQueue.Join(); + Flush(); - ObjectImpl::Stop(runtimeRemoved); + Log(LogInformation, "ElasticsearchWriter") + << "'" << GetName() << "' paused."; + + ObjectImpl::Pause(); } void ElasticsearchWriter::AddCheckResult(const Dictionary::Ptr& fields, const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) @@ -131,6 +139,8 @@ void ElasticsearchWriter::AddCheckResult(const Dictionary::Ptr& fields, const Ch Array::Ptr perfdata = cr->GetPerformanceData(); + CheckCommand::Ptr checkCommand = checkable->GetCheckCommand(); + if (perfdata) { ObjectLock olock(perfdata); for (const Value& val : perfdata) { @@ -143,8 +153,9 @@ void ElasticsearchWriter::AddCheckResult(const Dictionary::Ptr& fields, const Ch pdv = PerfdataValue::Parse(val); } catch (const std::exception&) { Log(LogWarning, "ElasticsearchWriter") - << "Ignoring invalid perfdata value: '" << val << "' for object '" - << checkable->GetName() << "'."; + << "Ignoring invalid perfdata for checkable '" + << checkable->GetName() << "' and command '" + << checkCommand->GetName() << "' with value: " << val; continue; } } @@ -176,6 +187,9 @@ void ElasticsearchWriter::AddCheckResult(const Dictionary::Ptr& fields, const Ch void ElasticsearchWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { + if (IsPaused()) + return; + m_WorkQueue.Enqueue(std::bind(&ElasticsearchWriter::InternalCheckResultHandler, this, checkable, cr)); } @@ -225,11 +239,14 @@ void ElasticsearchWriter::InternalCheckResultHandler(const Checkable::Ptr& check ts = cr->GetExecutionEnd(); } - Enqueue("checkresult", fields, ts); + Enqueue(checkable, "checkresult", fields, ts); } void ElasticsearchWriter::StateChangeHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type) { + if (IsPaused()) + return; + m_WorkQueue.Enqueue(std::bind(&ElasticsearchWriter::StateChangeHandlerInternal, this, checkable, cr, type)); } @@ -272,13 +289,16 @@ void ElasticsearchWriter::StateChangeHandlerInternal(const Checkable::Ptr& check ts = cr->GetExecutionEnd(); } - Enqueue("statechange", fields, ts); + Enqueue(checkable, "statechange", fields, ts); } void ElasticsearchWriter::NotificationSentToAllUsersHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set& users, NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text) { + if (IsPaused()) + return; + m_WorkQueue.Enqueue(std::bind(&ElasticsearchWriter::NotificationSentToAllUsersHandlerInternal, this, notification, checkable, users, type, cr, author, text)); } @@ -298,7 +318,7 @@ void ElasticsearchWriter::NotificationSentToAllUsersHandlerInternal(const Notifi Service::Ptr service; tie(host, service) = GetHostService(checkable); - String notificationTypeString = Notification::NotificationTypeToString(type); + String notificationTypeString = Notification::NotificationTypeToStringCompat(type); //TODO: Change that to our own types. Dictionary::Ptr fields = new Dictionary(); @@ -338,10 +358,11 @@ void ElasticsearchWriter::NotificationSentToAllUsersHandlerInternal(const Notifi ts = cr->GetExecutionEnd(); } - Enqueue("notification", fields, ts); + Enqueue(checkable, "notification", fields, ts); } -void ElasticsearchWriter::Enqueue(const String& type, const Dictionary::Ptr& fields, double ts) +void ElasticsearchWriter::Enqueue(const Checkable::Ptr& checkable, const String& type, + const Dictionary::Ptr& fields, double ts) { /* Atomically buffer the data point. */ boost::mutex::scoped_lock lock(m_DataBufferMutex); @@ -360,7 +381,7 @@ void ElasticsearchWriter::Enqueue(const String& type, const Dictionary::Ptr& fie String fieldsBody = JsonEncode(fields); Log(LogDebug, "ElasticsearchWriter") - << "Add to fields to message list: '" << fieldsBody << "'."; + << "Checkable '" << checkable->GetName() << "' adds to metric list: '" << fieldsBody << "'."; m_DataBuffer.emplace_back(indexBody + fieldsBody); @@ -389,6 +410,10 @@ void ElasticsearchWriter::FlushTimeout() void ElasticsearchWriter::Flush() { + /* Flush can be called from 1) Timeout 2) Threshold 3) on shutdown/reload. */ + if (m_DataBuffer.empty()) + return; + /* Ensure you hold a lock against m_DataBuffer so that things * don't go missing after creating the body and clearing the buffer. */ @@ -405,6 +430,9 @@ void ElasticsearchWriter::Flush() void ElasticsearchWriter::SendRequest(const String& body) { + namespace beast = boost::beast; + namespace http = beast::http; + Url::Ptr url = new Url(); url->SetScheme(GetEnableTls() ? "https" : "http"); @@ -428,58 +456,84 @@ void ElasticsearchWriter::SendRequest(const String& body) url->SetPath(path); - Stream::Ptr stream = Connect(); - HttpRequest req(stream); + OptionalTlsStream stream; + + try { + stream = Connect(); + } catch (const std::exception& ex) { + Log(LogWarning, "ElasticsearchWriter") + << "Flush failed, cannot connect to Elasticsearch: " << DiagnosticInformation(ex, false); + return; + } + + Defer s ([&stream]() { + if (stream.first) { + stream.first->next_layer().shutdown(); + } + }); + + http::request request (http::verb::post, std::string(url->Format(true)), 10); + + request.set(http::field::user_agent, "Icinga/" + Application::GetAppVersion()); + request.set(http::field::host, url->GetHost() + ":" + url->GetPort()); /* Specify required headers by Elasticsearch. */ - req.AddHeader("Accept", "application/json"); - req.AddHeader("Content-Type", "application/json"); + request.set(http::field::accept, "application/json"); + + /* Use application/x-ndjson for bulk streams. While ES + * is able to handle application/json, the newline separator + * causes problems with Logstash (#6609). + */ + request.set(http::field::content_type, "application/x-ndjson"); /* Send authentication if configured. */ String username = GetUsername(); String password = GetPassword(); if (!username.IsEmpty() && !password.IsEmpty()) - req.AddHeader("Authorization", "Basic " + Base64::Encode(username + ":" + password)); + request.set(http::field::authorization, "Basic " + Base64::Encode(username + ":" + password)); - req.RequestMethod = "POST"; - req.RequestUrl = url; + request.body() = body; + request.set(http::field::content_length, request.body().size()); /* Don't log the request body to debug log, this is already done above. */ Log(LogDebug, "ElasticsearchWriter") - << "Sending " << req.RequestMethod << " request" << ((!username.IsEmpty() && !password.IsEmpty()) ? " with basic auth" : "" ) + << "Sending " << request.method_string() << " request" << ((!username.IsEmpty() && !password.IsEmpty()) ? " with basic auth" : "" ) << " to '" << url->Format() << "'."; try { - req.WriteBody(body.CStr(), body.GetLength()); - req.Finish(); - } catch (const std::exception& ex) { + if (stream.first) { + http::write(*stream.first, request); + stream.first->flush(); + } else { + http::write(*stream.second, request); + stream.second->flush(); + } + } catch (const std::exception&) { Log(LogWarning, "ElasticsearchWriter") << "Cannot write to HTTP API on host '" << GetHost() << "' port '" << GetPort() << "'."; - throw ex; + throw; } - HttpResponse resp(stream, req); - StreamReadContext context; + http::parser parser; + beast::flat_buffer buf; try { - resp.Parse(context, true); - while (resp.Parse(context, true) && !resp.Complete) - ; /* Do nothing */ + if (stream.first) { + http::read(*stream.first, buf, parser); + } else { + http::read(*stream.second, buf, parser); + } } catch (const std::exception& ex) { Log(LogWarning, "ElasticsearchWriter") << "Failed to parse HTTP response from host '" << GetHost() << "' port '" << GetPort() << "': " << DiagnosticInformation(ex, false); - throw ex; + throw; } - if (!resp.Complete) { - Log(LogWarning, "ElasticsearchWriter") - << "Failed to read a complete HTTP response from the Elasticsearch server."; - return; - } + auto& response (parser.get()); - if (resp.StatusCode > 299) { - if (resp.StatusCode == 401) { + if (response.result_int() > 299) { + if (response.result() == http::status::unauthorized) { /* More verbose error logging with Elasticsearch is hidden behind a proxy. */ if (!username.IsEmpty() && !password.IsEmpty()) { Log(LogCritical, "ElasticsearchWriter") @@ -493,80 +547,83 @@ void ElasticsearchWriter::SendRequest(const String& body) return; } - Log(LogWarning, "ElasticsearchWriter") - << "Unexpected response code " << resp.StatusCode; + std::ostringstream msgbuf; + msgbuf << "Unexpected response code " << response.result_int() << " from URL '" << url->Format() << "'"; - String contentType = resp.Headers->Get("content-type"); + auto& contentType (response[http::field::content_type]); - if (contentType != "application/json") { - Log(LogWarning, "ElasticsearchWriter") - << "Unexpected Content-Type: " << contentType; - return; + if (contentType != "application/json" && contentType != "application/json; charset=utf-8") { + msgbuf << "; Unexpected Content-Type: '" << contentType << "'"; } - size_t responseSize = resp.GetBodySize(); - boost::scoped_array buffer(new char[responseSize + 1]); - resp.ReadBody(buffer.get(), responseSize); - buffer.get()[responseSize] = '\0'; + auto& body (response.body()); + +#ifdef I2_DEBUG + msgbuf << "; Response body: '" << body << "'"; +#endif /* I2_DEBUG */ Dictionary::Ptr jsonResponse; + try { - jsonResponse = JsonDecode(buffer.get()); + jsonResponse = JsonDecode(body); } catch (...) { Log(LogWarning, "ElasticsearchWriter") - << "Unable to parse JSON response:\n" << buffer.get(); + << "Unable to parse JSON response:\n" << body; return; } String error = jsonResponse->Get("error"); Log(LogCritical, "ElasticsearchWriter") - << "Elasticsearch error message:\n" << error; - - return; + << "Error: '" << error << "'. " << msgbuf.str(); } } -Stream::Ptr ElasticsearchWriter::Connect() +OptionalTlsStream ElasticsearchWriter::Connect() { - TcpSocket::Ptr socket = new TcpSocket(); - Log(LogNotice, "ElasticsearchWriter") << "Connecting to Elasticsearch on host '" << GetHost() << "' port '" << GetPort() << "'."; - try { - socket->Connect(GetHost(), GetPort()); - } catch (const std::exception& ex) { - Log(LogWarning, "ElasticsearchWriter") - << "Can't connect to Elasticsearch on host '" << GetHost() << "' port '" << GetPort() << "'."; - throw ex; - } + OptionalTlsStream stream; + bool tls = GetEnableTls(); - if (GetEnableTls()) { - std::shared_ptr sslContext; + if (tls) { + std::shared_ptr sslContext; try { - sslContext = MakeSSLContext(GetCertPath(), GetKeyPath(), GetCaPath()); - } catch (const std::exception& ex) { + sslContext = MakeAsioSslContext(GetCertPath(), GetKeyPath(), GetCaPath()); + } catch (const std::exception&) { Log(LogWarning, "ElasticsearchWriter") << "Unable to create SSL context."; - throw ex; + throw; } - TlsStream::Ptr tlsStream = new TlsStream(socket, GetHost(), RoleClient, sslContext); + stream.first = std::make_shared(IoEngine::Get().GetIoContext(), *sslContext, GetHost()); + } else { + stream.second = std::make_shared(IoEngine::Get().GetIoContext()); + } + + try { + icinga::Connect(tls ? stream.first->lowest_layer() : stream.second->lowest_layer(), GetHost(), GetPort()); + } catch (const std::exception&) { + Log(LogWarning, "ElasticsearchWriter") + << "Can't connect to Elasticsearch on host '" << GetHost() << "' port '" << GetPort() << "'."; + throw; + } + + if (tls) { + auto& tlsStream (stream.first->next_layer()); try { - tlsStream->Handshake(); - } catch (const std::exception& ex) { + tlsStream.handshake(tlsStream.client); + } catch (const std::exception&) { Log(LogWarning, "ElasticsearchWriter") << "TLS handshake with host '" << GetHost() << "' on port " << GetPort() << " failed."; - throw ex; + throw; } - - return tlsStream; - } else { - return new NetworkStream(socket); } + + return std::move(stream); } void ElasticsearchWriter::AssertOnWorkQueue() diff --git a/lib/perfdata/elasticsearchwriter.hpp b/lib/perfdata/elasticsearchwriter.hpp index e28ad4a7c..45658f574 100644 --- a/lib/perfdata/elasticsearchwriter.hpp +++ b/lib/perfdata/elasticsearchwriter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ELASTICSEARCHWRITER_H #define ELASTICSEARCHWRITER_H @@ -25,6 +8,7 @@ #include "base/configobject.hpp" #include "base/workqueue.hpp" #include "base/timer.hpp" +#include "base/tlsstream.hpp" namespace icinga { @@ -41,8 +25,8 @@ public: protected: void OnConfigLoaded() override; - void Start(bool runtimeCreated) override; - void Stop(bool runtimeRemoved) override; + void Resume() override; + void Pause() override; private: String m_EventPrefix; @@ -64,9 +48,10 @@ private: const Checkable::Ptr& checkable, const std::set& users, NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text); - void Enqueue(const String& type, const Dictionary::Ptr& fields, double ts); + void Enqueue(const Checkable::Ptr& checkable, const String& type, + const Dictionary::Ptr& fields, double ts); - Stream::Ptr Connect(); + OptionalTlsStream Connect(); void AssertOnWorkQueue(); void ExceptionHandler(boost::exception_ptr exp); void FlushTimeout(); diff --git a/lib/perfdata/elasticsearchwriter.ti b/lib/perfdata/elasticsearchwriter.ti index 2bbdd9d6a..87a7ea321 100644 --- a/lib/perfdata/elasticsearchwriter.ti +++ b/lib/perfdata/elasticsearchwriter.ti @@ -1,3 +1,5 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + #include "base/configobject.hpp" library perfdata; @@ -37,6 +39,9 @@ class ElasticsearchWriter : ConfigObject [config] int flush_threshold { default {{{ return 1024; }}} }; + [config] bool enable_ha { + default {{{ return false; }}} + }; }; } diff --git a/lib/perfdata/gelfwriter.cpp b/lib/perfdata/gelfwriter.cpp index e6b43e09f..c778a3813 100644 --- a/lib/perfdata/gelfwriter.cpp +++ b/lib/perfdata/gelfwriter.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "perfdata/gelfwriter.hpp" #include "perfdata/gelfwriter-ti.cpp" @@ -39,6 +22,11 @@ #include "base/statsfunction.hpp" #include #include +#include "base/io-engine.hpp" +#include +#include +#include +#include using namespace icinga; @@ -51,6 +39,15 @@ void GelfWriter::OnConfigLoaded() ObjectImpl::OnConfigLoaded(); m_WorkQueue.SetName("GelfWriter, " + GetName()); + + if (!GetEnableHa()) { + Log(LogDebug, "GelfWriter") + << "HA functionality disabled. Won't pause connection: " << GetName(); + + SetHAMode(HARunEverywhere); + } else { + SetHAMode(HARunOnce); + } } void GelfWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata) @@ -75,12 +72,12 @@ void GelfWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perf status->Set("gelfwriter", new Dictionary(std::move(nodes))); } -void GelfWriter::Start(bool runtimeCreated) +void GelfWriter::Resume() { - ObjectImpl::Start(runtimeCreated); + ObjectImpl::Resume(); Log(LogInformation, "GelfWriter") - << "'" << GetName() << "' started."; + << "'" << GetName() << "' resumed."; /* Register exception handler for WQ tasks. */ m_WorkQueue.SetExceptionCallback(std::bind(&GelfWriter::ExceptionHandler, this, _1)); @@ -94,18 +91,32 @@ void GelfWriter::Start(bool runtimeCreated) /* Register event handlers. */ Checkable::OnNewCheckResult.connect(std::bind(&GelfWriter::CheckResultHandler, this, _1, _2)); - Checkable::OnNotificationSentToUser.connect(std::bind(&GelfWriter::NotificationToUserHandler, this, _1, _2, _3, _4, _5, _6, _7, _8)); + Checkable::OnNotificationSentToUser.connect(std::bind(&GelfWriter::NotificationToUserHandler, this, _1, _2, _3, _4, _5, _6, _7, _8, _9)); Checkable::OnStateChange.connect(std::bind(&GelfWriter::StateChangeHandler, this, _1, _2, _3)); } -void GelfWriter::Stop(bool runtimeRemoved) +/* Pause is equivalent to Stop, but with HA capabilities to resume at runtime. */ +void GelfWriter::Pause() { - Log(LogInformation, "GelfWriter") - << "'" << GetName() << "' stopped."; + m_ReconnectTimer.reset(); + + try { + ReconnectInternal(); + } catch (const std::exception&) { + Log(LogInformation, "GelfWriter") + << "'" << GetName() << "' paused. Unable to connect, not flushing buffers. Data may be lost on reload."; + + ObjectImpl::Pause(); + return; + } m_WorkQueue.Join(); + DisconnectInternal(); - ObjectImpl::Stop(runtimeRemoved); + Log(LogInformation, "GelfWriter") + << "'" << GetName() << "' paused."; + + ObjectImpl::Pause(); } void GelfWriter::AssertOnWorkQueue() @@ -120,17 +131,23 @@ void GelfWriter::ExceptionHandler(boost::exception_ptr exp) Log(LogDebug, "GelfWriter") << "Exception during Graylog Gelf operation: " << DiagnosticInformation(std::move(exp)); - if (GetConnected()) { - m_Stream->Close(); - - SetConnected(false); - } + DisconnectInternal(); } void GelfWriter::Reconnect() { AssertOnWorkQueue(); + if (IsPaused()) { + SetConnected(false); + return; + } + + ReconnectInternal(); +} + +void GelfWriter::ReconnectInternal() +{ double startTime = Utility::GetTime(); CONTEXT("Reconnecting to Graylog Gelf '" + GetName() + "'"); @@ -140,20 +157,46 @@ void GelfWriter::Reconnect() if (GetConnected()) return; - TcpSocket::Ptr socket = new TcpSocket(); - Log(LogNotice, "GelfWriter") << "Reconnecting to Graylog Gelf on host '" << GetHost() << "' port '" << GetPort() << "'."; - try { - socket->Connect(GetHost(), GetPort()); - } catch (const std::exception& ex) { - Log(LogCritical, "GelfWriter") - << "Can't connect to Graylog Gelf on host '" << GetHost() << "' port '" << GetPort() << "'."; - throw ex; + bool ssl = GetEnableTls(); + + if (ssl) { + std::shared_ptr sslContext; + + try { + sslContext = MakeAsioSslContext(GetCertPath(), GetKeyPath(), GetCaPath()); + } catch (const std::exception& ex) { + Log(LogWarning, "GelfWriter") + << "Unable to create SSL context."; + throw; + } + + m_Stream.first = std::make_shared(IoEngine::Get().GetIoContext(), *sslContext, GetHost()); + } else { + m_Stream.second = std::make_shared(IoEngine::Get().GetIoContext()); } - m_Stream = new NetworkStream(socket); + try { + icinga::Connect(ssl ? m_Stream.first->lowest_layer() : m_Stream.second->lowest_layer(), GetHost(), GetPort()); + } catch (const std::exception& ex) { + Log(LogWarning, "GelfWriter") + << "Can't connect to Graylog Gelf on host '" << GetHost() << "' port '" << GetPort() << ".'"; + throw; + } + + if (ssl) { + auto& tlsStream (m_Stream.first->next_layer()); + + try { + tlsStream.handshake(tlsStream.client); + } catch (const std::exception& ex) { + Log(LogWarning, "GelfWriter") + << "TLS handshake with host '" << GetHost() << " failed.'"; + throw; + } + } SetConnected(true); @@ -170,16 +213,37 @@ void GelfWriter::Disconnect() { AssertOnWorkQueue(); + DisconnectInternal(); +} + +void GelfWriter::DisconnectInternal() +{ if (!GetConnected()) return; - m_Stream->Close(); + if (m_Stream.first) { + boost::system::error_code ec; + m_Stream.first->next_layer().shutdown(ec); + + // https://stackoverflow.com/a/25703699 + // As long as the error code's category is not an SSL category, then the protocol was securely shutdown + if (ec.category() == boost::asio::error::get_ssl_category()) { + Log(LogCritical, "GelfWriter") + << "TLS shutdown with host '" << GetHost() << "' could not be done securely."; + } + } else if (m_Stream.second) { + m_Stream.second->close(); + } SetConnected(false); + } void GelfWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { + if (IsPaused()) + return; + m_WorkQueue.Enqueue(std::bind(&GelfWriter::CheckResultHandlerInternal, this, checkable, cr)); } @@ -217,10 +281,10 @@ void GelfWriter::CheckResultHandlerInternal(const Checkable::Ptr& checkable, con fields->Set("_reachable", checkable->IsReachable()); - CheckCommand::Ptr commandObj = checkable->GetCheckCommand(); + CheckCommand::Ptr checkCommand = checkable->GetCheckCommand(); - if (commandObj) - fields->Set("_check_command", commandObj->GetName()); + if (checkCommand) + fields->Set("_check_command", checkCommand->GetName()); double ts = Utility::GetTime(); @@ -248,8 +312,9 @@ void GelfWriter::CheckResultHandlerInternal(const Checkable::Ptr& checkable, con pdv = PerfdataValue::Parse(val); } catch (const std::exception&) { Log(LogWarning, "GelfWriter") - << "Ignoring invalid perfdata value: '" << val << "' for object '" - << checkable->GetName() << "'."; + << "Ignoring invalid perfdata for checkable '" + << checkable->GetName() << "' and command '" + << checkCommand->GetName() << "' with value: " << val; continue; } } @@ -277,19 +342,22 @@ void GelfWriter::CheckResultHandlerInternal(const Checkable::Ptr& checkable, con } } - SendLogMessage(ComposeGelfMessage(fields, GetSource(), ts)); + SendLogMessage(checkable, ComposeGelfMessage(fields, GetSource(), ts)); } void GelfWriter::NotificationToUserHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, - const User::Ptr& user, NotificationType notificationType, CheckResult::Ptr const& cr, + const User::Ptr& user, NotificationType notificationType, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, const String& author, const String& commentText, const String& commandName) { + if (IsPaused()) + return; + m_WorkQueue.Enqueue(std::bind(&GelfWriter::NotificationToUserHandlerInternal, this, - notification, checkable, user, notificationType, cr, author, commentText, commandName)); + notification, checkable, user, notificationType, cr, nr, author, commentText, commandName)); } void GelfWriter::NotificationToUserHandlerInternal(const Notification::Ptr& notification, const Checkable::Ptr& checkable, - const User::Ptr& user, NotificationType notificationType, CheckResult::Ptr const& cr, + const User::Ptr& user, NotificationType notificationType, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, const String& author, const String& commentText, const String& commandName) { AssertOnWorkQueue(); @@ -303,7 +371,7 @@ void GelfWriter::NotificationToUserHandlerInternal(const Notification::Ptr& noti Service::Ptr service; tie(host, service) = GetHostService(checkable); - String notificationTypeString = Notification::NotificationTypeToString(notificationType); + String notificationTypeString = Notification::NotificationTypeToStringCompat(notificationType); //TODO: Change that to our own types. String authorComment = ""; @@ -343,11 +411,14 @@ void GelfWriter::NotificationToUserHandlerInternal(const Notification::Ptr& noti if (commandObj) fields->Set("_check_command", commandObj->GetName()); - SendLogMessage(ComposeGelfMessage(fields, GetSource(), ts)); + SendLogMessage(checkable, ComposeGelfMessage(fields, GetSource(), ts)); } void GelfWriter::StateChangeHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type) { + if (IsPaused()) + return; + m_WorkQueue.Enqueue(std::bind(&GelfWriter::StateChangeHandlerInternal, this, checkable, cr, type)); } @@ -396,7 +467,7 @@ void GelfWriter::StateChangeHandlerInternal(const Checkable::Ptr& checkable, con ts = cr->GetExecutionEnd(); } - SendLogMessage(ComposeGelfMessage(fields, GetSource(), ts)); + SendLogMessage(checkable, ComposeGelfMessage(fields, GetSource(), ts)); } String GelfWriter::ComposeGelfMessage(const Dictionary::Ptr& fields, const String& source, double ts) @@ -408,7 +479,7 @@ String GelfWriter::ComposeGelfMessage(const Dictionary::Ptr& fields, const Strin return JsonEncode(fields); } -void GelfWriter::SendLogMessage(const String& gelfMessage) +void GelfWriter::SendLogMessage(const Checkable::Ptr& checkable, const String& gelfMessage) { std::ostringstream msgbuf; msgbuf << gelfMessage; @@ -423,9 +494,15 @@ void GelfWriter::SendLogMessage(const String& gelfMessage) try { Log(LogDebug, "GelfWriter") - << "Sending '" << log << "'."; + << "Checkable '" << checkable->GetName() << "' sending message '" << log << "'."; - m_Stream->Write(log.CStr(), log.GetLength()); + if (m_Stream.first) { + boost::asio::write(*m_Stream.first, boost::asio::buffer(msgbuf.str())); + m_Stream.first->flush(); + } else { + boost::asio::write(*m_Stream.second, boost::asio::buffer(msgbuf.str())); + m_Stream.second->flush(); + } } catch (const std::exception& ex) { Log(LogCritical, "GelfWriter") << "Cannot write to TCP socket on host '" << GetHost() << "' port '" << GetPort() << "'."; diff --git a/lib/perfdata/gelfwriter.hpp b/lib/perfdata/gelfwriter.hpp index 0948ecdd7..f729c0c5c 100644 --- a/lib/perfdata/gelfwriter.hpp +++ b/lib/perfdata/gelfwriter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef GELFWRITER_H #define GELFWRITER_H @@ -46,11 +29,11 @@ public: protected: void OnConfigLoaded() override; - void Start(bool runtimeCreated) override; - void Stop(bool runtimeRemoved) override; + void Resume() override; + void Pause() override; private: - Stream::Ptr m_Stream; + OptionalTlsStream m_Stream; WorkQueue m_WorkQueue{10000000, 1}; Timer::Ptr m_ReconnectTimer; @@ -58,21 +41,23 @@ private: void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); void CheckResultHandlerInternal(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); void NotificationToUserHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, - const User::Ptr& user, NotificationType notificationType, const CheckResult::Ptr& cr, + const User::Ptr& user, NotificationType notificationType, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, const String& author, const String& commentText, const String& commandName); void NotificationToUserHandlerInternal(const Notification::Ptr& notification, const Checkable::Ptr& checkable, - const User::Ptr& user, NotificationType notification_type, const CheckResult::Ptr& cr, + const User::Ptr& user, NotificationType notification_type, const CheckResult::Ptr& cr, const NotificationResult::Ptr& nr, const String& author, const String& comment_text, const String& command_name); void StateChangeHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type); void StateChangeHandlerInternal(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type); String ComposeGelfMessage(const Dictionary::Ptr& fields, const String& source, double ts); - void SendLogMessage(const String& gelfMessage); + void SendLogMessage(const Checkable::Ptr& checkable, const String& gelfMessage); void ReconnectTimerHandler(); void Disconnect(); + void DisconnectInternal(); void Reconnect(); + void ReconnectInternal(); void AssertOnWorkQueue(); diff --git a/lib/perfdata/gelfwriter.ti b/lib/perfdata/gelfwriter.ti index facc94a6d..2176fd877 100644 --- a/lib/perfdata/gelfwriter.ti +++ b/lib/perfdata/gelfwriter.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" @@ -45,6 +28,15 @@ class GelfWriter : ConfigObject [no_user_modify] bool should_connect { default {{{ return true; }}} }; + [config] bool enable_ha { + default {{{ return false; }}} + }; + [config] bool enable_tls { + default {{{ return false; }}} + }; + [config] String ca_path; + [config] String cert_path; + [config] String key_path; }; } diff --git a/lib/perfdata/graphitewriter.cpp b/lib/perfdata/graphitewriter.cpp index d878cf953..642926f66 100644 --- a/lib/perfdata/graphitewriter.cpp +++ b/lib/perfdata/graphitewriter.cpp @@ -1,25 +1,9 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "perfdata/graphitewriter.hpp" #include "perfdata/graphitewriter-ti.cpp" #include "icinga/service.hpp" +#include "icinga/checkcommand.hpp" #include "icinga/macroprocessor.hpp" #include "icinga/icingaapplication.hpp" #include "base/tcpsocket.hpp" @@ -44,13 +28,31 @@ REGISTER_TYPE(GraphiteWriter); REGISTER_STATSFUNCTION(GraphiteWriter, &GraphiteWriter::StatsFunc); +/* + * Enable HA capabilities once the config object is loaded. + */ void GraphiteWriter::OnConfigLoaded() { ObjectImpl::OnConfigLoaded(); m_WorkQueue.SetName("GraphiteWriter, " + GetName()); + + if (!GetEnableHa()) { + Log(LogDebug, "GraphiteWriter") + << "HA functionality disabled. Won't pause connection: " << GetName(); + + SetHAMode(HARunEverywhere); + } else { + SetHAMode(HARunOnce); + } } +/** + * Feature stats interface + * + * @param status Key value pairs for feature stats + * @param perfdata Array of PerfdataValue objects + */ void GraphiteWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata) { DictionaryData nodes; @@ -72,12 +74,15 @@ void GraphiteWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& status->Set("graphitewriter", new Dictionary(std::move(nodes))); } -void GraphiteWriter::Start(bool runtimeCreated) +/** + * Resume is equivalent to Start, but with HA capabilities to resume at runtime. + */ +void GraphiteWriter::Resume() { - ObjectImpl::Start(runtimeCreated); + ObjectImpl::Resume(); Log(LogInformation, "GraphiteWriter") - << "'" << GetName() << "' started."; + << "'" << GetName() << "' resumed."; /* Register exception handler for WQ tasks. */ m_WorkQueue.SetExceptionCallback(std::bind(&GraphiteWriter::ExceptionHandler, this, _1)); @@ -93,21 +98,47 @@ void GraphiteWriter::Start(bool runtimeCreated) Checkable::OnNewCheckResult.connect(std::bind(&GraphiteWriter::CheckResultHandler, this, _1, _2)); } -void GraphiteWriter::Stop(bool runtimeRemoved) +/** + * Pause is equivalent to Stop, but with HA capabilities to resume at runtime. + */ +void GraphiteWriter::Pause() { - Log(LogInformation, "GraphiteWriter") - << "'" << GetName() << "' stopped."; + m_ReconnectTimer.reset(); + + try { + ReconnectInternal(); + } catch (const std::exception&) { + Log(LogInformation, "GraphiteWriter") + << "'" << GetName() << "' paused. Unable to connect, not flushing buffers. Data may be lost on reload."; + + ObjectImpl::Pause(); + return; + } m_WorkQueue.Join(); + DisconnectInternal(); - ObjectImpl::Stop(runtimeRemoved); + Log(LogInformation, "GraphiteWriter") + << "'" << GetName() << "' paused."; + + ObjectImpl::Pause(); } +/** + * Check if method is called inside the WQ thread. + */ void GraphiteWriter::AssertOnWorkQueue() { ASSERT(m_WorkQueue.IsWorkerThread()); } +/** + * Exception handler for the WQ. + * + * Closes the connection if connected. + * + * @param exp Exception pointer + */ void GraphiteWriter::ExceptionHandler(boost::exception_ptr exp) { Log(LogCritical, "GraphiteWriter", "Exception during Graphite operation: Verify that your backend is operational!"); @@ -116,16 +147,34 @@ void GraphiteWriter::ExceptionHandler(boost::exception_ptr exp) << "Exception during Graphite operation: " << DiagnosticInformation(std::move(exp)); if (GetConnected()) { - m_Stream->Close(); + m_Stream->close(); SetConnected(false); } } +/** + * Reconnect method, stops when the feature is paused in HA zones. + * + * Called inside the WQ. + */ void GraphiteWriter::Reconnect() { AssertOnWorkQueue(); + if (IsPaused()) { + SetConnected(false); + return; + } + + ReconnectInternal(); +} + +/** + * Reconnect method, connects to a TCP Stream + */ +void GraphiteWriter::ReconnectInternal() +{ double startTime = Utility::GetTime(); CONTEXT("Reconnecting to Graphite '" + GetName() + "'"); @@ -135,20 +184,17 @@ void GraphiteWriter::Reconnect() if (GetConnected()) return; - TcpSocket::Ptr socket = new TcpSocket(); - Log(LogNotice, "GraphiteWriter") << "Reconnecting to Graphite on host '" << GetHost() << "' port '" << GetPort() << "'."; - try { - socket->Connect(GetHost(), GetPort()); - } catch (const std::exception& ex) { - Log(LogCritical, "GraphiteWriter") - << "Can't connect to Graphite on host '" << GetHost() << "' port '" << GetPort() << "'."; - throw ex; - } + m_Stream = std::make_shared(IoEngine::Get().GetIoContext()); - m_Stream = new NetworkStream(socket); + try { + icinga::Connect(m_Stream->lowest_layer(), GetHost(), GetPort()); + } catch (const std::exception& ex) { + Log(LogWarning, "GraphiteWriter") + << "Can't connect to Graphite on host '" << GetHost() << "' port '" << GetPort() << ".'"; + } SetConnected(true); @@ -156,34 +202,78 @@ void GraphiteWriter::Reconnect() << "Finished reconnecting to Graphite in " << std::setw(2) << Utility::GetTime() - startTime << " second(s)."; } +/** + * Reconnect handler called by the timer. + * + * Enqueues a reconnect task into the WQ. + */ void GraphiteWriter::ReconnectTimerHandler() { - m_WorkQueue.Enqueue(std::bind(&GraphiteWriter::Reconnect, this), PriorityNormal); + if (IsPaused()) + return; + + m_WorkQueue.Enqueue(std::bind(&GraphiteWriter::Reconnect, this), PriorityHigh); } +/** + * Disconnect the stream. + * + * Called inside the WQ. + */ void GraphiteWriter::Disconnect() { AssertOnWorkQueue(); + DisconnectInternal(); +} + +/** + * Disconnect the stream. + * + * Called outside the WQ. + */ +void GraphiteWriter::DisconnectInternal() +{ if (!GetConnected()) return; - m_Stream->Close(); + m_Stream->close(); SetConnected(false); } +/** + * Check result event handler, checks whether feature is not paused in HA setups. + * + * @param checkable Host/Service object + * @param cr Check result including performance data + */ void GraphiteWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { + if (IsPaused()) + return; + m_WorkQueue.Enqueue(std::bind(&GraphiteWriter::CheckResultHandlerInternal, this, checkable, cr)); } +/** + * Check result event handler, prepares metadata and perfdata values and calls Send*() + * + * Called inside the WQ. + * + * @param checkable Host/Service object + * @param cr Check result including performance data + */ void GraphiteWriter::CheckResultHandlerInternal(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { AssertOnWorkQueue(); CONTEXT("Processing check result for '" + checkable->GetName() + "'"); + /* TODO: Deal with missing connection here. Needs refactoring + * into parsing the actual performance data and then putting it + * into a queue for re-inserting. */ + if (!IcingaApplication::GetInstance()->GetEnablePerfdata() || !checkable->GetEnablePerfdata()) return; @@ -212,31 +302,41 @@ void GraphiteWriter::CheckResultHandlerInternal(const Checkable::Ptr& checkable, if (GetEnableSendMetadata()) { if (service) { - SendMetric(prefixMetadata, "state", service->GetState(), ts); + SendMetric(checkable, prefixMetadata, "state", service->GetState(), ts); } else { - SendMetric(prefixMetadata, "state", host->GetState(), ts); + SendMetric(checkable, prefixMetadata, "state", host->GetState(), ts); } - SendMetric(prefixMetadata, "current_attempt", checkable->GetCheckAttempt(), ts); - SendMetric(prefixMetadata, "max_check_attempts", checkable->GetMaxCheckAttempts(), ts); - SendMetric(prefixMetadata, "state_type", checkable->GetStateType(), ts); - SendMetric(prefixMetadata, "reachable", checkable->IsReachable(), ts); - SendMetric(prefixMetadata, "downtime_depth", checkable->GetDowntimeDepth(), ts); - SendMetric(prefixMetadata, "acknowledgement", checkable->GetAcknowledgement(), ts); - SendMetric(prefixMetadata, "latency", cr->CalculateLatency(), ts); - SendMetric(prefixMetadata, "execution_time", cr->CalculateExecutionTime(), ts); + SendMetric(checkable, prefixMetadata, "current_attempt", checkable->GetCheckAttempt(), ts); + SendMetric(checkable, prefixMetadata, "max_check_attempts", checkable->GetMaxCheckAttempts(), ts); + SendMetric(checkable, prefixMetadata, "state_type", checkable->GetStateType(), ts); + SendMetric(checkable, prefixMetadata, "reachable", checkable->IsReachable(), ts); + SendMetric(checkable, prefixMetadata, "downtime_depth", checkable->GetDowntimeDepth(), ts); + SendMetric(checkable, prefixMetadata, "acknowledgement", checkable->GetAcknowledgement(), ts); + SendMetric(checkable, prefixMetadata, "latency", cr->CalculateLatency(), ts); + SendMetric(checkable, prefixMetadata, "execution_time", cr->CalculateExecutionTime(), ts); } - SendPerfdata(prefixPerfdata, cr, ts); + SendPerfdata(checkable, prefixPerfdata, cr, ts); } -void GraphiteWriter::SendPerfdata(const String& prefix, const CheckResult::Ptr& cr, double ts) +/** + * Parse performance data from check result and call SendMetric() + * + * @param checkable Host/service object + * @param prefix Metric prefix string + * @param cr Check result including performance data + * @param ts Timestamp when the check result was created + */ +void GraphiteWriter::SendPerfdata(const Checkable::Ptr& checkable, const String& prefix, const CheckResult::Ptr& cr, double ts) { Array::Ptr perfdata = cr->GetPerformanceData(); if (!perfdata) return; + CheckCommand::Ptr checkCommand = checkable->GetCheckCommand(); + ObjectLock olock(perfdata); for (const Value& val : perfdata) { PerfdataValue::Ptr pdv; @@ -248,47 +348,60 @@ void GraphiteWriter::SendPerfdata(const String& prefix, const CheckResult::Ptr& pdv = PerfdataValue::Parse(val); } catch (const std::exception&) { Log(LogWarning, "GraphiteWriter") - << "Ignoring invalid perfdata value: " << val; + << "Ignoring invalid perfdata for checkable '" + << checkable->GetName() << "' and command '" + << checkCommand->GetName() << "' with value: " << val; continue; } } String escapedKey = EscapeMetricLabel(pdv->GetLabel()); - SendMetric(prefix, escapedKey + ".value", pdv->GetValue(), ts); + SendMetric(checkable, prefix, escapedKey + ".value", pdv->GetValue(), ts); if (GetEnableSendThresholds()) { if (pdv->GetCrit()) - SendMetric(prefix, escapedKey + ".crit", pdv->GetCrit(), ts); + SendMetric(checkable, prefix, escapedKey + ".crit", pdv->GetCrit(), ts); if (pdv->GetWarn()) - SendMetric(prefix, escapedKey + ".warn", pdv->GetWarn(), ts); + SendMetric(checkable, prefix, escapedKey + ".warn", pdv->GetWarn(), ts); if (pdv->GetMin()) - SendMetric(prefix, escapedKey + ".min", pdv->GetMin(), ts); + SendMetric(checkable, prefix, escapedKey + ".min", pdv->GetMin(), ts); if (pdv->GetMax()) - SendMetric(prefix, escapedKey + ".max", pdv->GetMax(), ts); + SendMetric(checkable, prefix, escapedKey + ".max", pdv->GetMax(), ts); } } } -void GraphiteWriter::SendMetric(const String& prefix, const String& name, double value, double ts) +/** + * Computes metric data and sends to Graphite + * + * @param checkable Host/service object + * @param prefix Computed metric prefix string + * @param name Metric name + * @param value Metric value + * @param ts Timestamp when the check result was created + */ +void GraphiteWriter::SendMetric(const Checkable::Ptr& checkable, const String& prefix, const String& name, double value, double ts) { + namespace asio = boost::asio; + std::ostringstream msgbuf; msgbuf << prefix << "." << name << " " << Convert::ToString(value) << " " << static_cast(ts); Log(LogDebug, "GraphiteWriter") - << "Add to metric list:'" << msgbuf.str() << "'."; + << "Checkable '" << checkable->GetName() << "' adds to metric list: '" << msgbuf.str() << "'."; // do not send \n to debug log msgbuf << "\n"; - String metric = msgbuf.str(); - ObjectLock olock(this); + boost::mutex::scoped_lock lock(m_StreamMutex); if (!GetConnected()) return; try { - m_Stream->Write(metric.CStr(), metric.GetLength()); + asio::write(*m_Stream, asio::buffer(msgbuf.str())); + m_Stream->flush(); } catch (const std::exception& ex) { Log(LogCritical, "GraphiteWriter") << "Cannot write to TCP socket on host '" << GetHost() << "' port '" << GetPort() << "'."; @@ -297,6 +410,14 @@ void GraphiteWriter::SendMetric(const String& prefix, const String& name, double } } +/** + * Escape metric tree elements + * + * Dots are not allowed, e.g. in host names + * + * @param str Metric part name + * @return Escape string + */ String GraphiteWriter::EscapeMetric(const String& str) { String result = str; @@ -310,6 +431,14 @@ String GraphiteWriter::EscapeMetric(const String& str) return result; } +/** + * Escape metric label + * + * Dots are allowed - users can create trees from perfdata labels + * + * @param str Metric label name + * @return Escaped string + */ String GraphiteWriter::EscapeMetricLabel(const String& str) { String result = str; @@ -323,6 +452,12 @@ String GraphiteWriter::EscapeMetricLabel(const String& str) return result; } +/** + * Escape macro metrics found via host/service name templates + * + * @param value Array or string with macro metric names + * @return Escaped string. Arrays are joined with dots. + */ Value GraphiteWriter::EscapeMacroMetric(const Value& value) { if (value.IsObjectType()) { @@ -339,6 +474,12 @@ Value GraphiteWriter::EscapeMacroMetric(const Value& value) return EscapeMetric(value); } +/** + * Validate the configuration setting 'host_name_template' + * + * @param lvalue String containing runtime macros. + * @param utils Helper, unused + */ void GraphiteWriter::ValidateHostNameTemplate(const Lazy& lvalue, const ValidationUtils& utils) { ObjectImpl::ValidateHostNameTemplate(lvalue, utils); @@ -347,6 +488,12 @@ void GraphiteWriter::ValidateHostNameTemplate(const Lazy& lvalue, const BOOST_THROW_EXCEPTION(ValidationError(this, { "host_name_template" }, "Closing $ not found in macro format string '" + lvalue() + "'.")); } +/** + * Validate the configuration setting 'service_name_template' + * + * @param lvalue String containing runtime macros. + * @param utils Helper, unused + */ void GraphiteWriter::ValidateServiceNameTemplate(const Lazy& lvalue, const ValidationUtils& utils) { ObjectImpl::ValidateServiceNameTemplate(lvalue, utils); diff --git a/lib/perfdata/graphitewriter.hpp b/lib/perfdata/graphitewriter.hpp index 86a517f96..42f741cee 100644 --- a/lib/perfdata/graphitewriter.hpp +++ b/lib/perfdata/graphitewriter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef GRAPHITEWRITER_H #define GRAPHITEWRITER_H @@ -27,6 +10,7 @@ #include "base/timer.hpp" #include "base/workqueue.hpp" #include +#include namespace icinga { @@ -49,19 +33,20 @@ public: protected: void OnConfigLoaded() override; - void Start(bool runtimeCreated) override; - void Stop(bool runtimeRemoved) override; + void Resume() override; + void Pause() override; private: - Stream::Ptr m_Stream; + std::shared_ptr m_Stream; + boost::mutex m_StreamMutex; WorkQueue m_WorkQueue{10000000, 1}; Timer::Ptr m_ReconnectTimer; void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); void CheckResultHandlerInternal(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); - void SendMetric(const String& prefix, const String& name, double value, double ts); - void SendPerfdata(const String& prefix, const CheckResult::Ptr& cr, double ts); + void SendMetric(const Checkable::Ptr& checkable, const String& prefix, const String& name, double value, double ts); + void SendPerfdata(const Checkable::Ptr& checkable, const String& prefix, const CheckResult::Ptr& cr, double ts); static String EscapeMetric(const String& str); static String EscapeMetricLabel(const String& str); static Value EscapeMacroMetric(const Value& value); @@ -69,7 +54,9 @@ private: void ReconnectTimerHandler(); void Disconnect(); + void DisconnectInternal(); void Reconnect(); + void ReconnectInternal(); void AssertOnWorkQueue(); diff --git a/lib/perfdata/graphitewriter.ti b/lib/perfdata/graphitewriter.ti index ca04959f5..c8db0673b 100644 --- a/lib/perfdata/graphitewriter.ti +++ b/lib/perfdata/graphitewriter.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" @@ -47,6 +30,9 @@ class GraphiteWriter : ConfigObject [no_user_modify] bool should_connect { default {{{ return true; }}} }; + [config] bool enable_ha { + default {{{ return false; }}} + }; }; } diff --git a/lib/perfdata/influxdbwriter.cpp b/lib/perfdata/influxdbwriter.cpp index 8a3fe97b1..1e02e5213 100644 --- a/lib/perfdata/influxdbwriter.cpp +++ b/lib/perfdata/influxdbwriter.cpp @@ -1,31 +1,15 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "perfdata/influxdbwriter.hpp" #include "perfdata/influxdbwriter-ti.cpp" #include "remote/url.hpp" -#include "remote/httprequest.hpp" -#include "remote/httpresponse.hpp" #include "icinga/service.hpp" #include "icinga/macroprocessor.hpp" #include "icinga/icingaapplication.hpp" #include "icinga/checkcommand.hpp" +#include "base/application.hpp" +#include "base/defer.hpp" +#include "base/io-engine.hpp" #include "base/tcpsocket.hpp" #include "base/configtype.hpp" #include "base/objectlock.hpp" @@ -41,9 +25,21 @@ #include "base/tlsutility.hpp" #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include +#include +#include #include using namespace icinga; @@ -75,6 +71,15 @@ void InfluxdbWriter::OnConfigLoaded() ObjectImpl::OnConfigLoaded(); m_WorkQueue.SetName("InfluxdbWriter, " + GetName()); + + if (!GetEnableHa()) { + Log(LogDebug, "InfluxdbWriter") + << "HA functionality disabled. Won't pause connection: " << GetName(); + + SetHAMode(HARunEverywhere); + } else { + SetHAMode(HARunOnce); + } } void InfluxdbWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata) @@ -100,12 +105,12 @@ void InfluxdbWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& status->Set("influxdbwriter", new Dictionary(std::move(nodes))); } -void InfluxdbWriter::Start(bool runtimeCreated) +void InfluxdbWriter::Resume() { - ObjectImpl::Start(runtimeCreated); + ObjectImpl::Resume(); Log(LogInformation, "InfluxdbWriter") - << "'" << GetName() << "' started."; + << "'" << GetName() << "' resumed."; /* Register exception handler for WQ tasks. */ m_WorkQueue.SetExceptionCallback(std::bind(&InfluxdbWriter::ExceptionHandler, this, _1)); @@ -121,14 +126,31 @@ void InfluxdbWriter::Start(bool runtimeCreated) Checkable::OnNewCheckResult.connect(std::bind(&InfluxdbWriter::CheckResultHandler, this, _1, _2)); } -void InfluxdbWriter::Stop(bool runtimeRemoved) +/* Pause is equivalent to Stop, but with HA capabilities to resume at runtime. */ +void InfluxdbWriter::Pause() { - Log(LogInformation, "InfluxdbWriter") - << "'" << GetName() << "' stopped."; + /* Force a flush. */ + Log(LogDebug, "InfluxdbWriter") + << "Flushing pending data buffers."; + + Flush(); + + /* Work on the missing tasks. TODO: Find a way to cache them on disk. */ + Log(LogDebug, "InfluxdbWriter") + << "Joining existing WQ tasks."; m_WorkQueue.Join(); - ObjectImpl::Stop(runtimeRemoved); + /* Flush again after the WQ tasks have filled the data buffer. */ + Log(LogDebug, "InfluxdbWriter") + << "Flushing data buffers from WQ tasks."; + + Flush(); + + Log(LogInformation, "InfluxdbWriter") + << "'" << GetName() << "' paused."; + + ObjectImpl::Pause(); } void InfluxdbWriter::AssertOnWorkQueue() @@ -146,48 +168,58 @@ void InfluxdbWriter::ExceptionHandler(boost::exception_ptr exp) //TODO: Close the connection, if we keep it open. } -Stream::Ptr InfluxdbWriter::Connect() +OptionalTlsStream InfluxdbWriter::Connect() { - TcpSocket::Ptr socket = new TcpSocket(); - Log(LogNotice, "InfluxdbWriter") << "Reconnecting to InfluxDB on host '" << GetHost() << "' port '" << GetPort() << "'."; - try { - socket->Connect(GetHost(), GetPort()); - } catch (const std::exception& ex) { - Log(LogWarning, "InfluxdbWriter") - << "Can't connect to InfluxDB on host '" << GetHost() << "' port '" << GetPort() << "'."; - throw ex; - } + OptionalTlsStream stream; + bool ssl = GetSslEnable(); + + if (ssl) { + std::shared_ptr sslContext; - if (GetSslEnable()) { - std::shared_ptr sslContext; try { - sslContext = MakeSSLContext(GetSslCert(), GetSslKey(), GetSslCaCert()); + sslContext = MakeAsioSslContext(GetSslCert(), GetSslKey(), GetSslCaCert()); } catch (const std::exception& ex) { Log(LogWarning, "InfluxdbWriter") << "Unable to create SSL context."; - throw ex; + throw; } - TlsStream::Ptr tlsStream = new TlsStream(socket, GetHost(), RoleClient, sslContext); + stream.first = std::make_shared(IoEngine::Get().GetIoContext(), *sslContext, GetHost()); + } else { + stream.second = std::make_shared(IoEngine::Get().GetIoContext()); + } + + try { + icinga::Connect(ssl ? stream.first->lowest_layer() : stream.second->lowest_layer(), GetHost(), GetPort()); + } catch (const std::exception& ex) { + Log(LogWarning, "InfluxdbWriter") + << "Can't connect to InfluxDB on host '" << GetHost() << "' port '" << GetPort() << "'."; + throw; + } + + if (ssl) { + auto& tlsStream (stream.first->next_layer()); + try { - tlsStream->Handshake(); + tlsStream.handshake(tlsStream.client); } catch (const std::exception& ex) { Log(LogWarning, "InfluxdbWriter") << "TLS handshake with host '" << GetHost() << "' failed."; - throw ex; + throw; } - - return tlsStream; - } else { - return new NetworkStream(socket); } + + return std::move(stream); } void InfluxdbWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { + if (IsPaused()) + return; + m_WorkQueue.Enqueue(std::bind(&InfluxdbWriter::CheckResultHandlerWQ, this, checkable, cr), PriorityLow); } @@ -216,24 +248,32 @@ void InfluxdbWriter::CheckResultHandlerWQ(const Checkable::Ptr& checkable, const // Clone the template and perform an in-place macro expansion of measurement and tag values Dictionary::Ptr tmpl_clean = service ? GetServiceTemplate() : GetHostTemplate(); - Dictionary::Ptr tmpl = static_pointer_cast(tmpl_clean->Clone()); + Dictionary::Ptr tmpl = static_pointer_cast(tmpl_clean->ShallowClone()); tmpl->Set("measurement", MacroProcessor::ResolveMacros(tmpl->Get("measurement"), resolvers, cr)); - Dictionary::Ptr tags = tmpl->Get("tags"); - if (tags) { - ObjectLock olock(tags); - for (const Dictionary::Pair& pair : tags) { - String missing_macro; - Value value = MacroProcessor::ResolveMacros(pair.second, resolvers, cr, &missing_macro); + Dictionary::Ptr tagsClean = tmpl->Get("tags"); + if (tagsClean) { + Dictionary::Ptr tags = new Dictionary(); - if (!missing_macro.IsEmpty()) - continue; + { + ObjectLock olock(tagsClean); + for (const Dictionary::Pair& pair : tagsClean) { + String missing_macro; + Value value = MacroProcessor::ResolveMacros(pair.second, resolvers, cr, &missing_macro); - tags->Set(pair.first, value); + if (missing_macro.IsEmpty()) { + tags->Set(pair.first, value); + } + } } + + tmpl->Set("tags", tags); } + CheckCommand::Ptr checkCommand = checkable->GetCheckCommand(); + Array::Ptr perfdata = cr->GetPerformanceData(); + if (perfdata) { ObjectLock olock(perfdata); for (const Value& val : perfdata) { @@ -246,7 +286,9 @@ void InfluxdbWriter::CheckResultHandlerWQ(const Checkable::Ptr& checkable, const pdv = PerfdataValue::Parse(val); } catch (const std::exception&) { Log(LogWarning, "InfluxdbWriter") - << "Ignoring invalid perfdata value: " << val; + << "Ignoring invalid perfdata for checkable '" + << checkable->GetName() << "' and command '" + << checkCommand->GetName() << "' with value: " << val; continue; } } @@ -268,7 +310,7 @@ void InfluxdbWriter::CheckResultHandlerWQ(const Checkable::Ptr& checkable, const fields->Set("unit", pdv->GetUnit()); } - SendMetric(tmpl, pdv->GetLabel(), fields, ts); + SendMetric(checkable, tmpl, pdv->GetLabel(), fields, ts); } } @@ -293,7 +335,7 @@ void InfluxdbWriter::CheckResultHandlerWQ(const Checkable::Ptr& checkable, const fields->Set("latency", cr->CalculateLatency()); fields->Set("execution_time", cr->CalculateExecutionTime()); - SendMetric(tmpl, Empty, fields, ts); + SendMetric(checkable, tmpl, Empty, fields, ts); } } @@ -336,7 +378,8 @@ String InfluxdbWriter::EscapeValue(const Value& value) return value; } -void InfluxdbWriter::SendMetric(const Dictionary::Ptr& tmpl, const String& label, const Dictionary::Ptr& fields, double ts) +void InfluxdbWriter::SendMetric(const Checkable::Ptr& checkable, const Dictionary::Ptr& tmpl, + const String& label, const Dictionary::Ptr& fields, double ts) { std::ostringstream msgbuf; msgbuf << EscapeKeyOrTagValue(tmpl->Get("measurement")); @@ -374,10 +417,8 @@ void InfluxdbWriter::SendMetric(const Dictionary::Ptr& tmpl, const String& label msgbuf << " " << static_cast(ts); -#ifdef I2_DEBUG Log(LogDebug, "InfluxdbWriter") - << "Add to metric list: '" << msgbuf.str() << "'."; -#endif /* I2_DEBUG */ + << "Checkable '" << checkable->GetName() << "' adds to metric list:'" << msgbuf.str() << "'."; // Buffer the data point m_DataBuffer.emplace_back(msgbuf.str()); @@ -404,10 +445,6 @@ void InfluxdbWriter::FlushTimeoutWQ() { AssertOnWorkQueue(); - // Flush if there are any data available - if (m_DataBuffer.empty()) - return; - Log(LogDebug, "InfluxdbWriter") << "Timer expired writing " << m_DataBuffer.size() << " data points"; @@ -416,13 +453,34 @@ void InfluxdbWriter::FlushTimeoutWQ() void InfluxdbWriter::Flush() { + namespace beast = boost::beast; + namespace http = beast::http; + + /* Flush can be called from 1) Timeout 2) Threshold 3) on shutdown/reload. */ + if (m_DataBuffer.empty()) + return; + + Log(LogDebug, "InfluxdbWriter") + << "Flushing data buffer to InfluxDB."; + String body = boost::algorithm::join(m_DataBuffer, "\n"); m_DataBuffer.clear(); - Stream::Ptr stream = Connect(); + OptionalTlsStream stream; - if (!stream) + try { + stream = Connect(); + } catch (const std::exception& ex) { + Log(LogWarning, "InfluxDbWriter") + << "Flush failed, cannot connect to InfluxDB: " << DiagnosticInformation(ex, false); return; + } + + Defer s ([&stream]() { + if (stream.first) { + stream.first->next_layer().shutdown(); + } + }); Url::Ptr url = new Url(); url->SetScheme(GetSslEnable() ? "https" : "http"); @@ -440,59 +498,64 @@ void InfluxdbWriter::Flush() if (!GetPassword().IsEmpty()) url->AddQueryElement("p", GetPassword()); - HttpRequest req(stream); - req.RequestMethod = "POST"; - req.RequestUrl = url; + http::request request (http::verb::post, std::string(url->Format(true)), 10); + + request.set(http::field::user_agent, "Icinga/" + Application::GetAppVersion()); + request.set(http::field::host, url->GetHost() + ":" + url->GetPort()); + + request.body() = body; + request.set(http::field::content_length, request.body().size()); try { - req.WriteBody(body.CStr(), body.GetLength()); - req.Finish(); + if (stream.first) { + http::write(*stream.first, request); + stream.first->flush(); + } else { + http::write(*stream.second, request); + stream.second->flush(); + } } catch (const std::exception& ex) { Log(LogWarning, "InfluxdbWriter") << "Cannot write to TCP socket on host '" << GetHost() << "' port '" << GetPort() << "'."; - throw ex; + throw; } - HttpResponse resp(stream, req); - StreamReadContext context; + http::parser parser; + beast::flat_buffer buf; try { - while (resp.Parse(context, true) && !resp.Complete) - ; /* Do nothing */ + if (stream.first) { + http::read(*stream.first, buf, parser); + } else { + http::read(*stream.second, buf, parser); + } } catch (const std::exception& ex) { Log(LogWarning, "InfluxdbWriter") << "Failed to parse HTTP response from host '" << GetHost() << "' port '" << GetPort() << "': " << DiagnosticInformation(ex); - throw ex; + throw; } - if (!resp.Complete) { - Log(LogWarning, "InfluxdbWriter") - << "Failed to read a complete HTTP response from the InfluxDB server."; - return; - } + auto& response (parser.get()); - if (resp.StatusCode != 204) { + if (response.result() != http::status::no_content) { Log(LogWarning, "InfluxdbWriter") - << "Unexpected response code: " << resp.StatusCode; + << "Unexpected response code: " << response.result(); - String contentType = resp.Headers->Get("content-type"); + auto& contentType (response[http::field::content_type]); if (contentType != "application/json") { Log(LogWarning, "InfluxdbWriter") << "Unexpected Content-Type: " << contentType; return; } - size_t responseSize = resp.GetBodySize(); - boost::scoped_array buffer(new char[responseSize + 1]); - resp.ReadBody(buffer.get(), responseSize); - buffer.get()[responseSize] = '\0'; - Dictionary::Ptr jsonResponse; + auto& body (response.body()); + try { - jsonResponse = JsonDecode(buffer.get()); + jsonResponse = JsonDecode(body); } catch (...) { Log(LogWarning, "InfluxdbWriter") - << "Unable to parse JSON response:\n" << buffer.get(); + << "Unable to parse JSON response:\n" << body; return; } @@ -500,8 +563,6 @@ void InfluxdbWriter::Flush() Log(LogCritical, "InfluxdbWriter") << "InfluxDB error message:\n" << error; - - return; } } diff --git a/lib/perfdata/influxdbwriter.hpp b/lib/perfdata/influxdbwriter.hpp index 4d3e8037a..1f7ab8309 100644 --- a/lib/perfdata/influxdbwriter.hpp +++ b/lib/perfdata/influxdbwriter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef INFLUXDBWRITER_H #define INFLUXDBWRITER_H @@ -25,6 +8,7 @@ #include "base/configobject.hpp" #include "base/tcpsocket.hpp" #include "base/timer.hpp" +#include "base/tlsstream.hpp" #include "base/workqueue.hpp" #include @@ -49,8 +33,8 @@ public: protected: void OnConfigLoaded() override; - void Start(bool runtimeCreated) override; - void Stop(bool runtimeRemoved) override; + void Resume() override; + void Pause() override; private: WorkQueue m_WorkQueue{10000000, 1}; @@ -59,7 +43,8 @@ private: void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); void CheckResultHandlerWQ(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); - void SendMetric(const Dictionary::Ptr& tmpl, const String& label, const Dictionary::Ptr& fields, double ts); + void SendMetric(const Checkable::Ptr& checkable, const Dictionary::Ptr& tmpl, + const String& label, const Dictionary::Ptr& fields, double ts); void FlushTimeout(); void FlushTimeoutWQ(); void Flush(); @@ -67,7 +52,7 @@ private: static String EscapeKeyOrTagValue(const String& str); static String EscapeValue(const Value& value); - Stream::Ptr Connect(); + OptionalTlsStream Connect(); void AssertOnWorkQueue(); diff --git a/lib/perfdata/influxdbwriter.ti b/lib/perfdata/influxdbwriter.ti index 5ff116754..377c911ba 100644 --- a/lib/perfdata/influxdbwriter.ti +++ b/lib/perfdata/influxdbwriter.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" @@ -88,6 +71,9 @@ class InfluxdbWriter : ConfigObject [config] int flush_threshold { default {{{ return 1024; }}} }; + [config] bool enable_ha { + default {{{ return false; }}} + }; }; validator InfluxdbWriter { diff --git a/lib/perfdata/opentsdbwriter.cpp b/lib/perfdata/opentsdbwriter.cpp index c55529ffa..b936b3083 100644 --- a/lib/perfdata/opentsdbwriter.cpp +++ b/lib/perfdata/opentsdbwriter.cpp @@ -1,25 +1,9 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "perfdata/opentsdbwriter.hpp" #include "perfdata/opentsdbwriter-ti.cpp" #include "icinga/service.hpp" +#include "icinga/checkcommand.hpp" #include "icinga/macroprocessor.hpp" #include "icinga/icingaapplication.hpp" #include "icinga/compatutility.hpp" @@ -44,23 +28,50 @@ REGISTER_TYPE(OpenTsdbWriter); REGISTER_STATSFUNCTION(OpenTsdbWriter, &OpenTsdbWriter::StatsFunc); +/* + * Enable HA capabilities once the config object is loaded. + */ +void OpenTsdbWriter::OnConfigLoaded() +{ + ObjectImpl::OnConfigLoaded(); + + if (!GetEnableHa()) { + Log(LogDebug, "OpenTsdbWriter") + << "HA functionality disabled. Won't pause connection: " << GetName(); + + SetHAMode(HARunEverywhere); + } else { + SetHAMode(HARunOnce); + } +} + +/** + * Feature stats interface + * + * @param status Key value pairs for feature stats + */ void OpenTsdbWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&) { DictionaryData nodes; for (const OpenTsdbWriter::Ptr& opentsdbwriter : ConfigType::GetObjectsByType()) { - nodes.emplace_back(opentsdbwriter->GetName(), 1); //add more stats + nodes.emplace_back(opentsdbwriter->GetName(), new Dictionary({ + { "connected", opentsdbwriter->GetConnected() } + })); } status->Set("opentsdbwriter", new Dictionary(std::move(nodes))); } -void OpenTsdbWriter::Start(bool runtimeCreated) +/** + * Resume is equivalent to Start, but with HA capabilities to resume at runtime. + */ +void OpenTsdbWriter::Resume() { - ObjectImpl::Start(runtimeCreated); + ObjectImpl::Resume(); Log(LogInformation, "OpentsdbWriter") - << "'" << GetName() << "' started."; + << "'" << GetName() << "' resumed."; m_ReconnectTimer = new Timer(); m_ReconnectTimer->SetInterval(10); @@ -71,37 +82,69 @@ void OpenTsdbWriter::Start(bool runtimeCreated) Service::OnNewCheckResult.connect(std::bind(&OpenTsdbWriter::CheckResultHandler, this, _1, _2)); } -void OpenTsdbWriter::Stop(bool runtimeRemoved) +/** + * Pause is equivalent to Stop, but with HA capabilities to resume at runtime. + */ +void OpenTsdbWriter::Pause() { - Log(LogInformation, "OpentsdbWriter") - << "'" << GetName() << "' stopped."; + m_ReconnectTimer.reset(); - ObjectImpl::Stop(runtimeRemoved); + Log(LogInformation, "OpentsdbWriter") + << "'" << GetName() << "' paused."; + + m_Stream->close(); + + SetConnected(false); + + ObjectImpl::Pause(); } +/** + * Reconnect handler called by the timer. + * Handles TLS + */ void OpenTsdbWriter::ReconnectTimerHandler() { - if (m_Stream) + if (IsPaused()) return; - TcpSocket::Ptr socket = new TcpSocket(); + SetShouldConnect(true); + + if (GetConnected()) + return; Log(LogNotice, "OpenTsdbWriter") << "Reconnect to OpenTSDB TSD on host '" << GetHost() << "' port '" << GetPort() << "'."; + /* + * We're using telnet as input method. Future PRs may change this into using the HTTP API. + * http://opentsdb.net/docs/build/html/user_guide/writing/index.html#telnet + */ + + m_Stream = std::make_shared(IoEngine::Get().GetIoContext()); + try { - socket->Connect(GetHost(), GetPort()); - } catch (std::exception&) { - Log(LogCritical, "OpenTsdbWriter") - << "Can't connect to OpenTSDB TSD on host '" << GetHost() << "' port '" << GetPort() << "'."; - return; + icinga::Connect(m_Stream->lowest_layer(), GetHost(), GetPort()); + } catch (const std::exception& ex) { + Log(LogWarning, "OpenTsdbWriter") + << "Can't connect to OpenTSDB on host '" << GetHost() << "' port '" << GetPort() << ".'"; } - m_Stream = new NetworkStream(socket); + SetConnected(true); } +/** + * Registered check result handler processing data. + * Calculates tags from the config. + * + * @param checkable Host/service object + * @param cr Check result + */ void OpenTsdbWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { + if (IsPaused()) + return; + CONTEXT("Processing check result for '" + checkable->GetName() + "'"); if (!IcingaApplication::GetInstance()->GetEnablePerfdata() || !checkable->GetEnablePerfdata()) @@ -128,18 +171,18 @@ void OpenTsdbWriter::CheckResultHandler(const Checkable::Ptr& checkable, const C String escaped_serviceName = EscapeMetric(serviceName); metric = "icinga.service." + escaped_serviceName; - SendMetric(metric + ".state", tags, service->GetState(), ts); + SendMetric(checkable, metric + ".state", tags, service->GetState(), ts); } else { metric = "icinga.host"; - SendMetric(metric + ".state", tags, host->GetState(), ts); + SendMetric(checkable, metric + ".state", tags, host->GetState(), ts); } - SendMetric(metric + ".state_type", tags, checkable->GetStateType(), ts); - SendMetric(metric + ".reachable", tags, checkable->IsReachable(), ts); - SendMetric(metric + ".downtime_depth", tags, checkable->GetDowntimeDepth(), ts); - SendMetric(metric + ".acknowledgement", tags, checkable->GetAcknowledgement(), ts); + SendMetric(checkable, metric + ".state_type", tags, checkable->GetStateType(), ts); + SendMetric(checkable, metric + ".reachable", tags, checkable->IsReachable(), ts); + SendMetric(checkable, metric + ".downtime_depth", tags, checkable->GetDowntimeDepth(), ts); + SendMetric(checkable, metric + ".acknowledgement", tags, checkable->GetAcknowledgement(), ts); - SendPerfdata(metric, tags, cr, ts); + SendPerfdata(checkable, metric, tags, cr, ts); metric = "icinga.check"; @@ -152,19 +195,31 @@ void OpenTsdbWriter::CheckResultHandler(const Checkable::Ptr& checkable, const C tags["type"] = "host"; } - SendMetric(metric + ".current_attempt", tags, checkable->GetCheckAttempt(), ts); - SendMetric(metric + ".max_check_attempts", tags, checkable->GetMaxCheckAttempts(), ts); - SendMetric(metric + ".latency", tags, cr->CalculateLatency(), ts); - SendMetric(metric + ".execution_time", tags, cr->CalculateExecutionTime(), ts); + SendMetric(checkable, metric + ".current_attempt", tags, checkable->GetCheckAttempt(), ts); + SendMetric(checkable, metric + ".max_check_attempts", tags, checkable->GetMaxCheckAttempts(), ts); + SendMetric(checkable, metric + ".latency", tags, cr->CalculateLatency(), ts); + SendMetric(checkable, metric + ".execution_time", tags, cr->CalculateExecutionTime(), ts); } -void OpenTsdbWriter::SendPerfdata(const String& metric, const std::map& tags, const CheckResult::Ptr& cr, double ts) +/** + * Parse and send performance data metrics to OpenTSDB + * + * @param checkable Host/service object + * @param metric Full metric name + * @param tags Tag key pairs + * @param cr Check result containing performance data + * @param ts Timestamp when the check result was received + */ +void OpenTsdbWriter::SendPerfdata(const Checkable::Ptr& checkable, const String& metric, + const std::map& tags, const CheckResult::Ptr& cr, double ts) { Array::Ptr perfdata = cr->GetPerformanceData(); if (!perfdata) return; + CheckCommand::Ptr checkCommand = checkable->GetCheckCommand(); + ObjectLock olock(perfdata); for (const Value& val : perfdata) { PerfdataValue::Ptr pdv; @@ -176,7 +231,9 @@ void OpenTsdbWriter::SendPerfdata(const String& metric, const std::mapGetName() << "' and command '" + << checkCommand->GetName() << "' with value: " << val; continue; } } @@ -184,36 +241,47 @@ void OpenTsdbWriter::SendPerfdata(const String& metric, const std::mapGetLabel()); boost::algorithm::replace_all(escaped_key, "::", "."); - SendMetric(metric + "." + escaped_key, tags, pdv->GetValue(), ts); + SendMetric(checkable, metric + "." + escaped_key, tags, pdv->GetValue(), ts); if (pdv->GetCrit()) - SendMetric(metric + "." + escaped_key + "_crit", tags, pdv->GetCrit(), ts); + SendMetric(checkable, metric + "." + escaped_key + "_crit", tags, pdv->GetCrit(), ts); if (pdv->GetWarn()) - SendMetric(metric + "." + escaped_key + "_warn", tags, pdv->GetWarn(), ts); + SendMetric(checkable, metric + "." + escaped_key + "_warn", tags, pdv->GetWarn(), ts); if (pdv->GetMin()) - SendMetric(metric + "." + escaped_key + "_min", tags, pdv->GetMin(), ts); + SendMetric(checkable, metric + "." + escaped_key + "_min", tags, pdv->GetMin(), ts); if (pdv->GetMax()) - SendMetric(metric + "." + escaped_key + "_max", tags, pdv->GetMax(), ts); + SendMetric(checkable, metric + "." + escaped_key + "_max", tags, pdv->GetMax(), ts); } } -void OpenTsdbWriter::SendMetric(const String& metric, const std::map& tags, double value, double ts) +/** + * Send given metric to OpenTSDB + * + * @param checkable Host/service object + * @param metric Full metric name + * @param tags Tag key pairs + * @param value Floating point metric value + * @param ts Timestamp where the metric was received from the check result + */ +void OpenTsdbWriter::SendMetric(const Checkable::Ptr& checkable, const String& metric, + const std::map& tags, double value, double ts) { String tags_string = ""; + for (const Dictionary::Pair& tag : tags) { tags_string += " " + tag.first + "=" + Convert::ToString(tag.second); } std::ostringstream msgbuf; /* - * must be (http://opentsdb.net/docs/build/html/user_guide/writing.html) + * must be (http://opentsdb.net/docs/build/html/user_guide/query/timeseries.html) * put * "tags" must include at least one tag, we use "host=HOSTNAME" */ msgbuf << "put " << metric << " " << static_cast(ts) << " " << Convert::ToString(value) << " " << tags_string; Log(LogDebug, "OpenTsdbWriter") - << "Add to metric list:'" << msgbuf.str() << "'."; + << "Checkable '" << checkable->GetName() << "' adds to metric list: '" << msgbuf.str() << "'."; /* do not send \n to debug log */ msgbuf << "\n"; @@ -221,21 +289,27 @@ void OpenTsdbWriter::SendMetric(const String& metric, const std::mapWrite(put.CStr(), put.GetLength()); + Log(LogDebug, "OpenTsdbWriter") + << "Checkable '" << checkable->GetName() << "' sending message '" << put << "'."; + + boost::asio::write(*m_Stream, boost::asio::buffer(msgbuf.str())); + m_Stream->flush(); } catch (const std::exception& ex) { Log(LogCritical, "OpenTsdbWriter") - << "Cannot write to OpenTSDB TSD on host '" << GetHost() << "' port '" << GetPort() + "'."; - - m_Stream.reset(); + << "Cannot write to TCP socket on host '" << GetHost() << "' port '" << GetPort() << "'."; } } -/* for metric and tag name rules, see - * http://opentsdb.net/docs/build/html/user_guide/writing.html#metrics-and-tags +/** + * Escape tags for OpenTSDB + * http://opentsdb.net/docs/build/html/user_guide/query/timeseries.html#precisions-on-metrics-and-tags + * + * @param str Tag name + * @return Escaped tag */ String OpenTsdbWriter::EscapeTag(const String& str) { @@ -247,6 +321,13 @@ String OpenTsdbWriter::EscapeTag(const String& str) return result; } +/** + * Escape metric name for OpenTSDB + * http://opentsdb.net/docs/build/html/user_guide/query/timeseries.html#precisions-on-metrics-and-tags + * + * @param str Metric name + * @return Escaped metric + */ String OpenTsdbWriter::EscapeMetric(const String& str) { String result = str; diff --git a/lib/perfdata/opentsdbwriter.hpp b/lib/perfdata/opentsdbwriter.hpp index f368918e6..bf0fc8eb1 100644 --- a/lib/perfdata/opentsdbwriter.hpp +++ b/lib/perfdata/opentsdbwriter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef OPENTSDBWRITER_H #define OPENTSDBWRITER_H @@ -44,17 +27,20 @@ public: static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata); protected: - void Start(bool runtimeCreated) override; - void Stop(bool runtimeRemoved) override; + void OnConfigLoaded() override; + void Resume() override; + void Pause() override; private: - Stream::Ptr m_Stream; + std::shared_ptr m_Stream; Timer::Ptr m_ReconnectTimer; void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); - void SendMetric(const String& metric, const std::map& tags, double value, double ts); - void SendPerfdata(const String& metric, const std::map& tags, const CheckResult::Ptr& cr, double ts); + void SendMetric(const Checkable::Ptr& checkable, const String& metric, + const std::map& tags, double value, double ts); + void SendPerfdata(const Checkable::Ptr& checkable, const String& metric, + const std::map& tags, const CheckResult::Ptr& cr, double ts); static String EscapeTag(const String& str); static String EscapeMetric(const String& str); diff --git a/lib/perfdata/opentsdbwriter.ti b/lib/perfdata/opentsdbwriter.ti index ea9455b9e..de19a1eac 100644 --- a/lib/perfdata/opentsdbwriter.ti +++ b/lib/perfdata/opentsdbwriter.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" @@ -34,6 +17,14 @@ class OpenTsdbWriter : ConfigObject [config] String port { default {{{ return "4242"; }}} }; + [config] bool enable_ha { + default {{{ return false; }}} + }; + + [no_user_modify] bool connected; + [no_user_modify] bool should_connect { + default {{{ return true; }}} + }; }; } diff --git a/lib/perfdata/perfdatawriter.cpp b/lib/perfdata/perfdatawriter.cpp index 0d2f979be..91a220612 100644 --- a/lib/perfdata/perfdatawriter.cpp +++ b/lib/perfdata/perfdatawriter.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "perfdata/perfdatawriter.hpp" #include "perfdata/perfdatawriter-ti.cpp" @@ -38,6 +21,20 @@ REGISTER_TYPE(PerfdataWriter); REGISTER_STATSFUNCTION(PerfdataWriter, &PerfdataWriter::StatsFunc); +void PerfdataWriter::OnConfigLoaded() +{ + ObjectImpl::OnConfigLoaded(); + + if (!GetEnableHa()) { + Log(LogDebug, "PerfdataWriter") + << "HA functionality disabled. Won't pause connection: " << GetName(); + + SetHAMode(HARunEverywhere); + } else { + SetHAMode(HARunOnce); + } +} + void PerfdataWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&) { DictionaryData nodes; @@ -49,12 +46,12 @@ void PerfdataWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&) status->Set("perfdatawriter", new Dictionary(std::move(nodes))); } -void PerfdataWriter::Start(bool runtimeCreated) +void PerfdataWriter::Resume() { - ObjectImpl::Start(runtimeCreated); + ObjectImpl::Resume(); Log(LogInformation, "PerfdataWriter") - << "'" << GetName() << "' started."; + << "'" << GetName() << "' resumed."; Checkable::OnNewCheckResult.connect(std::bind(&PerfdataWriter::CheckResultHandler, this, _1, _2)); @@ -67,12 +64,22 @@ void PerfdataWriter::Start(bool runtimeCreated) RotateFile(m_HostOutputFile, GetHostTempPath(), GetHostPerfdataPath()); } -void PerfdataWriter::Stop(bool runtimeRemoved) +void PerfdataWriter::Pause() { - Log(LogInformation, "PerfdataWriter") - << "'" << GetName() << "' stopped."; + m_RotationTimer.reset(); - ObjectImpl::Stop(runtimeRemoved); +#ifdef I2_DEBUG + //m_HostOutputFile << "\n# Pause the feature" << "\n\n"; + //m_ServiceOutputFile << "\n# Pause the feature" << "\n\n"; +#endif /* I2_DEBUG */ + + /* Force a rotation closing the file stream. */ + RotateAllFiles(); + + Log(LogInformation, "PerfdataWriter") + << "'" << GetName() << "' paused."; + + ObjectImpl::Pause(); } Value PerfdataWriter::EscapeMacroMetric(const Value& value) @@ -85,6 +92,9 @@ Value PerfdataWriter::EscapeMacroMetric(const Value& value) void PerfdataWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { + if (IsPaused()) + return; + CONTEXT("Writing performance data for object '" + checkable->GetName() + "'"); if (!IcingaApplication::GetInstance()->GetEnablePerfdata() || !checkable->GetEnablePerfdata()) @@ -108,7 +118,8 @@ void PerfdataWriter::CheckResultHandler(const Checkable::Ptr& checkable, const C String line = MacroProcessor::ResolveMacros(GetServiceFormatTemplate(), resolvers, cr, nullptr, &PerfdataWriter::EscapeMacroMetric); { - ObjectLock olock(this); + boost::mutex::scoped_lock lock(m_StreamMutex); + if (!m_ServiceOutputFile.good()) return; @@ -118,7 +129,8 @@ void PerfdataWriter::CheckResultHandler(const Checkable::Ptr& checkable, const C String line = MacroProcessor::ResolveMacros(GetHostFormatTemplate(), resolvers, cr, nullptr, &PerfdataWriter::EscapeMacroMetric); { - ObjectLock olock(this); + boost::mutex::scoped_lock lock(m_StreamMutex); + if (!m_HostOutputFile.good()) return; @@ -129,30 +141,41 @@ void PerfdataWriter::CheckResultHandler(const Checkable::Ptr& checkable, const C void PerfdataWriter::RotateFile(std::ofstream& output, const String& temp_path, const String& perfdata_path) { - ObjectLock olock(this); + Log(LogDebug, "PerfdataWriter") + << "Rotating perfdata files."; + + boost::mutex::scoped_lock lock(m_StreamMutex); if (output.good()) { output.close(); if (Utility::PathExists(temp_path)) { String finalFile = perfdata_path + "." + Convert::ToString((long)Utility::GetTime()); - 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)); - } + + Log(LogDebug, "PerfdataWriter") + << "Closed output file and renaming into '" << finalFile << "'."; + + Utility::RenameFile(temp_path, finalFile); } } output.open(temp_path.CStr()); - if (!output.good()) + if (!output.good()) { Log(LogWarning, "PerfdataWriter") << "Could not open perfdata file '" << temp_path << "' for writing. Perfdata will be lost."; + } } void PerfdataWriter::RotationTimerHandler() +{ + if (IsPaused()) + return; + + RotateAllFiles(); +} + +void PerfdataWriter::RotateAllFiles() { RotateFile(m_ServiceOutputFile, GetServiceTempPath(), GetServicePerfdataPath()); RotateFile(m_HostOutputFile, GetHostTempPath(), GetHostPerfdataPath()); diff --git a/lib/perfdata/perfdatawriter.hpp b/lib/perfdata/perfdatawriter.hpp index 3e0306950..5b6e51ea4 100644 --- a/lib/perfdata/perfdatawriter.hpp +++ b/lib/perfdata/perfdatawriter.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PERFDATAWRITER_H #define PERFDATAWRITER_H @@ -46,18 +29,21 @@ public: void ValidateServiceFormatTemplate(const Lazy& lvalue, const ValidationUtils& utils) override; protected: - void Start(bool runtimeCreated) override; - void Stop(bool runtimeRemoved) override; + void OnConfigLoaded() override; + void Resume() override; + void Pause() override; private: + Timer::Ptr m_RotationTimer; + std::ofstream m_ServiceOutputFile; + std::ofstream m_HostOutputFile; + boost::mutex m_StreamMutex; + void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); static Value EscapeMacroMetric(const Value& value); - Timer::Ptr m_RotationTimer; void RotationTimerHandler(); - - std::ofstream m_ServiceOutputFile; - std::ofstream m_HostOutputFile; + void RotateAllFiles(); void RotateFile(std::ofstream& output, const String& temp_path, const String& perfdata_path); }; diff --git a/lib/perfdata/perfdatawriter.ti b/lib/perfdata/perfdatawriter.ti index c0988f339..d6d99e8d3 100644 --- a/lib/perfdata/perfdatawriter.ti +++ b/lib/perfdata/perfdatawriter.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" #include "base/application.hpp" @@ -30,16 +13,16 @@ class PerfdataWriter : ConfigObject activation_priority 100; [config] String host_perfdata_path { - default {{{ return Application::GetLocalStateDir() + "/spool/icinga2/perfdata/host-perfdata"; }}} + default {{{ return Configuration::SpoolDir + "/perfdata/host-perfdata"; }}} }; [config] String service_perfdata_path { - default {{{ return Application::GetLocalStateDir() + "/spool/icinga2/perfdata/service-perfdata"; }}} + default {{{ return Configuration::SpoolDir + "/perfdata/service-perfdata"; }}} }; [config] String host_temp_path { - default {{{ return Application::GetLocalStateDir() + "/spool/icinga2/tmp/host-perfdata"; }}} + default {{{ return Configuration::SpoolDir + "/tmp/host-perfdata"; }}} }; [config] String service_temp_path { - default {{{ return Application::GetLocalStateDir() + "/spool/icinga2/tmp/service-perfdata"; }}} + default {{{ return Configuration::SpoolDir + "/tmp/service-perfdata"; }}} }; [config] String host_format_template { default {{{ @@ -70,6 +53,9 @@ class PerfdataWriter : ConfigObject [config] double rotation_interval { default {{{ return 30; }}} }; + [config] bool enable_ha { + default {{{ return false; }}} + }; }; } diff --git a/lib/pgsql_shim/CMakeLists.txt b/lib/pgsql_shim/CMakeLists.txt index 35f4c5cc4..327b64a9d 100644 --- a/lib/pgsql_shim/CMakeLists.txt +++ b/lib/pgsql_shim/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ link_directories(${PostgreSQL_LIBRARY_DIRS}) include_directories(${PostgreSQL_INCLUDE_DIRS}) diff --git a/lib/pgsql_shim/pgsqlinterface.cpp b/lib/pgsql_shim/pgsqlinterface.cpp index b5b3ef351..95b6e7d02 100644 --- a/lib/pgsql_shim/pgsqlinterface.cpp +++ b/lib/pgsql_shim/pgsqlinterface.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "pgsql_shim/pgsqlinterface.hpp" diff --git a/lib/pgsql_shim/pgsqlinterface.hpp b/lib/pgsql_shim/pgsqlinterface.hpp index 49148080b..2fe33033c 100644 --- a/lib/pgsql_shim/pgsqlinterface.hpp +++ b/lib/pgsql_shim/pgsqlinterface.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PGSQLINTERFACE_H #define PGSQLINTERFACE_H diff --git a/lib/remote/CMakeLists.txt b/lib/remote/CMakeLists.txt index bb9593c56..2c5a0326a 100644 --- a/lib/remote/CMakeLists.txt +++ b/lib/remote/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ mkclass_target(apilistener.ti apilistener-ti.cpp apilistener-ti.hpp) mkclass_target(apiuser.ti apiuser-ti.cpp apiuser-ti.hpp) @@ -24,11 +9,10 @@ set(remote_SOURCES i2-remote.hpp actionshandler.cpp actionshandler.hpp apiaction.cpp apiaction.hpp - apiclient.cpp apiclient.hpp apifunction.cpp apifunction.hpp apilistener.cpp apilistener.hpp apilistener-ti.hpp apilistener-configsync.cpp apilistener-filesync.cpp + apilistener-authority.cpp apiuser.cpp apiuser.hpp apiuser-ti.hpp - authority.cpp configfileshandler.cpp configfileshandler.hpp configobjectutility.cpp configobjectutility.hpp configpackageshandler.cpp configpackageshandler.hpp @@ -41,11 +25,7 @@ set(remote_SOURCES eventqueue.cpp eventqueue.hpp eventshandler.cpp eventshandler.hpp filterutility.cpp filterutility.hpp - httpchunkedencoding.cpp httpchunkedencoding.hpp - httpclientconnection.cpp httpclientconnection.hpp httphandler.cpp httphandler.hpp - httprequest.cpp httprequest.hpp - httpresponse.cpp httpresponse.hpp httpserverconnection.cpp httpserverconnection.hpp httputility.cpp httputility.hpp infohandler.cpp infohandler.hpp @@ -76,10 +56,11 @@ set_target_properties ( FOLDER Lib ) -#install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api\")") -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/log\")") -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/zones\")") -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/certs\")") -install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/certificate-requests\")") +#install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_DATADIR}/api\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_DATADIR}/api/log\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_DATADIR}/api/zones\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_DATADIR}/api/zones-stage\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_DATADIR}/certs\")") +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_DATADIR}/certificate-requests\")") diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index be95ce6c8..7b61cd5b9 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/actionshandler.hpp" #include "remote/httputility.hpp" @@ -29,15 +12,26 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/actions", ActionsHandler); -bool ActionsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool ActionsHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() != 3) + namespace http = boost::beast::http; + + if (url->GetPath().size() != 3) return false; - if (request.RequestMethod != "POST") + if (request.method() != http::verb::post) return false; - String actionName = request.RequestUrl->GetPath()[2]; + String actionName = url->GetPath()[2]; ApiAction::Ptr action = ApiAction::GetByName(actionName); @@ -98,17 +92,15 @@ bool ActionsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& reques } int statusCode = 500; - String statusMessage = "No action executed successfully"; for (const Dictionary::Ptr& res : results) { if (res->Contains("code") && res->Get("code") == 200) { statusCode = 200; - statusMessage = "OK"; break; } } - response.SetStatus(statusCode, statusMessage); + response.result(statusCode); Dictionary::Ptr result = new Dictionary({ { "results", new Array(std::move(results)) } diff --git a/lib/remote/actionshandler.hpp b/lib/remote/actionshandler.hpp index dd5ab9032..c2465cf7e 100644 --- a/lib/remote/actionshandler.hpp +++ b/lib/remote/actionshandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ACTIONSHANDLER_H #define ACTIONSHANDLER_H @@ -30,8 +13,16 @@ class ActionsHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ActionsHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; }; } diff --git a/lib/remote/apiaction.cpp b/lib/remote/apiaction.cpp index a2ad15737..4da91f0d5 100644 --- a/lib/remote/apiaction.cpp +++ b/lib/remote/apiaction.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/apiaction.hpp" #include "base/singleton.hpp" diff --git a/lib/remote/apiaction.hpp b/lib/remote/apiaction.hpp index 96f8b4480..6492449bd 100644 --- a/lib/remote/apiaction.hpp +++ b/lib/remote/apiaction.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef APIACTION_H #define APIACTION_H diff --git a/lib/remote/apiclient.cpp b/lib/remote/apiclient.cpp deleted file mode 100644 index b0d0a63da..000000000 --- a/lib/remote/apiclient.cpp +++ /dev/null @@ -1,368 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#include "remote/apiclient.hpp" -#include "base/base64.hpp" -#include "base/json.hpp" -#include "base/logger.hpp" -#include "base/exception.hpp" -#include "base/convert.hpp" - -using namespace icinga; - -ApiClient::ApiClient(const String& host, const String& port, - String user, String password) - : m_Connection(new HttpClientConnection(host, port, true)), m_User(std::move(user)), m_Password(std::move(password)) -{ - m_Connection->Start(); -} - -void ApiClient::GetTypes(const TypesCompletionCallback& callback) const -{ - Url::Ptr url = new Url(); - url->SetScheme("https"); - url->SetHost(m_Connection->GetHost()); - url->SetPort(m_Connection->GetPort()); - url->SetPath({ "v1", "types" }); - - try { - std::shared_ptr req = m_Connection->NewRequest(); - req->RequestMethod = "GET"; - req->RequestUrl = url; - req->AddHeader("Authorization", "Basic " + Base64::Encode(m_User + ":" + m_Password)); - req->AddHeader("Accept", "application/json"); - m_Connection->SubmitRequest(req, std::bind(TypesHttpCompletionCallback, _1, _2, callback)); - } catch (const std::exception&) { - callback(boost::current_exception(), std::vector()); - } -} - -void ApiClient::TypesHttpCompletionCallback(HttpRequest& request, HttpResponse& response, - const TypesCompletionCallback& callback) -{ - Dictionary::Ptr result; - - String body; - char buffer[1024]; - size_t count; - - while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0) - body += String(buffer, buffer + count); - - try { - if (response.StatusCode < 200 || response.StatusCode > 299) { - std::string message = "HTTP request failed; Code: " + Convert::ToString(response.StatusCode) + "; Body: " + body; - - BOOST_THROW_EXCEPTION(ScriptError(message)); - } - - std::vector types; - - result = JsonDecode(body); - - Array::Ptr results = result->Get("results"); - - ObjectLock olock(results); - for (const Dictionary::Ptr typeInfo : results) - { - ApiType::Ptr type = new ApiType(); - type->Abstract = typeInfo->Get("abstract"); - type->BaseName = typeInfo->Get("base"); - type->Name = typeInfo->Get("name"); - type->PluralName = typeInfo->Get("plural_name"); - // TODO: attributes - types.emplace_back(std::move(type)); - } - - callback(boost::exception_ptr(), types); - } catch (const std::exception& ex) { - Log(LogCritical, "ApiClient") - << "Error while decoding response: " << DiagnosticInformation(ex); - callback(boost::current_exception(), std::vector()); - } - -} - -void ApiClient::GetObjects(const String& pluralType, const ObjectsCompletionCallback& callback, - const std::vector& names, const std::vector& attrs, const std::vector& joins, bool all_joins) const -{ - Url::Ptr url = new Url(); - url->SetScheme("https"); - url->SetHost(m_Connection->GetHost()); - url->SetPort(m_Connection->GetPort()); - url->SetPath({ "v1", "objects", pluralType }); - - std::map > params; - - for (const String& name : names) { - params[pluralType.ToLower()].push_back(name); - } - - for (const String& attr : attrs) { - params["attrs"].push_back(attr); - } - - for (const String& join : joins) { - params["joins"].push_back(join); - } - - params["all_joins"].emplace_back(all_joins ? "1" : "0"); - - url->SetQuery(params); - - try { - std::shared_ptr req = m_Connection->NewRequest(); - req->RequestMethod = "GET"; - req->RequestUrl = url; - req->AddHeader("Authorization", "Basic " + Base64::Encode(m_User + ":" + m_Password)); - req->AddHeader("Accept", "application/json"); - m_Connection->SubmitRequest(req, std::bind(ObjectsHttpCompletionCallback, _1, _2, callback)); - } catch (const std::exception&) { - callback(boost::current_exception(), std::vector()); - } -} - -void ApiClient::ObjectsHttpCompletionCallback(HttpRequest& request, - HttpResponse& response, const ObjectsCompletionCallback& callback) -{ - Dictionary::Ptr result; - - String body; - char buffer[1024]; - size_t count; - - while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0) - body += String(buffer, buffer + count); - - try { - if (response.StatusCode < 200 || response.StatusCode > 299) { - std::string message = "HTTP request failed; Code: " + Convert::ToString(response.StatusCode) + "; Body: " + body; - - BOOST_THROW_EXCEPTION(ScriptError(message)); - } - - std::vector objects; - - result = JsonDecode(body); - - Array::Ptr results = result->Get("results"); - - if (results) { - ObjectLock olock(results); - for (const Dictionary::Ptr objectInfo : results) { - ApiObject::Ptr object = new ApiObject(); - - object->Name = objectInfo->Get("name"); - object->Type = objectInfo->Get("type"); - - Dictionary::Ptr attrs = objectInfo->Get("attrs"); - - if (attrs) { - ObjectLock olock(attrs); - for (const Dictionary::Pair& kv : attrs) { - object->Attrs[object->Type.ToLower() + "." + kv.first] = kv.second; - } - } - - Dictionary::Ptr joins = objectInfo->Get("joins"); - - if (joins) { - ObjectLock olock(joins); - for (const Dictionary::Pair& kv : joins) { - Dictionary::Ptr attrs = kv.second; - - if (attrs) { - ObjectLock olock(attrs); - for (const Dictionary::Pair& kv2 : attrs) { - object->Attrs[kv.first + "." + kv2.first] = kv2.second; - } - } - } - } - - Array::Ptr used_by = objectInfo->Get("used_by"); - - if (used_by) { - ObjectLock olock(used_by); - for (const Dictionary::Ptr& refInfo : used_by) { - ApiObjectReference ref; - ref.Name = refInfo->Get("name"); - ref.Type = refInfo->Get("type"); - object->UsedBy.emplace_back(std::move(ref)); - } - } - - objects.push_back(object); - } - } - - callback(boost::exception_ptr(), objects); - } catch (const std::exception& ex) { - Log(LogCritical, "ApiClient") - << "Error while decoding response: " << DiagnosticInformation(ex); - callback(boost::current_exception(), std::vector()); - } -} - -void ApiClient::ExecuteScript(const String& session, const String& command, bool sandboxed, - const ExecuteScriptCompletionCallback& callback) const -{ - Url::Ptr url = new Url(); - url->SetScheme("https"); - url->SetHost(m_Connection->GetHost()); - url->SetPort(m_Connection->GetPort()); - url->SetPath({ "v1", "console", "execute-script" }); - - std::map > params; - params["session"].push_back(session); - params["command"].push_back(command); - params["sandboxed"].emplace_back(sandboxed ? "1" : "0"); - url->SetQuery(params); - - try { - std::shared_ptr req = m_Connection->NewRequest(); - req->RequestMethod = "POST"; - req->RequestUrl = url; - req->AddHeader("Authorization", "Basic " + Base64::Encode(m_User + ":" + m_Password)); - req->AddHeader("Accept", "application/json"); - m_Connection->SubmitRequest(req, std::bind(ExecuteScriptHttpCompletionCallback, _1, _2, callback)); - } catch (const std::exception&) { - callback(boost::current_exception(), Empty); - } -} - -void ApiClient::ExecuteScriptHttpCompletionCallback(HttpRequest& request, - HttpResponse& response, const ExecuteScriptCompletionCallback& callback) -{ - Dictionary::Ptr result; - - String body; - char buffer[1024]; - size_t count; - - while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0) - body += String(buffer, buffer + count); - - try { - if (response.StatusCode < 200 || response.StatusCode > 299) { - std::string message = "HTTP request failed; Code: " + Convert::ToString(response.StatusCode) + "; Body: " + body; - - BOOST_THROW_EXCEPTION(ScriptError(message)); - } - - result = JsonDecode(body); - - Array::Ptr results = result->Get("results"); - Value result; - String errorMessage = "Unexpected result from API."; - - if (results && results->GetLength() > 0) { - Dictionary::Ptr resultInfo = results->Get(0); - errorMessage = resultInfo->Get("status"); - - if (resultInfo->Get("code") >= 200 && resultInfo->Get("code") <= 299) { - result = resultInfo->Get("result"); - } else { - DebugInfo di; - Dictionary::Ptr debugInfo = resultInfo->Get("debug_info"); - if (debugInfo) { - di.Path = debugInfo->Get("path"); - di.FirstLine = debugInfo->Get("first_line"); - di.FirstColumn = debugInfo->Get("first_column"); - di.LastLine = debugInfo->Get("last_line"); - di.LastColumn = debugInfo->Get("last_column"); - } - bool incompleteExpression = resultInfo->Get("incomplete_expression"); - BOOST_THROW_EXCEPTION(ScriptError(errorMessage, di, incompleteExpression)); - } - } - - callback(boost::exception_ptr(), result); - } catch (const std::exception&) { - callback(boost::current_exception(), Empty); - } -} - -void ApiClient::AutocompleteScript(const String& session, const String& command, bool sandboxed, - const AutocompleteScriptCompletionCallback& callback) const -{ - Url::Ptr url = new Url(); - url->SetScheme("https"); - url->SetHost(m_Connection->GetHost()); - url->SetPort(m_Connection->GetPort()); - url->SetPath({ "v1", "console", "auto-complete-script" }); - - std::map > params; - params["session"].push_back(session); - params["command"].push_back(command); - params["sandboxed"].emplace_back(sandboxed ? "1" : "0"); - url->SetQuery(params); - - try { - std::shared_ptr req = m_Connection->NewRequest(); - req->RequestMethod = "POST"; - req->RequestUrl = url; - req->AddHeader("Authorization", "Basic " + Base64::Encode(m_User + ":" + m_Password)); - req->AddHeader("Accept", "application/json"); - m_Connection->SubmitRequest(req, std::bind(AutocompleteScriptHttpCompletionCallback, _1, _2, callback)); - } catch (const std::exception&) { - callback(boost::current_exception(), nullptr); - } -} - -void ApiClient::AutocompleteScriptHttpCompletionCallback(HttpRequest& request, - HttpResponse& response, const AutocompleteScriptCompletionCallback& callback) -{ - Dictionary::Ptr result; - - String body; - char buffer[1024]; - size_t count; - - while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0) - body += String(buffer, buffer + count); - - try { - if (response.StatusCode < 200 || response.StatusCode > 299) { - std::string message = "HTTP request failed; Code: " + Convert::ToString(response.StatusCode) + "; Body: " + body; - - BOOST_THROW_EXCEPTION(ScriptError(message)); - } - - result = JsonDecode(body); - - Array::Ptr results = result->Get("results"); - Array::Ptr suggestions; - String errorMessage = "Unexpected result from API."; - - if (results && results->GetLength() > 0) { - Dictionary::Ptr resultInfo = results->Get(0); - errorMessage = resultInfo->Get("status"); - - if (resultInfo->Get("code") >= 200 && resultInfo->Get("code") <= 299) - suggestions = resultInfo->Get("suggestions"); - else - BOOST_THROW_EXCEPTION(ScriptError(errorMessage)); - } - - callback(boost::exception_ptr(), suggestions); - } catch (const std::exception&) { - callback(boost::current_exception(), nullptr); - } -} diff --git a/lib/remote/apiclient.hpp b/lib/remote/apiclient.hpp deleted file mode 100644 index 7507765a0..000000000 --- a/lib/remote/apiclient.hpp +++ /dev/null @@ -1,129 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#ifndef APICLIENT_H -#define APICLIENT_H - -#include "remote/httpclientconnection.hpp" -#include "base/value.hpp" -#include "base/exception.hpp" -#include - -namespace icinga -{ - -struct ApiFieldAttributes -{ -public: - bool Config; - bool Navigation; - bool NoUserModify; - bool NouserView; - bool Required; - bool State; -}; - -class ApiType; - -struct ApiField -{ -public: - String Name; - int ID; - int ArrayRank; - ApiFieldAttributes FieldAttributes; - String TypeName; - intrusive_ptr Type; -}; - -class ApiType final : public Object -{ -public: - DECLARE_PTR_TYPEDEFS(ApiType); - - String Name; - String PluralName; - String BaseName; - ApiType::Ptr Base; - bool Abstract; - std::map Fields; - std::vector PrototypeKeys; -}; - -struct ApiObjectReference -{ -public: - String Name; - String Type; -}; - -struct ApiObject : public Object -{ -public: - DECLARE_PTR_TYPEDEFS(ApiObject); - - String Name; - String Type; - std::map Attrs; - std::vector UsedBy; -}; - -class ApiClient : public Object -{ -public: - DECLARE_PTR_TYPEDEFS(ApiClient); - - ApiClient(const String& host, const String& port, - String user, String password); - - typedef std::function&)> TypesCompletionCallback; - void GetTypes(const TypesCompletionCallback& callback) const; - - typedef std::function&)> ObjectsCompletionCallback; - void GetObjects(const String& pluralType, const ObjectsCompletionCallback& callback, - const std::vector& names = std::vector(), - const std::vector& attrs = std::vector(), - const std::vector& joins = std::vector(), bool all_joins = false) const; - - typedef std::function ExecuteScriptCompletionCallback; - void ExecuteScript(const String& session, const String& command, bool sandboxed, - const ExecuteScriptCompletionCallback& callback) const; - - typedef std::function AutocompleteScriptCompletionCallback; - void AutocompleteScript(const String& session, const String& command, bool sandboxed, - const AutocompleteScriptCompletionCallback& callback) const; - -private: - HttpClientConnection::Ptr m_Connection; - String m_User; - String m_Password; - - static void TypesHttpCompletionCallback(HttpRequest& request, - HttpResponse& response, const TypesCompletionCallback& callback); - static void ObjectsHttpCompletionCallback(HttpRequest& request, - HttpResponse& response, const ObjectsCompletionCallback& callback); - static void ExecuteScriptHttpCompletionCallback(HttpRequest& request, - HttpResponse& response, const ExecuteScriptCompletionCallback& callback); - static void AutocompleteScriptHttpCompletionCallback(HttpRequest& request, - HttpResponse& response, const AutocompleteScriptCompletionCallback& callback); -}; - -} - -#endif /* APICLIENT_H */ diff --git a/lib/remote/apifunction.cpp b/lib/remote/apifunction.cpp index b549bef29..5b855cc43 100644 --- a/lib/remote/apifunction.cpp +++ b/lib/remote/apifunction.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/apifunction.hpp" #include "base/singleton.hpp" diff --git a/lib/remote/apifunction.hpp b/lib/remote/apifunction.hpp index 8e41040ab..048b83c58 100644 --- a/lib/remote/apifunction.hpp +++ b/lib/remote/apifunction.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef APIFUNCTION_H #define APIFUNCTION_H diff --git a/lib/remote/apilistener-authority.cpp b/lib/remote/apilistener-authority.cpp new file mode 100644 index 000000000..d5fe5120c --- /dev/null +++ b/lib/remote/apilistener-authority.cpp @@ -0,0 +1,84 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "remote/zone.hpp" +#include "remote/apilistener.hpp" +#include "base/configtype.hpp" +#include "base/utility.hpp" +#include "base/convert.hpp" + +using namespace icinga; + +std::atomic ApiListener::m_UpdatedObjectAuthority (false); + +void ApiListener::UpdateObjectAuthority() +{ + /* Always run this, even if there is no 'api' feature enabled. */ + if (auto listener = ApiListener::GetInstance()) { + Log(LogNotice, "ApiListener") + << "Updating object authority for objects at endpoint '" << listener->GetIdentity() << "'."; + } else { + Log(LogNotice, "ApiListener") + << "Updating object authority for local objects."; + } + + Zone::Ptr my_zone = Zone::GetLocalZone(); + + std::vector endpoints; + Endpoint::Ptr my_endpoint; + + if (my_zone) { + my_endpoint = Endpoint::GetLocalEndpoint(); + + int num_total = 0; + + for (const Endpoint::Ptr& endpoint : my_zone->GetEndpoints()) { + num_total++; + + if (endpoint != my_endpoint && !endpoint->GetConnected()) + continue; + + endpoints.push_back(endpoint); + } + + double mainTime = Application::GetMainTime(); + + /* 30 seconds cold startup, don't update any authority to give the secondary endpoint time to reconnect. */ + if (num_total > 1 && endpoints.size() <= 1 && (mainTime == 0 || Utility::GetTime() - mainTime < 30)) + return; + + std::sort(endpoints.begin(), endpoints.end(), + [](const ConfigObject::Ptr& a, const ConfigObject::Ptr& b) { + return a->GetName() < b->GetName(); + } + ); + } + + for (const Type::Ptr& type : Type::GetAllTypes()) { + auto *dtype = dynamic_cast(type.get()); + + if (!dtype) + continue; + + for (const ConfigObject::Ptr& object : dtype->GetObjects()) { + if (!object->IsActive() || object->GetHAMode() != HARunOnce) + continue; + + bool authority; + + if (!my_zone) + authority = true; + else + authority = endpoints[Utility::SDBM(object->GetName()) % endpoints.size()] == my_endpoint; + +#ifdef I2_DEBUG +// //Enable on demand, causes heavy logging on each run. +// Log(LogDebug, "ApiListener") +// << "Setting authority '" << Convert::ToString(authority) << "' for object '" << object->GetName() << "' of type '" << object->GetReflectionType()->GetName() << "'."; +#endif /* I2_DEBUG */ + + object->SetAuthority(authority); + } + } + + m_UpdatedObjectAuthority.store(true); +} diff --git a/lib/remote/apilistener-configsync.cpp b/lib/remote/apilistener-configsync.cpp index 12c91065e..4a55c2883 100644 --- a/lib/remote/apilistener-configsync.cpp +++ b/lib/remote/apilistener-configsync.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/apilistener.hpp" #include "remote/apifunction.hpp" @@ -56,7 +39,7 @@ void ApiListener::ConfigUpdateObjectHandler(const ConfigObject::Ptr& object, con Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { Log(LogNotice, "ApiListener") - << "Received update for object: " << JsonEncode(params); + << "Received config update for object: " << JsonEncode(params); /* check permissions */ ApiListener::Ptr listener = ApiListener::GetInstance(); @@ -69,26 +52,32 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint(); + String identity = origin->FromClient->GetIdentity(); + /* discard messages if the client is not configured on this node */ if (!endpoint) { Log(LogNotice, "ApiListener") - << "Discarding 'config update object' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed)."; + << "Discarding 'config update object' message from '" << identity << "': Invalid endpoint origin (client not allowed)."; return Empty; } + Zone::Ptr endpointZone = endpoint->GetZone(); + /* discard messages if the sender is in a child zone */ - if (!Zone::GetLocalZone()->IsChildOf(endpoint->GetZone())) { + if (!Zone::GetLocalZone()->IsChildOf(endpointZone)) { Log(LogNotice, "ApiListener") - << "Discarding 'config update object' message from '" - << origin->FromClient->GetIdentity() << "' for object '" - << objName << "' of type '" << objType << "'. Sender is in a child zone."; + << "Discarding 'config update object' message" + << " from '" << identity << "' (endpoint: '" << endpoint->GetName() << "', zone: '" << endpointZone->GetName() << "')" + << " for object '" << objName << "' of type '" << objType << "'. Sender is in a child zone."; return Empty; } /* ignore messages if the endpoint does not accept config */ if (!listener->GetAcceptConfig()) { Log(LogWarning, "ApiListener") - << "Ignoring config update from '" << origin->FromClient->GetIdentity() << "' for object '" << objName << "' of type '" << objType << "'. '" << listener->GetName() << "' does not accept config."; + << "Ignoring config update" + << " from '" << identity << "' (endpoint: '" << endpoint->GetName() << "', zone: '" << endpointZone->GetName() << "')" + << " for object '" << objName << "' of type '" << objType << "'. '" << listener->GetName() << "' does not accept config."; return Empty; } @@ -99,6 +88,7 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin auto *ctype = dynamic_cast(ptype.get()); if (!ctype) { + // This never happens with icinga cluster endpoints, only with development errors. Log(LogCritical, "ApiListener") << "Config type '" << objType << "' does not exist."; return Empty; @@ -116,7 +106,11 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin /* object does not exist, create it through the API */ Array::Ptr errors = new Array(); - if (!ConfigObjectUtility::CreateObject(ptype, objName, config, errors, nullptr)) { + /* + * Create the config object through our internal API. + * IMPORTANT: Pass the origin to prevent cluster sync loops. + */ + if (!ConfigObjectUtility::CreateObject(ptype, objName, config, errors, nullptr, origin)) { Log(LogCritical, "ApiListener") << "Could not create object '" << objName << "':"; @@ -143,7 +137,9 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin /* update object attributes if version was changed or if this is a new object */ if (newObject || objVersion <= object->GetVersion()) { Log(LogNotice, "ApiListener") - << "Discarding config update for object '" << object->GetName() + << "Discarding config update" + << " from '" << identity << "' (endpoint: '" << endpoint->GetName() << "', zone: '" << endpointZone->GetName() << "')" + << " for object '" << object->GetName() << "': Object version " << std::fixed << object->GetVersion() << " is more recent than the received version " << std::fixed << objVersion << "."; @@ -151,7 +147,9 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin } Log(LogNotice, "ApiListener") - << "Processing config update for object '" << object->GetName() + << "Processing config update" + << " from '" << identity << "' (endpoint: '" << endpoint->GetName() << "', zone: '" << endpointZone->GetName() << "')" + << " for object '" << object->GetName() << "': Object version " << object->GetVersion() << " is older than the received version " << objVersion << "."; @@ -199,7 +197,7 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin Value ApiListener::ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { Log(LogNotice, "ApiListener") - << "Received update for object: " << JsonEncode(params); + << "Received config delete for object: " << JsonEncode(params); /* check permissions */ ApiListener::Ptr listener = ApiListener::GetInstance(); @@ -207,55 +205,75 @@ Value ApiListener::ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin if (!listener) return Empty; - if (!listener->GetAcceptConfig()) { - Log(LogWarning, "ApiListener") - << "Ignoring config update. '" << listener->GetName() << "' does not accept config."; - return Empty; - } + String objType = params->Get("type"); + String objName = params->Get("name"); Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint(); + String identity = origin->FromClient->GetIdentity(); + if (!endpoint) { Log(LogNotice, "ApiListener") - << "Discarding 'config update object' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed)."; + << "Discarding 'config delete object' message from '" << identity << "': Invalid endpoint origin (client not allowed)."; return Empty; } + Zone::Ptr endpointZone = endpoint->GetZone(); + /* discard messages if the sender is in a child zone */ - if (!Zone::GetLocalZone()->IsChildOf(endpoint->GetZone())) { + if (!Zone::GetLocalZone()->IsChildOf(endpointZone)) { + Log(LogNotice, "ApiListener") + << "Discarding 'config delete object' message" + << " from '" << identity << "' (endpoint: '" << endpoint->GetName() << "', zone: '" << endpointZone->GetName() << "')" + << " for object '" << objName << "' of type '" << objType << "'. Sender is in a child zone."; + return Empty; + } + + if (!listener->GetAcceptConfig()) { Log(LogWarning, "ApiListener") - << "Discarding 'config update object' message from '" - << origin->FromClient->GetIdentity() << "'."; + << "Ignoring config delete" + << " from '" << identity << "' (endpoint: '" << endpoint->GetName() << "', zone: '" << endpointZone->GetName() << "')" + << " for object '" << objName << "' of type '" << objType << "'. '" << listener->GetName() << "' does not accept config."; return Empty; } /* delete the object */ - Type::Ptr ptype = Type::GetByName(params->Get("type")); + Type::Ptr ptype = Type::GetByName(objType); auto *ctype = dynamic_cast(ptype.get()); if (!ctype) { + // This never happens with icinga cluster endpoints, only with development errors. Log(LogCritical, "ApiListener") - << "Config type '" << params->Get("type") << "' does not exist."; + << "Config type '" << objType << "' does not exist."; return Empty; } - ConfigObject::Ptr object = ctype->GetObject(params->Get("name")); + ConfigObject::Ptr object = ctype->GetObject(objName); if (!object) { Log(LogNotice, "ApiListener") - << "Could not delete non-existent object '" << params->Get("name") << "' with type '" << params->Get("type") << "'."; + << "Could not delete non-existent object '" << objName << "' with type '" << params->Get("type") << "'."; return Empty; } if (object->GetPackage() != "_api") { Log(LogCritical, "ApiListener") - << "Could not delete object '" << object->GetName() << "': Not created by the API."; + << "Could not delete object '" << objName << "': Not created by the API."; return Empty; } + Log(LogNotice, "ApiListener") + << "Processing config delete" + << " from '" << identity << "' (endpoint: '" << endpoint->GetName() << "', zone: '" << endpointZone->GetName() << "')" + << " for object '" << object->GetName() << "'."; + Array::Ptr errors = new Array(); - if (!ConfigObjectUtility::DeleteObject(object, true, errors, nullptr)) { + /* + * Delete the config object through our internal API. + * IMPORTANT: Pass the origin to prevent cluster sync loops. + */ + if (!ConfigObjectUtility::DeleteObject(object, true, errors, nullptr, origin)) { Log(LogCritical, "ApiListener", "Could not delete object:"); ObjectLock olock(errors); @@ -299,7 +317,15 @@ void ApiListener::UpdateConfigObject(const ConfigObject::Ptr& object, const Mess params->Set("version", object->GetVersion()); if (object->GetPackage() == "_api") { - String file = ConfigObjectUtility::GetObjectConfigPath(object->GetReflectionType(), object->GetName()); + String file; + + try { + file = ConfigObjectUtility::GetObjectConfigPath(object->GetReflectionType(), object->GetName()); + } catch (const std::exception& ex) { + Log(LogNotice, "ApiListener") + << "Cannot sync object '" << object->GetName() << "': " << ex.what(); + return; + } std::ifstream fp(file.CStr(), std::ifstream::binary); if (!fp) @@ -340,7 +366,7 @@ void ApiListener::UpdateConfigObject(const ConfigObject::Ptr& object, const Mess #endif /* I2_DEBUG */ if (client) - JsonRpc::SendMessage(client->GetStream(), message); + client->SendMessage(message); else { Zone::Ptr target = static_pointer_cast(object->GetZone()); @@ -390,7 +416,7 @@ void ApiListener::DeleteConfigObject(const ConfigObject::Ptr& object, const Mess #endif /* I2_DEBUG */ if (client) - JsonRpc::SendMessage(client->GetStream(), message); + client->SendMessage(message); else { Zone::Ptr target = static_pointer_cast(object->GetZone()); diff --git a/lib/remote/apilistener-filesync.cpp b/lib/remote/apilistener-filesync.cpp index 30a79a35f..4aa381c43 100644 --- a/lib/remote/apilistener-filesync.cpp +++ b/lib/remote/apilistener-filesync.cpp @@ -1,29 +1,16 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/apilistener.hpp" #include "remote/apifunction.hpp" #include "config/configcompiler.hpp" +#include "base/tlsutility.hpp" +#include "base/json.hpp" #include "base/configtype.hpp" #include "base/logger.hpp" #include "base/convert.hpp" +#include "base/application.hpp" #include "base/exception.hpp" +#include "base/utility.hpp" #include #include @@ -31,233 +18,202 @@ using namespace icinga; REGISTER_APIFUNCTION(Update, config, &ApiListener::ConfigUpdateHandler); -void ApiListener::ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file) -{ - CONTEXT("Creating config update for file '" + file + "'"); +boost::mutex ApiListener::m_ConfigSyncStageLock; - Log(LogNotice, "ApiListener") - << "Creating config update for file '" << file << "'."; - - std::ifstream fp(file.CStr(), std::ifstream::binary); - if (!fp) - return; - - String content((std::istreambuf_iterator(fp)), std::istreambuf_iterator()); - - Dictionary::Ptr update; - - if (Utility::Match("*.conf", file)) - update = config.UpdateV1; - else - update = config.UpdateV2; - - update->Set(file.SubStr(path.GetLength()), content); -} - -Dictionary::Ptr ApiListener::MergeConfigUpdate(const ConfigDirInformation& config) -{ - Dictionary::Ptr result = new Dictionary(); - - if (config.UpdateV1) - config.UpdateV1->CopyTo(result); - - if (config.UpdateV2) - config.UpdateV2->CopyTo(result); - - return result; -} - -ConfigDirInformation ApiListener::LoadConfigDir(const String& dir) -{ - ConfigDirInformation config; - config.UpdateV1 = new Dictionary(); - config.UpdateV2 = new Dictionary(); - Utility::GlobRecursive(dir, "*", std::bind(&ApiListener::ConfigGlobHandler, std::ref(config), dir, _1), GlobFile); - return config; -} - -bool ApiListener::UpdateConfigDir(const ConfigDirInformation& oldConfigInfo, const ConfigDirInformation& newConfigInfo, const String& configDir, bool authoritative) -{ - bool configChange = false; - - Dictionary::Ptr oldConfig = MergeConfigUpdate(oldConfigInfo); - Dictionary::Ptr newConfig = MergeConfigUpdate(newConfigInfo); - - double oldTimestamp; - - if (!oldConfig->Contains("/.timestamp")) - oldTimestamp = 0; - else - oldTimestamp = oldConfig->Get("/.timestamp"); - - double newTimestamp; - - if (!newConfig->Contains("/.timestamp")) - newTimestamp = Utility::GetTime(); - else - newTimestamp = newConfig->Get("/.timestamp"); - - /* skip update if our configuration files are more recent */ - if (oldTimestamp >= newTimestamp) { - Log(LogNotice, "ApiListener") - << "Our configuration is more recent than the received configuration update." - << " Ignoring configuration file update for path '" << configDir << "'. Current timestamp '" - << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", oldTimestamp) << "' (" - << std::fixed << std::setprecision(6) << oldTimestamp - << ") >= received timestamp '" - << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", newTimestamp) << "' (" - << newTimestamp << ")."; - return false; - } - - size_t numBytes = 0; - - { - ObjectLock olock(newConfig); - for (const Dictionary::Pair& kv : newConfig) { - if (oldConfig->Get(kv.first) != kv.second) { - if (!Utility::Match("*/.timestamp", kv.first)) - configChange = true; - - String path = configDir + "/" + kv.first; - Log(LogInformation, "ApiListener") - << "Updating configuration file: " << path; - - /* Sync string content only. */ - String content = kv.second; - - /* Generate a directory tree (zones/1/2/3 might not exist yet). */ - Utility::MkDirP(Utility::DirName(path), 0755); - std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc); - fp << content; - fp.close(); - - numBytes += content.GetLength(); - } - } - } - - Log(LogInformation, "ApiListener") - << "Applying configuration file update for path '" << configDir << "' (" << numBytes << " Bytes). Received timestamp '" - << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", newTimestamp) << "' (" - << std::fixed << std::setprecision(6) << newTimestamp - << "), Current timestamp '" - << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", oldTimestamp) << "' (" - << oldTimestamp << ")."; - - ObjectLock xlock(oldConfig); - for (const Dictionary::Pair& kv : oldConfig) { - if (!newConfig->Contains(kv.first)) { - configChange = true; - - String path = configDir + "/" + kv.first; - (void) unlink(path.CStr()); - } - } - - String tsPath = configDir + "/.timestamp"; - if (!Utility::PathExists(tsPath)) { - std::ofstream fp(tsPath.CStr(), std::ofstream::out | std::ostream::trunc); - fp << std::fixed << newTimestamp; - fp.close(); - } - - if (authoritative) { - String authPath = configDir + "/.authoritative"; - if (!Utility::PathExists(authPath)) { - std::ofstream fp(authPath.CStr(), std::ofstream::out | std::ostream::trunc); - fp.close(); - } - } - - return configChange; -} - -void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const -{ - ConfigDirInformation newConfigInfo; - newConfigInfo.UpdateV1 = new Dictionary(); - newConfigInfo.UpdateV2 = new Dictionary(); - - for (const ZoneFragment& zf : ConfigCompiler::GetZoneDirs(zone->GetName())) { - ConfigDirInformation newConfigPart = LoadConfigDir(zf.Path); - - { - ObjectLock olock(newConfigPart.UpdateV1); - for (const Dictionary::Pair& kv : newConfigPart.UpdateV1) { - newConfigInfo.UpdateV1->Set("/" + zf.Tag + kv.first, kv.second); - } - } - - { - ObjectLock olock(newConfigPart.UpdateV2); - for (const Dictionary::Pair& kv : newConfigPart.UpdateV2) { - newConfigInfo.UpdateV2->Set("/" + zf.Tag + kv.first, kv.second); - } - } - } - - int sumUpdates = newConfigInfo.UpdateV1->GetLength() + newConfigInfo.UpdateV2->GetLength(); - - if (sumUpdates == 0) - return; - - String oldDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones/" + zone->GetName(); - - Log(LogInformation, "ApiListener") - << "Copying " << sumUpdates << " zone configuration files for zone '" << zone->GetName() << "' to '" << oldDir << "'."; - - Utility::MkDirP(oldDir, 0700); - - ConfigDirInformation oldConfigInfo = LoadConfigDir(oldDir); - - UpdateConfigDir(oldConfigInfo, newConfigInfo, oldDir, true); -} - -void ApiListener::SyncZoneDirs() const +/** + * Entrypoint for updating all authoritative configs from /etc/zones.d, packages, etc. + * into var/lib/icinga2/api/zones + */ +void ApiListener::SyncLocalZoneDirs() const { for (const Zone::Ptr& zone : ConfigType::GetObjectsByType()) { try { - SyncZoneDir(zone); + SyncLocalZoneDir(zone); } catch (const std::exception&) { continue; } } } +/** + * Sync a zone directory where we have an authoritative copy (zones.d, packages, etc.) + * + * This function collects the registered zone config dirs from + * the config compiler and reads the file content into the config + * information structure. + * + * Returns early when there are no updates. + * + * @param zone Pointer to the zone object being synced. + */ +void ApiListener::SyncLocalZoneDir(const Zone::Ptr& zone) const +{ + if (!zone) + return; + + ConfigDirInformation newConfigInfo; + newConfigInfo.UpdateV1 = new Dictionary(); + newConfigInfo.UpdateV2 = new Dictionary(); + newConfigInfo.Checksums = new Dictionary(); + + String zoneName = zone->GetName(); + + // Load registered zone paths, e.g. '_etc', '_api' and user packages. + for (const ZoneFragment& zf : ConfigCompiler::GetZoneDirs(zoneName)) { + ConfigDirInformation newConfigPart = LoadConfigDir(zf.Path); + + // Config files '*.conf'. + { + ObjectLock olock(newConfigPart.UpdateV1); + for (const Dictionary::Pair& kv : newConfigPart.UpdateV1) { + String path = "/" + zf.Tag + kv.first; + + newConfigInfo.UpdateV1->Set(path, kv.second); + newConfigInfo.Checksums->Set(path, GetChecksum(kv.second)); + } + } + + // Meta files. + { + ObjectLock olock(newConfigPart.UpdateV2); + for (const Dictionary::Pair& kv : newConfigPart.UpdateV2) { + String path = "/" + zf.Tag + kv.first; + + newConfigInfo.UpdateV2->Set(path, kv.second); + newConfigInfo.Checksums->Set(path, GetChecksum(kv.second)); + } + } + } + + size_t sumUpdates = newConfigInfo.UpdateV1->GetLength() + newConfigInfo.UpdateV2->GetLength(); + + // Return early if there are no updates. + if (sumUpdates == 0) + return; + + String productionZonesDir = GetApiZonesDir() + zoneName; + + Log(LogInformation, "ApiListener") + << "Copying " << sumUpdates << " zone configuration files for zone '" << zoneName << "' to '" << productionZonesDir << "'."; + + // Purge files to allow deletion via zones.d. + if (Utility::PathExists(productionZonesDir)) + Utility::RemoveDirRecursive(productionZonesDir); + + Utility::MkDirP(productionZonesDir, 0700); + + // Copy content and add additional meta data. + size_t numBytes = 0; + + /* Note: We cannot simply copy directories here. + * + * Zone directories are registered from everywhere and we already + * have read their content into memory with LoadConfigDir(). + */ + Dictionary::Ptr newConfig = MergeConfigUpdate(newConfigInfo); + + { + ObjectLock olock(newConfig); + + for (const Dictionary::Pair& kv : newConfig) { + String dst = productionZonesDir + "/" + kv.first; + + Utility::MkDirP(Utility::DirName(dst), 0755); + + Log(LogInformation, "ApiListener") + << "Updating configuration file: " << dst; + + String content = kv.second; + + std::ofstream fp(dst.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc); + + fp << content; + fp.close(); + + numBytes += content.GetLength(); + } + } + + // Additional metadata. + String tsPath = productionZonesDir + "/.timestamp"; + + if (!Utility::PathExists(tsPath)) { + std::ofstream fp(tsPath.CStr(), std::ofstream::out | std::ostream::trunc); + + fp << std::fixed << Utility::GetTime(); + fp.close(); + } + + String authPath = productionZonesDir + "/.authoritative"; + + if (!Utility::PathExists(authPath)) { + std::ofstream fp(authPath.CStr(), std::ofstream::out | std::ostream::trunc); + fp.close(); + } + + // Checksums. + String checksumsPath = productionZonesDir + "/.checksums"; + + if (Utility::PathExists(checksumsPath)) + Utility::Remove(checksumsPath); + + std::ofstream fp(checksumsPath.CStr(), std::ofstream::out | std::ostream::trunc); + + fp << std::fixed << JsonEncode(newConfigInfo.Checksums); + fp.close(); + + Log(LogNotice, "ApiListener") + << "Updated meta data for cluster config sync. Checksum: '" << checksumsPath + << "', timestamp: '" << tsPath << "', auth: '" << authPath << "'."; +} + +/** + * Entrypoint for sending a file based config update to a cluster client. + * This includes security checks for zone relations. + * Loads the zone config files where this client belongs to + * and sends the 'config::Update' JSON-RPC message. + * + * @param aclient Connected JSON-RPC client. + */ void ApiListener::SendConfigUpdate(const JsonRpcConnection::Ptr& aclient) { Endpoint::Ptr endpoint = aclient->GetEndpoint(); ASSERT(endpoint); - Zone::Ptr azone = endpoint->GetZone(); - Zone::Ptr lzone = Zone::GetLocalZone(); + Zone::Ptr clientZone = endpoint->GetZone(); + Zone::Ptr localZone = Zone::GetLocalZone(); - /* don't try to send config updates to our master */ - if (!azone->IsChildOf(lzone)) + // Don't send config updates to parent zones + if (!clientZone->IsChildOf(localZone)) return; Dictionary::Ptr configUpdateV1 = new Dictionary(); Dictionary::Ptr configUpdateV2 = new Dictionary(); + Dictionary::Ptr configUpdateChecksums = new Dictionary(); // new since 2.11 - String zonesDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones"; + String zonesDir = GetApiZonesDir(); for (const Zone::Ptr& zone : ConfigType::GetObjectsByType()) { - String zoneDir = zonesDir + "/" + zone->GetName(); + String zoneName = zone->GetName(); + String zoneDir = zonesDir + zoneName; - if (!zone->IsChildOf(azone) && !zone->IsGlobal()) + // Only sync child and global zones. + if (!zone->IsChildOf(clientZone) && !zone->IsGlobal()) continue; + // Zone was configured, but there's no configuration directory. if (!Utility::PathExists(zoneDir)) continue; Log(LogInformation, "ApiListener") << "Syncing configuration files for " << (zone->IsGlobal() ? "global " : "") - << "zone '" << zone->GetName() << "' to endpoint '" << endpoint->GetName() << "'."; + << "zone '" << zoneName << "' to endpoint '" << endpoint->GetName() << "'."; - ConfigDirInformation config = LoadConfigDir(zonesDir + "/" + zone->GetName()); - configUpdateV1->Set(zone->GetName(), config.UpdateV1); - configUpdateV2->Set(zone->GetName(), config.UpdateV2); + ConfigDirInformation config = LoadConfigDir(zoneDir); + + configUpdateV1->Set(zoneName, config.UpdateV1); + configUpdateV2->Set(zoneName, config.UpdateV2); + configUpdateChecksums->Set(zoneName, config.Checksums); // new since 2.11 } Dictionary::Ptr message = new Dictionary({ @@ -265,15 +221,29 @@ void ApiListener::SendConfigUpdate(const JsonRpcConnection::Ptr& aclient) { "method", "config::Update" }, { "params", new Dictionary({ { "update", configUpdateV1 }, - { "update_v2", configUpdateV2 } + { "update_v2", configUpdateV2 }, // Since 2.4.2. + { "checksums", configUpdateChecksums } // Since 2.11.0. }) } }); aclient->SendMessage(message); } +/** + * Registered handler when a new config::Update message is received. + * + * Checks destination and permissions first, locks the transaction and analyses the update. + * The newly received configuration is not copied to production immediately, + * but into the staging directory first. + * Last, the async validation and restart is triggered. + * + * @param origin Where this message came from. + * @param params Message parameters including the config updates. + * @returns Empty, required by the interface. + */ Value ApiListener::ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { + // Verify permissions and trust relationship. if (!origin->FromClient->GetEndpoint() || (origin->FromZone && !Zone::GetLocalZone()->IsChildOf(origin->FromZone))) return Empty; @@ -290,52 +260,586 @@ Value ApiListener::ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const D return Empty; } - Log(LogInformation, "ApiListener") - << "Applying config update from endpoint '" << origin->FromClient->GetEndpoint()->GetName() - << "' of zone '" << GetFromZoneName(origin->FromZone) << "'."; + /* Only one transaction is allowed, concurrent message handlers need to wait. + * This affects two parent endpoints sending the config in the same moment. + */ + boost::mutex::scoped_lock lock(m_ConfigSyncStageLock); + String apiZonesStageDir = GetApiZonesStageDir(); + String fromEndpointName = origin->FromClient->GetEndpoint()->GetName(); + String fromZoneName = GetFromZoneName(origin->FromZone); + + Log(LogInformation, "ApiListener") + << "Applying config update from endpoint '" << fromEndpointName + << "' of zone '" << fromZoneName << "'."; + + // Config files. Dictionary::Ptr updateV1 = params->Get("update"); + // Meta data files: .timestamp, etc. Dictionary::Ptr updateV2 = params->Get("update_v2"); + // New since 2.11.0. + Dictionary::Ptr checksums; + + if (params->Contains("checksums")) + checksums = params->Get("checksums"); + bool configChange = false; + // Keep track of the relative config paths for later validation and copying. TODO: Find a better algorithm. + std::vector relativePaths; + + /* + * We can and must safely purge the staging directory, as the difference is taken between + * runtime production config and newly received configuration. + * This is needed to not mix deleted/changed content between received and stage + * config. + */ + if (Utility::PathExists(apiZonesStageDir)) + Utility::RemoveDirRecursive(apiZonesStageDir); + + Utility::MkDirP(apiZonesStageDir, 0700); + + // Analyse and process the update. + size_t count = 0; + ObjectLock olock(updateV1); + for (const Dictionary::Pair& kv : updateV1) { - Zone::Ptr zone = Zone::GetByName(kv.first); + + // Check for the configured zones. + String zoneName = kv.first; + Zone::Ptr zone = Zone::GetByName(zoneName); if (!zone) { Log(LogWarning, "ApiListener") - << "Ignoring config update for unknown zone '" << kv.first << "'."; + << "Ignoring config update from endpoint '" << fromEndpointName + << "' for unknown zone '" << zoneName << "'."; + continue; } - if (ConfigCompiler::HasZoneConfigAuthority(kv.first)) { - Log(LogWarning, "ApiListener") - << "Ignoring config update for zone '" << kv.first << "' because we have an authoritative version of the zone's config."; + // Ignore updates where we have an authoritive copy in etc/zones.d, packages, etc. + if (ConfigCompiler::HasZoneConfigAuthority(zoneName)) { + Log(LogInformation, "ApiListener") + << "Ignoring config update from endpoint '" << fromEndpointName + << "' for zone '" << zoneName << "' because we have an authoritative version of the zone's config."; + continue; } - String oldDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones/" + zone->GetName(); + // Put the received configuration into our stage directory. + String productionConfigZoneDir = GetApiZonesDir() + zoneName; + String stageConfigZoneDir = GetApiZonesStageDir() + zoneName; - Utility::MkDirP(oldDir, 0700); + Utility::MkDirP(productionConfigZoneDir, 0700); + Utility::MkDirP(stageConfigZoneDir, 0700); + // Merge the config information. ConfigDirInformation newConfigInfo; newConfigInfo.UpdateV1 = kv.second; + // Load metadata. if (updateV2) newConfigInfo.UpdateV2 = updateV2->Get(kv.first); - Dictionary::Ptr newConfig = kv.second; - ConfigDirInformation oldConfigInfo = LoadConfigDir(oldDir); + // Load checksums. New since 2.11. + if (checksums) + newConfigInfo.Checksums = checksums->Get(kv.first); - if (UpdateConfigDir(oldConfigInfo, newConfigInfo, oldDir, false)) - configChange = true; + // Load the current production config details. + ConfigDirInformation productionConfigInfo = LoadConfigDir(productionConfigZoneDir); + + // Merge updateV1 and updateV2 + Dictionary::Ptr productionConfig = MergeConfigUpdate(productionConfigInfo); + Dictionary::Ptr newConfig = MergeConfigUpdate(newConfigInfo); + + /* If we have received 'checksums' via cluster message, go for it. + * Otherwise do the old timestamp dance for versions < 2.11. + */ + if (checksums) { + Log(LogInformation, "ApiListener") + << "Received configuration for zone '" << zoneName << "' from endpoint '" + << fromEndpointName << "'. Comparing the checksums."; + + // TODO: Do this earlier in hello-handshakes? + if (CheckConfigChange(productionConfigInfo, newConfigInfo)) + configChange = true; + + } else { + /* Fallback to timestamp handling when the parent endpoint didn't send checks. + * This can happen when the satellite is 2.11 and the master is 2.10. + * + * TODO: Deprecate and remove this behaviour in 2.13+. + */ + + Log(LogWarning, "ApiListener") + << "Received configuration update without checksums from parent endpoint " + << fromEndpointName << ". This behaviour is deprecated. Please upgrade the parent endpoint to 2.11+"; + + double productionTimestamp; + + if (!productionConfig->Contains("/.timestamp")) + productionTimestamp = 0; + else + productionTimestamp = productionConfig->Get("/.timestamp"); + + double newTimestamp; + + if (!newConfig->Contains("/.timestamp")) + newTimestamp = Utility::GetTime(); + else + newTimestamp = newConfig->Get("/.timestamp"); + + // Skip update if our configuration files are more recent. + if (productionTimestamp >= newTimestamp) { + + Log(LogInformation, "ApiListener") + << "Our configuration is more recent than the received configuration update." + << " Ignoring configuration file update for path '" << stageConfigZoneDir << "'. Current timestamp '" + << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", productionTimestamp) << "' (" + << std::fixed << std::setprecision(6) << productionTimestamp + << ") >= received timestamp '" + << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", newTimestamp) << "' (" + << newTimestamp << ")."; + + } else { + configChange = true; + } + + // Keep another hack when there's a timestamp file missing. + { + ObjectLock olock(newConfig); + + for (const Dictionary::Pair &kv : newConfig) { + + // This is super expensive with a string content comparison. + if (productionConfig->Get(kv.first) != kv.second) { + if (!Utility::Match("*/.timestamp", kv.first)) + configChange = true; + } + } + } + + // Update the .timestamp file. + String tsPath = stageConfigZoneDir + "/.timestamp"; + if (!Utility::PathExists(tsPath)) { + std::ofstream fp(tsPath.CStr(), std::ofstream::out | std::ostream::trunc); + fp << std::fixed << newTimestamp; + fp.close(); + } + } + + // Dump the received configuration for this zone into the stage directory. + size_t numBytes = 0; + + { + ObjectLock olock(newConfig); + + for (const Dictionary::Pair& kv : newConfig) { + + /* Store the relative config file path for later validation and activation. + * IMPORTANT: Store this prior to any filters. + * */ + relativePaths.push_back(zoneName + "/" + kv.first); + + String path = stageConfigZoneDir + "/" + kv.first; + + if (Utility::Match("*.conf", path)) { + Log(LogInformation, "ApiListener") + << "Stage: Updating received configuration file '" << path << "' for zone '" << zoneName << "'."; + } + + // Sync string content only. + String content = kv.second; + + // Generate a directory tree (zones/1/2/3 might not exist yet). + Utility::MkDirP(Utility::DirName(path), 0755); + + // Write the content to file. + std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc); + fp << content; + fp.close(); + + numBytes += content.GetLength(); + } + } + + Log(LogInformation, "ApiListener") + << "Applying configuration file update for path '" << stageConfigZoneDir << "' (" + << numBytes << " Bytes)."; + + // If the update removes a path, delete it on disk and signal a config change. + { + ObjectLock xlock(productionConfig); + + for (const Dictionary::Pair& kv : productionConfig) { + if (!newConfig->Contains(kv.first)) { + configChange = true; + + String path = stageConfigZoneDir + "/" + kv.first; + Utility::Remove(path); + } + } + } + + count++; } + /* + * We have processed all configuration files and stored them in the staging directory. + * + * We need to store them locally for later analysis. A config change means + * that we will validate the configuration in a separate process sandbox, + * and only copy the configuration to production when everything is ok. + * + * A successful validation also triggers the final restart. + */ if (configChange) { - Log(LogInformation, "ApiListener", "Restarting after configuration change."); - Application::RequestRestart(); + Log(LogInformation, "ApiListener") + << "Received configuration updates (" << count << ") from endpoint '" << fromEndpointName + << "' are different to production, triggering validation and reload."; + AsyncTryActivateZonesStage(relativePaths); + } else { + Log(LogInformation, "ApiListener") + << "Received configuration updates (" << count << ") from endpoint '" << fromEndpointName + << "' are equal to production, not triggering reload."; } return Empty; } + +/** + * Callback for stage config validation. + * When validation was successful, the configuration is copied from + * stage to production and a restart is triggered. + * On failure, there's no restart and this is logged. + * + * @param pr Result of the validation process. + * @param relativePaths Collected paths including the zone name, which are copied from stage to current directories. + */ +void ApiListener::TryActivateZonesStageCallback(const ProcessResult& pr, + const std::vector& relativePaths) +{ + String apiZonesDir = GetApiZonesDir(); + String apiZonesStageDir = GetApiZonesStageDir(); + + String logFile = apiZonesStageDir + "/startup.log"; + std::ofstream fpLog(logFile.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc); + fpLog << pr.Output; + fpLog.close(); + + String statusFile = apiZonesStageDir + "/status"; + std::ofstream fpStatus(statusFile.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc); + fpStatus << pr.ExitStatus; + fpStatus.close(); + + // Validation went fine, copy stage and reload. + if (pr.ExitStatus == 0) { + Log(LogInformation, "ApiListener") + << "Config validation for stage '" << apiZonesStageDir << "' was OK, replacing into '" << apiZonesDir << "' and triggering reload."; + + // Purge production before copying stage. + if (Utility::PathExists(apiZonesDir)) + Utility::RemoveDirRecursive(apiZonesDir); + + Utility::MkDirP(apiZonesDir, 0700); + + // Copy all synced configuration files from stage to production. + for (const String& path : relativePaths) { + if (!Utility::PathExists(apiZonesStageDir + path)) + continue; + + Log(LogInformation, "ApiListener") + << "Copying file '" << path << "' from config sync staging to production zones directory."; + + String stagePath = apiZonesStageDir + path; + String currentPath = apiZonesDir + path; + + Utility::MkDirP(Utility::DirName(currentPath), 0700); + + Utility::CopyFile(stagePath, currentPath); + } + + // Clear any failed deployment before + ApiListener::Ptr listener = ApiListener::GetInstance(); + + if (listener) + listener->ClearLastFailedZonesStageValidation(); + + Application::RequestRestart(); + + // All good, return early. + return; + } + + // Error case. + Log(LogCritical, "ApiListener") + << "Config validation failed for staged cluster config sync in '" << apiZonesStageDir + << "'. Aborting. Logs: '" << logFile << "'"; + + ApiListener::Ptr listener = ApiListener::GetInstance(); + + if (listener) + listener->UpdateLastFailedZonesStageValidation(pr.Output); +} + +/** + * Spawns a new validation process and waits for its output. + * Sets 'System.ZonesStageVarDir' to override the config validation zone dirs with our current stage. + * + * @param relativePaths Required for later file operations in the callback. Provides the zone name plus path in a list. + */ +void ApiListener::AsyncTryActivateZonesStage(const std::vector& relativePaths) +{ + VERIFY(Application::GetArgC() >= 1); + + /* Inherit parent process args. */ + Array::Ptr args = new Array({ + Application::GetExePath(Application::GetArgV()[0]), + }); + + for (int i = 1; i < Application::GetArgC(); i++) { + String argV = Application::GetArgV()[i]; + + if (argV == "-d" || argV == "--daemonize") + continue; + + args->Add(argV); + } + + args->Add("--validate"); + + // Set the ZonesStageDir. This creates our own local chroot without any additional automated zone includes. + args->Add("--define"); + args->Add("System.ZonesStageVarDir=" + GetApiZonesStageDir()); + + Process::Ptr process = new Process(Process::PrepareCommand(args)); + process->SetTimeout(Application::GetReloadTimeout()); + process->Run(std::bind(&TryActivateZonesStageCallback, _1, relativePaths)); +} + +/** + * Update the structure from the last failed validation output. + * Uses the current timestamp. + * + * @param log The process output from the config validation. + */ +void ApiListener::UpdateLastFailedZonesStageValidation(const String& log) +{ + Dictionary::Ptr lastFailedZonesStageValidation = new Dictionary({ + { "log", log }, + { "ts", Utility::GetTime() } + }); + + SetLastFailedZonesStageValidation(lastFailedZonesStageValidation); +} + +/** + * Clear the structure for the last failed reload. + * + */ +void ApiListener::ClearLastFailedZonesStageValidation() +{ + SetLastFailedZonesStageValidation(Dictionary::Ptr()); +} + +/** + * Generate a config checksum. + * + * @param content String content used for generating the checksum. + * @returns The checksum as string. + */ +String ApiListener::GetChecksum(const String& content) +{ + return SHA256(content); +} + +bool ApiListener::CheckConfigChange(const ConfigDirInformation& oldConfig, const ConfigDirInformation& newConfig) +{ + Dictionary::Ptr oldChecksums = oldConfig.Checksums; + Dictionary::Ptr newChecksums = newConfig.Checksums; + + // TODO: Figure out whether normal users need this for debugging. + Log(LogDebug, "ApiListener") + << "Checking for config change between stage and production. Old (" << oldChecksums->GetLength() << "): '" + << JsonEncode(oldChecksums) + << "' vs. new (" << newChecksums->GetLength() << "): '" + << JsonEncode(newChecksums) << "'."; + + /* Since internal files are synced here too, we can not depend on length. + * So we need to go through both checksum sets to cover the cases"everything is new" and "everything was deleted". + */ + { + ObjectLock olock(oldChecksums); + for (const Dictionary::Pair& kv : oldChecksums) { + String path = kv.first; + String oldChecksum = kv.second; + + /* Ignore internal files, especially .timestamp and .checksums. + * + * If we don't, this results in "always change" restart loops. + */ + if (Utility::Match("/.*", path)) { + Log(LogDebug, "ApiListener") + << "Ignoring old internal file '" << path << "'."; + + continue; + } + + Log(LogDebug, "ApiListener") + << "Checking " << path << " for old checksum: " << oldChecksum << "."; + + // Check if key exists first for more verbose logging. + // Note: Don't do this later on. + if (!newChecksums->Contains(path)) { + Log(LogDebug, "ApiListener") + << "File '" << path << "' was deleted by remote."; + + return true; + } + + String newChecksum = newChecksums->Get(path); + + if (newChecksum != kv.second) { + Log(LogDebug, "ApiListener") + << "Path '" << path << "' doesn't match old checksum '" + << oldChecksum << "' with new checksum '" << newChecksum << "'."; + + return true; + } + } + } + + { + ObjectLock olock(newChecksums); + for (const Dictionary::Pair& kv : newChecksums) { + String path = kv.first; + String newChecksum = kv.second; + + /* Ignore internal files, especially .timestamp and .checksums. + * + * If we don't, this results in "always change" restart loops. + */ + if (Utility::Match("/.*", path)) { + Log(LogDebug, "ApiListener") + << "Ignoring new internal file '" << path << "'."; + + continue; + } + + Log(LogDebug, "ApiListener") + << "Checking " << path << " for new checksum: " << newChecksum << "."; + + // Check if the checksum exists, checksums in both sets have already been compared + if (!oldChecksums->Contains(path)) { + Log(LogDebug, "ApiListener") + << "File '" << path << "' was added by remote."; + + return true; + } + } + } + + return false; +} + +/** + * Load the given config dir and read their file content into the config structure. + * + * @param dir Path to the config directory. + * @returns ConfigDirInformation structure. + */ +ConfigDirInformation ApiListener::LoadConfigDir(const String& dir) +{ + ConfigDirInformation config; + config.UpdateV1 = new Dictionary(); + config.UpdateV2 = new Dictionary(); + config.Checksums = new Dictionary(); + + Utility::GlobRecursive(dir, "*", std::bind(&ApiListener::ConfigGlobHandler, std::ref(config), dir, _1), GlobFile); + return config; +} + +/** + * Read the given file and store it in the config information structure. + * Callback function for Glob(). + * + * @param config Reference to the config information object. + * @param path File path. + * @param file Full file name. + */ +void ApiListener::ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file) +{ + // Avoid loading the authoritative marker for syncs at all cost. + if (Utility::BaseName(file) == ".authoritative") + return; + + CONTEXT("Creating config update for file '" + file + "'"); + + Log(LogNotice, "ApiListener") + << "Creating config update for file '" << file << "'."; + + std::ifstream fp(file.CStr(), std::ifstream::binary); + if (!fp) + return; + + String content((std::istreambuf_iterator(fp)), std::istreambuf_iterator()); + + Dictionary::Ptr update; + String relativePath = file.SubStr(path.GetLength()); + + /* + * 'update' messages contain conf files. 'update_v2' syncs everything else (.timestamp). + * + * **Keep this intact to stay compatible with older clients.** + */ + String sanitizedContent = Utility::ValidateUTF8(content); + + if (Utility::Match("*.conf", file)) { + update = config.UpdateV1; + + // Configuration files should be automatically sanitized with UTF8. + update->Set(relativePath, sanitizedContent); + } else { + update = config.UpdateV2; + + /* + * Ensure that only valid UTF8 content is being read for the cluster config sync. + * Binary files are not supported when wrapped into JSON encoded messages. + * Rationale: https://github.com/Icinga/icinga2/issues/7382 + */ + if (content != sanitizedContent) { + Log(LogCritical, "ApiListener") + << "Ignoring file '" << file << "' for cluster config sync: Does not contain valid UTF8. Binary files are not supported."; + return; + } + + update->Set(relativePath, content); + } + + /* Calculate a checksum for each file (and a global one later). + * + * IMPORTANT: Ignore the .authoritative file above, this must not be synced. + * */ + config.Checksums->Set(relativePath, GetChecksum(content)); +} + +/** + * Compatibility helper for merging config update v1 and v2 into a global result. + * + * @param config Config information structure. + * @returns Dictionary which holds the merged information. + */ +Dictionary::Ptr ApiListener::MergeConfigUpdate(const ConfigDirInformation& config) +{ + Dictionary::Ptr result = new Dictionary(); + + if (config.UpdateV1) + config.UpdateV1->CopyTo(result); + + if (config.UpdateV2) + config.UpdateV2->CopyTo(result); + + return result; +} diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index d7e01da67..81385e6da 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/apilistener.hpp" #include "remote/apilistener-ti.cpp" @@ -23,7 +6,11 @@ #include "remote/endpoint.hpp" #include "remote/jsonrpc.hpp" #include "remote/apifunction.hpp" +#include "remote/configpackageutility.hpp" +#include "remote/configobjectutility.hpp" #include "base/convert.hpp" +#include "base/defer.hpp" +#include "base/io-engine.hpp" #include "base/netstring.hpp" #include "base/json.hpp" #include "base/configtype.hpp" @@ -35,7 +22,20 @@ #include "base/context.hpp" #include "base/statsfunction.hpp" #include "base/exception.hpp" +#include "base/tcpsocket.hpp" +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include using namespace icinga; @@ -56,22 +56,32 @@ ApiListener::ApiListener() String ApiListener::GetApiDir() { - return Application::GetLocalStateDir() + "/lib/icinga2/api/"; + return Configuration::DataDir + "/api/"; +} + +String ApiListener::GetApiZonesDir() +{ + return GetApiDir() + "zones/"; +} + +String ApiListener::GetApiZonesStageDir() +{ + return GetApiDir() + "zones-stage/"; } String ApiListener::GetCertsDir() { - return Application::GetLocalStateDir() + "/lib/icinga2/certs/"; + return Configuration::DataDir + "/certs/"; } String ApiListener::GetCaDir() { - return Application::GetLocalStateDir() + "/lib/icinga2/ca/"; + return Configuration::DataDir + "/ca/"; } String ApiListener::GetCertificateRequestsDir() { - return Application::GetLocalStateDir() + "/lib/icinga2/certificate-requests/"; + return Configuration::DataDir + "/certificate-requests/"; } String ApiListener::GetDefaultCertPath() @@ -89,6 +99,16 @@ String ApiListener::GetDefaultCaPath() return GetCertsDir() + "/ca.crt"; } +double ApiListener::GetTlsHandshakeTimeout() const +{ + return Configuration::TlsHandshakeTimeout; +} + +void ApiListener::SetTlsHandshakeTimeout(double value, bool suppress_events, const Value& cookie) +{ + Configuration::TlsHandshakeTimeout = value; +} + void ApiListener::CopyCertificateFile(const String& oldCertPath, const String& newCertPath) { struct stat st1, st2; @@ -123,9 +143,15 @@ void ApiListener::OnConfigLoaded() CopyCertificateFile(oldCaPath, defaultCaPath); if (!oldCertPath.IsEmpty() && !oldKeyPath.IsEmpty() && !oldCaPath.IsEmpty()) { - Log(LogWarning, "ApiListener", "Please read the upgrading documentation for v2.8: https://www.icinga.com/docs/icinga2/latest/doc/16-upgrading-icinga-2/"); + Log(LogWarning, "ApiListener", "Please read the upgrading documentation for v2.8: https://icinga.com/docs/icinga2/latest/doc/16-upgrading-icinga-2/"); } + /* Create the internal API object storage. */ + ConfigObjectUtility::CreateStorage(); + + /* Cache API packages and their active stage name. */ + UpdateActivePackageStagesCache(); + /* set up SSL context */ std::shared_ptr cert; try { @@ -150,10 +176,12 @@ void ApiListener::OnConfigLoaded() void ApiListener::UpdateSSLContext() { - std::shared_ptr context; + namespace ssl = boost::asio::ssl; + + std::shared_ptr context; try { - context = MakeSSLContext(GetDefaultCertPath(), GetDefaultKeyPath(), GetDefaultCaPath()); + context = MakeAsioSslContext(GetDefaultCertPath(), GetDefaultKeyPath(), GetDefaultCaPath()); } catch (const std::exception&) { BOOST_THROW_EXCEPTION(ScriptError("Cannot make SSL context for cert path: '" + GetDefaultCertPath() + "' key path: '" + GetDefaultKeyPath() + "' ca path: '" + GetDefaultCaPath() + "'.", GetDebugInfo())); @@ -214,13 +242,12 @@ void ApiListener::Start(bool runtimeCreated) Log(LogInformation, "ApiListener") << "'" << GetName() << "' started."; - SyncZoneDirs(); + SyncLocalZoneDirs(); ObjectImpl::Start(runtimeCreated); { boost::mutex::scoped_lock lock(m_LogLock); - RotateLogFile(); OpenLogFile(); } @@ -239,13 +266,17 @@ void ApiListener::Start(bool runtimeCreated) m_ReconnectTimer = new Timer(); m_ReconnectTimer->OnTimerExpired.connect(std::bind(&ApiListener::ApiReconnectTimerHandler, this)); - m_ReconnectTimer->SetInterval(60); + m_ReconnectTimer->SetInterval(10); m_ReconnectTimer->Start(); m_ReconnectTimer->Reschedule(0); + /* Keep this in relative sync with the cold startup in UpdateObjectAuthority() and the reconnect interval above. + * Previous: 60s reconnect, 30s OA, 60s cold startup. + * Now: 10s reconnect, 10s OA, 30s cold startup. + */ m_AuthorityTimer = new Timer(); m_AuthorityTimer->OnTimerExpired.connect(std::bind(&ApiListener::UpdateObjectAuthority)); - m_AuthorityTimer->SetInterval(30); + m_AuthorityTimer->SetInterval(10); m_AuthorityTimer->Start(); m_CleanupCertificateRequestsTimer = new Timer(); @@ -254,6 +285,11 @@ void ApiListener::Start(bool runtimeCreated) m_CleanupCertificateRequestsTimer->Start(); m_CleanupCertificateRequestsTimer->Reschedule(0); + m_ApiPackageIntegrityTimer = new Timer(); + m_ApiPackageIntegrityTimer->OnTimerExpired.connect(std::bind(&ApiListener::CheckApiPackageIntegrity, this)); + m_ApiPackageIntegrityTimer->SetInterval(300); + m_ApiPackageIntegrityTimer->Start(); + OnMasterChanged(true); } @@ -264,8 +300,13 @@ void ApiListener::Stop(bool runtimeDeleted) Log(LogInformation, "ApiListener") << "'" << GetName() << "' stopped."; - boost::mutex::scoped_lock lock(m_LogLock); - CloseLogFile(); + { + boost::mutex::scoped_lock lock(m_LogLock); + CloseLogFile(); + RotateLogFile(); + } + + RemoveStatusFile(); } ApiListener::Ptr ApiListener::GetInstance() @@ -309,49 +350,95 @@ bool ApiListener::IsMaster() const */ bool ApiListener::AddListener(const String& node, const String& service) { + namespace asio = boost::asio; + namespace ip = asio::ip; + using ip::tcp; + ObjectLock olock(this); - std::shared_ptr sslContext = m_SSLContext; + auto sslContext (m_SSLContext); if (!sslContext) { Log(LogCritical, "ApiListener", "SSL context is required for AddListener()"); return false; } - Log(LogInformation, "ApiListener") - << "Adding new listener on port '" << service << "'"; - - TcpSocket::Ptr server = new TcpSocket(); + auto& io (IoEngine::Get().GetIoContext()); + auto acceptor (std::make_shared(io)); try { - server->Bind(node, service, AF_UNSPEC); - } catch (const std::exception&) { + tcp::resolver resolver (io); + tcp::resolver::query query (node, service, tcp::resolver::query::passive); + + auto result (resolver.resolve(query)); + auto current (result.begin()); + + for (;;) { + try { + acceptor->open(current->endpoint().protocol()); + + { + auto fd (acceptor->native_handle()); + + const int optFalse = 0; + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&optFalse), sizeof(optFalse)); + + const int optTrue = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&optTrue), sizeof(optTrue)); +#ifndef _WIN32 + setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast(&optTrue), sizeof(optTrue)); +#endif /* _WIN32 */ + } + + acceptor->bind(current->endpoint()); + + break; + } catch (const std::exception&) { + if (++current == result.end()) { + throw; + } + + if (acceptor->is_open()) { + acceptor->close(); + } + } + } + } catch (const std::exception& ex) { Log(LogCritical, "ApiListener") - << "Cannot bind TCP socket for host '" << node << "' on port '" << service << "'."; + << "Cannot bind TCP socket for host '" << node << "' on port '" << service << "': " << ex.what(); return false; } - std::thread thread(std::bind(&ApiListener::ListenerThreadProc, this, server)); - thread.detach(); + acceptor->listen(INT_MAX); - m_Servers.insert(server); + auto localEndpoint (acceptor->local_endpoint()); + + Log(LogInformation, "ApiListener") + << "Started new listener on '[" << localEndpoint.address() << "]:" << localEndpoint.port() << "'"; + + IoEngine::SpawnCoroutine(io, [this, acceptor, sslContext](asio::yield_context yc) { ListenerCoroutineProc(yc, acceptor, sslContext); }); + + UpdateStatusFile(localEndpoint); return true; } -void ApiListener::ListenerThreadProc(const Socket::Ptr& server) +void ApiListener::ListenerCoroutineProc(boost::asio::yield_context yc, const std::shared_ptr& server, const std::shared_ptr& sslContext) { - Utility::SetThreadName("API Listener"); + namespace asio = boost::asio; - server->Listen(); + auto& io (IoEngine::Get().GetIoContext()); for (;;) { try { - Socket::Ptr client = server->Accept(); - std::thread thread(std::bind(&ApiListener::NewClientHandler, this, client, String(), RoleServer)); - thread.detach(); - } catch (const std::exception&) { - Log(LogCritical, "ApiListener", "Cannot accept new connection."); + auto sslConn (std::make_shared(io, *sslContext)); + + server->async_accept(sslConn->lowest_layer(), yc); + + IoEngine::SpawnCoroutine(io, [this, sslConn](asio::yield_context yc) { NewClientHandler(yc, sslConn, String(), RoleServer); }); + } catch (const std::exception& ex) { + Log(LogCritical, "ApiListener") + << "Cannot accept new connection: " << ex.what(); } } } @@ -363,55 +450,48 @@ void ApiListener::ListenerThreadProc(const Socket::Ptr& server) */ void ApiListener::AddConnection(const Endpoint::Ptr& endpoint) { - { - ObjectLock olock(this); + namespace asio = boost::asio; + using asio::ip::tcp; - std::shared_ptr sslContext = m_SSLContext; + auto sslContext (m_SSLContext); - if (!sslContext) { - Log(LogCritical, "ApiListener", "SSL context is required for AddConnection()"); - return; + if (!sslContext) { + Log(LogCritical, "ApiListener", "SSL context is required for AddConnection()"); + return; + } + + auto& io (IoEngine::Get().GetIoContext()); + + IoEngine::SpawnCoroutine(io, [this, endpoint, &io, sslContext](asio::yield_context yc) { + String host = endpoint->GetHost(); + String port = endpoint->GetPort(); + + Log(LogInformation, "ApiListener") + << "Reconnecting to endpoint '" << endpoint->GetName() << "' via host '" << host << "' and port '" << port << "'"; + + try { + auto sslConn (std::make_shared(io, *sslContext, endpoint->GetName())); + + Connect(sslConn->lowest_layer(), host, port, yc); + + NewClientHandler(yc, sslConn, endpoint->GetName(), RoleClient); + + endpoint->SetConnecting(false); + Log(LogInformation, "ApiListener") + << "Finished reconnecting to endpoint '" << endpoint->GetName() << "' via host '" << host << "' and port '" << port << "'"; + } catch (const std::exception& ex) { + endpoint->SetConnecting(false); + + Log(LogCritical, "ApiListener") + << "Cannot connect to host '" << host << "' on port '" << port << "': " << ex.what(); } - } - - String host = endpoint->GetHost(); - String port = endpoint->GetPort(); - - Log(LogInformation, "ApiListener") - << "Reconnecting to endpoint '" << endpoint->GetName() << "' via host '" << host << "' and port '" << port << "'"; - - TcpSocket::Ptr client = new TcpSocket(); - - String serverName = endpoint->GetName(); - - String env = ScriptGlobal::Get("Environment", &Empty); - if (env != "" && env != "production") - serverName += ":" + env; - - try { - endpoint->SetConnecting(true); - client->Connect(host, port); - NewClientHandler(client, serverName, RoleClient); - endpoint->SetConnecting(false); - } catch (const std::exception& ex) { - endpoint->SetConnecting(false); - client->Close(); - - std::ostringstream info; - info << "Cannot connect to host '" << host << "' on port '" << port << "'"; - Log(LogCritical, "ApiListener", info.str()); - Log(LogDebug, "ApiListener") - << info.str() << "\n" << DiagnosticInformation(ex); - } - - Log(LogInformation, "ApiListener") - << "Finished reconnecting to endpoint '" << endpoint->GetName() << "' via host '" << host << "' and port '" << port << "'"; + }); } -void ApiListener::NewClientHandler(const Socket::Ptr& client, const String& hostname, ConnectionRole role) +void ApiListener::NewClientHandler(boost::asio::yield_context yc, const std::shared_ptr& client, const String& hostname, ConnectionRole role) { try { - NewClientHandlerInternal(client, hostname, role); + NewClientHandlerInternal(yc, client, hostname, role); } catch (const std::exception& ex) { Log(LogCritical, "ApiListener") << "Exception while handling new API client connection: " << DiagnosticInformation(ex, false); @@ -426,46 +506,70 @@ void ApiListener::NewClientHandler(const Socket::Ptr& client, const String& host * * @param client The new client. */ -void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const String& hostname, ConnectionRole role) +void ApiListener::NewClientHandlerInternal(boost::asio::yield_context yc, const std::shared_ptr& client, const String& hostname, ConnectionRole role) { - CONTEXT("Handling new API client connection"); + namespace asio = boost::asio; + namespace ssl = asio::ssl; String conninfo; - if (role == RoleClient) - conninfo = "to"; - else - conninfo = "from"; - - conninfo += " " + client->GetPeerAddress(); - - TlsStream::Ptr tlsStream; - { - ObjectLock olock(this); - try { - tlsStream = new TlsStream(client, hostname, role, m_SSLContext); - } catch (const std::exception&) { - Log(LogCritical, "ApiListener") - << "Cannot create TLS stream from client connection (" << conninfo << ")"; - return; + std::ostringstream conninfo_; + + if (role == RoleClient) { + conninfo_ << "to"; + } else { + conninfo_ << "from"; } + + auto endpoint (client->lowest_layer().remote_endpoint()); + + conninfo_ << " [" << endpoint.address() << "]:" << endpoint.port(); + + conninfo = conninfo_.str(); } - try { - tlsStream->Handshake(); - } catch (const std::exception&) { + auto& sslConn (client->next_layer()); + + boost::system::error_code ec; + + sslConn.async_handshake(role == RoleClient ? sslConn.client : sslConn.server, yc[ec]); + + if (ec) { + // https://github.com/boostorg/beast/issues/915 + // Google Chrome 73+ seems not close the connection properly, https://stackoverflow.com/questions/56272906/how-to-fix-certificate-unknown-error-from-chrome-v73 + if (ec == asio::ssl::error::stream_truncated) { + Log(LogNotice, "ApiListener") + << "TLS stream was truncated, ignoring connection from " << conninfo; + return; + } + Log(LogCritical, "ApiListener") - << "Client TLS handshake failed (" << conninfo << ")"; + << "Client TLS handshake failed (" << conninfo << "): " << ec.message(); return; } - std::shared_ptr cert = tlsStream->GetPeerCertificate(); + bool willBeShutDown = false; + + Defer shutDownIfNeeded ([&sslConn, &willBeShutDown, &yc]() { + if (!willBeShutDown) { + // Ignore the error, but do not throw an exception being swallowed at all cost. + // https://github.com/Icinga/icinga2/issues/7351 + boost::system::error_code ec; + sslConn.async_shutdown(yc[ec]); + } + }); + + std::shared_ptr cert (sslConn.GetPeerCertificate()); + bool verify_ok = false; String identity; Endpoint::Ptr endpoint; - bool verify_ok = false; if (cert) { + verify_ok = sslConn.IsVerifyOK(); + + String verifyError = sslConn.GetVerifyError(); + try { identity = GetCertificateCN(cert); } catch (const std::exception&) { @@ -474,7 +578,6 @@ void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const Stri return; } - verify_ok = tlsStream->IsVerifyOK(); if (!hostname.IsEmpty()) { if (identity != hostname) { Log(LogWarning, "ApiListener") @@ -484,22 +587,22 @@ void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const Stri } else if (!verify_ok) { Log(LogWarning, "ApiListener") << "Certificate validation failed for endpoint '" << hostname - << "': " << tlsStream->GetVerifyError(); + << "': " << verifyError; } } - if (verify_ok) + if (verify_ok) { endpoint = Endpoint::GetByName(identity); + } - { - Log log(LogInformation, "ApiListener"); + Log log(LogInformation, "ApiListener"); - log << "New client connection for identity '" << identity << "' " << conninfo; + log << "New client connection for identity '" << identity << "' " << conninfo; - if (!verify_ok) - log << " (certificate validation failed: " << tlsStream->GetVerifyError() << ")"; - else if (!endpoint) - log << " (no Endpoint object found for identity)"; + if (!verify_ok) { + log << " (certificate validation failed: " << verifyError << ")"; + } else if (!endpoint) { + log << " (no Endpoint object found for identity)"; } } else { Log(LogInformation, "ApiListener") @@ -509,62 +612,85 @@ void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const Stri ClientType ctype; if (role == RoleClient) { - Dictionary::Ptr message = new Dictionary({ + JsonRpc::SendMessage(client, new Dictionary({ { "jsonrpc", "2.0" }, { "method", "icinga::Hello" }, { "params", new Dictionary() } - }); + }), yc); + + client->async_flush(yc); - JsonRpc::SendMessage(tlsStream, message); ctype = ClientJsonRpc; } else { - tlsStream->WaitForData(10); + { + boost::system::error_code ec; - if (!tlsStream->IsDataAvailable()) { - if (identity.IsEmpty()) - Log(LogInformation, "ApiListener") - << "No data received on new API connection. " - << "Ensure that the remote endpoints are properly configured in a cluster setup."; - else - Log(LogWarning, "ApiListener") - << "No data received on new API connection for identity '" << identity << "'. " - << "Ensure that the remote endpoints are properly configured in a cluster setup."; - return; + if (client->async_fill(yc[ec]) == 0u) { + if (identity.IsEmpty()) { + Log(LogInformation, "ApiListener") + << "No data received on new API connection " << conninfo << ". " + << "Ensure that the remote endpoints are properly configured in a cluster setup."; + } else { + Log(LogWarning, "ApiListener") + << "No data received on new API connection " << conninfo << " for identity '" << identity << "'. " + << "Ensure that the remote endpoints are properly configured in a cluster setup."; + } + + return; + } } - char firstByte; - tlsStream->Peek(&firstByte, 1, false); + char firstByte = 0; - if (firstByte >= '0' && firstByte <= '9') + { + asio::mutable_buffer firstByteBuf (&firstByte, 1); + client->peek(firstByteBuf); + } + + if (firstByte >= '0' && firstByte <= '9') { ctype = ClientJsonRpc; - else + } else { ctype = ClientHttp; + } } if (ctype == ClientJsonRpc) { Log(LogNotice, "ApiListener", "New JSON-RPC client"); - JsonRpcConnection::Ptr aclient = new JsonRpcConnection(identity, verify_ok, tlsStream, role); - aclient->Start(); + JsonRpcConnection::Ptr aclient = new JsonRpcConnection(identity, verify_ok, client, role); if (endpoint) { bool needSync = !endpoint->GetConnected(); endpoint->AddClient(aclient); - m_SyncQueue.Enqueue(std::bind(&ApiListener::SyncClient, this, aclient, endpoint, needSync)); - } else { - if (!AddAnonymousClient(aclient)) { - Log(LogNotice, "ApiListener", "Ignoring anonymous JSON-RPC connection. Max connections exceeded."); - aclient->Disconnect(); - } + IoEngine::SpawnCoroutine(IoEngine::Get().GetIoContext(), [this, aclient, endpoint, needSync](asio::yield_context yc) { + CpuBoundWork syncClient (yc); + + SyncClient(aclient, endpoint, needSync); + }); + + } else if (!AddAnonymousClient(aclient)) { + Log(LogNotice, "ApiListener") + << "Ignoring anonymous JSON-RPC connection " << conninfo + << ". Max connections (" << GetMaxAnonymousClients() << ") exceeded."; + + aclient = nullptr; + } + + if (aclient) { + aclient->Start(); + + willBeShutDown = true; } } else { Log(LogNotice, "ApiListener", "New HTTP client"); - HttpServerConnection::Ptr aclient = new HttpServerConnection(identity, verify_ok, tlsStream); - aclient->Start(); + HttpServerConnection::Ptr aclient = new HttpServerConnection(identity, verify_ok, client); AddHttpClient(aclient); + aclient->Start(); + + willBeShutDown = true; } } @@ -653,11 +779,19 @@ void ApiListener::ApiTimerHandler() for (int ts : files) { bool need = false; + auto localZone (GetLocalEndpoint()->GetZone()); for (const Endpoint::Ptr& endpoint : ConfigType::GetObjectsByType()) { if (endpoint == GetLocalEndpoint()) continue; + auto zone (endpoint->GetZone()); + + /* only care for endpoints in a) the same zone b) our parent zone c) immediate child zones */ + if (!(zone == localZone || zone == localZone->GetParent() || zone->GetParent() == localZone)) { + continue; + } + if (endpoint->GetLogDuration() >= 0 && ts < now - endpoint->GetLogDuration()) continue; @@ -700,10 +834,11 @@ void ApiListener::ApiTimerHandler() } for (const JsonRpcConnection::Ptr& client : endpoint->GetClients()) { - if (client->GetTimestamp() != maxTs) - client->Disconnect(); - else + if (client->GetTimestamp() == maxTs) { client->SendMessage(lmessage); + } else { + client->Disconnect(); + } } Log(LogNotice, "ApiListener") @@ -761,8 +896,10 @@ void ApiListener::ApiReconnectTimerHandler() continue; } - std::thread thread(std::bind(&ApiListener::AddConnection, this, endpoint)); - thread.detach(); + /* Set connecting state to prevent duplicated queue inserts later. */ + endpoint->SetConnecting(true); + + AddConnection(endpoint); } } @@ -873,21 +1010,21 @@ void ApiListener::SyncSendMessage(const Endpoint::Ptr& endpoint, const Dictionar } } -bool ApiListener::RelayMessageOne(const Zone::Ptr& targetZone, const MessageOrigin::Ptr& origin, const Dictionary::Ptr& message, const Endpoint::Ptr& currentMaster) +bool ApiListener::RelayMessageOne(const Zone::Ptr& targetZone, const MessageOrigin::Ptr& origin, const Dictionary::Ptr& message, const Endpoint::Ptr& currentZoneMaster) { ASSERT(targetZone); - Zone::Ptr myZone = Zone::GetLocalZone(); + Zone::Ptr localZone = Zone::GetLocalZone(); - /* only relay the message to a) the same zone, b) the parent zone and c) direct child zones. Exception is a global zone. */ + /* only relay the message to a) the same local zone, b) the parent zone and c) direct child zones. Exception is a global zone. */ if (!targetZone->GetGlobal() && - targetZone != myZone && - targetZone != myZone->GetParent() && - targetZone->GetParent() != myZone) { + targetZone != localZone && + targetZone != localZone->GetParent() && + targetZone->GetParent() != localZone) { return true; } - Endpoint::Ptr myEndpoint = GetLocalEndpoint(); + Endpoint::Ptr localEndpoint = GetLocalEndpoint(); std::vector skippedEndpoints; @@ -896,11 +1033,11 @@ bool ApiListener::RelayMessageOne(const Zone::Ptr& targetZone, const MessageOrig std::set targetEndpoints; if (targetZone->GetGlobal()) { - targetEndpoints = myZone->GetEndpoints(); + targetEndpoints = localZone->GetEndpoints(); for (const Zone::Ptr& zone : ConfigType::GetObjectsByType()) { /* Fetch immediate child zone members */ - if (zone->GetParent() == myZone) { + if (zone->GetParent() == localZone) { std::set endpoints = zone->GetEndpoints(); targetEndpoints.insert(endpoints.begin(), endpoints.end()); } @@ -909,16 +1046,16 @@ bool ApiListener::RelayMessageOne(const Zone::Ptr& targetZone, const MessageOrig targetEndpoints = targetZone->GetEndpoints(); } - for (const Endpoint::Ptr& endpoint : targetEndpoints) { - /* don't relay messages to ourselves */ - if (endpoint == GetLocalEndpoint()) + for (const Endpoint::Ptr& targetEndpoint : targetEndpoints) { + /* Don't relay messages to ourselves. */ + if (targetEndpoint == localEndpoint) continue; log_needed = true; - /* don't relay messages to disconnected endpoints */ - if (!endpoint->GetConnected()) { - if (targetZone == myZone) + /* Don't relay messages to disconnected endpoints. */ + if (!targetEndpoint->GetConnected()) { + if (targetZone == localZone) log_done = false; continue; @@ -926,40 +1063,54 @@ bool ApiListener::RelayMessageOne(const Zone::Ptr& targetZone, const MessageOrig log_done = true; - /* don't relay the message to the zone through more than one endpoint unless this is our own zone */ - if (relayed && targetZone != myZone) { - skippedEndpoints.push_back(endpoint); + /* Don't relay the message to the zone through more than one endpoint unless this is our own zone. + * 'relayed' is set to true on success below, enabling the checks in the second iteration. + */ + if (relayed && targetZone != localZone) { + skippedEndpoints.push_back(targetEndpoint); continue; } - /* don't relay messages back to the endpoint which we got the message from */ - if (origin && origin->FromClient && endpoint == origin->FromClient->GetEndpoint()) { - skippedEndpoints.push_back(endpoint); + /* Don't relay messages back to the endpoint which we got the message from. */ + if (origin && origin->FromClient && targetEndpoint == origin->FromClient->GetEndpoint()) { + skippedEndpoints.push_back(targetEndpoint); continue; } - /* don't relay messages back to the zone which we got the message from */ + /* Don't relay messages back to the zone which we got the message from. */ if (origin && origin->FromZone && targetZone == origin->FromZone) { - skippedEndpoints.push_back(endpoint); + skippedEndpoints.push_back(targetEndpoint); continue; } - /* only relay message to the master if we're not currently the master */ - if (currentMaster != myEndpoint && currentMaster != endpoint) { - skippedEndpoints.push_back(endpoint); + /* Only relay message to the zone master if we're not currently the zone master. + * e1 is zone master, e2 and e3 are zone members. + * + * Message is sent from e2 or e3: + * !isMaster == true + * targetEndpoint e1 is zone master -> send the message + * targetEndpoint e3 is not zone master -> skip it, avoid routing loops + * + * Message is sent from e1: + * !isMaster == false -> send the messages to e2 and e3 being the zone routing master. + */ + bool isMaster = (currentZoneMaster == localEndpoint); + + if (!isMaster && targetEndpoint != currentZoneMaster) { + skippedEndpoints.push_back(targetEndpoint); continue; } relayed = true; - SyncSendMessage(endpoint, message); + SyncSendMessage(targetEndpoint, message); } if (!skippedEndpoints.empty()) { double ts = message->Get("ts"); - for (const Endpoint::Ptr& endpoint : skippedEndpoints) - endpoint->SetLocalLogPosition(ts); + for (const Endpoint::Ptr& skippedEndpoint : skippedEndpoints) + skippedEndpoint->SetLocalLogPosition(ts); } return !log_needed || log_done; @@ -993,7 +1144,7 @@ void ApiListener::SyncRelayMessage(const MessageOrigin::Ptr& origin, bool need_log = !RelayMessageOne(target_zone, origin, message, master); - for (const Zone::Ptr& zone : target_zone->GetAllParents()) { + for (const Zone::Ptr& zone : target_zone->GetAllParentsRaw()) { if (!RelayMessageOne(zone, origin, message, master)) need_log = true; } @@ -1042,7 +1193,12 @@ void ApiListener::RotateLogFile() String oldpath = GetApiDir() + "log/current"; String newpath = GetApiDir() + "log/" + Convert::ToString(static_cast(ts)+1); - (void) rename(oldpath.CStr(), newpath.CStr()); + + // If the log is being rotated more than once per second, + // don't overwrite the previous one, but silently deny rotation. + if (!Utility::PathExists(newpath)) { + (void) rename(oldpath.CStr(), newpath.CStr()); + } } void ApiListener::LogGlobHandler(std::vector& files, const String& file) @@ -1095,7 +1251,6 @@ void ApiListener::ReplayLog(const JsonRpcConnection::Ptr& client) boost::mutex::scoped_lock lock(m_LogLock); CloseLogFile(); - RotateLogFile(); if (count == -1 || count > 50000) { OpenLogFile(); @@ -1110,16 +1265,21 @@ void ApiListener::ReplayLog(const JsonRpcConnection::Ptr& client) Utility::Glob(GetApiDir() + "log/*", std::bind(&ApiListener::LogGlobHandler, std::ref(files), _1), GlobFile); std::sort(files.begin(), files.end()); + std::vector> allFiles; + for (int ts : files) { - String path = GetApiDir() + "log/" + Convert::ToString(ts); + if (ts >= peer_ts) { + allFiles.emplace_back(ts, GetApiDir() + "log/" + Convert::ToString(ts)); + } + } - if (ts < peer_ts) - continue; + allFiles.emplace_back(Utility::GetTime() + 1, GetApiDir() + "log/current"); + for (auto& file : allFiles) { Log(LogNotice, "ApiListener") - << "Replaying log: " << path; + << "Replaying log: " << file.second; - auto *fp = new std::fstream(path.CStr(), std::fstream::in | std::fstream::binary); + auto *fp = new std::fstream(file.second.CStr(), std::fstream::in | std::fstream::binary); StdioStream::Ptr logStream = new StdioStream(fp, true); String message; @@ -1139,7 +1299,7 @@ void ApiListener::ReplayLog(const JsonRpcConnection::Ptr& client) pmessage = JsonDecode(message); } catch (const std::exception&) { Log(LogWarning, "ApiListener") - << "Unexpected end-of-file for cluster log: " << path; + << "Unexpected end-of-file for cluster log: " << file.second; /* Log files may be incomplete or corrupted. This is perfectly OK. */ break; @@ -1161,8 +1321,7 @@ void ApiListener::ReplayLog(const JsonRpcConnection::Ptr& client) } try { - size_t bytesSent = NetString::WriteStringToStream(client->GetStream(), pmessage->Get("message")); - endpoint->AddMessageSent(bytesSent); + client->SendRawMessage(pmessage->Get("message")); count++; } catch (const std::exception& ex) { Log(LogWarning, "ApiListener") @@ -1176,8 +1335,8 @@ void ApiListener::ReplayLog(const JsonRpcConnection::Ptr& client) peer_ts = pmessage->Get("timestamp"); - if (ts > logpos_ts + 10) { - logpos_ts = ts; + if (file.first > logpos_ts + 10) { + logpos_ts = file.first; Dictionary::Ptr lmessage = new Dictionary({ { "jsonrpc", "2.0" }, @@ -1187,8 +1346,7 @@ void ApiListener::ReplayLog(const JsonRpcConnection::Ptr& client) }) } }); - size_t bytesSent = JsonRpc::SendMessage(client->GetStream(), lmessage); - endpoint->AddMessageSent(bytesSent); + client->SendMessage(lmessage); } } @@ -1199,9 +1357,10 @@ void ApiListener::ReplayLog(const JsonRpcConnection::Ptr& client) Log(LogInformation, "ApiListener") << "Replayed " << count << " messages."; } - - Log(LogNotice, "ApiListener") - << "Replayed " << count << " messages."; + else { + Log(LogNotice, "ApiListener") + << "Replayed " << count << " messages."; + } if (last_sync) { { @@ -1304,10 +1463,8 @@ std::pair ApiListener::GetStatus() } /* connection stats */ - size_t jsonRpcClients = GetAnonymousClients().size(); + size_t jsonRpcAnonymousClients = GetAnonymousClients().size(); size_t httpClients = GetHttpClients().size(); - size_t workQueueItems = JsonRpcConnection::GetWorkQueueLength(); - size_t workQueueCount = JsonRpcConnection::GetWorkQueueCount(); size_t syncQueueItems = m_SyncQueue.GetLength(); size_t relayQueueItems = m_RelayQueue.GetLength(); double workQueueItemRate = JsonRpcConnection::GetWorkQueueRate(); @@ -1325,9 +1482,7 @@ std::pair ApiListener::GetStatus() { "zones", connectedZones }, { "json_rpc", new Dictionary({ - { "clients", jsonRpcClients }, - { "work_queue_items", workQueueItems }, - { "work_queue_count", workQueueCount }, + { "anonymous_clients", jsonRpcAnonymousClients }, { "sync_queue_items", syncQueueItems }, { "relay_queue_items", relayQueueItems }, { "work_queue_item_rate", workQueueItemRate }, @@ -1345,10 +1500,8 @@ std::pair ApiListener::GetStatus() perfdata->Set("num_conn_endpoints", Convert::ToDouble(allConnectedEndpoints->GetLength())); perfdata->Set("num_not_conn_endpoints", Convert::ToDouble(allNotConnectedEndpoints->GetLength())); - perfdata->Set("num_json_rpc_clients", jsonRpcClients); + perfdata->Set("num_json_rpc_anonymous_clients", jsonRpcAnonymousClients); perfdata->Set("num_http_clients", httpClients); - perfdata->Set("num_json_rpc_work_queue_items", workQueueItems); - perfdata->Set("num_json_rpc_work_queue_count", workQueueCount); perfdata->Set("num_json_rpc_sync_queue_items", syncQueueItems); perfdata->Set("num_json_rpc_relay_queue_items", relayQueueItems); @@ -1373,7 +1526,8 @@ double ApiListener::CalculateZoneLag(const Endpoint::Ptr& endpoint) bool ApiListener::AddAnonymousClient(const JsonRpcConnection::Ptr& aclient) { boost::mutex::scoped_lock lock(m_AnonymousClientsLock); - if (m_AnonymousClients.size() > 25) + + if (GetMaxAnonymousClients() >= 0 && (long)m_AnonymousClients.size() + 1 > (long)GetMaxAnonymousClients()) return false; m_AnonymousClients.insert(aclient); @@ -1420,25 +1574,102 @@ Endpoint::Ptr ApiListener::GetLocalEndpoint() const return m_LocalEndpoint; } +void ApiListener::UpdateActivePackageStagesCache() +{ + boost::mutex::scoped_lock lock(m_ActivePackageStagesLock); + + for (auto package : ConfigPackageUtility::GetPackages()) { + String activeStage; + + try { + activeStage = ConfigPackageUtility::GetActiveStageFromFile(package); + } catch (const std::exception& ex) { + Log(LogCritical, "ApiListener") + << ex.what(); + continue; + } + + Log(LogNotice, "ApiListener") + << "Updating cache: Config package '" << package << "' has active stage '" << activeStage << "'."; + + m_ActivePackageStages[package] = activeStage; + } +} + +void ApiListener::CheckApiPackageIntegrity() +{ + boost::mutex::scoped_lock lock(m_ActivePackageStagesLock); + + for (auto package : ConfigPackageUtility::GetPackages()) { + String activeStage; + try { + activeStage = ConfigPackageUtility::GetActiveStageFromFile(package); + } catch (const std::exception& ex) { + /* An error means that the stage is broken, try to repair it. */ + auto it = m_ActivePackageStages.find(package); + + if (it == m_ActivePackageStages.end()) + continue; + + String activeStageCached = it->second; + + Log(LogInformation, "ApiListener") + << "Repairing broken API config package '" << package + << "', setting active stage '" << activeStageCached << "'."; + + ConfigPackageUtility::SetActiveStageToFile(package, activeStageCached); + } + } +} + +void ApiListener::SetActivePackageStage(const String& package, const String& stage) +{ + boost::mutex::scoped_lock lock(m_ActivePackageStagesLock); + m_ActivePackageStages[package] = stage; +} + +String ApiListener::GetActivePackageStage(const String& package) +{ + boost::mutex::scoped_lock lock(m_ActivePackageStagesLock); + + if (m_ActivePackageStages.find(package) == m_ActivePackageStages.end()) + BOOST_THROW_EXCEPTION(ScriptError("Package " + package + " has no active stage.")); + + return m_ActivePackageStages[package]; +} + +void ApiListener::RemoveActivePackageStage(const String& package) +{ + /* This is the rare occassion when a package has been deleted. */ + boost::mutex::scoped_lock lock(m_ActivePackageStagesLock); + + auto it = m_ActivePackageStages.find(package); + + if (it == m_ActivePackageStages.end()) + return; + + m_ActivePackageStages.erase(it); +} + void ApiListener::ValidateTlsProtocolmin(const Lazy& lvalue, const ValidationUtils& utils) { ObjectImpl::ValidateTlsProtocolmin(lvalue, utils); - if (lvalue() != SSL_TXT_TLSV1 -#ifdef SSL_TXT_TLSV1_1 - && lvalue() != SSL_TXT_TLSV1_1 && - lvalue() != SSL_TXT_TLSV1_2 -#endif /* SSL_TXT_TLSV1_1 */ - ) { - String message = "Invalid TLS version. Must be one of '" SSL_TXT_TLSV1 "'"; -#ifdef SSL_TXT_TLSV1_1 - message += ", '" SSL_TXT_TLSV1_1 "' or '" SSL_TXT_TLSV1_2 "'"; -#endif /* SSL_TXT_TLSV1_1 */ + if (lvalue() != SSL_TXT_TLSV1_2) { + String message = "Invalid TLS version. Must be '" SSL_TXT_TLSV1_2 "'"; BOOST_THROW_EXCEPTION(ValidationError(this, { "tls_protocolmin" }, message)); } } +void ApiListener::ValidateTlsHandshakeTimeout(const Lazy& lvalue, const ValidationUtils& utils) +{ + ObjectImpl::ValidateTlsHandshakeTimeout(lvalue, utils); + + if (lvalue() <= 0) + BOOST_THROW_EXCEPTION(ValidationError(this, { "tls_handshake_timeout" }, "Value must be greater than 0.")); +} + bool ApiListener::IsHACluster() { Zone::Ptr zone = Zone::GetLocalZone(); @@ -1465,3 +1696,20 @@ String ApiListener::GetFromZoneName(const Zone::Ptr& fromZone) return fromZoneName; } + +void ApiListener::UpdateStatusFile(boost::asio::ip::tcp::endpoint localEndpoint) +{ + String path = Configuration::CacheDir + "/api-state.json"; + + Utility::SaveJsonFile(path, 0644, new Dictionary({ + {"host", String(localEndpoint.address().to_string())}, + {"port", localEndpoint.port()} + })); +} + +void ApiListener::RemoveStatusFile() +{ + String path = Configuration::CacheDir + "/api-state.json"; + + Utility::Remove(path); +} diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index b3894992a..ecbeed415 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef APILISTENER_H #define APILISTENER_H @@ -26,10 +9,16 @@ #include "remote/endpoint.hpp" #include "remote/messageorigin.hpp" #include "base/configobject.hpp" +#include "base/process.hpp" #include "base/timer.hpp" #include "base/workqueue.hpp" #include "base/tcpsocket.hpp" #include "base/tlsstream.hpp" +#include "base/threadpool.hpp" +#include +#include +#include +#include #include namespace icinga @@ -44,6 +33,7 @@ struct ConfigDirInformation { Dictionary::Ptr UpdateV1; Dictionary::Ptr UpdateV2; + Dictionary::Ptr Checksums; }; /** @@ -60,6 +50,8 @@ public: ApiListener(); static String GetApiDir(); + static String GetApiZonesDir(); + static String GetApiZonesStageDir(); static String GetCertsDir(); static String GetCaDir(); static String GetCertificateRequestsDir(); @@ -97,6 +89,11 @@ public: static Value ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static Value ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); + /* API config packages */ + void SetActivePackageStage(const String& package, const String& stage); + String GetActivePackageStage(const String& package); + void RemoveActivePackageStage(const String& package); + static Value HelloAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static void UpdateObjectAuthority(); @@ -108,6 +105,15 @@ public: static String GetDefaultKeyPath(); static String GetDefaultCaPath(); + static inline + bool UpdatedObjectAuthority() + { + return m_UpdatedObjectAuthority.load(); + } + + double GetTlsHandshakeTimeout() const override; + void SetTlsHandshakeTimeout(double value, bool suppress_events, const Value& cookie) override; + protected: void OnConfigLoaded() override; void OnAllConfigLoaded() override; @@ -115,10 +121,10 @@ protected: void Stop(bool runtimeDeleted) override; void ValidateTlsProtocolmin(const Lazy& lvalue, const ValidationUtils& utils) override; + void ValidateTlsHandshakeTimeout(const Lazy& lvalue, const ValidationUtils& utils) override; private: - std::shared_ptr m_SSLContext; - std::set m_Servers; + std::shared_ptr m_SSLContext; mutable boost::mutex m_AnonymousClientsLock; mutable boost::mutex m_HttpClientsLock; @@ -129,20 +135,24 @@ private: Timer::Ptr m_ReconnectTimer; Timer::Ptr m_AuthorityTimer; Timer::Ptr m_CleanupCertificateRequestsTimer; + Timer::Ptr m_ApiPackageIntegrityTimer; + Endpoint::Ptr m_LocalEndpoint; static ApiListener::Ptr m_Instance; + static std::atomic m_UpdatedObjectAuthority; void ApiTimerHandler(); void ApiReconnectTimerHandler(); void CleanupCertificateRequestsTimerHandler(); + void CheckApiPackageIntegrity(); bool AddListener(const String& node, const String& service); void AddConnection(const Endpoint::Ptr& endpoint); - void NewClientHandler(const Socket::Ptr& client, const String& hostname, ConnectionRole role); - void NewClientHandlerInternal(const Socket::Ptr& client, const String& hostname, ConnectionRole role); - void ListenerThreadProc(const Socket::Ptr& server); + void NewClientHandler(boost::asio::yield_context yc, const std::shared_ptr& client, const String& hostname, ConnectionRole role); + void NewClientHandlerInternal(boost::asio::yield_context yc, const std::shared_ptr& client, const String& hostname, ConnectionRole role); + void ListenerCoroutineProc(boost::asio::yield_context yc, const std::shared_ptr& server, const std::shared_ptr& sslContext); WorkQueue m_RelayQueue; WorkQueue m_SyncQueue{0, 4}; @@ -151,7 +161,7 @@ private: Stream::Ptr m_LogFile; size_t m_LogMessageCount{0}; - bool RelayMessageOne(const Zone::Ptr& zone, const MessageOrigin::Ptr& origin, const Dictionary::Ptr& message, const Endpoint::Ptr& currentMaster); + bool RelayMessageOne(const Zone::Ptr& zone, const MessageOrigin::Ptr& origin, const Dictionary::Ptr& message, const Endpoint::Ptr& currentZoneMaster); void SyncRelayMessage(const MessageOrigin::Ptr& origin, const ConfigObject::Ptr& secobj, const Dictionary::Ptr& message, bool log); void PersistMessage(const Dictionary::Ptr& message, const ConfigObject::Ptr& secobj); @@ -163,17 +173,32 @@ private: static void CopyCertificateFile(const String& oldCertPath, const String& newCertPath); + void UpdateStatusFile(boost::asio::ip::tcp::endpoint localEndpoint); + void RemoveStatusFile(); + /* filesync */ - static ConfigDirInformation LoadConfigDir(const String& dir); - static Dictionary::Ptr MergeConfigUpdate(const ConfigDirInformation& config); - static bool UpdateConfigDir(const ConfigDirInformation& oldConfig, const ConfigDirInformation& newConfig, const String& configDir, bool authoritative); + static boost::mutex m_ConfigSyncStageLock; - void SyncZoneDirs() const; - void SyncZoneDir(const Zone::Ptr& zone) const; + void SyncLocalZoneDirs() const; + void SyncLocalZoneDir(const Zone::Ptr& zone) const; - static void ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file); void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient); + static Dictionary::Ptr MergeConfigUpdate(const ConfigDirInformation& config); + + static ConfigDirInformation LoadConfigDir(const String& dir); + static void ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file); + + static void TryActivateZonesStageCallback(const ProcessResult& pr, + const std::vector& relativePaths); + static void AsyncTryActivateZonesStage(const std::vector& relativePaths); + + static String GetChecksum(const String& content); + static bool CheckConfigChange(const ConfigDirInformation& oldConfig, const ConfigDirInformation& newConfig); + + void UpdateLastFailedZonesStageValidation(const String& log); + void ClearLastFailedZonesStageValidation(); + /* configsync */ void UpdateConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin, const JsonRpcConnection::Ptr& client = nullptr); @@ -182,6 +207,12 @@ private: void SendRuntimeConfigObjects(const JsonRpcConnection::Ptr& aclient); void SyncClient(const JsonRpcConnection::Ptr& aclient, const Endpoint::Ptr& endpoint, bool needSync); + + /* API Config Packages */ + mutable boost::mutex m_ActivePackageStagesLock; + std::map m_ActivePackageStages; + + void UpdateActivePackageStagesCache(); }; } diff --git a/lib/remote/apilistener.ti b/lib/remote/apilistener.ti index e04fa0185..ede50b606 100644 --- a/lib/remote/apilistener.ti +++ b/lib/remote/apilistener.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/i2-remote.hpp" #include "base/configobject.hpp" @@ -35,19 +18,30 @@ class ApiListener : ConfigObject [config, deprecated] String ca_path; [config] String crl_path; [config] String cipher_list { - default {{{ return "ALL:!LOW:!WEAK:!MEDIUM:!EXP:!NULL"; }}} + default {{{ return "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384:AES128-GCM-SHA256"; }}} }; [config] String tls_protocolmin { - default {{{ return "TLSv1"; }}} + default {{{ return "TLSv1.2"; }}} }; - [config] String bind_host; + [config] String bind_host { + default {{{ return Configuration::ApiBindHost; }}} + }; [config] String bind_port { - default {{{ return "5665"; }}} + default {{{ return Configuration::ApiBindPort; }}} }; [config] bool accept_config; [config] bool accept_commands; + [config] int max_anonymous_clients { + default {{{ return -1; }}} + }; + + [config] double tls_handshake_timeout { + get; + set; + default {{{ return Configuration::TlsHandshakeTimeout; }}} + }; [config] String ticket_salt; @@ -60,6 +54,8 @@ class ApiListener : ConfigObject [state, no_user_modify] Timestamp log_message_timestamp; [no_user_modify] String identity; + + [state, no_user_modify] Dictionary::Ptr last_failed_zones_stage_validation; }; } diff --git a/lib/remote/apiuser.cpp b/lib/remote/apiuser.cpp index 02a6efc07..2959d895c 100644 --- a/lib/remote/apiuser.cpp +++ b/lib/remote/apiuser.cpp @@ -1,44 +1,16 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/apiuser.hpp" #include "remote/apiuser-ti.cpp" #include "base/configtype.hpp" #include "base/base64.hpp" #include "base/tlsutility.hpp" +#include "base/utility.hpp" using namespace icinga; REGISTER_TYPE(ApiUser); -void ApiUser::OnConfigLoaded(void) -{ - ObjectImpl::OnConfigLoaded(); - - if (GetPasswordHash().IsEmpty()) { - String hashedPassword = CreateHashedPasswordString(GetPassword(), RandomString(8), 5); - VERIFY(hashedPassword != String()); - SetPasswordHash(hashedPassword); - SetPassword("********"); - } -} - ApiUser::Ptr ApiUser::GetByClientCN(const String& cn) { for (const ApiUser::Ptr& user : ConfigType::GetObjectsByType()) { @@ -75,31 +47,9 @@ ApiUser::Ptr ApiUser::GetByAuthHeader(const String& auth_header) */ if (!user || password.IsEmpty()) return nullptr; - else if (user && user->GetPassword() != password) { - Dictionary::Ptr passwordDict = user->GetPasswordDict(); - if (!passwordDict || !ComparePassword(passwordDict->Get("password"), password, passwordDict->Get("salt"))) - return nullptr; - } + else if (user && !Utility::ComparePasswords(password, user->GetPassword())) + return nullptr; return user; } -Dictionary::Ptr ApiUser::GetPasswordDict(void) const -{ - String password = GetPasswordHash(); - if (password.IsEmpty() || password[0] != '$') - return nullptr; - - String::SizeType saltBegin = password.FindFirstOf('$', 1); - String::SizeType passwordBegin = password.FindFirstOf('$', saltBegin+1); - - if (saltBegin == String::NPos || saltBegin == 1 || passwordBegin == String::NPos) - return nullptr; - - Dictionary::Ptr passwordDict = new Dictionary(); - passwordDict->Set("algorithm", password.SubStr(1, saltBegin - 1)); - passwordDict->Set("salt", password.SubStr(saltBegin + 1, passwordBegin - saltBegin - 1)); - passwordDict->Set("password", password.SubStr(passwordBegin + 1)); - - return passwordDict; -} diff --git a/lib/remote/apiuser.hpp b/lib/remote/apiuser.hpp index 44cb84df7..fc132eee8 100644 --- a/lib/remote/apiuser.hpp +++ b/lib/remote/apiuser.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef APIUSER_H #define APIUSER_H @@ -35,12 +18,8 @@ public: DECLARE_OBJECT(ApiUser); DECLARE_OBJECTNAME(ApiUser); - virtual void OnConfigLoaded(void) override; - static ApiUser::Ptr GetByClientCN(const String& cn); static ApiUser::Ptr GetByAuthHeader(const String& auth_header); - - Dictionary::Ptr GetPasswordDict(void) const; }; } diff --git a/lib/remote/apiuser.ti b/lib/remote/apiuser.ti index a767f833a..85fc764f6 100644 --- a/lib/remote/apiuser.ti +++ b/lib/remote/apiuser.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" #include "base/function.hpp" @@ -29,7 +12,7 @@ class ApiUser : ConfigObject { /* No show config */ [no_user_view, no_user_modify] String password; - [config, no_user_view] String password_hash; + [deprecated, config, no_user_view] String password_hash; [config] String client_cn (ClientCN); [config] array(Value) permissions; }; diff --git a/lib/remote/authority.cpp b/lib/remote/authority.cpp deleted file mode 100644 index 0be53b156..000000000 --- a/lib/remote/authority.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#include "remote/zone.hpp" -#include "remote/apilistener.hpp" -#include "base/configtype.hpp" -#include "base/utility.hpp" - -using namespace icinga; - -void ApiListener::UpdateObjectAuthority() -{ - Zone::Ptr my_zone = Zone::GetLocalZone(); - - std::vector endpoints; - Endpoint::Ptr my_endpoint; - - if (my_zone) { - my_endpoint = Endpoint::GetLocalEndpoint(); - - int num_total = 0; - - for (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(), - [](const ConfigObject::Ptr& a, const ConfigObject::Ptr& b) { - return a->GetName() < b->GetName(); - } - ); - } - - for (const Type::Ptr& type : Type::GetAllTypes()) { - auto *dtype = dynamic_cast(type.get()); - - if (!dtype) - continue; - - for (const ConfigObject::Ptr& object : dtype->GetObjects()) { - if (!object->IsActive() || object->GetHAMode() != HARunOnce) - continue; - - bool authority; - - if (!my_zone) - authority = true; - else - authority = endpoints[Utility::SDBM(object->GetName()) % endpoints.size()] == my_endpoint; - - object->SetAuthority(authority); - } - } -} diff --git a/lib/remote/configfileshandler.cpp b/lib/remote/configfileshandler.cpp index 49703ae00..d714f4d86 100644 --- a/lib/remote/configfileshandler.cpp +++ b/lib/remote/configfileshandler.cpp @@ -1,27 +1,11 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/configfileshandler.hpp" #include "remote/configpackageutility.hpp" #include "remote/httputility.hpp" #include "remote/filterutility.hpp" #include "base/exception.hpp" +#include "base/utility.hpp" #include #include @@ -29,12 +13,23 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/config/files", ConfigFilesHandler); -bool ConfigFilesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool ConfigFilesHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestMethod != "GET") + namespace http = boost::beast::http; + + if (request.method() != http::verb::get) return false; - const std::vector& urlPath = request.RequestUrl->GetPath(); + const std::vector& urlPath = url->GetPath(); if (urlPath.size() >= 4) params->Set("package", urlPath[3]); @@ -47,7 +42,7 @@ bool ConfigFilesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re params->Set("path", boost::algorithm::join(tmpPath, "/")); } - if (request.Headers->Get("accept") == "application/json") { + if (request[http::field::accept] == "application/json") { HttpUtility::SendJsonError(response, params, 400, "Invalid Accept header. Either remove the Accept header or set it to 'application/octet-stream'."); return true; } @@ -86,9 +81,10 @@ bool ConfigFilesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re fp.exceptions(std::ifstream::badbit); String content((std::istreambuf_iterator(fp)), std::istreambuf_iterator()); - response.SetStatus(200, "OK"); - response.AddHeader("Content-Type", "application/octet-stream"); - response.WriteBody(content.CStr(), content.GetLength()); + response.result(http::status::ok); + response.set(http::field::content_type, "application/octet-stream"); + response.body() = content; + response.set(http::field::content_length, response.body().size()); } catch (const std::exception& ex) { HttpUtility::SendJsonError(response, params, 500, "Could not read file.", DiagnosticInformation(ex)); diff --git a/lib/remote/configfileshandler.hpp b/lib/remote/configfileshandler.hpp index 8551aeb9d..ea48b1ef4 100644 --- a/lib/remote/configfileshandler.hpp +++ b/lib/remote/configfileshandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGFILESHANDLER_H #define CONFIGFILESHANDLER_H @@ -30,8 +13,16 @@ class ConfigFilesHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ConfigFilesHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; }; } diff --git a/lib/remote/configobjectutility.cpp b/lib/remote/configobjectutility.cpp index ea58ba79d..3bc67910c 100644 --- a/lib/remote/configobjectutility.cpp +++ b/lib/remote/configobjectutility.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/configobjectutility.hpp" #include "remote/configpackageutility.hpp" @@ -25,15 +8,23 @@ #include "base/configwriter.hpp" #include "base/exception.hpp" #include "base/dependencygraph.hpp" +#include "base/utility.hpp" #include +#include +#include #include using namespace icinga; String ConfigObjectUtility::GetConfigDir() { - return ConfigPackageUtility::GetPackageDir() + "/_api/" + - ConfigPackageUtility::GetActiveStage("_api"); + String prefix = ConfigPackageUtility::GetPackageDir() + "/_api/"; + String activeStage = ConfigPackageUtility::GetActiveStage("_api"); + + if (activeStage.IsEmpty()) + RepairPackage("_api"); + + return prefix + activeStage; } String ConfigObjectUtility::GetObjectConfigPath(const Type::Ptr& type, const String& fullName) @@ -41,10 +32,69 @@ String ConfigObjectUtility::GetObjectConfigPath(const Type::Ptr& type, const Str String typeDir = type->GetPluralName(); boost::algorithm::to_lower(typeDir); - return GetConfigDir() + "/conf.d/" + typeDir + + /* This may throw an exception the caller above must handle. */ + String prefix = GetConfigDir(); + + return prefix + "/conf.d/" + typeDir + "/" + EscapeName(fullName) + ".conf"; } +void ConfigObjectUtility::RepairPackage(const String& package) +{ + /* Try to fix the active stage, whenever we find a directory in there. + * This automatically heals packages < 2.11 which remained broken. + */ + String dir = ConfigPackageUtility::GetPackageDir() + "/" + package + "/"; + + namespace fs = boost::filesystem; + + /* Use iterators to workaround VS builds on Windows. */ + fs::path path(dir.Begin(), dir.End()); + + fs::recursive_directory_iterator end; + + String foundActiveStage; + + for (fs::recursive_directory_iterator it(path); it != end; it++) { + boost::system::error_code ec; + + const fs::path d = *it; + if (fs::is_directory(d, ec)) { + /* Extract the relative directory name. */ + foundActiveStage = d.stem().string(); + + break; // Use the first found directory. + } + } + + if (!foundActiveStage.IsEmpty()) { + Log(LogInformation, "ConfigObjectUtility") + << "Repairing config package '" << package << "' with stage '" << foundActiveStage << "'."; + + ConfigPackageUtility::ActivateStage(package, foundActiveStage); + } else { + BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot repair package '" + package + "', please check the troubleshooting docs.")); + } +} + +void ConfigObjectUtility::CreateStorage() +{ + boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticPackageMutex()); + + /* For now, we only use _api as our creation target. */ + String package = "_api"; + + if (!ConfigPackageUtility::PackageExists(package)) { + Log(LogNotice, "ConfigObjectUtility") + << "Package " << package << " doesn't exist yet, creating it."; + + ConfigPackageUtility::CreatePackage(package); + + String stage = ConfigPackageUtility::CreateStage(package); + ConfigPackageUtility::ActivateStage(package, stage); + } +} + String ConfigObjectUtility::EscapeName(const String& name) { return Utility::EscapeString(name, "<>:\"/\\|?*", true); @@ -98,17 +148,9 @@ String ConfigObjectUtility::CreateObjectConfig(const Type::Ptr& type, const Stri } bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& fullName, - const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation) + const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation, const Value& cookie) { - { - boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticMutex()); - if (!ConfigPackageUtility::PackageExists("_api")) { - ConfigPackageUtility::CreatePackage("_api"); - - String stage = ConfigPackageUtility::CreateStage("_api"); - ConfigPackageUtility::ActivateStage("_api", stage); - } - } + CreateStorage(); ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, fullName); @@ -117,14 +159,17 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full return false; } - String path = GetObjectConfigPath(type, fullName); - Utility::MkDirP(Utility::DirName(path), 0700); + String path; - if (Utility::PathExists(path)) { - errors->Add("Cannot create object '" + fullName + "'. Configuration file '" + path + "' already exists."); + try { + path = GetObjectConfigPath(type, fullName); + } catch (const std::exception& ex) { + errors->Add("Config package broken: " + DiagnosticInformation(ex, false)); return false; } + Utility::MkDirP(Utility::DirName(path), 0700); + std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::trunc); fp << config; fp.close(); @@ -139,16 +184,20 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full expr.reset(); WorkQueue upq; + upq.SetName("ConfigObjectUtility::CreateObject"); + std::vector newItems; - if (!ConfigItem::CommitItems(ascope.GetContext(), upq, newItems) || !ConfigItem::ActivateItems(upq, newItems, true)) { + /* + * Disable logging for object creation, but do so ourselves later on. + * Duplicate the error handling for better logging and debugging here. + */ + if (!ConfigItem::CommitItems(ascope.GetContext(), upq, newItems, true)) { if (errors) { - if (unlink(path.CStr()) < 0 && errno != ENOENT) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("unlink") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(path)); - } + Log(LogNotice, "ConfigObjectUtility") + << "Failed to commit config item '" << fullName << "'. Aborting and emoving config path '" << path << "'."; + + Utility::Remove(path); for (const boost::exception_ptr& ex : upq.GetExceptions()) { errors->Add(DiagnosticInformation(ex, false)); @@ -161,15 +210,51 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full return false; } - ApiListener::UpdateObjectAuthority(); - } catch (const std::exception& ex) { - if (unlink(path.CStr()) < 0 && errno != ENOENT) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("unlink") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(path)); + /* + * Activate the config object. + * uq, items, runtimeCreated, silent, withModAttrs, cookie + * IMPORTANT: Forward the cookie aka origin in order to prevent sync loops in the same zone! + */ + if (!ConfigItem::ActivateItems(upq, newItems, true, true, false, cookie)) { + if (errors) { + Log(LogNotice, "ConfigObjectUtility") + << "Failed to activate config object '" << fullName << "'. Aborting and emoving config path '" << path << "'."; + + Utility::Remove(path); + + for (const boost::exception_ptr& ex : upq.GetExceptions()) { + errors->Add(DiagnosticInformation(ex, false)); + + if (diagnosticInformation) + diagnosticInformation->Add(DiagnosticInformation(ex)); + } + } + + return false; } + /* if (type != Comment::TypeInstance && type != Downtime::TypeInstance) + * Does not work since this would require libicinga, which has a dependency on libremote + * Would work if these libs were static. + */ + if (type->GetName() != "Comment" && type->GetName() != "Downtime") + ApiListener::UpdateObjectAuthority(); + + // At this stage we should have a config object already. If not, it was ignored before. + auto *ctype = dynamic_cast(type.get()); + ConfigObject::Ptr obj = ctype->GetObject(fullName); + + if (obj) { + Log(LogInformation, "ConfigObjectUtility") + << "Created and activated object '" << fullName << "' of type '" << type->GetName() << "'."; + } else { + Log(LogNotice, "ConfigObjectUtility") + << "Object '" << fullName << "' was not created but ignored due to errors."; + } + + } catch (const std::exception& ex) { + Utility::Remove(path); + if (errors) errors->Add(DiagnosticInformation(ex, false)); @@ -183,7 +268,7 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full } bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, - const Array::Ptr& errors, const Array::Ptr& diagnosticInformation) + const Array::Ptr& errors, const Array::Ptr& diagnosticInformation, const Value& cookie) { std::vector parents = DependencyGraph::GetParents(object); @@ -207,7 +292,7 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo if (!parentObj) continue; - DeleteObjectHelper(parentObj, cascade, errors, diagnosticInformation); + DeleteObjectHelper(parentObj, cascade, errors, diagnosticInformation, cookie); } ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, name); @@ -215,8 +300,13 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo try { /* mark this object for cluster delete event */ object->SetExtension("ConfigObjectDeleted", true); - /* triggers signal for DB IDO and other interfaces */ - object->Deactivate(true); + + /* + * Trigger deactivation signal for DB IDO and runtime object delections. + * IMPORTANT: Specify the cookie aka origin in order to prevent sync loops + * in the same zone! + */ + object->Deactivate(true, cookie); if (item) item->Unregister(); @@ -233,21 +323,25 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo return false; } - String path = GetObjectConfigPath(object->GetReflectionType(), name); + String path; - if (Utility::PathExists(path)) { - if (unlink(path.CStr()) < 0 && errno != ENOENT) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("unlink") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(path)); - } + try { + path = GetObjectConfigPath(object->GetReflectionType(), name); + } catch (const std::exception& ex) { + errors->Add("Config package broken: " + DiagnosticInformation(ex, false)); + return false; } + Utility::Remove(path); + + Log(LogInformation, "ConfigObjectUtility") + << "Deleted object '" << name << "' of type '" << type->GetName() << "'."; + return true; } -bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation) +bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors, + const Array::Ptr& diagnosticInformation, const Value& cookie) { if (object->GetPackage() != "_api") { if (errors) @@ -256,5 +350,5 @@ bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cas return false; } - return DeleteObjectHelper(object, cascade, errors, diagnosticInformation); + return DeleteObjectHelper(object, cascade, errors, diagnosticInformation, cookie); } diff --git a/lib/remote/configobjectutility.hpp b/lib/remote/configobjectutility.hpp index 472af3c83..f383a211b 100644 --- a/lib/remote/configobjectutility.hpp +++ b/lib/remote/configobjectutility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGOBJECTUTILITY_H #define CONFIGOBJECTUTILITY_H @@ -40,20 +23,22 @@ class ConfigObjectUtility public: static String GetConfigDir(); static String GetObjectConfigPath(const Type::Ptr& type, const String& fullName); + static void RepairPackage(const String& package); + static void CreateStorage(); static String CreateObjectConfig(const Type::Ptr& type, const String& fullName, bool ignoreOnError, const Array::Ptr& templates, const Dictionary::Ptr& attrs); static bool CreateObject(const Type::Ptr& type, const String& fullName, - const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation); + const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation, const Value& cookie = Empty); static bool DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors, - const Array::Ptr& diagnosticInformation); + const Array::Ptr& diagnosticInformation, const Value& cookie = Empty); private: static String EscapeName(const String& name); static bool DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors, - const Array::Ptr& diagnosticInformation); + const Array::Ptr& diagnosticInformation, const Value& cookie = Empty); }; } diff --git a/lib/remote/configpackageshandler.cpp b/lib/remote/configpackageshandler.cpp index 8ad25d0cb..c1b1a1076 100644 --- a/lib/remote/configpackageshandler.cpp +++ b/lib/remote/configpackageshandler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/configpackageshandler.hpp" #include "remote/configpackageutility.hpp" @@ -27,25 +10,44 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/config/packages", ConfigPackagesHandler); -bool ConfigPackagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool ConfigPackagesHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() > 4) + namespace http = boost::beast::http; + + if (url->GetPath().size() > 4) return false; - if (request.RequestMethod == "GET") - HandleGet(user, request, response, params); - else if (request.RequestMethod == "POST") - HandlePost(user, request, response, params); - else if (request.RequestMethod == "DELETE") - HandleDelete(user, request, response, params); + if (request.method() == http::verb::get) + HandleGet(user, request, url, response, params); + else if (request.method() == http::verb::post) + HandlePost(user, request, url, response, params); + else if (request.method() == http::verb::delete_) + HandleDelete(user, request, url, response, params); else return false; return true; } -void ConfigPackagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +void ConfigPackagesHandler::HandleGet( + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params +) { + namespace http = boost::beast::http; + FilterUtility::CheckPermission(user, "config/query"); std::vector packages; @@ -61,12 +63,19 @@ void ConfigPackagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& req ArrayData results; { - boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticMutex()); + boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticPackageMutex()); + for (const String& package : packages) { + String activeStage; + + try { + activeStage = ConfigPackageUtility::GetActiveStage(package); + } catch (const std::exception&) { } /* Should never happen. */ + results.emplace_back(new Dictionary({ { "name", package }, { "stages", Array::FromVector(ConfigPackageUtility::GetStages(package)) }, - { "active-stage", ConfigPackageUtility::GetActiveStage(package) } + { "active-stage", activeStage } })); } } @@ -75,16 +84,24 @@ void ConfigPackagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& req { "results", new Array(std::move(results)) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); } -void ConfigPackagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +void ConfigPackagesHandler::HandlePost( + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params +) { + namespace http = boost::beast::http; + FilterUtility::CheckPermission(user, "config/modify"); - if (request.RequestUrl->GetPath().size() >= 4) - params->Set("package", request.RequestUrl->GetPath()[3]); + if (url->GetPath().size() >= 4) + params->Set("package", url->GetPath()[3]); String packageName = HttpUtility::GetLastParameter(params, "package"); @@ -94,7 +111,8 @@ void ConfigPackagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& re } try { - boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticMutex()); + boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticPackageMutex()); + ConfigPackageUtility::CreatePackage(packageName); } catch (const std::exception& ex) { HttpUtility::SendJsonError(response, params, 500, "Could not create package '" + packageName + "'.", @@ -112,16 +130,24 @@ void ConfigPackagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& re { "results", new Array({ result1 }) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); } -void ConfigPackagesHandler::HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +void ConfigPackagesHandler::HandleDelete( + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params +) { + namespace http = boost::beast::http; + FilterUtility::CheckPermission(user, "config/modify"); - if (request.RequestUrl->GetPath().size() >= 4) - params->Set("package", request.RequestUrl->GetPath()[3]); + if (url->GetPath().size() >= 4) + params->Set("package", url->GetPath()[3]); String packageName = HttpUtility::GetLastParameter(params, "package"); @@ -148,6 +174,6 @@ void ConfigPackagesHandler::HandleDelete(const ApiUser::Ptr& user, HttpRequest& { "results", new Array({ result1 }) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); } diff --git a/lib/remote/configpackageshandler.hpp b/lib/remote/configpackageshandler.hpp index 4663fbf18..0a05ea10a 100644 --- a/lib/remote/configpackageshandler.hpp +++ b/lib/remote/configpackageshandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGMODULESHANDLER_H #define CONFIGMODULESHANDLER_H @@ -30,16 +13,39 @@ class ConfigPackagesHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ConfigPackagesHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; private: - 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); + void HandleGet( + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params + ); + void HandlePost( + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params + ); + void HandleDelete( + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params + ); }; diff --git a/lib/remote/configpackageutility.cpp b/lib/remote/configpackageutility.cpp index 56ed57f7b..ac877d16c 100644 --- a/lib/remote/configpackageutility.cpp +++ b/lib/remote/configpackageutility.cpp @@ -1,23 +1,7 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/configpackageutility.hpp" +#include "remote/apilistener.hpp" #include "base/application.hpp" #include "base/exception.hpp" #include "base/utility.hpp" @@ -30,7 +14,7 @@ using namespace icinga; String ConfigPackageUtility::GetPackageDir() { - return Application::GetLocalStateDir() + "/lib/icinga2/api/packages"; + return Configuration::DataDir + "/api/packages"; } void ConfigPackageUtility::CreatePackage(const String& name) @@ -51,6 +35,14 @@ void ConfigPackageUtility::DeletePackage(const String& name) if (!Utility::PathExists(path)) BOOST_THROW_EXCEPTION(std::invalid_argument("Package does not exist.")); + ApiListener::Ptr listener = ApiListener::GetInstance(); + + /* config packages without API make no sense. */ + if (!listener) + BOOST_THROW_EXCEPTION(std::invalid_argument("No ApiListener instance configured.")); + + listener->RemoveActivePackageStage(name); + Utility::RemoveDirRecursive(path); Application::RequestRestart(); } @@ -174,10 +166,7 @@ void ConfigPackageUtility::WriteStageConfig(const String& packageName, const Str void ConfigPackageUtility::ActivateStage(const String& packageName, const String& stageName) { - String activeStagePath = GetPackageDir() + "/" + packageName + "/active-stage"; - std::ofstream fpActiveStage(activeStagePath.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc); - fpActiveStage << stageName; - fpActiveStage.close(); + SetActiveStage(packageName, stageName); WritePackageConfig(packageName); } @@ -197,7 +186,8 @@ void ConfigPackageUtility::TryActivateStageCallback(const ProcessResult& pr, con /* validation went fine, activate stage and reload */ if (pr.ExitStatus == 0) { { - boost::mutex::scoped_lock lock(GetStaticMutex()); + boost::mutex::scoped_lock lock(GetStaticPackageMutex()); + ActivateStage(packageName, stageName); } @@ -217,14 +207,25 @@ void ConfigPackageUtility::AsyncTryActivateStage(const String& packageName, cons // prepare arguments Array::Ptr args = new Array({ Application::GetExePath(Application::GetArgV()[0]), - "daemon", - "--validate", - "--define", - "ActiveStageOverride=" + packageName + ":" + stageName }); + // copy all arguments of parent process + for (int i = 1; i < Application::GetArgC(); i++) { + String argV = Application::GetArgV()[i]; + + if (argV == "-d" || argV == "--daemonize") + continue; + + args->Add(argV); + } + + // add arguments for validation + args->Add("--validate"); + args->Add("--define"); + args->Add("ActiveStageOverride=" + packageName + ":" + stageName); + Process::Ptr process = new Process(Process::PrepareCommand(args)); - process->SetTimeout(300); + process->SetTimeout(Application::GetReloadTimeout()); process->Run(std::bind(&TryActivateStageCallback, _1, packageName, stageName, reload)); } @@ -248,8 +249,11 @@ std::vector ConfigPackageUtility::GetStages(const String& packageName) return stages; } -String ConfigPackageUtility::GetActiveStage(const String& packageName) +String ConfigPackageUtility::GetActiveStageFromFile(const String& packageName) { + /* Lock the transaction, reading this only happens on startup or when something really is broken. */ + boost::mutex::scoped_lock lock(GetStaticActiveStageMutex()); + String path = GetPackageDir() + "/" + packageName + "/active-stage"; std::ifstream fp; @@ -261,11 +265,63 @@ String ConfigPackageUtility::GetActiveStage(const String& packageName) fp.close(); if (fp.fail()) - return ""; + return ""; /* Don't use exceptions here. The caller must deal with empty stages at this point. Happens on initial package creation for example. */ return stage.Trim(); } +void ConfigPackageUtility::SetActiveStageToFile(const String& packageName, const String& stageName) +{ + boost::mutex::scoped_lock lock(GetStaticActiveStageMutex()); + + String activeStagePath = GetPackageDir() + "/" + packageName + "/active-stage"; + + std::ofstream fpActiveStage(activeStagePath.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc); //TODO: fstream exceptions + fpActiveStage << stageName; + fpActiveStage.close(); +} + +String ConfigPackageUtility::GetActiveStage(const String& packageName) +{ + String activeStage; + + ApiListener::Ptr listener = ApiListener::GetInstance(); + + /* If we don't have an API feature, just use the file storage without caching this. + * This happens when ScheduledDowntime objects generate Downtime objects. + * TODO: Make the API a first class citizen. + */ + if (!listener) + return GetActiveStageFromFile(packageName); + + /* First use runtime state. */ + try { + activeStage = listener->GetActivePackageStage(packageName); + } catch (const std::exception& ex) { + /* Fallback to reading the file, happens on restarts. */ + activeStage = GetActiveStageFromFile(packageName); + + /* When we've read something, correct memory. */ + if (!activeStage.IsEmpty()) + listener->SetActivePackageStage(packageName, activeStage); + } + + return activeStage; +} + +void ConfigPackageUtility::SetActiveStage(const String& packageName, const String& stageName) +{ + /* Update the marker on disk for restarts. */ + SetActiveStageToFile(packageName, stageName); + + ApiListener::Ptr listener = ApiListener::GetInstance(); + + /* No API, no caching. */ + if (!listener) + return; + + listener->SetActivePackageStage(packageName, stageName); +} std::vector > ConfigPackageUtility::GetFiles(const String& packageName, const String& stageName) { @@ -326,7 +382,13 @@ bool ConfigPackageUtility::ValidateName(const String& name) return (!boost::regex_search(name.GetData(), what, expr)); } -boost::mutex& ConfigPackageUtility::GetStaticMutex() +boost::mutex& ConfigPackageUtility::GetStaticPackageMutex() +{ + static boost::mutex mutex; + return mutex; +} + +boost::mutex& ConfigPackageUtility::GetStaticActiveStageMutex() { static boost::mutex mutex; return mutex; diff --git a/lib/remote/configpackageutility.hpp b/lib/remote/configpackageutility.hpp index bea1a5a51..9f105794c 100644 --- a/lib/remote/configpackageutility.hpp +++ b/lib/remote/configpackageutility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGMODULEUTILITY_H #define CONFIGMODULEUTILITY_H @@ -49,7 +32,10 @@ public: static String CreateStage(const String& packageName, const Dictionary::Ptr& files = nullptr); static void DeleteStage(const String& packageName, const String& stageName); static std::vector GetStages(const String& packageName); + static String GetActiveStageFromFile(const String& packageName); static String GetActiveStage(const String& packageName); + static void SetActiveStage(const String& packageName, const String& stageName); + static void SetActiveStageToFile(const String& packageName, const String& stageName); static void ActivateStage(const String& packageName, const String& stageName); static void AsyncTryActivateStage(const String& packageName, const String& stageName, bool reload); @@ -58,7 +44,8 @@ public: static bool ContainsDotDot(const String& path); static bool ValidateName(const String& name); - static boost::mutex& GetStaticMutex(); + static boost::mutex& GetStaticPackageMutex(); + static boost::mutex& GetStaticActiveStageMutex(); private: static void CollectDirNames(const String& path, std::vector& dirs); diff --git a/lib/remote/configstageshandler.cpp b/lib/remote/configstageshandler.cpp index 673e134a9..2730ac37b 100644 --- a/lib/remote/configstageshandler.cpp +++ b/lib/remote/configstageshandler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/configstageshandler.hpp" #include "remote/configpackageutility.hpp" @@ -28,32 +11,51 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/config/stages", ConfigStagesHandler); -bool ConfigStagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool ConfigStagesHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() > 5) + namespace http = boost::beast::http; + + if (url->GetPath().size() > 5) return false; - if (request.RequestMethod == "GET") - HandleGet(user, request, response, params); - else if (request.RequestMethod == "POST") - HandlePost(user, request, response, params); - else if (request.RequestMethod == "DELETE") - HandleDelete(user, request, response, params); + if (request.method() == http::verb::get) + HandleGet(user, request, url, response, params); + else if (request.method() == http::verb::post) + HandlePost(user, request, url, response, params); + else if (request.method() == http::verb::delete_) + HandleDelete(user, request, url, response, params); else return false; return true; } -void ConfigStagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +void ConfigStagesHandler::HandleGet( + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params +) { + namespace http = boost::beast::http; + FilterUtility::CheckPermission(user, "config/query"); - if (request.RequestUrl->GetPath().size() >= 4) - params->Set("package", request.RequestUrl->GetPath()[3]); + if (url->GetPath().size() >= 4) + params->Set("package", url->GetPath()[3]); - if (request.RequestUrl->GetPath().size() >= 5) - params->Set("stage", request.RequestUrl->GetPath()[4]); + if (url->GetPath().size() >= 5) + params->Set("stage", url->GetPath()[4]); String packageName = HttpUtility::GetLastParameter(params, "package"); String stageName = HttpUtility::GetLastParameter(params, "stage"); @@ -81,16 +83,24 @@ void ConfigStagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& reque { "results", new Array(std::move(results)) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); } -void ConfigStagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +void ConfigStagesHandler::HandlePost( + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params +) { + namespace http = boost::beast::http; + FilterUtility::CheckPermission(user, "config/modify"); - if (request.RequestUrl->GetPath().size() >= 4) - params->Set("package", request.RequestUrl->GetPath()[3]); + if (url->GetPath().size() >= 4) + params->Set("package", url->GetPath()[3]); String packageName = HttpUtility::GetLastParameter(params, "package"); @@ -110,7 +120,8 @@ void ConfigStagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& requ if (!files) BOOST_THROW_EXCEPTION(std::invalid_argument("Parameter 'files' must be specified.")); - boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticMutex()); + boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticPackageMutex()); + stageName = ConfigPackageUtility::CreateStage(packageName, files); /* validate the config. on success, activate stage and reload */ @@ -140,19 +151,27 @@ void ConfigStagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& requ { "results", new Array({ result1 }) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); } -void ConfigStagesHandler::HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +void ConfigStagesHandler::HandleDelete( + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params +) { + namespace http = boost::beast::http; + FilterUtility::CheckPermission(user, "config/modify"); - if (request.RequestUrl->GetPath().size() >= 4) - params->Set("package", request.RequestUrl->GetPath()[3]); + if (url->GetPath().size() >= 4) + params->Set("package", url->GetPath()[3]); - if (request.RequestUrl->GetPath().size() >= 5) - params->Set("stage", request.RequestUrl->GetPath()[4]); + if (url->GetPath().size() >= 5) + params->Set("stage", url->GetPath()[4]); String packageName = HttpUtility::GetLastParameter(params, "package"); String stageName = HttpUtility::GetLastParameter(params, "stage"); @@ -182,7 +201,7 @@ void ConfigStagesHandler::HandleDelete(const ApiUser::Ptr& user, HttpRequest& re { "results", new Array({ result1 }) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); } diff --git a/lib/remote/configstageshandler.hpp b/lib/remote/configstageshandler.hpp index 159856e51..c6d644366 100644 --- a/lib/remote/configstageshandler.hpp +++ b/lib/remote/configstageshandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONFIGSTAGESHANDLER_H #define CONFIGSTAGESHANDLER_H @@ -30,16 +13,39 @@ class ConfigStagesHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ConfigStagesHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; private: - 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); + void HandleGet( + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params + ); + void HandlePost( + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params + ); + void HandleDelete( + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params + ); }; diff --git a/lib/remote/consolehandler.cpp b/lib/remote/consolehandler.cpp index 5c5af049d..e836f8844 100644 --- a/lib/remote/consolehandler.cpp +++ b/lib/remote/consolehandler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/consolehandler.hpp" #include "remote/httputility.hpp" @@ -27,7 +10,9 @@ #include "base/logger.hpp" #include "base/serializer.hpp" #include "base/timer.hpp" +#include "base/namespace.hpp" #include "base/initialize.hpp" +#include "base/utility.hpp" #include #include @@ -69,17 +54,28 @@ static void EnsureFrameCleanupTimer() }); } -bool ConsoleHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool ConsoleHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() != 3) + namespace http = boost::beast::http; + + if (url->GetPath().size() != 3) return false; - if (request.RequestMethod != "POST") + if (request.method() != http::verb::post) return false; QueryDescription qd; - String methodName = request.RequestUrl->GetPath()[2]; + String methodName = url->GetPath()[2]; FilterUtility::CheckPermission(user, "console"); @@ -101,9 +97,12 @@ bool ConsoleHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& reques return true; } -bool ConsoleHandler::ExecuteScriptHelper(HttpRequest& request, HttpResponse& response, +bool ConsoleHandler::ExecuteScriptHelper(boost::beast::http::request& request, + boost::beast::http::response& response, const Dictionary::Ptr& params, const String& command, const String& session, bool sandboxed) { + namespace http = boost::beast::http; + Log(LogNotice, "Console") << "Executing expression: " << command; @@ -167,15 +166,18 @@ bool ConsoleHandler::ExecuteScriptHelper(HttpRequest& request, HttpResponse& res { "results", new Array({ resultInfo }) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); return true; } -bool ConsoleHandler::AutocompleteScriptHelper(HttpRequest& request, HttpResponse& response, +bool ConsoleHandler::AutocompleteScriptHelper(boost::beast::http::request& request, + boost::beast::http::response& response, const Dictionary::Ptr& params, const String& command, const String& session, bool sandboxed) { + namespace http = boost::beast::http; + Log(LogInformation, "Console") << "Auto-completing expression: " << command; @@ -203,7 +205,7 @@ bool ConsoleHandler::AutocompleteScriptHelper(HttpRequest& request, HttpResponse { "results", new Array({ result1 }) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); return true; @@ -233,6 +235,15 @@ static void AddSuggestions(std::vector& matches, const String& word, con } } + if (value.IsObjectType()) { + Namespace::Ptr ns = value; + + ObjectLock olock(ns); + for (const Namespace::Pair& kv : ns) { + AddSuggestion(matches, word, prefix + kv.first); + } + } + if (withFields) { Type::Ptr type = value.GetReflectionType(); @@ -275,18 +286,17 @@ std::vector ConsoleHandler::GetAutocompletionSuggestions(const String& w { ObjectLock olock(ScriptGlobal::GetGlobals()); - for (const Dictionary::Pair& kv : ScriptGlobal::GetGlobals()) { + for (const Namespace::Pair& kv : ScriptGlobal::GetGlobals()) { AddSuggestion(matches, word, kv.first); } } - { - Array::Ptr imports = ScriptFrame::GetImports(); - ObjectLock olock(imports); - for (const Value& import : imports) { - AddSuggestions(matches, word, "", false, import); - } - } + Namespace::Ptr systemNS = ScriptGlobal::Get("System"); + + AddSuggestions(matches, word, "", false, systemNS); + AddSuggestions(matches, word, "", true, systemNS->Get("Configuration")); + AddSuggestions(matches, word, "", false, ScriptGlobal::Get("Types")); + AddSuggestions(matches, word, "", false, ScriptGlobal::Get("Icinga")); String::SizeType cperiod = word.RFind("."); diff --git a/lib/remote/consolehandler.hpp b/lib/remote/consolehandler.hpp index 762cb83d4..df0d77d01 100644 --- a/lib/remote/consolehandler.hpp +++ b/lib/remote/consolehandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CONSOLEHANDLER_H #define CONSOLEHANDLER_H @@ -39,15 +22,25 @@ class ConsoleHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ConsoleHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; static std::vector GetAutocompletionSuggestions(const String& word, ScriptFrame& frame); private: - static bool ExecuteScriptHelper(HttpRequest& request, HttpResponse& response, + static bool ExecuteScriptHelper(boost::beast::http::request& request, + boost::beast::http::response& response, const Dictionary::Ptr& params, const String& command, const String& session, bool sandboxed); - static bool AutocompleteScriptHelper(HttpRequest& request, HttpResponse& response, + static bool AutocompleteScriptHelper(boost::beast::http::request& request, + boost::beast::http::response& response, const Dictionary::Ptr& params, const String& command, const String& session, bool sandboxed); }; diff --git a/lib/remote/createobjecthandler.cpp b/lib/remote/createobjecthandler.cpp index a365ed664..c01b23641 100644 --- a/lib/remote/createobjecthandler.cpp +++ b/lib/remote/createobjecthandler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/createobjecthandler.hpp" #include "remote/configobjectutility.hpp" @@ -31,15 +14,26 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/objects", CreateObjectHandler); -bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool CreateObjectHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() != 4) + namespace http = boost::beast::http; + + if (url->GetPath().size() != 4) return false; - if (request.RequestMethod != "PUT") + if (request.method() != http::verb::put) return false; - Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[2]); + Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]); if (!type) { HttpUtility::SendJsonError(response, params, 400, "Invalid type specified."); @@ -48,7 +42,7 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r FilterUtility::CheckPermission(user, "objects/create/" + type->GetName()); - String name = request.RequestUrl->GetPath()[3]; + String name = url->GetPath()[3]; Array::Ptr templates = params->Get("templates"); Dictionary::Ptr attrs = params->Get("attrs"); @@ -116,7 +110,7 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r result1->Set("code", 500); result1->Set("status", "Object could not be created."); - response.SetStatus(500, "Object could not be created"); + response.result(http::status::internal_server_error); HttpUtility::SendJsonBody(response, params, result); return true; @@ -130,7 +124,7 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r if (verbose) result1->Set("diagnostic_information", diagnosticInformation); - response.SetStatus(500, "Object could not be created"); + response.result(http::status::internal_server_error); HttpUtility::SendJsonBody(response, params, result); return true; @@ -146,7 +140,7 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r else if (!obj && ignoreOnError) result1->Set("status", "Object was not created but 'ignore_on_error' was set to true"); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); return true; diff --git a/lib/remote/createobjecthandler.hpp b/lib/remote/createobjecthandler.hpp index 12dc12377..4bcf21b55 100644 --- a/lib/remote/createobjecthandler.hpp +++ b/lib/remote/createobjecthandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef CREATEOBJECTHANDLER_H #define CREATEOBJECTHANDLER_H @@ -30,8 +13,16 @@ class CreateObjectHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(CreateObjectHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; }; } diff --git a/lib/remote/deleteobjecthandler.cpp b/lib/remote/deleteobjecthandler.cpp index 0d4053df3..2edb0e455 100644 --- a/lib/remote/deleteobjecthandler.cpp +++ b/lib/remote/deleteobjecthandler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/deleteobjecthandler.hpp" #include "remote/configobjectutility.hpp" @@ -31,15 +14,26 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/objects", DeleteObjectHandler); -bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool DeleteObjectHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() < 3 || request.RequestUrl->GetPath().size() > 4) + namespace http = boost::beast::http; + + if (url->GetPath().size() < 3 || url->GetPath().size() > 4) return false; - if (request.RequestMethod != "DELETE") + if (request.method() != http::verb::delete_) return false; - Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[2]); + Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]); if (!type) { HttpUtility::SendJsonError(response, params, 400, "Invalid type specified."); @@ -52,10 +46,10 @@ bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r params->Set("type", type->GetName()); - if (request.RequestUrl->GetPath().size() >= 4) { + if (url->GetPath().size() >= 4) { String attr = type->GetName(); boost::algorithm::to_lower(attr); - params->Set(attr, request.RequestUrl->GetPath()[3]); + params->Set(attr, url->GetPath()[3]); } std::vector objs; @@ -110,9 +104,9 @@ bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r }); if (!success) - response.SetStatus(500, "One or more objects could not be deleted"); + response.result(http::status::internal_server_error); else - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); diff --git a/lib/remote/deleteobjecthandler.hpp b/lib/remote/deleteobjecthandler.hpp index 77e68ea5b..19a46e475 100644 --- a/lib/remote/deleteobjecthandler.hpp +++ b/lib/remote/deleteobjecthandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef DELETEOBJECTHANDLER_H #define DELETEOBJECTHANDLER_H @@ -30,8 +13,16 @@ class DeleteObjectHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(DeleteObjectHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; }; } diff --git a/lib/remote/endpoint.cpp b/lib/remote/endpoint.cpp index 2b2fb655b..155532d5a 100644 --- a/lib/remote/endpoint.cpp +++ b/lib/remote/endpoint.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/endpoint.hpp" #include "remote/endpoint-ti.cpp" diff --git a/lib/remote/endpoint.hpp b/lib/remote/endpoint.hpp index 41aea0270..7c490514d 100644 --- a/lib/remote/endpoint.hpp +++ b/lib/remote/endpoint.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ENDPOINT_H #define ENDPOINT_H diff --git a/lib/remote/endpoint.ti b/lib/remote/endpoint.ti index 079782053..1c3b654d1 100644 --- a/lib/remote/endpoint.ti +++ b/lib/remote/endpoint.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" diff --git a/lib/remote/eventqueue.cpp b/lib/remote/eventqueue.cpp index 5f3b01a2f..5b6219c10 100644 --- a/lib/remote/eventqueue.cpp +++ b/lib/remote/eventqueue.cpp @@ -1,26 +1,17 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ +#include "config/configcompiler.hpp" #include "remote/eventqueue.hpp" #include "remote/filterutility.hpp" +#include "base/io-engine.hpp" #include "base/singleton.hpp" #include "base/logger.hpp" +#include "base/utility.hpp" +#include +#include +#include +#include +#include using namespace icinga; @@ -37,7 +28,8 @@ bool EventQueue::CanProcessEvent(const String& type) const void EventQueue::ProcessEvent(const Dictionary::Ptr& event) { - ScriptFrame frame(true); + Namespace::Ptr frameNS = new Namespace(); + ScriptFrame frame(true, frameNS); frame.Sandboxed = true; try { @@ -65,6 +57,10 @@ void EventQueue::AddClient(void *client) auto result = m_Events.insert(std::make_pair(client, std::deque())); ASSERT(result.second); + +#ifndef I2_DEBUG + (void)result; +#endif /* I2_DEBUG */ } void EventQueue::RemoveClient(void *client) @@ -94,25 +90,6 @@ void EventQueue::SetFilter(std::unique_ptr filter) m_Filter.swap(filter); } -Dictionary::Ptr EventQueue::WaitForEvent(void *client, double timeout) -{ - boost::mutex::scoped_lock lock(m_Mutex); - - for (;;) { - auto it = m_Events.find(client); - ASSERT(it != m_Events.end()); - - if (!it->second.empty()) { - Dictionary::Ptr result = *it->second.begin(); - it->second.pop_front(); - return result; - } - - if (!m_CV.timed_wait(lock, boost::posix_time::milliseconds(long(timeout * 1000)))) - return nullptr; - } -} - std::vector EventQueue::GetQueuesForType(const String& type) { EventQueueRegistry::ItemMap queues = EventQueueRegistry::GetInstance()->GetItems(); @@ -147,3 +124,208 @@ EventQueueRegistry *EventQueueRegistry::GetInstance() { return Singleton::GetInstance(); } + +std::mutex EventsInbox::m_FiltersMutex; +std::map EventsInbox::m_Filters ({{"", EventsInbox::Filter{1, nullptr}}}); + +EventsRouter EventsRouter::m_Instance; + +EventsInbox::EventsInbox(String filter, const String& filterSource) + : m_Timer(IoEngine::Get().GetIoContext()) +{ + std::unique_lock lock (m_FiltersMutex); + m_Filter = m_Filters.find(filter); + + if (m_Filter == m_Filters.end()) { + lock.unlock(); + + auto expr (ConfigCompiler::CompileText(filterSource, filter)); + + lock.lock(); + + m_Filter = m_Filters.find(filter); + + if (m_Filter == m_Filters.end()) { + m_Filter = m_Filters.emplace(std::move(filter), Filter{1, std::shared_ptr(expr.release())}).first; + } else { + ++m_Filter->second.Refs; + } + } else { + ++m_Filter->second.Refs; + } +} + +EventsInbox::~EventsInbox() +{ + std::unique_lock lock (m_FiltersMutex); + + if (!--m_Filter->second.Refs) { + m_Filters.erase(m_Filter); + } +} + +const std::shared_ptr& EventsInbox::GetFilter() +{ + return m_Filter->second.Expr; +} + +void EventsInbox::Push(Dictionary::Ptr event) +{ + std::unique_lock lock (m_Mutex); + + m_Queue.emplace(std::move(event)); + m_Timer.expires_at(boost::posix_time::neg_infin); +} + +Dictionary::Ptr EventsInbox::Shift(boost::asio::yield_context yc, double timeout) +{ + std::unique_lock lock (m_Mutex, std::defer_lock); + + m_Timer.expires_at(boost::posix_time::neg_infin); + + { + boost::system::error_code ec; + + while (!lock.try_lock()) { + m_Timer.async_wait(yc[ec]); + } + } + + if (m_Queue.empty()) { + m_Timer.expires_from_now(boost::posix_time::milliseconds((unsigned long)(timeout * 1000.0))); + lock.unlock(); + + { + boost::system::error_code ec; + m_Timer.async_wait(yc[ec]); + + while (!lock.try_lock()) { + m_Timer.async_wait(yc[ec]); + } + } + + if (m_Queue.empty()) { + return nullptr; + } + } + + auto event (std::move(m_Queue.front())); + m_Queue.pop(); + return std::move(event); +} + +EventsSubscriber::EventsSubscriber(std::set types, String filter, const String& filterSource) + : m_Types(std::move(types)), m_Inbox(new EventsInbox(std::move(filter), filterSource)) +{ + EventsRouter::GetInstance().Subscribe(m_Types, m_Inbox); +} + +EventsSubscriber::~EventsSubscriber() +{ + EventsRouter::GetInstance().Unsubscribe(m_Types, m_Inbox); +} + +const EventsInbox::Ptr& EventsSubscriber::GetInbox() +{ + return m_Inbox; +} + +EventsFilter::EventsFilter(std::map, std::set> inboxes) + : m_Inboxes(std::move(inboxes)) +{ +} + +EventsFilter::operator bool() +{ + return !m_Inboxes.empty(); +} + +void EventsFilter::Push(Dictionary::Ptr event) +{ + for (auto& perFilter : m_Inboxes) { + if (perFilter.first) { + ScriptFrame frame(true, new Namespace()); + frame.Sandboxed = true; + + try { + if (!FilterUtility::EvaluateFilter(frame, perFilter.first.get(), event, "event")) { + continue; + } + } catch (const std::exception& ex) { + Log(LogWarning, "EventQueue") + << "Error occurred while evaluating event filter for queue: " << DiagnosticInformation(ex); + continue; + } + } + + for (auto& inbox : perFilter.second) { + inbox->Push(event); + } + } +} + +EventsRouter& EventsRouter::GetInstance() +{ + return m_Instance; +} + +void EventsRouter::Subscribe(const std::set& types, const EventsInbox::Ptr& inbox) +{ + const auto& filter (inbox->GetFilter()); + std::unique_lock lock (m_Mutex); + + for (auto type : types) { + auto perType (m_Subscribers.find(type)); + + if (perType == m_Subscribers.end()) { + perType = m_Subscribers.emplace(type, decltype(perType->second)()).first; + } + + auto perFilter (perType->second.find(filter)); + + if (perFilter == perType->second.end()) { + perFilter = perType->second.emplace(filter, decltype(perFilter->second)()).first; + } + + perFilter->second.emplace(inbox); + } +} + +void EventsRouter::Unsubscribe(const std::set& types, const EventsInbox::Ptr& inbox) +{ + const auto& filter (inbox->GetFilter()); + std::unique_lock lock (m_Mutex); + + for (auto type : types) { + auto perType (m_Subscribers.find(type)); + + if (perType != m_Subscribers.end()) { + auto perFilter (perType->second.find(filter)); + + if (perFilter != perType->second.end()) { + perFilter->second.erase(inbox); + + if (perFilter->second.empty()) { + perType->second.erase(perFilter); + } + } + + if (perType->second.empty()) { + m_Subscribers.erase(perType); + } + } + } +} + +EventsFilter EventsRouter::GetInboxes(EventType type) +{ + std::unique_lock lock (m_Mutex); + + auto perType (m_Subscribers.find(type)); + + if (perType == m_Subscribers.end()) { + return EventsFilter({}); + } + + return EventsFilter(perType->second); +} diff --git a/lib/remote/eventqueue.hpp b/lib/remote/eventqueue.hpp index 56fe23c6f..ec389929a 100644 --- a/lib/remote/eventqueue.hpp +++ b/lib/remote/eventqueue.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef EVENTQUEUE_H #define EVENTQUEUE_H @@ -23,11 +6,17 @@ #include "remote/httphandler.hpp" #include "base/object.hpp" #include "config/expression.hpp" +#include +#include #include #include +#include +#include +#include #include #include #include +#include namespace icinga { @@ -47,8 +36,6 @@ public: void SetTypes(const std::set& types); void SetFilter(std::unique_ptr filter); - Dictionary::Ptr WaitForEvent(void *client, double timeout = 5); - static std::vector GetQueuesForType(const String& type); static void UnregisterIfUnused(const String& name, const EventQueue::Ptr& queue); @@ -79,6 +66,108 @@ public: static EventQueueRegistry *GetInstance(); }; +enum class EventType : uint_fast8_t +{ + AcknowledgementCleared, + AcknowledgementSet, + CheckResult, + CommentAdded, + CommentRemoved, + DowntimeAdded, + DowntimeRemoved, + DowntimeStarted, + DowntimeTriggered, + Flapping, + Notification, + StateChange +}; + +class EventsInbox : public Object +{ +public: + DECLARE_PTR_TYPEDEFS(EventsInbox); + + EventsInbox(String filter, const String& filterSource); + EventsInbox(const EventsInbox&) = delete; + EventsInbox(EventsInbox&&) = delete; + EventsInbox& operator=(const EventsInbox&) = delete; + EventsInbox& operator=(EventsInbox&&) = delete; + ~EventsInbox(); + + const std::shared_ptr& GetFilter(); + + void Push(Dictionary::Ptr event); + Dictionary::Ptr Shift(boost::asio::yield_context yc, double timeout = 5); + +private: + struct Filter + { + std::size_t Refs; + std::shared_ptr Expr; + }; + + static std::mutex m_FiltersMutex; + static std::map m_Filters; + + std::mutex m_Mutex; + decltype(m_Filters.begin()) m_Filter; + std::queue m_Queue; + boost::asio::deadline_timer m_Timer; +}; + +class EventsSubscriber +{ +public: + EventsSubscriber(std::set types, String filter, const String& filterSource); + EventsSubscriber(const EventsSubscriber&) = delete; + EventsSubscriber(EventsSubscriber&&) = delete; + EventsSubscriber& operator=(const EventsSubscriber&) = delete; + EventsSubscriber& operator=(EventsSubscriber&&) = delete; + ~EventsSubscriber(); + + const EventsInbox::Ptr& GetInbox(); + +private: + std::set m_Types; + EventsInbox::Ptr m_Inbox; +}; + +class EventsFilter +{ +public: + EventsFilter(std::map, std::set> inboxes); + + operator bool(); + + void Push(Dictionary::Ptr event); + +private: + std::map, std::set> m_Inboxes; +}; + +class EventsRouter +{ +public: + static EventsRouter& GetInstance(); + + void Subscribe(const std::set& types, const EventsInbox::Ptr& inbox); + void Unsubscribe(const std::set& types, const EventsInbox::Ptr& inbox); + EventsFilter GetInboxes(EventType type); + +private: + static EventsRouter m_Instance; + + EventsRouter() = default; + EventsRouter(const EventsRouter&) = delete; + EventsRouter(EventsRouter&&) = delete; + EventsRouter& operator=(const EventsRouter&) = delete; + EventsRouter& operator=(EventsRouter&&) = delete; + ~EventsRouter() = default; + + std::mutex m_Mutex; + std::map, std::set>> m_Subscribers; +}; + } #endif /* EVENTQUEUE_H */ diff --git a/lib/remote/eventshandler.cpp b/lib/remote/eventshandler.cpp index bfdca28dc..e92b14a2d 100644 --- a/lib/remote/eventshandler.cpp +++ b/lib/remote/eventshandler.cpp @@ -1,44 +1,62 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/eventshandler.hpp" #include "remote/httputility.hpp" #include "remote/filterutility.hpp" #include "config/configcompiler.hpp" #include "config/expression.hpp" +#include "base/defer.hpp" +#include "base/io-engine.hpp" #include "base/objectlock.hpp" #include "base/json.hpp" +#include +#include #include +#include +#include using namespace icinga; REGISTER_URLHANDLER("/v1/events", EventsHandler); -bool EventsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +const std::map l_EventTypes ({ + {"AcknowledgementCleared", EventType::AcknowledgementCleared}, + {"AcknowledgementSet", EventType::AcknowledgementSet}, + {"CheckResult", EventType::CheckResult}, + {"CommentAdded", EventType::CommentAdded}, + {"CommentRemoved", EventType::CommentRemoved}, + {"DowntimeAdded", EventType::DowntimeAdded}, + {"DowntimeRemoved", EventType::DowntimeRemoved}, + {"DowntimeStarted", EventType::DowntimeStarted}, + {"DowntimeTriggered", EventType::DowntimeTriggered}, + {"Flapping", EventType::Flapping}, + {"Notification", EventType::Notification}, + {"StateChange", EventType::StateChange} +}); + +const String l_ApiQuery (""); + +bool EventsHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() != 2) + namespace asio = boost::asio; + namespace http = boost::beast::http; + + if (url->GetPath().size() != 2) return false; - if (request.RequestMethod != "POST") + if (request.method() != http::verb::post) return false; - if (request.ProtocolVersion == HttpVersion10) { + if (request.version() == 10) { HttpUtility::SendJsonError(response, params, 400, "HTTP/1.0 not supported for event streams."); return true; } @@ -64,53 +82,53 @@ bool EventsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request return true; } - String filter = HttpUtility::GetLastParameter(params, "filter"); + std::set eventTypes; - std::unique_ptr ufilter; + { + ObjectLock olock(types); + for (const String& type : types) { + auto typeId (l_EventTypes.find(type)); - if (!filter.IsEmpty()) - ufilter = ConfigCompiler::CompileText("", filter); - - /* create a new queue or update an existing one */ - EventQueue::Ptr queue = EventQueue::GetByName(queueName); - - if (!queue) { - queue = new EventQueue(queueName); - EventQueue::Register(queueName, queue); + if (typeId != l_EventTypes.end()) { + eventTypes.emplace(typeId->second); + } + } } - queue->SetTypes(types->ToSet()); - queue->SetFilter(std::move(ufilter)); + EventsSubscriber subscriber (std::move(eventTypes), HttpUtility::GetLastParameter(params, "filter"), l_ApiQuery); - queue->AddClient(&request); + server.StartStreaming(); - response.SetStatus(200, "OK"); - response.AddHeader("Content-Type", "application/json"); + response.result(http::status::ok); + response.set(http::field::content_type, "application/json"); + + IoBoundWorkSlot dontLockTheIoThread (yc); + + http::async_write(stream, response, yc); + stream.async_flush(yc); + + asio::const_buffer newLine ("\n", 1); for (;;) { - Dictionary::Ptr result = queue->WaitForEvent(&request); + auto event (subscriber.GetInbox()->Shift(yc)); - if (!response.IsPeerConnected()) { - queue->RemoveClient(&request); - EventQueue::UnregisterIfUnused(queueName, queue); + if (event) { + CpuBoundWork buildingResponse (yc); + + String body = JsonEncode(event); + + boost::algorithm::replace_all(body, "\n", ""); + + asio::const_buffer payload (body.CStr(), body.GetLength()); + + buildingResponse.Done(); + + asio::async_write(stream, payload, yc); + asio::async_write(stream, newLine, yc); + stream.async_flush(yc); + } else if (server.Disconnected()) { return true; } - - if (!result) - continue; - - String body = JsonEncode(result); - - boost::algorithm::replace_all(body, "\n", ""); - - try { - response.WriteBody(body.CStr(), body.GetLength()); - response.WriteBody("\n", 1); - } catch (const std::exception&) { - queue->RemoveClient(&request); - EventQueue::UnregisterIfUnused(queueName, queue); - throw; - } } } diff --git a/lib/remote/eventshandler.hpp b/lib/remote/eventshandler.hpp index 0e89972e9..c823415d3 100644 --- a/lib/remote/eventshandler.hpp +++ b/lib/remote/eventshandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef EVENTSHANDLER_H #define EVENTSHANDLER_H @@ -31,8 +14,16 @@ class EventsHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(EventsHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; }; } diff --git a/lib/remote/filterutility.cpp b/lib/remote/filterutility.cpp index f17c5e3d4..40498f306 100644 --- a/lib/remote/filterutility.cpp +++ b/lib/remote/filterutility.cpp @@ -1,29 +1,14 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/filterutility.hpp" #include "remote/httputility.hpp" #include "config/configcompiler.hpp" #include "config/expression.hpp" +#include "base/namespace.hpp" #include "base/json.hpp" #include "base/configtype.hpp" #include "base/logger.hpp" +#include "base/utility.hpp" #include using namespace icinga; @@ -95,16 +80,22 @@ bool FilterUtility::EvaluateFilter(ScriptFrame& frame, Expression *filter, else varName = variableName; - Dictionary::Ptr vars; + Namespace::Ptr frameNS; if (frame.Self.IsEmpty()) { - vars = new Dictionary(); - frame.Self = vars; - } else - vars = frame.Self; + frameNS = new Namespace(); + frame.Self = frameNS; + } else { + /* Enforce a namespace object for 'frame.self'. */ + ASSERT(frame.Self.IsObjectType()); - vars->Set("obj", target); - vars->Set(varName, target); + frameNS = frame.Self; + + ASSERT(frameNS != ScriptGlobal::GetGlobals()); + } + + frameNS->Set("obj", target); + frameNS->Set(varName, target); for (int fid = 0; fid < type->GetFieldCount(); fid++) { Field field = type->GetFieldInfo(fid); @@ -115,9 +106,9 @@ bool FilterUtility::EvaluateFilter(ScriptFrame& frame, Expression *filter, Object::Ptr joinedObj = target->NavigateField(fid); if (field.NavigationName) - vars->Set(field.NavigationName, joinedObj); + frameNS->Set(field.NavigationName, joinedObj); else - vars->Set(field.Name, joinedObj); + frameNS->Set(field.Name, joinedObj); } return Convert::ToBool(filter->Evaluate(frame)); @@ -126,8 +117,11 @@ bool FilterUtility::EvaluateFilter(ScriptFrame& frame, Expression *filter, static void FilteredAddTarget(ScriptFrame& permissionFrame, Expression *permissionFilter, ScriptFrame& frame, Expression *ufilter, std::vector& result, const String& variableName, const Object::Ptr& target) { - if (FilterUtility::EvaluateFilter(permissionFrame, permissionFilter, target, variableName) && FilterUtility::EvaluateFilter(frame, ufilter, target, variableName)) - result.emplace_back(target); + if (FilterUtility::EvaluateFilter(permissionFrame, permissionFilter, target, variableName)) { + if (FilterUtility::EvaluateFilter(frame, ufilter, target, variableName)) { + result.emplace_back(std::move(target)); + } + } } void FilterUtility::CheckPermission(const ApiUser::Ptr& user, const String& permission, Expression **permissionFilter) @@ -163,7 +157,7 @@ void FilterUtility::CheckPermission(const ApiUser::Ptr& user, const String& perm if (filter && permissionFilter) { std::vector > args; - args.emplace_back(new GetScopeExpression(ScopeLocal)); + args.emplace_back(new GetScopeExpression(ScopeThis)); std::unique_ptr indexer{new IndexerExpression(std::unique_ptr(MakeLiteral(filter)), std::unique_ptr(MakeLiteral("call")))}; FunctionCallExpression *fexpr = new FunctionCallExpression(std::move(indexer), std::move(args)); @@ -197,7 +191,8 @@ std::vector FilterUtility::GetFilterTargets(const QueryDescription& qd, c Expression *permissionFilter; CheckPermission(user, qd.Permission, &permissionFilter); - ScriptFrame permissionFrame(true); + Namespace::Ptr permissionFrameNS = new Namespace(); + ScriptFrame permissionFrame(false, permissionFrameNS); for (const String& type : qd.Types) { String attr = type; @@ -206,7 +201,7 @@ std::vector FilterUtility::GetFilterTargets(const QueryDescription& qd, c if (attr == "type") attr = "name"; - if (query->Contains(attr)) { + if (query && query->Contains(attr)) { String name = HttpUtility::GetLastParameter(query, attr); Object::Ptr target = provider->GetTargetByName(type, name); @@ -219,7 +214,7 @@ std::vector FilterUtility::GetFilterTargets(const QueryDescription& qd, c attr = provider->GetPluralName(type); boost::algorithm::to_lower(attr); - if (query->Contains(attr)) { + if (query && query->Contains(attr)) { Array::Ptr names = query->Get(attr); if (names) { ObjectLock olock(names); @@ -235,7 +230,7 @@ std::vector FilterUtility::GetFilterTargets(const QueryDescription& qd, c } } - if (query->Contains("filter") || result.empty()) { + if ((query && query->Contains("filter")) || result.empty()) { if (!query->Contains("type")) BOOST_THROW_EXCEPTION(std::invalid_argument("Type must be specified when using a filter.")); @@ -247,30 +242,33 @@ std::vector FilterUtility::GetFilterTargets(const QueryDescription& qd, c if (qd.Types.find(type) == qd.Types.end()) BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid type specified for this query.")); - ScriptFrame frame(true); + Namespace::Ptr frameNS = new Namespace(); + ScriptFrame frame(false, frameNS); frame.Sandboxed = true; - Dictionary::Ptr uvars = new Dictionary(); - - std::unique_ptr ufilter; if (query->Contains("filter")) { String filter = HttpUtility::GetLastParameter(query, "filter"); - ufilter = ConfigCompiler::CompileText("", filter); - } + std::unique_ptr ufilter = ConfigCompiler::CompileText("", filter); - Dictionary::Ptr filter_vars = query->Get("filter_vars"); - if (filter_vars) { - ObjectLock olock(filter_vars); - for (const Dictionary::Pair& kv : filter_vars) { - uvars->Set(kv.first, kv.second); + Dictionary::Ptr filter_vars = query->Get("filter_vars"); + if (filter_vars) { + ObjectLock olock(filter_vars); + for (const Dictionary::Pair& kv : filter_vars) { + frameNS->Set(kv.first, kv.second); + } } + + provider->FindTargets(type, std::bind(&FilteredAddTarget, + std::ref(permissionFrame), permissionFilter, + std::ref(frame), &*ufilter, std::ref(result), variableName, _1)); + } else { + /* Ensure to pass a nullptr as filter expression. + * GCC 8.1.1 on F28 causes problems, see GH #6533. + */ + provider->FindTargets(type, std::bind(&FilteredAddTarget, + std::ref(permissionFrame), permissionFilter, + std::ref(frame), nullptr, std::ref(result), variableName, _1)); } - - frame.Self = uvars; - - provider->FindTargets(type, std::bind(&FilteredAddTarget, - std::ref(permissionFrame), permissionFilter, - std::ref(frame), &*ufilter, std::ref(result), variableName, _1)); } return result; diff --git a/lib/remote/filterutility.hpp b/lib/remote/filterutility.hpp index e2c69dfc3..70e5b7e6f 100644 --- a/lib/remote/filterutility.hpp +++ b/lib/remote/filterutility.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef FILTERUTILITY_H #define FILTERUTILITY_H diff --git a/lib/remote/httpchunkedencoding.cpp b/lib/remote/httpchunkedencoding.cpp deleted file mode 100644 index 9981749c2..000000000 --- a/lib/remote/httpchunkedencoding.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#include "remote/httpchunkedencoding.hpp" -#include - -using namespace icinga; - -StreamReadStatus HttpChunkedEncoding::ReadChunkFromStream(const Stream::Ptr& stream, - char **data, size_t *size, ChunkReadContext& context, bool may_wait) -{ - if (context.LengthIndicator == -1) { - String line; - StreamReadStatus status = stream->ReadLine(&line, context.StreamContext, may_wait); - may_wait = false; - - if (status != StatusNewItem) - return status; - - std::stringstream msgbuf; - msgbuf << std::hex << line; - msgbuf >> context.LengthIndicator; - - if (context.LengthIndicator < 0) - BOOST_THROW_EXCEPTION(std::invalid_argument("HTTP chunk length must not be negative.")); - } - - StreamReadContext& scontext = context.StreamContext; - if (scontext.Eof) - return StatusEof; - - if (scontext.MustRead) { - if (!scontext.FillFromStream(stream, may_wait)) { - scontext.Eof = true; - return StatusEof; - } - - scontext.MustRead = false; - } - - size_t NewlineLength = context.LengthIndicator ? 2 : 0; - - if (scontext.Size < (size_t)context.LengthIndicator + NewlineLength) { - scontext.MustRead = true; - return StatusNeedData; - } - - *data = new char[context.LengthIndicator]; - *size = context.LengthIndicator; - memcpy(*data, scontext.Buffer, context.LengthIndicator); - - scontext.DropData(context.LengthIndicator + NewlineLength); - context.LengthIndicator = -1; - - return StatusNewItem; -} - -void HttpChunkedEncoding::WriteChunkToStream(const Stream::Ptr& stream, const char *data, size_t count) -{ - std::ostringstream msgbuf; - msgbuf << std::hex << count << "\r\n"; - String lengthIndicator = msgbuf.str(); - stream->Write(lengthIndicator.CStr(), lengthIndicator.GetLength()); - stream->Write(data, count); - if (count > 0) - stream->Write("\r\n", 2); -} diff --git a/lib/remote/httpchunkedencoding.hpp b/lib/remote/httpchunkedencoding.hpp deleted file mode 100644 index c0a658c99..000000000 --- a/lib/remote/httpchunkedencoding.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#ifndef HTTPCHUNKEDENCODING_H -#define HTTPCHUNKEDENCODING_H - -#include "remote/i2-remote.hpp" -#include "base/stream.hpp" - -namespace icinga -{ - -struct ChunkReadContext -{ - StreamReadContext& StreamContext; - int LengthIndicator; - - ChunkReadContext(StreamReadContext& scontext) - : StreamContext(scontext), LengthIndicator(-1) - { } -}; - -/** - * HTTP chunked encoding. - * - * @ingroup remote - */ -struct HttpChunkedEncoding -{ - static StreamReadStatus ReadChunkFromStream(const Stream::Ptr& stream, - char **data, size_t *size, ChunkReadContext& ccontext, bool may_wait = false); - static void WriteChunkToStream(const Stream::Ptr& stream, const char *data, size_t count); - -}; - -} - -#endif /* HTTPCHUNKEDENCODING_H */ diff --git a/lib/remote/httpclientconnection.cpp b/lib/remote/httpclientconnection.cpp deleted file mode 100644 index 28d6e50fa..000000000 --- a/lib/remote/httpclientconnection.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#include "remote/httpclientconnection.hpp" -#include "base/configtype.hpp" -#include "base/objectlock.hpp" -#include "base/base64.hpp" -#include "base/utility.hpp" -#include "base/logger.hpp" -#include "base/exception.hpp" -#include "base/convert.hpp" -#include "base/tcpsocket.hpp" -#include "base/tlsstream.hpp" -#include "base/networkstream.hpp" - -using namespace icinga; - -HttpClientConnection::HttpClientConnection(String host, String port, bool tls) - : m_Host(std::move(host)), m_Port(std::move(port)), m_Tls(tls) -{ } - -void HttpClientConnection::Start() -{ - /* Nothing to do here atm. */ -} - -void HttpClientConnection::Reconnect() -{ - if (m_Stream) - m_Stream->Close(); - - m_Context.~StreamReadContext(); - new (&m_Context) StreamReadContext(); - - m_Requests.clear(); - m_CurrentResponse.reset(); - - TcpSocket::Ptr socket = new TcpSocket(); - socket->Connect(m_Host, m_Port); - - if (m_Tls) - m_Stream = new TlsStream(socket, m_Host, RoleClient); - else - ASSERT(!"Non-TLS HTTP connections not supported."); - /* m_Stream = new NetworkStream(socket); - * -- does not currently work because the NetworkStream class doesn't support async I/O - */ - - /* the stream holds an owning reference to this object through the callback we're registering here */ - m_Stream->RegisterDataHandler(std::bind(&HttpClientConnection::DataAvailableHandler, HttpClientConnection::Ptr(this), _1)); - if (m_Stream->IsDataAvailable()) - DataAvailableHandler(m_Stream); -} - -Stream::Ptr HttpClientConnection::GetStream() const -{ - return m_Stream; -} - -String HttpClientConnection::GetHost() const -{ - return m_Host; -} - -String HttpClientConnection::GetPort() const -{ - return m_Port; -} - -bool HttpClientConnection::GetTls() const -{ - return m_Tls; -} - -void HttpClientConnection::Disconnect() -{ - Log(LogDebug, "HttpClientConnection", "Http client disconnected"); - - m_Stream->Shutdown(); -} - -bool HttpClientConnection::ProcessMessage() -{ - bool res; - - if (m_Requests.empty()) { - m_Stream->Close(); - return false; - } - - const std::pair, HttpCompletionCallback>& currentRequest = *m_Requests.begin(); - HttpRequest& request = *currentRequest.first.get(); - const HttpCompletionCallback& callback = currentRequest.second; - - if (!m_CurrentResponse) - m_CurrentResponse = std::make_shared(m_Stream, request); - - std::shared_ptr currentResponse = m_CurrentResponse; - HttpResponse& response = *currentResponse.get(); - - try { - res = response.Parse(m_Context, false); - } catch (const std::exception&) { - callback(request, response); - - m_Stream->Shutdown(); - return false; - } - - if (response.Complete) { - callback(request, response); - - m_Requests.pop_front(); - m_CurrentResponse.reset(); - - return true; - } - - return res; -} - -void HttpClientConnection::DataAvailableHandler(const Stream::Ptr& stream) -{ - ASSERT(stream == m_Stream); - - bool close = false; - - if (!m_Stream->IsEof()) { - boost::mutex::scoped_lock lock(m_DataHandlerMutex); - - try { - while (ProcessMessage()) - ; /* empty loop body */ - } catch (const std::exception& ex) { - Log(LogWarning, "HttpClientConnection") - << "Error while reading Http response: " << DiagnosticInformation(ex); - - close = true; - Disconnect(); - } - } else - close = true; - - if (close) - m_Stream->Close(); -} - -std::shared_ptr HttpClientConnection::NewRequest() -{ - Reconnect(); - return std::make_shared(m_Stream); -} - -void HttpClientConnection::SubmitRequest(const std::shared_ptr& request, - const HttpCompletionCallback& callback) -{ - m_Requests.emplace_back(request, callback); - request->Finish(); -} - diff --git a/lib/remote/httpclientconnection.hpp b/lib/remote/httpclientconnection.hpp deleted file mode 100644 index 752c40c22..000000000 --- a/lib/remote/httpclientconnection.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#ifndef HTTPCLIENTCONNECTION_H -#define HTTPCLIENTCONNECTION_H - -#include "remote/httprequest.hpp" -#include "remote/httpresponse.hpp" -#include "base/stream.hpp" -#include "base/timer.hpp" -#include - -namespace icinga -{ - -/** - * An HTTP client connection. - * - * @ingroup remote - */ -class HttpClientConnection final : public Object -{ -public: - DECLARE_PTR_TYPEDEFS(HttpClientConnection); - - HttpClientConnection(String host, String port, bool tls = true); - - void Start(); - - Stream::Ptr GetStream() const; - String GetHost() const; - String GetPort() const; - bool GetTls() const; - - void Disconnect(); - - std::shared_ptr NewRequest(); - - typedef std::function HttpCompletionCallback; - void SubmitRequest(const std::shared_ptr& request, const HttpCompletionCallback& callback); - -private: - String m_Host; - String m_Port; - bool m_Tls; - Stream::Ptr m_Stream; - std::deque, HttpCompletionCallback> > m_Requests; - std::shared_ptr m_CurrentResponse; - boost::mutex m_DataHandlerMutex; - - StreamReadContext m_Context; - - void Reconnect(); - bool ProcessMessage(); - void DataAvailableHandler(const Stream::Ptr& stream); - - void ProcessMessageAsync(HttpRequest& request); -}; - -} - -#endif /* HTTPCLIENTCONNECTION_H */ diff --git a/lib/remote/httphandler.cpp b/lib/remote/httphandler.cpp index e8fa64427..78e09d2ad 100644 --- a/lib/remote/httphandler.cpp +++ b/lib/remote/httphandler.cpp @@ -1,27 +1,11 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/httphandler.hpp" #include "remote/httputility.hpp" #include "base/singleton.hpp" #include "base/exception.hpp" #include +#include using namespace icinga; @@ -61,11 +45,20 @@ void HttpHandler::Register(const Url::Ptr& url, const HttpHandler::Ptr& handler) handlers->Add(handler); } -void HttpHandler::ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +void HttpHandler::ProcessRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + boost::beast::http::response& response, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { Dictionary::Ptr node = m_UrlTree; std::vector handlers; - const std::vector& path = request.RequestUrl->GetPath(); + + Url::Ptr url = new Url(request.target().to_string()); + auto& path (url->GetPath()); for (std::vector::size_type i = 0; i <= path.size(); i++) { Array::Ptr current_handlers = node->Get("handlers"); @@ -98,7 +91,7 @@ void HttpHandler::ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request, Dictionary::Ptr params; try { - params = HttpUtility::FetchRequestParameters(request); + params = HttpUtility::FetchRequestParameters(url, request.body()); } catch (const std::exception& ex) { HttpUtility::SendJsonError(response, params, 400, "Invalid request body: " + DiagnosticInformation(ex, false)); return; @@ -106,16 +99,15 @@ void HttpHandler::ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request, bool processed = false; for (const HttpHandler::Ptr& handler : handlers) { - if (handler->HandleRequest(user, request, response, params)) { + if (handler->HandleRequest(stream, user, request, url, response, params, yc, server)) { processed = true; break; } } if (!processed) { - String path = boost::algorithm::join(request.RequestUrl->GetPath(), "/"); - HttpUtility::SendJsonError(response, params, 404, "The requested path '" + path + - "' could not be found or the request method is not valid for this path."); + HttpUtility::SendJsonError(response, params, 404, "The requested path '" + boost::algorithm::join(path, "/") + + "' could not be found or the request method is not valid for this path."); return; } } diff --git a/lib/remote/httphandler.hpp b/lib/remote/httphandler.hpp index c06c4030b..a6a730255 100644 --- a/lib/remote/httphandler.hpp +++ b/lib/remote/httphandler.hpp @@ -1,30 +1,17 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef HTTPHANDLER_H #define HTTPHANDLER_H #include "remote/i2-remote.hpp" -#include "remote/httpresponse.hpp" +#include "remote/url.hpp" +#include "remote/httpserverconnection.hpp" #include "remote/apiuser.hpp" #include "base/registry.hpp" +#include "base/tlsstream.hpp" #include +#include +#include namespace icinga { @@ -39,10 +26,26 @@ class HttpHandler : public Object public: DECLARE_PTR_TYPEDEFS(HttpHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) = 0; + virtual bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) = 0; static void Register(const Url::Ptr& url, const HttpHandler::Ptr& handler); - static void ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response); + static void ProcessRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + boost::beast::http::response& response, + boost::asio::yield_context& yc, + HttpServerConnection& server + ); private: static Dictionary::Ptr m_UrlTree; diff --git a/lib/remote/httprequest.cpp b/lib/remote/httprequest.cpp deleted file mode 100644 index f698b2101..000000000 --- a/lib/remote/httprequest.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#include "remote/httprequest.hpp" -#include "base/logger.hpp" -#include "base/application.hpp" -#include "base/convert.hpp" - -using namespace icinga; - -HttpRequest::HttpRequest(Stream::Ptr stream) - : CompleteHeaders(false), - CompleteHeaderCheck(false), - CompleteBody(false), - ProtocolVersion(HttpVersion11), - Headers(new Dictionary()), - m_Stream(std::move(stream)), - m_State(HttpRequestStart) -{ } - -bool HttpRequest::ParseHeaders(StreamReadContext& src, bool may_wait) -{ - if (!m_Stream) - return false; - - if (m_State != HttpRequestStart && m_State != HttpRequestHeaders) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid HTTP state")); - - String line; - StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait); - - if (srs != StatusNewItem) { - if (src.Size > 8 * 1024) - BOOST_THROW_EXCEPTION(std::invalid_argument("Line length for HTTP header exceeded")); - - return false; - } - - if (line.GetLength() > 8 * 1024) { -#ifdef I2_DEBUG /* I2_DEBUG */ - Log(LogDebug, "HttpRequest") - << "Header size: " << line.GetLength() << " content: '" << line << "'."; -#endif /* I2_DEBUG */ - - BOOST_THROW_EXCEPTION(std::invalid_argument("Line length for HTTP header exceeded")); - } - - if (m_State == HttpRequestStart) { - /* ignore trailing new-lines */ - if (line == "") - return true; - - std::vector tokens = line.Split(" "); - Log(LogDebug, "HttpRequest") - << "line: " << line << ", tokens: " << tokens.size(); - if (tokens.size() != 3) - BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request")); - - RequestMethod = tokens[0]; - RequestUrl = new class Url(tokens[1]); - - if (tokens[2] == "HTTP/1.0") - ProtocolVersion = HttpVersion10; - else if (tokens[2] == "HTTP/1.1") { - ProtocolVersion = HttpVersion11; - } else - BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported HTTP version")); - - m_State = HttpRequestHeaders; - return true; - } else { // m_State = HttpRequestHeaders - if (line == "") { - m_State = HttpRequestBody; - CompleteHeaders = true; - return true; - - } else { - if (Headers->GetLength() > 128) - BOOST_THROW_EXCEPTION(std::invalid_argument("Maximum number of HTTP request headers exceeded")); - - String::SizeType pos = line.FindFirstOf(":"); - if (pos == String::NPos) - BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request")); - - String key = line.SubStr(0, pos).ToLower().Trim(); - String value = line.SubStr(pos + 1).Trim(); - Headers->Set(key, value); - - if (key == "x-http-method-override") - RequestMethod = value; - - return true; - } - } -} - -bool HttpRequest::ParseBody(StreamReadContext& src, bool may_wait) -{ - if (!m_Stream) - return false; - - if (m_State != HttpRequestBody) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid HTTP state")); - - /* we're done if the request doesn't contain a message body */ - if (!Headers->Contains("content-length") && !Headers->Contains("transfer-encoding")) { - CompleteBody = true; - return true; - } else if (!m_Body) - m_Body = new FIFO(); - - if (Headers->Get("transfer-encoding") == "chunked") { - if (!m_ChunkContext) - m_ChunkContext = std::make_shared(std::ref(src)); - - char *data; - size_t size; - StreamReadStatus srs = HttpChunkedEncoding::ReadChunkFromStream(m_Stream, &data, &size, *m_ChunkContext.get(), may_wait); - - if (srs != StatusNewItem) - return false; - - m_Body->Write(data, size); - - delete [] data; - - if (size == 0) { - CompleteBody = true; - return false; - } else - return true; - } - - if (src.Eof) - BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body")); - - if (src.MustRead) { - if (!src.FillFromStream(m_Stream, false)) { - src.Eof = true; - BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body")); - } - - src.MustRead = false; - } - - long length_indicator_signed = Convert::ToLong(Headers->Get("content-length")); - - if (length_indicator_signed < 0) - BOOST_THROW_EXCEPTION(std::invalid_argument("Content-Length must not be negative.")); - - size_t length_indicator = length_indicator_signed; - - if (src.Size < length_indicator) { - src.MustRead = true; - return false; - } - - m_Body->Write(src.Buffer, length_indicator); - src.DropData(length_indicator); - CompleteBody = true; - return true; -} - -size_t HttpRequest::ReadBody(char *data, size_t count) -{ - if (!m_Body) - return 0; - else - return m_Body->Read(data, count, true); -} - -void HttpRequest::AddHeader(const String& key, const String& value) -{ - ASSERT(m_State == HttpRequestStart || m_State == HttpRequestHeaders); - Headers->Set(key.ToLower(), value); -} - -void HttpRequest::FinishHeaders() -{ - if (m_State == HttpRequestStart) { - String rqline = RequestMethod + " " + RequestUrl->Format(true) + " HTTP/1." + (ProtocolVersion == HttpVersion10 ? "0" : "1") + "\n"; - m_Stream->Write(rqline.CStr(), rqline.GetLength()); - m_State = HttpRequestHeaders; - } - - if (m_State == HttpRequestHeaders) { - AddHeader("User-Agent", "Icinga/" + Application::GetAppVersion()); - - if (ProtocolVersion == HttpVersion11) { - AddHeader("Transfer-Encoding", "chunked"); - if (!Headers->Contains("Host")) - AddHeader("Host", RequestUrl->GetHost() + ":" + RequestUrl->GetPort()); - } - - ObjectLock olock(Headers); - for (const Dictionary::Pair& kv : Headers) - { - String header = kv.first + ": " + kv.second + "\n"; - m_Stream->Write(header.CStr(), header.GetLength()); - } - - m_Stream->Write("\n", 1); - - m_State = HttpRequestBody; - } -} - -void HttpRequest::WriteBody(const char *data, size_t count) -{ - ASSERT(m_State == HttpRequestStart || m_State == HttpRequestHeaders || m_State == HttpRequestBody); - - if (ProtocolVersion == HttpVersion10) { - if (!m_Body) - m_Body = new FIFO(); - - m_Body->Write(data, count); - } else { - FinishHeaders(); - - HttpChunkedEncoding::WriteChunkToStream(m_Stream, data, count); - } -} - -void HttpRequest::Finish() -{ - ASSERT(m_State != HttpRequestEnd); - - if (ProtocolVersion == HttpVersion10) { - if (m_Body) - AddHeader("Content-Length", Convert::ToString(m_Body->GetAvailableBytes())); - - FinishHeaders(); - - while (m_Body && m_Body->IsDataAvailable()) { - char buffer[1024]; - size_t rc = m_Body->Read(buffer, sizeof(buffer), true); - m_Stream->Write(buffer, rc); - } - } else { - if (m_State == HttpRequestStart || m_State == HttpRequestHeaders) - FinishHeaders(); - - WriteBody(nullptr, 0); - m_Stream->Write("\r\n", 2); - } - - m_State = HttpRequestEnd; -} - diff --git a/lib/remote/httprequest.hpp b/lib/remote/httprequest.hpp deleted file mode 100644 index 14e0677cd..000000000 --- a/lib/remote/httprequest.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#ifndef HTTPREQUEST_H -#define HTTPREQUEST_H - -#include "remote/i2-remote.hpp" -#include "remote/httpchunkedencoding.hpp" -#include "remote/url.hpp" -#include "base/stream.hpp" -#include "base/fifo.hpp" -#include "base/dictionary.hpp" - -namespace icinga -{ - -enum HttpVersion -{ - HttpVersion10, - HttpVersion11 -}; - -enum HttpRequestState -{ - HttpRequestStart, - HttpRequestHeaders, - HttpRequestBody, - HttpRequestEnd -}; - -/** - * An HTTP request. - * - * @ingroup remote - */ -struct HttpRequest -{ -public: - bool CompleteHeaders; - bool CompleteHeaderCheck; - bool CompleteBody; - - String RequestMethod; - Url::Ptr RequestUrl; - HttpVersion ProtocolVersion; - - Dictionary::Ptr Headers; - - HttpRequest(Stream::Ptr stream); - - bool ParseHeaders(StreamReadContext& src, bool may_wait); - bool ParseBody(StreamReadContext& src, bool may_wait); - size_t ReadBody(char *data, size_t count); - - void AddHeader(const String& key, const String& value); - void WriteBody(const char *data, size_t count); - void Finish(); - -private: - Stream::Ptr m_Stream; - std::shared_ptr m_ChunkContext; - HttpRequestState m_State; - FIFO::Ptr m_Body; - - void FinishHeaders(); -}; - -} - -#endif /* HTTPREQUEST_H */ diff --git a/lib/remote/httpresponse.cpp b/lib/remote/httpresponse.cpp deleted file mode 100644 index 45ac25c81..000000000 --- a/lib/remote/httpresponse.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#include "remote/httpresponse.hpp" -#include "remote/httpchunkedencoding.hpp" -#include "base/logger.hpp" -#include "base/application.hpp" -#include "base/convert.hpp" - -using namespace icinga; - -HttpResponse::HttpResponse(Stream::Ptr stream, const HttpRequest& request) - : Complete(false), m_State(HttpResponseStart), m_Request(&request), m_Stream(std::move(stream)) -{ } - -void HttpResponse::SetStatus(int code, const String& message) -{ - ASSERT(code >= 100 && code <= 599); - ASSERT(!message.IsEmpty()); - - if (m_State != HttpResponseStart) { - Log(LogWarning, "HttpResponse", "Tried to set Http response status after headers had already been sent."); - return; - } - - String status = "HTTP/"; - - if (m_Request->ProtocolVersion == HttpVersion10) - status += "1.0"; - else - status += "1.1"; - - status += " " + Convert::ToString(code) + " " + message + "\r\n"; - - m_Stream->Write(status.CStr(), status.GetLength()); - - m_State = HttpResponseHeaders; -} - -void HttpResponse::AddHeader(const String& key, const String& value) -{ - m_Headers.emplace_back(key + ": " + value + "\r\n"); -} - -void HttpResponse::FinishHeaders() -{ - if (m_State == HttpResponseHeaders) { - if (m_Request->ProtocolVersion == HttpVersion11) - AddHeader("Transfer-Encoding", "chunked"); - - AddHeader("Server", "Icinga/" + Application::GetAppVersion()); - - for (const String& header : m_Headers) - m_Stream->Write(header.CStr(), header.GetLength()); - - m_Stream->Write("\r\n", 2); - m_State = HttpResponseBody; - } -} - -void HttpResponse::WriteBody(const char *data, size_t count) -{ - ASSERT(m_State == HttpResponseHeaders || m_State == HttpResponseBody); - - if (m_Request->ProtocolVersion == HttpVersion10) { - if (!m_Body) - m_Body = new FIFO(); - - m_Body->Write(data, count); - } else { - FinishHeaders(); - - HttpChunkedEncoding::WriteChunkToStream(m_Stream, data, count); - } -} - -void HttpResponse::Finish() -{ - ASSERT(m_State != HttpResponseEnd); - - if (m_Request->ProtocolVersion == HttpVersion10) { - if (m_Body) - AddHeader("Content-Length", Convert::ToString(m_Body->GetAvailableBytes())); - - FinishHeaders(); - - while (m_Body && m_Body->IsDataAvailable()) { - char buffer[1024]; - size_t rc = m_Body->Read(buffer, sizeof(buffer), true); - m_Stream->Write(buffer, rc); - } - } else { - WriteBody(nullptr, 0); - m_Stream->Write("\r\n", 2); - } - - m_State = HttpResponseEnd; - - if (m_Request->ProtocolVersion == HttpVersion10 || m_Request->Headers->Get("connection") == "close") - m_Stream->Shutdown(); -} - -bool HttpResponse::Parse(StreamReadContext& src, bool may_wait) -{ - if (m_State != HttpResponseBody) { - String line; - StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait); - - if (srs != StatusNewItem) - return false; - - if (m_State == HttpResponseStart) { - /* ignore trailing new-lines */ - if (line == "") - return true; - - std::vector tokens = line.Split(" "); - Log(LogDebug, "HttpRequest") - << "line: " << line << ", tokens: " << tokens.size(); - if (tokens.size() < 2) - BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP response (Status line)")); - - if (tokens[0] == "HTTP/1.0") - ProtocolVersion = HttpVersion10; - else if (tokens[0] == "HTTP/1.1") { - ProtocolVersion = HttpVersion11; - } else - BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported HTTP version")); - - StatusCode = Convert::ToLong(tokens[1]); - - if (tokens.size() >= 3) - StatusMessage = tokens[2]; // TODO: Join tokens[2..end] - - m_State = HttpResponseHeaders; - } else if (m_State == HttpResponseHeaders) { - if (!Headers) - Headers = new Dictionary(); - - if (line == "") { - m_State = HttpResponseBody; - m_Body = new FIFO(); - - return true; - - } else { - String::SizeType pos = line.FindFirstOf(":"); - if (pos == String::NPos) - BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request")); - String key = line.SubStr(0, pos).ToLower().Trim(); - - String value = line.SubStr(pos + 1).Trim(); - Headers->Set(key, value); - } - } else { - VERIFY(!"Invalid HTTP request state."); - } - } else if (m_State == HttpResponseBody) { - if (Headers->Get("transfer-encoding") == "chunked") { - if (!m_ChunkContext) - m_ChunkContext = std::make_shared(std::ref(src)); - - char *data; - size_t size; - StreamReadStatus srs = HttpChunkedEncoding::ReadChunkFromStream(m_Stream, &data, &size, *m_ChunkContext.get(), may_wait); - - if (srs != StatusNewItem) - return false; - - Log(LogNotice, "HttpResponse") - << "Read " << size << " bytes"; - - m_Body->Write(data, size); - - delete[] data; - - if (size == 0) { - Complete = true; - return true; - } - } else { - bool hasLengthIndicator = false; - size_t lengthIndicator = 0; - Value contentLengthHeader; - - if (Headers->Get("content-length", &contentLengthHeader)) { - hasLengthIndicator = true; - lengthIndicator = Convert::ToLong(contentLengthHeader); - } - - if (!hasLengthIndicator && ProtocolVersion != HttpVersion10 && !Headers->Contains("transfer-encoding")) { - Complete = true; - return true; - } - - if (hasLengthIndicator && src.Eof) - BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body")); - - if (src.MustRead) { - if (!src.FillFromStream(m_Stream, may_wait)) - src.Eof = true; - - src.MustRead = false; - } - - if (!hasLengthIndicator) - lengthIndicator = src.Size; - - if (src.Size < lengthIndicator) { - src.MustRead = true; - return may_wait; - } - - m_Body->Write(src.Buffer, lengthIndicator); - src.DropData(lengthIndicator); - - if (!hasLengthIndicator && !src.Eof) { - src.MustRead = true; - return may_wait; - } - - Complete = true; - return true; - } - } - - return true; -} - -size_t HttpResponse::ReadBody(char *data, size_t count) -{ - if (!m_Body) - return 0; - else - return m_Body->Read(data, count, true); -} - -size_t HttpResponse::GetBodySize() const -{ - if (!m_Body) - return 0; - else - return m_Body->GetAvailableBytes(); -} - -bool HttpResponse::IsPeerConnected() const -{ - return !m_Stream->IsEof(); -} - -void HttpResponse::RebindRequest(const HttpRequest& request) -{ - m_Request = &request; -} diff --git a/lib/remote/httpresponse.hpp b/lib/remote/httpresponse.hpp deleted file mode 100644 index 5021bbbde..000000000 --- a/lib/remote/httpresponse.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#ifndef HTTPRESPONSE_H -#define HTTPRESPONSE_H - -#include "remote/httprequest.hpp" -#include "base/stream.hpp" -#include "base/fifo.hpp" -#include - -namespace icinga -{ - -enum HttpResponseState -{ - HttpResponseStart, - HttpResponseHeaders, - HttpResponseBody, - HttpResponseEnd -}; - -/** - * An HTTP response. - * - * @ingroup remote - */ -struct HttpResponse -{ -public: - bool Complete; - - HttpVersion ProtocolVersion; - int StatusCode; - String StatusMessage; - - Dictionary::Ptr Headers; - - HttpResponse(Stream::Ptr stream, const HttpRequest& request); - - bool Parse(StreamReadContext& src, bool may_wait); - size_t ReadBody(char *data, size_t count); - size_t GetBodySize() const; - - void SetStatus(int code, const String& message); - void AddHeader(const String& key, const String& value); - void WriteBody(const char *data, size_t count); - void Finish(); - - bool IsPeerConnected() const; - - void RebindRequest(const HttpRequest& request); - -private: - HttpResponseState m_State; - std::shared_ptr m_ChunkContext; - const HttpRequest *m_Request; - Stream::Ptr m_Stream; - FIFO::Ptr m_Body; - std::vector m_Headers; - - void FinishHeaders(); -}; - -} - -#endif /* HTTPRESPONSE_H */ diff --git a/lib/remote/httpserverconnection.cpp b/lib/remote/httpserverconnection.cpp index 6e8ea1c58..cdbcc7c57 100644 --- a/lib/remote/httpserverconnection.cpp +++ b/lib/remote/httpserverconnection.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/httpserverconnection.hpp" #include "remote/httphandler.hpp" @@ -23,296 +6,195 @@ #include "remote/apilistener.hpp" #include "remote/apifunction.hpp" #include "remote/jsonrpc.hpp" +#include "base/application.hpp" #include "base/base64.hpp" #include "base/convert.hpp" #include "base/configtype.hpp" +#include "base/defer.hpp" #include "base/exception.hpp" +#include "base/io-engine.hpp" #include "base/logger.hpp" #include "base/objectlock.hpp" #include "base/timer.hpp" +#include "base/tlsstream.hpp" #include "base/utility.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include using namespace icinga; -static boost::once_flag l_HttpServerConnectionOnceFlag = BOOST_ONCE_INIT; -static Timer::Ptr l_HttpServerConnectionTimeoutTimer; +auto const l_ServerHeader ("Icinga/" + Application::GetAppVersion()); -HttpServerConnection::HttpServerConnection(const String& identity, bool authenticated, const TlsStream::Ptr& stream) - : m_Stream(stream), m_Seen(Utility::GetTime()), m_CurrentRequest(stream), m_PendingRequests(0) +HttpServerConnection::HttpServerConnection(const String& identity, bool authenticated, const std::shared_ptr& stream) + : HttpServerConnection(identity, authenticated, stream, IoEngine::Get().GetIoContext()) { - boost::call_once(l_HttpServerConnectionOnceFlag, &HttpServerConnection::StaticInitialize); - - m_RequestQueue.SetName("HttpServerConnection"); - - if (authenticated) - m_ApiUser = ApiUser::GetByClientCN(identity); } -void HttpServerConnection::StaticInitialize() +HttpServerConnection::HttpServerConnection(const String& identity, bool authenticated, const std::shared_ptr& stream, boost::asio::io_context& io) + : m_Stream(stream), m_Seen(Utility::GetTime()), m_IoStrand(io), m_ShuttingDown(false), m_HasStartedStreaming(false), + m_CheckLivenessTimer(io) { - l_HttpServerConnectionTimeoutTimer = new Timer(); - l_HttpServerConnectionTimeoutTimer->OnTimerExpired.connect(std::bind(&HttpServerConnection::TimeoutTimerHandler)); - l_HttpServerConnectionTimeoutTimer->SetInterval(5); - l_HttpServerConnectionTimeoutTimer->Start(); + if (authenticated) { + m_ApiUser = ApiUser::GetByClientCN(identity); + } + + { + std::ostringstream address; + auto endpoint (stream->lowest_layer().remote_endpoint()); + + address << '[' << endpoint.address() << "]:" << endpoint.port(); + + m_PeerAddress = address.str(); + } } void HttpServerConnection::Start() { - /* the stream holds an owning reference to this object through the callback we're registering here */ - m_Stream->RegisterDataHandler(std::bind(&HttpServerConnection::DataAvailableHandler, HttpServerConnection::Ptr(this))); - if (m_Stream->IsDataAvailable()) - DataAvailableHandler(); -} + namespace asio = boost::asio; -ApiUser::Ptr HttpServerConnection::GetApiUser() const -{ - return m_ApiUser; -} + HttpServerConnection::Ptr keepAlive (this); -TlsStream::Ptr HttpServerConnection::GetStream() const -{ - return m_Stream; + IoEngine::SpawnCoroutine(m_IoStrand, [this, keepAlive](asio::yield_context yc) { ProcessMessages(yc); }); + IoEngine::SpawnCoroutine(m_IoStrand, [this, keepAlive](asio::yield_context yc) { CheckLiveness(yc); }); } void HttpServerConnection::Disconnect() { - boost::mutex::scoped_try_lock lock(m_DataHandlerMutex); - if (!lock.owns_lock()) { - Log(LogInformation, "HttpServerConnection", "Unable to disconnect Http client, I/O thread busy"); - return; - } + namespace asio = boost::asio; - Log(LogDebug, "HttpServerConnection", "Http client disconnected"); + HttpServerConnection::Ptr keepAlive (this); - ApiListener::Ptr listener = ApiListener::GetInstance(); - listener->RemoveHttpClient(this); + IoEngine::SpawnCoroutine(m_IoStrand, [this, keepAlive](asio::yield_context yc) { + if (!m_ShuttingDown) { + m_ShuttingDown = true; - m_CurrentRequest.~HttpRequest(); - new (&m_CurrentRequest) HttpRequest(nullptr); + Log(LogInformation, "HttpServerConnection") + << "HTTP client disconnected (from " << m_PeerAddress << ")"; - m_Stream->Close(); -} + /* + * Do not swallow exceptions in a coroutine. + * https://github.com/Icinga/icinga2/issues/7351 + * We must not catch `detail::forced_unwind exception` as + * this is used for unwinding the stack. + * + * Just use the error_code dummy here. + */ + boost::system::error_code ec; -bool HttpServerConnection::ProcessMessage() -{ - bool res; - HttpResponse response(m_Stream, m_CurrentRequest); + m_CheckLivenessTimer.cancel(); - if (!m_CurrentRequest.CompleteHeaders) { - try { - res = m_CurrentRequest.ParseHeaders(m_Context, false); - } catch (const std::invalid_argument& ex) { - response.SetStatus(400, "Bad Request"); - String msg = String("

Bad Request

") + ex.what() + "

"; - response.WriteBody(msg.CStr(), msg.GetLength()); - response.Finish(); + m_Stream->lowest_layer().cancel(ec); - m_CurrentRequest.~HttpRequest(); - new (&m_CurrentRequest) HttpRequest(m_Stream); + m_Stream->next_layer().async_shutdown(yc[ec]); - m_Stream->Shutdown(); + m_Stream->lowest_layer().shutdown(m_Stream->lowest_layer().shutdown_both, ec); - return false; - } catch (const std::exception& ex) { - response.SetStatus(500, "Internal Server Error"); - String msg = "

Internal Server Error

" + DiagnosticInformation(ex) + "

"; - response.WriteBody(msg.CStr(), msg.GetLength()); - response.Finish(); + auto listener (ApiListener::GetInstance()); - m_CurrentRequest.~HttpRequest(); - new (&m_CurrentRequest) HttpRequest(m_Stream); + if (listener) { + CpuBoundWork removeHttpClient (yc); - m_Stream->Shutdown(); - - return false; - } - return res; - } - - if (!m_CurrentRequest.CompleteHeaderCheck) { - m_CurrentRequest.CompleteHeaderCheck = true; - if (!ManageHeaders(response)) { - m_CurrentRequest.~HttpRequest(); - new (&m_CurrentRequest) HttpRequest(m_Stream); - - m_Stream->Shutdown(); - - return false; - } - } - - if (!m_CurrentRequest.CompleteBody) { - try { - res = m_CurrentRequest.ParseBody(m_Context, false); - } catch (const std::invalid_argument& ex) { - response.SetStatus(400, "Bad Request"); - String msg = String("

Bad Request

") + ex.what() + "

"; - response.WriteBody(msg.CStr(), msg.GetLength()); - response.Finish(); - - m_CurrentRequest.~HttpRequest(); - new (&m_CurrentRequest) HttpRequest(m_Stream); - - m_Stream->Shutdown(); - - return false; - } catch (const std::exception& ex) { - response.SetStatus(500, "Internal Server Error"); - String msg = "

Internal Server Error

" + DiagnosticInformation(ex) + "

"; - response.WriteBody(msg.CStr(), msg.GetLength()); - response.Finish(); - - m_CurrentRequest.~HttpRequest(); - new (&m_CurrentRequest) HttpRequest(m_Stream); - - m_Stream->Shutdown(); - - return false; - } - return res; - } - - m_RequestQueue.Enqueue(std::bind(&HttpServerConnection::ProcessMessageAsync, - HttpServerConnection::Ptr(this), m_CurrentRequest, response, m_AuthenticatedUser)); - - m_Seen = Utility::GetTime(); - m_PendingRequests++; - - m_CurrentRequest.~HttpRequest(); - new (&m_CurrentRequest) HttpRequest(m_Stream); - - return false; -} - -bool HttpServerConnection::ManageHeaders(HttpResponse& response) -{ - if (m_CurrentRequest.Headers->Get("expect") == "100-continue") { - String continueResponse = "HTTP/1.1 100 Continue\r\n\r\n"; - m_Stream->Write(continueResponse.CStr(), continueResponse.GetLength()); - } - - /* client_cn matched. */ - if (m_ApiUser) - m_AuthenticatedUser = m_ApiUser; - else - m_AuthenticatedUser = ApiUser::GetByAuthHeader(m_CurrentRequest.Headers->Get("authorization")); - - String requestUrl = m_CurrentRequest.RequestUrl->Format(); - - Socket::Ptr socket = m_Stream->GetSocket(); - - Log(LogInformation, "HttpServerConnection") - << "Request: " << m_CurrentRequest.RequestMethod << " " << requestUrl - << " (from " << (socket ? socket->GetPeerAddress() : "") - << ", user: " << (m_AuthenticatedUser ? m_AuthenticatedUser->GetName() : "") << ")"; - - ApiListener::Ptr listener = ApiListener::GetInstance(); - - if (!listener) - return false; - - Array::Ptr headerAllowOrigin = listener->GetAccessControlAllowOrigin(); - - if (headerAllowOrigin && headerAllowOrigin->GetLength() != 0) { - String origin = m_CurrentRequest.Headers->Get("origin"); - { - ObjectLock olock(headerAllowOrigin); - - for (const String& allowedOrigin : headerAllowOrigin) { - if (allowedOrigin == origin) - response.AddHeader("Access-Control-Allow-Origin", origin); + listener->RemoveHttpClient(this); } } + }); +} - response.AddHeader("Access-Control-Allow-Credentials", "true"); +void HttpServerConnection::StartStreaming() +{ + namespace asio = boost::asio; - String accessControlRequestMethodHeader = m_CurrentRequest.Headers->Get("access-control-request-method"); + m_HasStartedStreaming = true; - if (m_CurrentRequest.RequestMethod == "OPTIONS" && !accessControlRequestMethodHeader.IsEmpty()) { - response.SetStatus(200, "OK"); + HttpServerConnection::Ptr keepAlive (this); - response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); - response.AddHeader("Access-Control-Allow-Headers", "Authorization, X-HTTP-Method-Override"); + IoEngine::SpawnCoroutine(m_IoStrand, [this, keepAlive](asio::yield_context yc) { + if (!m_ShuttingDown) { + char buf[128]; + asio::mutable_buffer readBuf (buf, 128); + boost::system::error_code ec; - String msg = "Preflight OK"; - response.WriteBody(msg.CStr(), msg.GetLength()); + do { + m_Stream->async_read_some(readBuf, yc[ec]); + } while (!ec); - response.Finish(); + Disconnect(); + } + }); +} + +bool HttpServerConnection::Disconnected() +{ + return m_ShuttingDown; +} + +static inline +bool EnsureValidHeaders( + AsioTlsStream& stream, + boost::beast::flat_buffer& buf, + boost::beast::http::parser& parser, + boost::beast::http::response& response, + bool& shuttingDown, + boost::asio::yield_context& yc +) +{ + namespace http = boost::beast::http; + + if (shuttingDown) + return false; + + bool httpError = false; + String errorMsg; + + boost::system::error_code ec; + + http::async_read_header(stream, buf, parser, yc[ec]); + + if (ec) { + if (ec == boost::asio::error::operation_aborted) return false; + + errorMsg = ec.message(); + httpError = true; + } else { + switch (parser.get().version()) { + case 10: + case 11: + break; + default: + errorMsg = "Unsupported HTTP version"; } } - if (m_CurrentRequest.RequestMethod != "GET" && m_CurrentRequest.Headers->Get("accept") != "application/json") { - response.SetStatus(400, "Wrong Accept header"); - response.AddHeader("Content-Type", "text/html"); - String msg = "

Accept header is missing or not set to 'application/json'.

"; - response.WriteBody(msg.CStr(), msg.GetLength()); - response.Finish(); - return false; - } + if (!errorMsg.IsEmpty() || httpError) { + response.result(http::status::bad_request); - if (!m_AuthenticatedUser) { - Log(LogWarning, "HttpServerConnection") - << "Unauthorized request: " << m_CurrentRequest.RequestMethod << " " << requestUrl; - - response.SetStatus(401, "Unauthorized"); - response.AddHeader("WWW-Authenticate", "Basic realm=\"Icinga 2\""); - - if (m_CurrentRequest.Headers->Get("accept") == "application/json") { - Dictionary::Ptr result = new Dictionary({ - { "error", 401 }, - { "status", "Unauthorized. Please check your user credentials." } - }); - - HttpUtility::SendJsonBody(response, nullptr, result); + if (!httpError && parser.get()[http::field::accept] == "application/json") { + HttpUtility::SendJsonBody(response, nullptr, new Dictionary({ + { "error", 400 }, + { "status", String("Bad Request: ") + errorMsg } + })); } else { - response.AddHeader("Content-Type", "text/html"); - String msg = "

Unauthorized. Please check your user credentials.

"; - response.WriteBody(msg.CStr(), msg.GetLength()); + response.set(http::field::content_type, "text/html"); + response.body() = String("

Bad Request

") + errorMsg + "

"; + response.set(http::field::content_length, response.body().size()); } - response.Finish(); - return false; - } + response.set(http::field::connection, "close"); - static const size_t defaultContentLengthLimit = 1 * 1024 * 1024; - size_t maxSize = defaultContentLengthLimit; + boost::system::error_code ec; - Array::Ptr permissions = m_AuthenticatedUser->GetPermissions(); - - if (permissions) { - ObjectLock olock(permissions); - - for (const Value& permissionInfo : permissions) { - String permission; - - if (permissionInfo.IsObjectType()) - permission = static_cast(permissionInfo)->Get("permission"); - else - permission = permissionInfo; - - static std::vector> specialContentLengthLimits { - { "config/modify", 512 * 1024 * 1024 } - }; - - for (const auto& limitInfo : specialContentLengthLimits) { - if (limitInfo.second <= maxSize) - continue; - - if (Utility::Match(permission, limitInfo.first)) - maxSize = limitInfo.second; - } - } - } - - size_t contentLength = m_CurrentRequest.Headers->Get("content-length"); - - if (contentLength > maxSize) { - response.SetStatus(400, "Bad Request"); - String msg = String("

Content length exceeded maximum

"); - response.WriteBody(msg.CStr(), msg.GetLength()); - response.Finish(); + http::async_write(stream, response, yc[ec]); + stream.async_flush(yc[ec]); return false; } @@ -320,65 +202,404 @@ bool HttpServerConnection::ManageHeaders(HttpResponse& response) return true; } -void HttpServerConnection::ProcessMessageAsync(HttpRequest& request, HttpResponse& response, const ApiUser::Ptr& user) +static inline +void HandleExpect100( + AsioTlsStream& stream, + boost::beast::http::request& request, + boost::asio::yield_context& yc +) { - response.RebindRequest(request); + namespace http = boost::beast::http; - try { - HttpHandler::ProcessRequest(user, request, response); - } catch (const std::exception& ex) { - Log(LogCritical, "HttpServerConnection") - << "Unhandled exception while processing Http request: " << DiagnosticInformation(ex); - HttpUtility::SendJsonError(response, nullptr, 503, "Unhandled exception" , DiagnosticInformation(ex)); + if (request[http::field::expect] == "100-continue") { + http::response response; + + response.result(http::status::continue_); + + boost::system::error_code ec; + + http::async_write(stream, response, yc[ec]); + stream.async_flush(yc[ec]); } - - response.Finish(); - m_PendingRequests--; - m_Stream->SetCorked(false); } -void HttpServerConnection::DataAvailableHandler() +static inline +bool HandleAccessControl( + AsioTlsStream& stream, + boost::beast::http::request& request, + boost::beast::http::response& response, + boost::asio::yield_context& yc +) { - bool close = false; + namespace http = boost::beast::http; - if (!m_Stream->IsEof()) { - boost::mutex::scoped_lock lock(m_DataHandlerMutex); + auto listener (ApiListener::GetInstance()); - m_Stream->SetCorked(true); + if (listener) { + auto headerAllowOrigin (listener->GetAccessControlAllowOrigin()); - try { - while (ProcessMessage()) - ; /* empty loop body */ - } catch (const std::exception& ex) { - Log(LogWarning, "HttpServerConnection") - << "Error while reading Http request: " << DiagnosticInformation(ex); + if (headerAllowOrigin) { + CpuBoundWork allowOriginHeader (yc); - close = true; + auto allowedOrigins (headerAllowOrigin->ToSet()); + + if (!allowedOrigins.empty()) { + auto& origin (request[http::field::origin]); + + if (allowedOrigins.find(origin.to_string()) != allowedOrigins.end()) { + response.set(http::field::access_control_allow_origin, origin); + } + + allowOriginHeader.Done(); + + response.set(http::field::access_control_allow_credentials, "true"); + + if (request.method() == http::verb::options && !request[http::field::access_control_request_method].empty()) { + response.result(http::status::ok); + response.set(http::field::access_control_allow_methods, "GET, POST, PUT, DELETE"); + response.set(http::field::access_control_allow_headers, "Authorization, X-HTTP-Method-Override"); + response.body() = "Preflight OK"; + response.set(http::field::content_length, response.body().size()); + response.set(http::field::connection, "close"); + + boost::system::error_code ec; + + http::async_write(stream, response, yc[ec]); + stream.async_flush(yc[ec]); + + return false; + } + } + } + } + + return true; +} + +static inline +bool EnsureAcceptHeader( + AsioTlsStream& stream, + boost::beast::http::request& request, + boost::beast::http::response& response, + boost::asio::yield_context& yc +) +{ + namespace http = boost::beast::http; + + if (request.method() != http::verb::get && request[http::field::accept] != "application/json") { + response.result(http::status::bad_request); + response.set(http::field::content_type, "text/html"); + response.body() = "

Accept header is missing or not set to 'application/json'.

"; + response.set(http::field::content_length, response.body().size()); + response.set(http::field::connection, "close"); + + boost::system::error_code ec; + + http::async_write(stream, response, yc[ec]); + stream.async_flush(yc[ec]); + + return false; + } + + return true; +} + +static inline +bool EnsureAuthenticatedUser( + AsioTlsStream& stream, + boost::beast::http::request& request, + ApiUser::Ptr& authenticatedUser, + boost::beast::http::response& response, + boost::asio::yield_context& yc +) +{ + namespace http = boost::beast::http; + + if (!authenticatedUser) { + Log(LogWarning, "HttpServerConnection") + << "Unauthorized request: " << request.method_string() << ' ' << request.target(); + + response.result(http::status::unauthorized); + response.set(http::field::www_authenticate, "Basic realm=\"Icinga 2\""); + response.set(http::field::connection, "close"); + + if (request[http::field::accept] == "application/json") { + HttpUtility::SendJsonBody(response, nullptr, new Dictionary({ + { "error", 401 }, + { "status", "Unauthorized. Please check your user credentials." } + })); + } else { + response.set(http::field::content_type, "text/html"); + response.body() = "

Unauthorized. Please check your user credentials.

"; + response.set(http::field::content_length, response.body().size()); } - m_RequestQueue.Enqueue(std::bind(&Stream::SetCorked, m_Stream, false)); - } else - close = true; + boost::system::error_code ec; - if (close) - Disconnect(); + http::async_write(stream, response, yc[ec]); + stream.async_flush(yc[ec]); + + return false; + } + + return true; } -void HttpServerConnection::CheckLiveness() +static inline +bool EnsureValidBody( + AsioTlsStream& stream, + boost::beast::flat_buffer& buf, + boost::beast::http::parser& parser, + ApiUser::Ptr& authenticatedUser, + boost::beast::http::response& response, + bool& shuttingDown, + boost::asio::yield_context& yc +) { - if (m_Seen < Utility::GetTime() - 10 && m_PendingRequests == 0) { - Log(LogInformation, "HttpServerConnection") - << "No messages for Http connection have been received in the last 10 seconds."; - Disconnect(); + namespace http = boost::beast::http; + + { + size_t maxSize = 1024 * 1024; + Array::Ptr permissions = authenticatedUser->GetPermissions(); + + if (permissions) { + CpuBoundWork evalPermissions (yc); + + ObjectLock olock(permissions); + + for (const Value& permissionInfo : permissions) { + String permission; + + if (permissionInfo.IsObjectType()) { + permission = static_cast(permissionInfo)->Get("permission"); + } else { + permission = permissionInfo; + } + + static std::vector> specialContentLengthLimits { + { "config/modify", 512 * 1024 * 1024 } + }; + + for (const auto& limitInfo : specialContentLengthLimits) { + if (limitInfo.second <= maxSize) { + continue; + } + + if (Utility::Match(permission, limitInfo.first)) { + maxSize = limitInfo.second; + } + } + } + } + + parser.body_limit(maxSize); + } + + if (shuttingDown) + return false; + + boost::system::error_code ec; + + http::async_read(stream, buf, parser, yc[ec]); + + if (ec) { + if (ec == boost::asio::error::operation_aborted) + return false; + + /** + * Unfortunately there's no way to tell an HTTP protocol error + * from an error on a lower layer: + * + * + */ + + response.result(http::status::bad_request); + + if (parser.get()[http::field::accept] == "application/json") { + HttpUtility::SendJsonBody(response, nullptr, new Dictionary({ + { "error", 400 }, + { "status", String("Bad Request: ") + ec.message() } + })); + } else { + response.set(http::field::content_type, "text/html"); + response.body() = String("

Bad Request

") + ec.message() + "

"; + response.set(http::field::content_length, response.body().size()); + } + + response.set(http::field::connection, "close"); + + http::async_write(stream, response, yc[ec]); + stream.async_flush(yc[ec]); + + return false; + } + + return true; +} + +static inline +bool ProcessRequest( + AsioTlsStream& stream, + boost::beast::http::request& request, + ApiUser::Ptr& authenticatedUser, + boost::beast::http::response& response, + HttpServerConnection& server, + bool& hasStartedStreaming, + boost::asio::yield_context& yc +) +{ + namespace http = boost::beast::http; + + try { + CpuBoundWork handlingRequest (yc); + + HttpHandler::ProcessRequest(stream, authenticatedUser, request, response, yc, server); + } catch (const std::exception& ex) { + if (hasStartedStreaming) { + return false; + } + + auto sysErr (dynamic_cast(&ex)); + + if (sysErr && sysErr->code() == boost::asio::error::operation_aborted) { + throw; + } + + http::response response; + + HttpUtility::SendJsonError(response, nullptr, 500, "Unhandled exception" , DiagnosticInformation(ex)); + + boost::system::error_code ec; + + http::async_write(stream, response, yc[ec]); + stream.async_flush(yc[ec]); + + return true; + } + + if (hasStartedStreaming) { + return false; + } + + boost::system::error_code ec; + + http::async_write(stream, response, yc[ec]); + stream.async_flush(yc[ec]); + + return true; +} + +void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc) +{ + namespace beast = boost::beast; + namespace http = beast::http; + + try { + /* Do not reset the buffer in the state machine. + * EnsureValidHeaders already reads from the stream into the buffer, + * EnsureValidBody continues. ProcessRequest() actually handles the request + * and needs the full buffer. + */ + beast::flat_buffer buf; + + for (;;) { + m_Seen = Utility::GetTime(); + + http::parser parser; + http::response response; + + parser.header_limit(1024 * 1024); + parser.body_limit(-1); + + response.set(http::field::server, l_ServerHeader); + + if (!EnsureValidHeaders(*m_Stream, buf, parser, response, m_ShuttingDown, yc)) { + break; + } + + m_Seen = Utility::GetTime(); + + auto& request (parser.get()); + + { + auto method (http::string_to_verb(request["X-Http-Method-Override"])); + + if (method != http::verb::unknown) { + request.method(method); + } + } + + HandleExpect100(*m_Stream, request, yc); + + auto authenticatedUser (m_ApiUser); + + if (!authenticatedUser) { + CpuBoundWork fetchingAuthenticatedUser (yc); + + authenticatedUser = ApiUser::GetByAuthHeader(request[http::field::authorization].to_string()); + } + + Log(LogInformation, "HttpServerConnection") + << "Request: " << request.method_string() << ' ' << request.target() + << " (from " << m_PeerAddress + << "), user: " << (authenticatedUser ? authenticatedUser->GetName() : "") + << ", agent: " << request[http::field::user_agent] << ")."; //operator[] - Returns the value for a field, or "" if it does not exist. + + + if (!HandleAccessControl(*m_Stream, request, response, yc)) { + break; + } + + if (!EnsureAcceptHeader(*m_Stream, request, response, yc)) { + break; + } + + if (!EnsureAuthenticatedUser(*m_Stream, request, authenticatedUser, response, yc)) { + break; + } + + if (!EnsureValidBody(*m_Stream, buf, parser, authenticatedUser, response, m_ShuttingDown, yc)) { + break; + } + + m_Seen = std::numeric_limits::max(); + + if (!ProcessRequest(*m_Stream, request, authenticatedUser, response, *this, m_HasStartedStreaming, yc)) { + break; + } + + if (request.version() != 11 || request[http::field::connection] == "close") { + break; + } + } + } catch (const std::exception& ex) { + if (!m_ShuttingDown) { + Log(LogCritical, "HttpServerConnection") + << "Unhandled exception while processing HTTP request: " << ex.what(); + } + } + + Disconnect(); +} + +void HttpServerConnection::CheckLiveness(boost::asio::yield_context yc) +{ + boost::system::error_code ec; + + for (;;) { + m_CheckLivenessTimer.expires_from_now(boost::posix_time::seconds(5)); + m_CheckLivenessTimer.async_wait(yc[ec]); + + if (m_ShuttingDown) { + break; + } + + if (m_Seen < Utility::GetTime() - 10) { + Log(LogInformation, "HttpServerConnection") + << "No messages for HTTP connection have been received in the last 10 seconds."; + + Disconnect(); + break; + } } } - -void HttpServerConnection::TimeoutTimerHandler() -{ - ApiListener::Ptr listener = ApiListener::GetInstance(); - - for (const HttpServerConnection::Ptr& client : listener->GetHttpClients()) { - client->CheckLiveness(); - } -} - diff --git a/lib/remote/httpserverconnection.hpp b/lib/remote/httpserverconnection.hpp index 5f959b8a0..43851e76e 100644 --- a/lib/remote/httpserverconnection.hpp +++ b/lib/remote/httpserverconnection.hpp @@ -1,30 +1,16 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef HTTPSERVERCONNECTION_H #define HTTPSERVERCONNECTION_H -#include "remote/httprequest.hpp" -#include "remote/httpresponse.hpp" #include "remote/apiuser.hpp" +#include "base/string.hpp" #include "base/tlsstream.hpp" -#include "base/workqueue.hpp" +#include +#include +#include +#include +#include namespace icinga { @@ -39,38 +25,28 @@ class HttpServerConnection final : public Object public: DECLARE_PTR_TYPEDEFS(HttpServerConnection); - HttpServerConnection(const String& identity, bool authenticated, const TlsStream::Ptr& stream); + HttpServerConnection(const String& identity, bool authenticated, const std::shared_ptr& stream); void Start(); - - ApiUser::Ptr GetApiUser() const; - bool IsAuthenticated() const; - TlsStream::Ptr GetStream() const; - void Disconnect(); + void StartStreaming(); + + bool Disconnected(); private: ApiUser::Ptr m_ApiUser; - ApiUser::Ptr m_AuthenticatedUser; - TlsStream::Ptr m_Stream; + std::shared_ptr m_Stream; double m_Seen; - HttpRequest m_CurrentRequest; - boost::mutex m_DataHandlerMutex; - WorkQueue m_RequestQueue; - int m_PendingRequests; + String m_PeerAddress; + boost::asio::io_context::strand m_IoStrand; + bool m_ShuttingDown; + bool m_HasStartedStreaming; + boost::asio::deadline_timer m_CheckLivenessTimer; - StreamReadContext m_Context; + HttpServerConnection(const String& identity, bool authenticated, const std::shared_ptr& stream, boost::asio::io_context& io); - bool ProcessMessage(); - void DataAvailableHandler(); - - static void StaticInitialize(); - static void TimeoutTimerHandler(); - void CheckLiveness(); - - bool ManageHeaders(HttpResponse& response); - - void ProcessMessageAsync(HttpRequest& request, HttpResponse& response, const ApiUser::Ptr&); + void ProcessMessages(boost::asio::yield_context yc); + void CheckLiveness(boost::asio::yield_context yc); }; } diff --git a/lib/remote/httputility.cpp b/lib/remote/httputility.cpp index 2788a03b2..91902ba50 100644 --- a/lib/remote/httputility.cpp +++ b/lib/remote/httputility.cpp @@ -1,72 +1,42 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/httputility.hpp" +#include "remote/url.hpp" #include "base/json.hpp" #include "base/logger.hpp" +#include +#include +#include +#include using namespace icinga; -Dictionary::Ptr HttpUtility::FetchRequestParameters(HttpRequest& request) +Dictionary::Ptr HttpUtility::FetchRequestParameters(const Url::Ptr& url, const std::string& body) { Dictionary::Ptr result; - String body; - char buffer[1024]; - size_t count; - - while ((count = request.ReadBody(buffer, sizeof(buffer))) > 0) - body += String(buffer, buffer + count); - - if (!body.IsEmpty()) { -#ifdef I2_DEBUG + if (!body.empty()) { Log(LogDebug, "HttpUtility") - << "Request body: '" << body << "'"; -#endif /* I2_DEBUG */ + << "Request body: '" << body << '\''; + result = JsonDecode(body); } if (!result) result = new Dictionary(); - typedef std::pair > kv_pair; - for (const kv_pair& kv : request.RequestUrl->GetQuery()) { + std::map> query; + for (const auto& kv : url->GetQuery()) { + query[kv.first].emplace_back(kv.second); + } + + for (auto& kv : query) { result->Set(kv.first, Array::FromVector(kv.second)); } return result; } -void HttpUtility::SendJsonBody(HttpResponse& response, const Dictionary::Ptr& params, const Value& val) -{ - response.AddHeader("Content-Type", "application/json"); - - bool prettyPrint = false; - - if (params) - prettyPrint = GetLastParameter(params, "pretty"); - - String body = JsonEncode(val, prettyPrint); - - response.WriteBody(body.CStr(), body.GetLength()); -} - Value HttpUtility::GetLastParameter(const Dictionary::Ptr& params, const String& key) { Value varr = params->Get(key); @@ -82,54 +52,29 @@ Value HttpUtility::GetLastParameter(const Dictionary::Ptr& params, const String& return arr->Get(arr->GetLength() - 1); } -void HttpUtility::SendJsonError(HttpResponse& response, const Dictionary::Ptr& params, - int code, const String& info, const String& diagnosticInformation) +void HttpUtility::SendJsonBody(boost::beast::http::response& response, const Dictionary::Ptr& params, const Value& val) { - Dictionary::Ptr result = new Dictionary(); - response.SetStatus(code, HttpUtility::GetErrorNameByCode(code)); - result->Set("error", code); + namespace http = boost::beast::http; - bool verbose = false; + response.set(http::field::content_type, "application/json"); + response.body() = JsonEncode(val, params && GetLastParameter(params, "pretty")); + response.set(http::field::content_length, response.body().size()); +} - if (params) - verbose = HttpUtility::GetLastParameter(params, "verbose"); +void HttpUtility::SendJsonError(boost::beast::http::response& response, + const Dictionary::Ptr& params, int code, const String& info, const String& diagnosticInformation) +{ + Dictionary::Ptr result = new Dictionary({ { "error", code } }); - if (!info.IsEmpty()) + if (!info.IsEmpty()) { result->Set("status", info); - - if (verbose) { - if (!diagnosticInformation.IsEmpty()) - result->Set("diagnostic_information", diagnosticInformation); } + if (params && HttpUtility::GetLastParameter(params, "verbose") && !diagnosticInformation.IsEmpty()) { + result->Set("diagnostic_information", diagnosticInformation); + } + + response.result(code); + HttpUtility::SendJsonBody(response, params, result); } - -String HttpUtility::GetErrorNameByCode(const int code) -{ - switch(code) { - case 200: - return "OK"; - case 201: - return "Created"; - case 204: - return "No Content"; - case 304: - return "Not Modified"; - case 400: - return "Bad Request"; - case 401: - return "Unauthorized"; - case 403: - return "Forbidden"; - case 404: - return "Not Found"; - case 409: - return "Conflict"; - case 500: - return "Internal Server Error"; - default: - return "Unknown Error Code"; - } -} - diff --git a/lib/remote/httputility.hpp b/lib/remote/httputility.hpp index 2bc3690d8..6465b4af9 100644 --- a/lib/remote/httputility.hpp +++ b/lib/remote/httputility.hpp @@ -1,28 +1,12 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef HTTPUTILITY_H #define HTTPUTILITY_H -#include "remote/httprequest.hpp" -#include "remote/httpresponse.hpp" +#include "remote/url.hpp" #include "base/dictionary.hpp" +#include +#include namespace icinga { @@ -36,15 +20,12 @@ class HttpUtility { public: - static Dictionary::Ptr FetchRequestParameters(HttpRequest& request); - static void SendJsonBody(HttpResponse& response, const Dictionary::Ptr& params, const Value& val); + static Dictionary::Ptr FetchRequestParameters(const Url::Ptr& url, const std::string& body); static Value GetLastParameter(const Dictionary::Ptr& params, const String& key); - static void SendJsonError(HttpResponse& response, const Dictionary::Ptr& params, const int code, + + static void SendJsonBody(boost::beast::http::response& response, const Dictionary::Ptr& params, const Value& val); + static void SendJsonError(boost::beast::http::response& response, const Dictionary::Ptr& params, const int code, const String& verbose = String(), const String& diagnosticInformation = String()); - -private: - static String GetErrorNameByCode(int code); - }; } diff --git a/lib/remote/i2-remote.hpp b/lib/remote/i2-remote.hpp index 292e8f2e0..5755befdb 100644 --- a/lib/remote/i2-remote.hpp +++ b/lib/remote/i2-remote.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef I2REMOTE_H #define I2REMOTE_H diff --git a/lib/remote/infohandler.cpp b/lib/remote/infohandler.cpp index 5336c84cf..18c18c0e0 100644 --- a/lib/remote/infohandler.cpp +++ b/lib/remote/infohandler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/infohandler.hpp" #include "remote/httputility.hpp" @@ -25,24 +8,35 @@ using namespace icinga; REGISTER_URLHANDLER("/", InfoHandler); -bool InfoHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool InfoHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() > 2) + namespace http = boost::beast::http; + + if (url->GetPath().size() > 2) return false; - if (request.RequestMethod != "GET") + if (request.method() != http::verb::get) return false; - if (request.RequestUrl->GetPath().empty()) { - response.SetStatus(302, "Found"); - response.AddHeader("Location", "/v1"); + if (url->GetPath().empty()) { + response.result(http::status::found); + response.set(http::field::location, "/v1"); return true; } - if (request.RequestUrl->GetPath()[0] != "v1" || request.RequestUrl->GetPath().size() != 1) + if (url->GetPath()[0] != "v1" || url->GetPath().size() != 1) return false; - response.SetStatus(200, "OK"); + response.result(http::status::ok); std::vector permInfo; Array::Ptr permissions = user->GetPermissions(); @@ -66,12 +60,12 @@ bool InfoHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, } } - if (request.Headers->Get("accept") == "application/json") { + if (request[http::field::accept] == "application/json") { Dictionary::Ptr result1 = new Dictionary({ { "user", user->GetName() }, { "permissions", Array::FromVector(permInfo) }, { "version", Application::GetAppVersion() }, - { "info", "More information about API requests is available in the documentation at https://docs.icinga.com/icinga2/latest." } + { "info", "More information about API requests is available in the documentation at https://icinga.com/docs/icinga2/latest/" } }); Dictionary::Ptr result = new Dictionary({ @@ -80,7 +74,7 @@ bool InfoHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpUtility::SendJsonBody(response, params, result); } else { - response.AddHeader("Content-Type", "text/html"); + response.set(http::field::content_type, "text/html"); String body = "Icinga 2

Hello from Icinga 2 (Version: " + Application::GetAppVersion() + ")!

"; body += "

You are authenticated as " + user->GetName() + ". "; @@ -96,8 +90,9 @@ bool InfoHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, } else body += "Your user does not have any permissions.

"; - body += R"(

More information about API requests is available in the documentation.

)"; - response.WriteBody(body.CStr(), body.GetLength()); + body += R"(

More information about API requests is available in the documentation.

)"; + response.body() = body; + response.set(http::field::content_length, response.body().size()); } return true; diff --git a/lib/remote/infohandler.hpp b/lib/remote/infohandler.hpp index f99eab5c8..e1fe98314 100644 --- a/lib/remote/infohandler.hpp +++ b/lib/remote/infohandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef INFOHANDLER_H #define INFOHANDLER_H @@ -30,8 +13,16 @@ class InfoHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(InfoHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; }; } diff --git a/lib/remote/jsonrpc.cpp b/lib/remote/jsonrpc.cpp index fa6c3df00..d17b5780f 100644 --- a/lib/remote/jsonrpc.cpp +++ b/lib/remote/jsonrpc.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/jsonrpc.hpp" #include "base/netstring.hpp" @@ -23,11 +6,20 @@ #include "base/console.hpp" #include "base/scriptglobal.hpp" #include "base/convert.hpp" +#include "base/tlsstream.hpp" #include +#include +#include +#include using namespace icinga; #ifdef I2_DEBUG +/** + * Determine whether the developer wants to see raw JSON messages. + * + * @return Internal.DebugJsonRpc boolean + */ static bool GetDebugJsonRpcCached() { static int debugJsonRpc = -1; @@ -37,7 +29,7 @@ static bool GetDebugJsonRpcCached() debugJsonRpc = false; - Dictionary::Ptr internal = ScriptGlobal::Get("Internal", &Empty); + Namespace::Ptr internal = ScriptGlobal::Get("Internal", &Empty); if (!internal) return false; @@ -60,7 +52,7 @@ static bool GetDebugJsonRpcCached() * * @return The amount of bytes sent. */ -size_t JsonRpc::SendMessage(const Stream::Ptr& stream, const Dictionary::Ptr& message) +size_t JsonRpc::SendMessage(const std::shared_ptr& stream, const Dictionary::Ptr& message) { String json = JsonEncode(message); @@ -72,24 +64,86 @@ size_t JsonRpc::SendMessage(const Stream::Ptr& stream, const Dictionary::Ptr& me return NetString::WriteStringToStream(stream, json); } -StreamReadStatus JsonRpc::ReadMessage(const Stream::Ptr& stream, String *message, StreamReadContext& src, bool may_wait, ssize_t maxMessageLength) +/** + * Sends a message to the connected peer and returns the bytes sent. + * + * @param message The message. + * + * @return The amount of bytes sent. + */ +size_t JsonRpc::SendMessage(const std::shared_ptr& stream, const Dictionary::Ptr& message, boost::asio::yield_context yc) { - String jsonString; - StreamReadStatus srs = NetString::ReadStringFromStream(stream, &jsonString, src, may_wait, maxMessageLength); + return JsonRpc::SendRawMessage(stream, JsonEncode(message), yc); +} - if (srs != StatusNewItem) - return srs; + /** + * Sends a raw message to the connected peer. + * + * @param stream ASIO TLS Stream + * @param json message + * @param yc Yield context required for ASIO + * + * @return bytes sent + */ +size_t JsonRpc::SendRawMessage(const std::shared_ptr& stream, const String& json, boost::asio::yield_context yc) +{ +#ifdef I2_DEBUG + if (GetDebugJsonRpcCached()) + std::cerr << ConsoleColorTag(Console_ForegroundBlue) << ">> " << json << ConsoleColorTag(Console_Normal) << "\n"; +#endif /* I2_DEBUG */ - *message = jsonString; + return NetString::WriteStringToStream(stream, json, yc); +} + +/** + * Reads a message from the connected peer. + * + * @param stream ASIO TLS Stream + * @param maxMessageLength maximum size of bytes read. + * + * @return A JSON string + */ + +String JsonRpc::ReadMessage(const std::shared_ptr& stream, ssize_t maxMessageLength) +{ + String jsonString = NetString::ReadStringFromStream(stream, maxMessageLength); #ifdef I2_DEBUG if (GetDebugJsonRpcCached()) std::cerr << ConsoleColorTag(Console_ForegroundBlue) << "<< " << jsonString << ConsoleColorTag(Console_Normal) << "\n"; #endif /* I2_DEBUG */ - return StatusNewItem; + return std::move(jsonString); } +/** + * Reads a message from the connected peer. + * + * @param stream ASIO TLS Stream + * @param yc Yield Context for ASIO + * @param maxMessageLength maximum size of bytes read. + * + * @return A JSON string + */ +String JsonRpc::ReadMessage(const std::shared_ptr& stream, boost::asio::yield_context yc, ssize_t maxMessageLength) +{ + String jsonString = NetString::ReadStringFromStream(stream, yc, maxMessageLength); + +#ifdef I2_DEBUG + if (GetDebugJsonRpcCached()) + std::cerr << ConsoleColorTag(Console_ForegroundBlue) << "<< " << jsonString << ConsoleColorTag(Console_Normal) << "\n"; +#endif /* I2_DEBUG */ + + return std::move(jsonString); +} + +/** + * Decode message, enforce a Dictionary + * + * @param message JSON string + * + * @return Dictionary ptr + */ Dictionary::Ptr JsonRpc::DecodeMessage(const String& message) { Value value = JsonDecode(message); diff --git a/lib/remote/jsonrpc.hpp b/lib/remote/jsonrpc.hpp index e7d2a05ea..cc4cc7b6d 100644 --- a/lib/remote/jsonrpc.hpp +++ b/lib/remote/jsonrpc.hpp @@ -1,28 +1,14 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef JSONRPC_H #define JSONRPC_H #include "base/stream.hpp" #include "base/dictionary.hpp" +#include "base/tlsstream.hpp" #include "remote/i2-remote.hpp" +#include +#include namespace icinga { @@ -35,8 +21,13 @@ namespace icinga class JsonRpc { public: - static size_t SendMessage(const Stream::Ptr& stream, const Dictionary::Ptr& message); - static StreamReadStatus ReadMessage(const Stream::Ptr& stream, String *message, StreamReadContext& src, bool may_wait = false, ssize_t maxMessageLength = -1); + static size_t SendMessage(const std::shared_ptr& stream, const Dictionary::Ptr& message); + static size_t SendMessage(const std::shared_ptr& stream, const Dictionary::Ptr& message, boost::asio::yield_context yc); + static size_t SendRawMessage(const std::shared_ptr& stream, const String& json, boost::asio::yield_context yc); + + static String ReadMessage(const std::shared_ptr& stream, ssize_t maxMessageLength = -1); + static String ReadMessage(const std::shared_ptr& stream, boost::asio::yield_context yc, ssize_t maxMessageLength = -1); + static Dictionary::Ptr DecodeMessage(const String& message); private: diff --git a/lib/remote/jsonrpcconnection-heartbeat.cpp b/lib/remote/jsonrpcconnection-heartbeat.cpp index 12b0d0c53..993877e4e 100644 --- a/lib/remote/jsonrpcconnection-heartbeat.cpp +++ b/lib/remote/jsonrpcconnection-heartbeat.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/jsonrpcconnection.hpp" #include "remote/messageorigin.hpp" @@ -24,34 +7,42 @@ #include "base/configtype.hpp" #include "base/logger.hpp" #include "base/utility.hpp" +#include +#include +#include using namespace icinga; REGISTER_APIFUNCTION(Heartbeat, event, &JsonRpcConnection::HeartbeatAPIHandler); -void JsonRpcConnection::HeartbeatTimerHandler() +void JsonRpcConnection::HandleAndWriteHeartbeats(boost::asio::yield_context yc) { - for (const Endpoint::Ptr& endpoint : ConfigType::GetObjectsByType()) { - for (const JsonRpcConnection::Ptr& client : endpoint->GetClients()) { - if (client->m_NextHeartbeat != 0 && client->m_NextHeartbeat < Utility::GetTime()) { - Log(LogWarning, "JsonRpcConnection") - << "Client for endpoint '" << endpoint->GetName() << "' has requested " - << "heartbeat message but hasn't responded in time. Closing connection."; + boost::system::error_code ec; - client->Disconnect(); - continue; - } + for (;;) { + m_HeartbeatTimer.expires_from_now(boost::posix_time::seconds(10)); + m_HeartbeatTimer.async_wait(yc[ec]); - Dictionary::Ptr request = new Dictionary({ - { "jsonrpc", "2.0" }, - { "method", "event::Heartbeat" }, - { "params", new Dictionary({ - { "timeout", 120 } - }) } - }); - - client->SendMessage(request); + if (m_ShuttingDown) { + break; } + + if (m_NextHeartbeat != 0 && m_NextHeartbeat < Utility::GetTime()) { + Log(LogWarning, "JsonRpcConnection") + << "Client for endpoint '" << m_Endpoint->GetName() << "' has requested " + << "heartbeat message but hasn't responded in time. Closing connection."; + + Disconnect(); + break; + } + + SendMessageInternal(new Dictionary({ + { "jsonrpc", "2.0" }, + { "method", "event::Heartbeat" }, + { "params", new Dictionary({ + { "timeout", 120 } + }) } + })); } } @@ -61,7 +52,6 @@ Value JsonRpcConnection::HeartbeatAPIHandler(const MessageOrigin::Ptr& origin, c if (!vtimeout.IsEmpty()) { origin->FromClient->m_NextHeartbeat = Utility::GetTime() + vtimeout; - origin->FromClient->m_HeartbeatTimeout = vtimeout; } return Empty; diff --git a/lib/remote/jsonrpcconnection-pki.cpp b/lib/remote/jsonrpcconnection-pki.cpp index 6c867400c..c538bb1de 100644 --- a/lib/remote/jsonrpcconnection-pki.cpp +++ b/lib/remote/jsonrpcconnection-pki.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/jsonrpcconnection.hpp" #include "remote/apilistener.hpp" @@ -30,6 +13,8 @@ #include #include #include +#include +#include using namespace icinga; @@ -47,10 +32,21 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona Dictionary::Ptr result = new Dictionary(); /* Use the presented client certificate if not provided. */ - if (certText.IsEmpty()) - cert = origin->FromClient->GetStream()->GetPeerCertificate(); - else + if (certText.IsEmpty()) { + auto stream (origin->FromClient->GetStream()); + cert = stream->next_layer().GetPeerCertificate(); + } else { cert = StringToCertificate(certText); + } + + if (!cert) { + Log(LogWarning, "JsonRpcConnection") << "No certificate or CSR received"; + + result->Set("status_code", 1); + result->Set("error", "No certificate or CSR received."); + + return result; + } ApiListener::Ptr listener = ApiListener::GetInstance(); std::shared_ptr cacert = GetX509Certificate(listener->GetDefaultCaPath()); @@ -129,10 +125,16 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona { "method", "pki::UpdateCertificate" }, { "params", result } }); - JsonRpc::SendMessage(client->GetStream(), message); + client->SendMessage(message); return result; } + } else if (Utility::PathExists(requestDir + "/" + certFingerprint + ".removed")) { + Log(LogInformation, "JsonRpcConnection") + << "Certificate for CN " << cn << " has been removed. Ignoring signing request."; + result->Set("status_code", 1); + result->Set("error", "Ticket for CN " + cn + " declined by administrator."); + return result; } std::shared_ptr newcert; @@ -152,14 +154,32 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona ticket = params->Get("ticket"); - /* Auto-signing is disabled by either a) no TicketSalt - * or b) the client did not include a ticket in its request. - */ - if (salt.IsEmpty() || ticket.IsEmpty()) + // Auto-signing is disabled: Client did not include a ticket in its request. + if (ticket.IsEmpty()) { + Log(LogNotice, "JsonRpcConnection") + << "Certificate request for CN '" << cn + << "': No ticket included, skipping auto-signing and waiting for on-demand signing approval."; + goto delayed_request; + } + + // Auto-signing is disabled: no TicketSalt + if (salt.IsEmpty()) { + Log(LogNotice, "JsonRpcConnection") + << "Certificate request for CN '" << cn + << "': This instance is the signing master for the Icinga CA." + << " The 'ticket_salt' attribute in the 'api' feature is not set." + << " Not signing the request. Please check the docs."; + + goto delayed_request; + } String realTicket = PBKDF2_SHA1(cn, salt, 50000); + Log(LogDebug, "JsonRpcConnection") + << "Certificate request for CN '" << cn << "': Comparing received ticket '" + << ticket << "' with calculated ticket '" << realTicket << "'."; + if (ticket != realTicket) { Log(LogWarning, "JsonRpcConnection") << "Ticket '" << ticket << "' for CN '" << cn << "' is invalid."; @@ -200,7 +220,7 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona { "method", "pki::UpdateCertificate" }, { "params", result } }); - JsonRpc::SendMessage(client->GetStream(), message); + client->SendMessage(message); return result; @@ -263,7 +283,7 @@ void JsonRpcConnection::SendCertificateRequest(const JsonRpcConnection::Ptr& acl * or b) the local zone and all parents. */ if (aclient) - JsonRpc::SendMessage(aclient->GetStream(), message); + aclient->SendMessage(message); else listener->RelayMessage(origin, Zone::GetLocalZone(), message, false); } @@ -338,16 +358,7 @@ Value UpdateCertificateHandler(const MessageOrigin::Ptr& origin, const Dictionar cafp << ca; cafp.close(); -#ifdef _WIN32 - _unlink(caPath.CStr()); -#endif /* _WIN32 */ - - if (rename(tempCaPath.CStr(), caPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempCaPath)); - } + Utility::RenameFile(tempCaPath, caPath); /* Update signed certificate. */ String certPath = listener->GetDefaultCertPath(); @@ -360,26 +371,12 @@ Value UpdateCertificateHandler(const MessageOrigin::Ptr& origin, const Dictionar certfp << cert; certfp.close(); -#ifdef _WIN32 - _unlink(certPath.CStr()); -#endif /* _WIN32 */ - - if (rename(tempCertPath.CStr(), certPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempCertPath)); - } + Utility::RenameFile(tempCertPath, certPath); /* Remove ticket for successful signing request. */ String ticketPath = ApiListener::GetCertsDir() + "/ticket"; - if (unlink(ticketPath.CStr()) < 0 && errno != ENOENT) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("unlink") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(ticketPath)); - } + Utility::Remove(ticketPath); /* Update the certificates at runtime and reconnect all endpoints. */ Log(LogInformation, "JsonRpcConnection") diff --git a/lib/remote/jsonrpcconnection.cpp b/lib/remote/jsonrpcconnection.cpp index 259679edc..b6d1d41e6 100644 --- a/lib/remote/jsonrpcconnection.cpp +++ b/lib/remote/jsonrpcconnection.cpp @@ -1,32 +1,25 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/jsonrpcconnection.hpp" #include "remote/apilistener.hpp" #include "remote/apifunction.hpp" #include "remote/jsonrpc.hpp" +#include "base/defer.hpp" #include "base/configtype.hpp" +#include "base/io-engine.hpp" +#include "base/json.hpp" #include "base/objectlock.hpp" #include "base/utility.hpp" #include "base/logger.hpp" #include "base/exception.hpp" #include "base/convert.hpp" +#include "base/tlsstream.hpp" +#include +#include +#include +#include +#include +#include #include using namespace icinga; @@ -34,50 +27,115 @@ using namespace icinga; static Value SetLogPositionHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); REGISTER_APIFUNCTION(SetLogPosition, log, &SetLogPositionHandler); -static boost::once_flag l_JsonRpcConnectionOnceFlag = BOOST_ONCE_INIT; -static Timer::Ptr l_JsonRpcConnectionTimeoutTimer; -static WorkQueue *l_JsonRpcConnectionWorkQueues; -static size_t l_JsonRpcConnectionWorkQueueCount; -static int l_JsonRpcConnectionNextID; -static Timer::Ptr l_HeartbeatTimer; +static RingBuffer l_TaskStats (15 * 60); JsonRpcConnection::JsonRpcConnection(const String& identity, bool authenticated, - TlsStream::Ptr stream, ConnectionRole role) - : m_ID(l_JsonRpcConnectionNextID++), m_Identity(identity), m_Authenticated(authenticated), m_Stream(std::move(stream)), - m_Role(role), m_Timestamp(Utility::GetTime()), m_Seen(Utility::GetTime()), m_NextHeartbeat(0), m_HeartbeatTimeout(0) + const std::shared_ptr& stream, ConnectionRole role) + : JsonRpcConnection(identity, authenticated, stream, role, IoEngine::Get().GetIoContext()) { - boost::call_once(l_JsonRpcConnectionOnceFlag, &JsonRpcConnection::StaticInitialize); +} +JsonRpcConnection::JsonRpcConnection(const String& identity, bool authenticated, + const std::shared_ptr& stream, ConnectionRole role, boost::asio::io_context& io) + : m_Identity(identity), m_Authenticated(authenticated), m_Stream(stream), m_Role(role), + m_Timestamp(Utility::GetTime()), m_Seen(Utility::GetTime()), m_NextHeartbeat(0), m_IoStrand(io), + m_OutgoingMessagesQueued(io), m_WriterDone(io), m_ShuttingDown(false), + m_CheckLivenessTimer(io), m_HeartbeatTimer(io) +{ if (authenticated) m_Endpoint = Endpoint::GetByName(identity); } -void JsonRpcConnection::StaticInitialize() -{ - l_JsonRpcConnectionTimeoutTimer = new Timer(); - l_JsonRpcConnectionTimeoutTimer->OnTimerExpired.connect(std::bind(&JsonRpcConnection::TimeoutTimerHandler)); - l_JsonRpcConnectionTimeoutTimer->SetInterval(15); - l_JsonRpcConnectionTimeoutTimer->Start(); - - l_JsonRpcConnectionWorkQueueCount = Application::GetConcurrency(); - l_JsonRpcConnectionWorkQueues = new WorkQueue[l_JsonRpcConnectionWorkQueueCount]; - - for (size_t i = 0; i < l_JsonRpcConnectionWorkQueueCount; i++) { - l_JsonRpcConnectionWorkQueues[i].SetName("JsonRpcConnection, #" + Convert::ToString(i)); - } - - l_HeartbeatTimer = new Timer(); - l_HeartbeatTimer->OnTimerExpired.connect(std::bind(&JsonRpcConnection::HeartbeatTimerHandler)); - l_HeartbeatTimer->SetInterval(10); - l_HeartbeatTimer->Start(); -} - void JsonRpcConnection::Start() { - /* the stream holds an owning reference to this object through the callback we're registering here */ - m_Stream->RegisterDataHandler(std::bind(&JsonRpcConnection::DataAvailableHandler, JsonRpcConnection::Ptr(this))); - if (m_Stream->IsDataAvailable()) - DataAvailableHandler(); + namespace asio = boost::asio; + + JsonRpcConnection::Ptr keepAlive (this); + + IoEngine::SpawnCoroutine(m_IoStrand, [this, keepAlive](asio::yield_context yc) { HandleIncomingMessages(yc); }); + IoEngine::SpawnCoroutine(m_IoStrand, [this, keepAlive](asio::yield_context yc) { WriteOutgoingMessages(yc); }); + IoEngine::SpawnCoroutine(m_IoStrand, [this, keepAlive](asio::yield_context yc) { HandleAndWriteHeartbeats(yc); }); + IoEngine::SpawnCoroutine(m_IoStrand, [this, keepAlive](asio::yield_context yc) { CheckLiveness(yc); }); +} + +void JsonRpcConnection::HandleIncomingMessages(boost::asio::yield_context yc) +{ + for (;;) { + String message; + + try { + message = JsonRpc::ReadMessage(m_Stream, yc, m_Endpoint ? -1 : 1024 * 1024); + } catch (const std::exception& ex) { + if (!m_ShuttingDown) { + Log(LogNotice, "JsonRpcConnection") + << "Error while reading JSON-RPC message for identity '" << m_Identity + << "': " << DiagnosticInformation(ex); + } + + break; + } + + m_Seen = Utility::GetTime(); + + try { + CpuBoundWork handleMessage (yc); + + MessageHandler(message); + } catch (const std::exception& ex) { + if (!m_ShuttingDown) { + Log(LogWarning, "JsonRpcConnection") + << "Error while processing JSON-RPC message for identity '" << m_Identity + << "': " << DiagnosticInformation(ex); + } + + break; + } + + CpuBoundWork taskStats (yc); + + l_TaskStats.InsertValue(Utility::GetTime(), 1); + } + + Disconnect(); +} + +void JsonRpcConnection::WriteOutgoingMessages(boost::asio::yield_context yc) +{ + Defer signalWriterDone ([this]() { m_WriterDone.Set(); }); + + do { + m_OutgoingMessagesQueued.Wait(yc); + + auto queue (std::move(m_OutgoingMessagesQueue)); + + m_OutgoingMessagesQueue.clear(); + m_OutgoingMessagesQueued.Clear(); + + if (!queue.empty()) { + try { + for (auto& message : queue) { + size_t bytesSent = JsonRpc::SendRawMessage(m_Stream, message, yc); + + if (m_Endpoint) { + m_Endpoint->AddMessageSent(bytesSent); + } + } + + m_Stream->async_flush(yc); + } catch (const std::exception& ex) { + if (!m_ShuttingDown) { + std::ostringstream info; + info << "Error while sending JSON-RPC message for identity '" << m_Identity << "'"; + Log(LogWarning, "JsonRpcConnection") + << info.str() << "\n" << DiagnosticInformation(ex); + } + + break; + } + } + } while (!m_ShuttingDown); + + Disconnect(); } double JsonRpcConnection::GetTimestamp() const @@ -100,7 +158,7 @@ Endpoint::Ptr JsonRpcConnection::GetEndpoint() const return m_Endpoint; } -TlsStream::Ptr JsonRpcConnection::GetStream() const +std::shared_ptr JsonRpcConnection::GetStream() const { return m_Stream; } @@ -112,69 +170,76 @@ ConnectionRole JsonRpcConnection::GetRole() const void JsonRpcConnection::SendMessage(const Dictionary::Ptr& message) { - try { - ObjectLock olock(m_Stream); + m_IoStrand.post([this, message]() { SendMessageInternal(message); }); +} - if (m_Stream->IsEof()) - return; +void JsonRpcConnection::SendRawMessage(const String& message) +{ + m_IoStrand.post([this, message]() { + m_OutgoingMessagesQueue.emplace_back(message); + m_OutgoingMessagesQueued.Set(); + }); +} - size_t bytesSent = JsonRpc::SendMessage(m_Stream, message); - - if (m_Endpoint) - m_Endpoint->AddMessageSent(bytesSent); - - } catch (const std::exception& ex) { - std::ostringstream info; - info << "Error while sending JSON-RPC message for identity '" << m_Identity << "'"; - Log(LogWarning, "JsonRpcConnection") - << info.str() << "\n" << DiagnosticInformation(ex); - - Disconnect(); - } +void JsonRpcConnection::SendMessageInternal(const Dictionary::Ptr& message) +{ + m_OutgoingMessagesQueue.emplace_back(JsonEncode(message)); + m_OutgoingMessagesQueued.Set(); } void JsonRpcConnection::Disconnect() { - Log(LogWarning, "JsonRpcConnection") - << "API client disconnected for identity '" << m_Identity << "'"; + namespace asio = boost::asio; - m_Stream->Close(); + JsonRpcConnection::Ptr keepAlive (this); - if (m_Endpoint) - m_Endpoint->RemoveClient(this); - else { - ApiListener::Ptr listener = ApiListener::GetInstance(); - listener->RemoveAnonymousClient(this); - } -} + IoEngine::SpawnCoroutine(m_IoStrand, [this, keepAlive](asio::yield_context yc) { + if (!m_ShuttingDown) { + m_ShuttingDown = true; -void JsonRpcConnection::MessageHandlerWrapper(const String& jsonString) -{ - if (m_Stream->IsEof()) - return; + Log(LogWarning, "JsonRpcConnection") + << "API client disconnected for identity '" << m_Identity << "'"; - try { - MessageHandler(jsonString); - } catch (const std::exception& ex) { - Log(LogWarning, "JsonRpcConnection") - << "Error while reading JSON-RPC message for identity '" << m_Identity - << "': " << DiagnosticInformation(ex); + { + CpuBoundWork removeClient (yc); - Disconnect(); + if (m_Endpoint) { + m_Endpoint->RemoveClient(this); + } else { + ApiListener::GetInstance()->RemoveAnonymousClient(this); + } + } - return; - } + m_OutgoingMessagesQueued.Set(); + + m_WriterDone.Wait(yc); + + /* + * Do not swallow exceptions in a coroutine. + * https://github.com/Icinga/icinga2/issues/7351 + * We must not catch `detail::forced_unwind exception` as + * this is used for unwinding the stack. + * + * Just use the error_code dummy here. + */ + boost::system::error_code ec; + + m_CheckLivenessTimer.cancel(); + m_HeartbeatTimer.cancel(); + + m_Stream->lowest_layer().cancel(ec); + + m_Stream->next_layer().async_shutdown(yc[ec]); + + m_Stream->lowest_layer().shutdown(m_Stream->lowest_layer().shutdown_both, ec); + } + }); } void JsonRpcConnection::MessageHandler(const String& jsonString) { Dictionary::Ptr message = JsonRpc::DecodeMessage(jsonString); - m_Seen = Utility::GetTime(); - - if (m_HeartbeatTimeout != 0) - m_NextHeartbeat = Utility::GetTime() + m_HeartbeatTimeout; - if (m_Endpoint && message->Contains("ts")) { double ts = message->Get("ts"); @@ -214,7 +279,7 @@ void JsonRpcConnection::MessageHandler(const String& jsonString) String method = vmethod; Log(LogNotice, "JsonRpcConnection") - << "Received '" << method << "' message from '" << m_Identity << "'"; + << "Received '" << method << "' message from identity '" << m_Identity << "'."; Dictionary::Ptr resultMessage = new Dictionary(); @@ -242,62 +307,11 @@ void JsonRpcConnection::MessageHandler(const String& jsonString) if (message->Contains("id")) { resultMessage->Set("jsonrpc", "2.0"); resultMessage->Set("id", message->Get("id")); - SendMessage(resultMessage); + + SendMessageInternal(resultMessage); } } -bool JsonRpcConnection::ProcessMessage() -{ - ssize_t maxMessageLength = 64 * 1024; - - if (m_Endpoint) - maxMessageLength = -1; /* no limit */ - - String message; - - StreamReadStatus srs = JsonRpc::ReadMessage(m_Stream, &message, m_Context, false, maxMessageLength); - - if (srs != StatusNewItem) - return false; - - l_JsonRpcConnectionWorkQueues[m_ID % l_JsonRpcConnectionWorkQueueCount].Enqueue(std::bind(&JsonRpcConnection::MessageHandlerWrapper, JsonRpcConnection::Ptr(this), message)); - - return true; -} - -void JsonRpcConnection::DataAvailableHandler() -{ - bool close = false; - - if (!m_Stream) - return; - - if (!m_Stream->IsEof()) { - boost::mutex::scoped_lock lock(m_DataHandlerMutex); - - m_Stream->SetCorked(true); - - try { - while (ProcessMessage()) - ; /* empty loop body */ - } catch (const std::exception& ex) { - Log(LogWarning, "JsonRpcConnection") - << "Error while reading JSON-RPC message for identity '" << m_Identity - << "': " << DiagnosticInformation(ex); - - Disconnect(); - - return; - } - - l_JsonRpcConnectionWorkQueues[m_ID % l_JsonRpcConnectionWorkQueueCount].Enqueue(std::bind(&Stream::SetCorked, m_Stream, false)); - } else - close = true; - - if (close) - Disconnect(); -} - Value SetLogPositionHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { double log_position = params->Get("log_position"); @@ -312,57 +326,29 @@ Value SetLogPositionHandler(const MessageOrigin::Ptr& origin, const Dictionary:: return Empty; } -void JsonRpcConnection::CheckLiveness() +void JsonRpcConnection::CheckLiveness(boost::asio::yield_context yc) { - if (m_Seen < Utility::GetTime() - 60 && (!m_Endpoint || !m_Endpoint->GetSyncing())) { - Log(LogInformation, "JsonRpcConnection") - << "No messages for identity '" << m_Identity << "' have been received in the last 60 seconds."; - Disconnect(); - } -} + boost::system::error_code ec; -void JsonRpcConnection::TimeoutTimerHandler() -{ - ApiListener::Ptr listener = ApiListener::GetInstance(); + for (;;) { + m_CheckLivenessTimer.expires_from_now(boost::posix_time::seconds(30)); + m_CheckLivenessTimer.async_wait(yc[ec]); - for (const JsonRpcConnection::Ptr& client : listener->GetAnonymousClients()) { - client->CheckLiveness(); - } + if (m_ShuttingDown) { + break; + } - for (const Endpoint::Ptr& endpoint : ConfigType::GetObjectsByType()) { - for (const JsonRpcConnection::Ptr& client : endpoint->GetClients()) { - client->CheckLiveness(); + if (m_Seen < Utility::GetTime() - 60 && (!m_Endpoint || !m_Endpoint->GetSyncing())) { + Log(LogInformation, "JsonRpcConnection") + << "No messages for identity '" << m_Identity << "' have been received in the last 60 seconds."; + + Disconnect(); + break; } } } -size_t JsonRpcConnection::GetWorkQueueCount() -{ - return l_JsonRpcConnectionWorkQueueCount; -} - -size_t JsonRpcConnection::GetWorkQueueLength() -{ - size_t itemCount = 0; - - for (size_t i = 0; i < GetWorkQueueCount(); i++) - itemCount += l_JsonRpcConnectionWorkQueues[i].GetLength(); - - return itemCount; -} - double JsonRpcConnection::GetWorkQueueRate() { - double rate = 0.0; - size_t count = GetWorkQueueCount(); - - /* If this is a standalone environment, we don't have any queues. */ - if (count == 0) - return 0.0; - - for (size_t i = 0; i < count; i++) - rate += l_JsonRpcConnectionWorkQueues[i].GetTaskCount(60) / 60.0; - - return rate / count; + return l_TaskStats.UpdateAndGetValues(Utility::GetTime(), 60) / 60.0; } - diff --git a/lib/remote/jsonrpcconnection.hpp b/lib/remote/jsonrpcconnection.hpp index db97f6fee..0fbf6c605 100644 --- a/lib/remote/jsonrpcconnection.hpp +++ b/lib/remote/jsonrpcconnection.hpp @@ -1,30 +1,19 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef JSONRPCCONNECTION_H #define JSONRPCCONNECTION_H #include "remote/i2-remote.hpp" #include "remote/endpoint.hpp" +#include "base/io-engine.hpp" #include "base/tlsstream.hpp" #include "base/timer.hpp" #include "base/workqueue.hpp" +#include +#include +#include +#include +#include namespace icinga { @@ -53,7 +42,7 @@ class JsonRpcConnection final : public Object public: DECLARE_PTR_TYPEDEFS(JsonRpcConnection); - JsonRpcConnection(const String& identity, bool authenticated, TlsStream::Ptr stream, ConnectionRole role); + JsonRpcConnection(const String& identity, bool authenticated, const std::shared_ptr& stream, ConnectionRole role); void Start(); @@ -61,47 +50,49 @@ public: String GetIdentity() const; bool IsAuthenticated() const; Endpoint::Ptr GetEndpoint() const; - TlsStream::Ptr GetStream() const; + std::shared_ptr GetStream() const; ConnectionRole GetRole() const; void Disconnect(); void SendMessage(const Dictionary::Ptr& request); + void SendRawMessage(const String& request); - static void HeartbeatTimerHandler(); static Value HeartbeatAPIHandler(const intrusive_ptr& origin, const Dictionary::Ptr& params); - static size_t GetWorkQueueCount(); - static size_t GetWorkQueueLength(); static double GetWorkQueueRate(); static void SendCertificateRequest(const JsonRpcConnection::Ptr& aclient, const intrusive_ptr& origin, const String& path); private: - int m_ID; String m_Identity; bool m_Authenticated; Endpoint::Ptr m_Endpoint; - TlsStream::Ptr m_Stream; + std::shared_ptr m_Stream; ConnectionRole m_Role; double m_Timestamp; double m_Seen; double m_NextHeartbeat; - double m_HeartbeatTimeout; - boost::mutex m_DataHandlerMutex; + boost::asio::io_context::strand m_IoStrand; + std::vector m_OutgoingMessagesQueue; + AsioConditionVariable m_OutgoingMessagesQueued; + AsioConditionVariable m_WriterDone; + bool m_ShuttingDown; + boost::asio::deadline_timer m_CheckLivenessTimer, m_HeartbeatTimer; - StreamReadContext m_Context; + JsonRpcConnection(const String& identity, bool authenticated, const std::shared_ptr& stream, ConnectionRole role, boost::asio::io_context& io); + + void HandleIncomingMessages(boost::asio::yield_context yc); + void WriteOutgoingMessages(boost::asio::yield_context yc); + void HandleAndWriteHeartbeats(boost::asio::yield_context yc); + void CheckLiveness(boost::asio::yield_context yc); bool ProcessMessage(); - void MessageHandlerWrapper(const String& jsonString); void MessageHandler(const String& jsonString); - void DataAvailableHandler(); - - static void StaticInitialize(); - static void TimeoutTimerHandler(); - void CheckLiveness(); void CertificateRequestResponseHandler(const Dictionary::Ptr& message); + + void SendMessageInternal(const Dictionary::Ptr& request); }; } diff --git a/lib/remote/messageorigin.cpp b/lib/remote/messageorigin.cpp index 184038497..7de0ca7a4 100644 --- a/lib/remote/messageorigin.cpp +++ b/lib/remote/messageorigin.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/messageorigin.hpp" diff --git a/lib/remote/messageorigin.hpp b/lib/remote/messageorigin.hpp index 71be262fe..8a91ecc46 100644 --- a/lib/remote/messageorigin.hpp +++ b/lib/remote/messageorigin.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef MESSAGEORIGIN_H #define MESSAGEORIGIN_H diff --git a/lib/remote/modifyobjecthandler.cpp b/lib/remote/modifyobjecthandler.cpp index c08d1dee2..3f360fe30 100644 --- a/lib/remote/modifyobjecthandler.cpp +++ b/lib/remote/modifyobjecthandler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/modifyobjecthandler.hpp" #include "remote/httputility.hpp" @@ -29,15 +12,26 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/objects", ModifyObjectHandler); -bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool ModifyObjectHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() < 3 || request.RequestUrl->GetPath().size() > 4) + namespace http = boost::beast::http; + + if (url->GetPath().size() < 3 || url->GetPath().size() > 4) return false; - if (request.RequestMethod != "POST") + if (request.method() != http::verb::post) return false; - Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[2]); + Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]); if (!type) { HttpUtility::SendJsonError(response, params, 400, "Invalid type specified."); @@ -50,10 +44,10 @@ bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r params->Set("type", type->GetName()); - if (request.RequestUrl->GetPath().size() >= 4) { + if (url->GetPath().size() >= 4) { String attr = type->GetName(); boost::algorithm::to_lower(attr); - params->Set(attr, request.RequestUrl->GetPath()[3]); + params->Set(attr, url->GetPath()[3]); } std::vector objs; @@ -118,7 +112,7 @@ bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r { "results", new Array(std::move(results)) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); return true; diff --git a/lib/remote/modifyobjecthandler.hpp b/lib/remote/modifyobjecthandler.hpp index c5583ac44..f4693013f 100644 --- a/lib/remote/modifyobjecthandler.hpp +++ b/lib/remote/modifyobjecthandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef MODIFYOBJECTHANDLER_H #define MODIFYOBJECTHANDLER_H @@ -30,8 +13,16 @@ class ModifyObjectHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ModifyObjectHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; }; } diff --git a/lib/remote/objectqueryhandler.cpp b/lib/remote/objectqueryhandler.cpp index 490b50dad..3f827037c 100644 --- a/lib/remote/objectqueryhandler.cpp +++ b/lib/remote/objectqueryhandler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/objectqueryhandler.hpp" #include "remote/httputility.hpp" @@ -104,15 +87,26 @@ Dictionary::Ptr ObjectQueryHandler::SerializeObjectAttrs(const Object::Ptr& obje return new Dictionary(std::move(resultAttrs)); } -bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool ObjectQueryHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() < 3 || request.RequestUrl->GetPath().size() > 4) + namespace http = boost::beast::http; + + if (url->GetPath().size() < 3 || url->GetPath().size() > 4) return false; - if (request.RequestMethod != "GET") + if (request.method() != http::verb::get) return false; - Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[2]); + Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]); if (!type) { HttpUtility::SendJsonError(response, params, 400, "Invalid type specified."); @@ -153,10 +147,10 @@ bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re params->Set("type", type->GetName()); - if (request.RequestUrl->GetPath().size() >= 4) { + if (url->GetPath().size() >= 4) { String attr = type->GetName(); boost::algorithm::to_lower(attr); - params->Set(attr, request.RequestUrl->GetPath()[3]); + params->Set(attr, url->GetPath()[3]); } std::vector objs; @@ -282,7 +276,7 @@ bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re { "results", new Array(std::move(results)) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); return true; diff --git a/lib/remote/objectqueryhandler.hpp b/lib/remote/objectqueryhandler.hpp index acefdb63b..691b2cfcf 100644 --- a/lib/remote/objectqueryhandler.hpp +++ b/lib/remote/objectqueryhandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef OBJECTQUERYHANDLER_H #define OBJECTQUERYHANDLER_H @@ -30,8 +13,16 @@ class ObjectQueryHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ObjectQueryHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; private: static Dictionary::Ptr SerializeObjectAttrs(const Object::Ptr& object, const String& attrPrefix, diff --git a/lib/remote/pkiutility.cpp b/lib/remote/pkiutility.cpp index 20d9ca6c2..a95e3554c 100644 --- a/lib/remote/pkiutility.cpp +++ b/lib/remote/pkiutility.cpp @@ -1,26 +1,12 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/pkiutility.hpp" #include "remote/apilistener.hpp" +#include "base/defer.hpp" +#include "base/io-engine.hpp" #include "base/logger.hpp" #include "base/application.hpp" +#include "base/tcpsocket.hpp" #include "base/tlsutility.hpp" #include "base/console.hpp" #include "base/tlsstream.hpp" @@ -31,6 +17,8 @@ #include "remote/jsonrpc.hpp" #include #include +#include +#include using namespace icinga; @@ -41,7 +29,7 @@ int PkiUtility::NewCa() String caKeyFile = caDir + "/ca.key"; if (Utility::PathExists(caCertFile) && Utility::PathExists(caKeyFile)) { - Log(LogCritical, "cli") + Log(LogWarning, "cli") << "CA files '" << caCertFile << "' and '" << caKeyFile << "' already exist."; return 1; } @@ -66,7 +54,7 @@ int PkiUtility::NewCert(const String& cn, const String& keyfile, const String& c int PkiUtility::SignCsr(const String& csrfile, const String& certfile) { - char errbuf[120]; + char errbuf[256]; InitializeOpenSSL(); @@ -93,22 +81,10 @@ int PkiUtility::SignCsr(const String& csrfile, const String& certfile) std::shared_ptr PkiUtility::FetchCert(const String& host, const String& port) { - TcpSocket::Ptr client = new TcpSocket(); + std::shared_ptr sslContext; try { - client->Connect(host, port); - } catch (const std::exception& ex) { - Log(LogCritical, "pki") - << "Cannot connect to host '" << host << "' on port '" << port << "'"; - Log(LogDebug, "pki") - << "Cannot connect to host '" << host << "' on port '" << port << "':\n" << DiagnosticInformation(ex); - return std::shared_ptr(); - } - - std::shared_ptr sslContext; - - try { - sslContext = MakeSSLContext(); + sslContext = MakeAsioSslContext(); } catch (const std::exception& ex) { Log(LogCritical, "pki") << "Cannot make SSL context."; @@ -117,17 +93,31 @@ std::shared_ptr PkiUtility::FetchCert(const String& host, const String& po return std::shared_ptr(); } - TlsStream::Ptr stream = new TlsStream(client, host, RoleClient, sslContext); + auto stream (std::make_shared(IoEngine::Get().GetIoContext(), *sslContext, host)); try { - stream->Handshake(); + Connect(stream->lowest_layer(), host, port); + } catch (const std::exception& ex) { + Log(LogCritical, "pki") + << "Cannot connect to host '" << host << "' on port '" << port << "'"; + Log(LogDebug, "pki") + << "Cannot connect to host '" << host << "' on port '" << port << "':\n" << DiagnosticInformation(ex); + return std::shared_ptr(); + } + + auto& sslConn (stream->next_layer()); + + try { + sslConn.handshake(sslConn.client); } catch (const std::exception& ex) { Log(LogCritical, "pki") << "Client TLS handshake failed. (" << ex.what() << ")"; return std::shared_ptr(); } - return stream->GetPeerCertificate(); + Defer shutdown ([&sslConn]() { sslConn.shutdown(); }); + + return sslConn.GetPeerCertificate(); } int PkiUtility::WriteCert(const std::shared_ptr& cert, const String& trustedfile) @@ -159,22 +149,10 @@ int PkiUtility::GenTicket(const String& cn, const String& salt, std::ostream& ti int PkiUtility::RequestCertificate(const String& host, const String& port, const String& keyfile, const String& certfile, const String& cafile, const std::shared_ptr& trustedCert, const String& ticket) { - TcpSocket::Ptr client = new TcpSocket(); + std::shared_ptr sslContext; try { - client->Connect(host, port); - } catch (const std::exception& ex) { - Log(LogCritical, "cli") - << "Cannot connect to host '" << host << "' on port '" << port << "'"; - Log(LogDebug, "cli") - << "Cannot connect to host '" << host << "' on port '" << port << "':\n" << DiagnosticInformation(ex); - return 1; - } - - std::shared_ptr sslContext; - - try { - sslContext = MakeSSLContext(certfile, keyfile); + sslContext = MakeAsioSslContext(certfile, keyfile); } catch (const std::exception& ex) { Log(LogCritical, "cli") << "Cannot make SSL context for cert path: '" << certfile << "' key path: '" << keyfile << "' ca path: '" << cafile << "'."; @@ -183,16 +161,31 @@ int PkiUtility::RequestCertificate(const String& host, const String& port, const return 1; } - TlsStream::Ptr stream = new TlsStream(client, host, RoleClient, sslContext); + auto stream (std::make_shared(IoEngine::Get().GetIoContext(), *sslContext, host)); try { - stream->Handshake(); - } catch (const std::exception&) { - Log(LogCritical, "cli", "Client TLS handshake failed."); + Connect(stream->lowest_layer(), host, port); + } catch (const std::exception& ex) { + Log(LogCritical, "cli") + << "Cannot connect to host '" << host << "' on port '" << port << "'"; + Log(LogDebug, "cli") + << "Cannot connect to host '" << host << "' on port '" << port << "':\n" << DiagnosticInformation(ex); return 1; } - std::shared_ptr peerCert = stream->GetPeerCertificate(); + auto& sslConn (stream->next_layer()); + + try { + sslConn.handshake(sslConn.client); + } catch (const std::exception& ex) { + Log(LogCritical, "cli") + << "Client TLS handshake failed: " << DiagnosticInformation(ex, false); + return 1; + } + + Defer shutdown ([&sslConn]() { sslConn.shutdown(); }); + + auto peerCert (sslConn.GetPeerCertificate()); if (X509_cmp(peerCert.get(), trustedCert.get())) { Log(LogCritical, "cli", "Peer certificate does not match trusted certificate."); @@ -212,36 +205,32 @@ int PkiUtility::RequestCertificate(const String& host, const String& port, const { "params", params } }); - JsonRpc::SendMessage(stream, request); - - String jsonString; Dictionary::Ptr response; - StreamReadContext src; - for (;;) { - StreamReadStatus srs = JsonRpc::ReadMessage(stream, &jsonString, src); + try { + JsonRpc::SendMessage(stream, request); + stream->flush(); - if (srs == StatusEof) - break; + for (;;) { + response = JsonRpc::DecodeMessage(JsonRpc::ReadMessage(stream)); - if (srs != StatusNewItem) - continue; - - response = JsonRpc::DecodeMessage(jsonString); - - if (response && response->Contains("error")) { - Log(LogCritical, "cli", "Could not fetch valid response. Please check the master log (notice or debug)."); + if (response && response->Contains("error")) { + Log(LogCritical, "cli", "Could not fetch valid response. Please check the master log (notice or debug)."); #ifdef I2_DEBUG - /* we shouldn't expose master errors to the user in production environments */ - Log(LogCritical, "cli", response->Get("error")); + /* we shouldn't expose master errors to the user in production environments */ + Log(LogCritical, "cli", response->Get("error")); #endif /* I2_DEBUG */ - return 1; + return 1; + } + + if (response && (response->Get("id") != msgid)) + continue; + + break; } - - if (response && (response->Get("id") != msgid)) - continue; - - break; + } catch (...) { + Log(LogCritical, "cli", "Could not fetch valid response. Please check the master log."); + return 1; } if (!response) { @@ -380,8 +369,9 @@ static void CollectRequestHandler(const Dictionary::Ptr& requests, const String& Dictionary::Ptr result = new Dictionary(); - String fingerprint = Utility::BaseName(requestFile); - fingerprint = fingerprint.SubStr(0, fingerprint.GetLength() - 5); + namespace fs = boost::filesystem; + fs::path file(requestFile.Begin(), requestFile.End()); + String fingerprint = file.stem().string(); String certRequestText = request->Get("cert_request"); result->Set("cert_request", certRequestText); @@ -426,14 +416,19 @@ static void CollectRequestHandler(const Dictionary::Ptr& requests, const String& requests->Set(fingerprint, result); } -Dictionary::Ptr PkiUtility::GetCertificateRequests() +Dictionary::Ptr PkiUtility::GetCertificateRequests(bool removed) { Dictionary::Ptr requests = new Dictionary(); String requestDir = ApiListener::GetCertificateRequestsDir(); + String ext = "json"; + + if (removed) + ext = "removed"; if (Utility::PathExists(requestDir)) - Utility::Glob(requestDir + "/*.json", std::bind(&CollectRequestHandler, requests, _1), GlobFile); + Utility::Glob(requestDir + "/*." + ext, std::bind(&CollectRequestHandler, requests, _1), GlobFile); return requests; } + diff --git a/lib/remote/pkiutility.hpp b/lib/remote/pkiutility.hpp index 500b7be69..50d47e01a 100644 --- a/lib/remote/pkiutility.hpp +++ b/lib/remote/pkiutility.hpp @@ -1,29 +1,14 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef PKIUTILITY_H #define PKIUTILITY_H #include "remote/i2-remote.hpp" +#include "base/exception.hpp" #include "base/dictionary.hpp" #include "base/string.hpp" #include +#include namespace icinga { @@ -44,7 +29,7 @@ public: const String& certfile, const String& cafile, const std::shared_ptr& trustedcert, const String& ticket = String()); static String GetCertificateInformation(const std::shared_ptr& certificate); - static Dictionary::Ptr GetCertificateRequests(); + static Dictionary::Ptr GetCertificateRequests(bool removed = false); private: PkiUtility(); diff --git a/lib/remote/statushandler.cpp b/lib/remote/statushandler.cpp index b2a9fca10..1f3f61899 100644 --- a/lib/remote/statushandler.cpp +++ b/lib/remote/statushandler.cpp @@ -1,27 +1,11 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/statushandler.hpp" #include "remote/httputility.hpp" #include "remote/filterutility.hpp" #include "base/serializer.hpp" #include "base/statsfunction.hpp" +#include "base/namespace.hpp" using namespace icinga; @@ -35,24 +19,29 @@ public: void FindTargets(const String& type, const std::function& addTarget) const override { - Dictionary::Ptr statsFunctions = ScriptGlobal::Get("StatsFunctions", &Empty); + Namespace::Ptr statsFunctions = ScriptGlobal::Get("StatsFunctions", &Empty); if (statsFunctions) { ObjectLock olock(statsFunctions); - for (const Dictionary::Pair& kv : statsFunctions) + for (const Namespace::Pair& kv : statsFunctions) addTarget(GetTargetByName("Status", kv.first)); } } Value GetTargetByName(const String& type, const String& name) const override { - Dictionary::Ptr statsFunctions = ScriptGlobal::Get("StatsFunctions", &Empty); + Namespace::Ptr statsFunctions = ScriptGlobal::Get("StatsFunctions", &Empty); if (!statsFunctions) BOOST_THROW_EXCEPTION(std::invalid_argument("No status functions are available.")); - Function::Ptr func = statsFunctions->Get(name); + Value vfunc; + + if (!statsFunctions->Get(name, &vfunc)) + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid status function name.")); + + Function::Ptr func = vfunc; if (!func) BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid status function name.")); @@ -79,12 +68,23 @@ public: } }; -bool StatusHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool StatusHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() > 3) + namespace http = boost::beast::http; + + if (url->GetPath().size() > 3) return false; - if (request.RequestMethod != "GET") + if (request.method() != http::verb::get) return false; QueryDescription qd; @@ -94,8 +94,8 @@ bool StatusHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request params->Set("type", "Status"); - if (request.RequestUrl->GetPath().size() >= 3) - params->Set("status", request.RequestUrl->GetPath()[2]); + if (url->GetPath().size() >= 3) + params->Set("status", url->GetPath()[2]); std::vector objs; @@ -112,7 +112,7 @@ bool StatusHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request { "results", new Array(std::move(objs)) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); return true; diff --git a/lib/remote/statushandler.hpp b/lib/remote/statushandler.hpp index 7dfa8380e..c722ab3e2 100644 --- a/lib/remote/statushandler.hpp +++ b/lib/remote/statushandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef STATUSHANDLER_H #define STATUSHANDLER_H @@ -30,8 +13,16 @@ class StatusHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(StatusHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; }; } diff --git a/lib/remote/templatequeryhandler.cpp b/lib/remote/templatequeryhandler.cpp index 153c887e5..e70dafb65 100644 --- a/lib/remote/templatequeryhandler.cpp +++ b/lib/remote/templatequeryhandler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/templatequeryhandler.hpp" #include "remote/httputility.hpp" @@ -92,15 +75,26 @@ public: } }; -bool TemplateQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool TemplateQueryHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() < 3 || request.RequestUrl->GetPath().size() > 4) + namespace http = boost::beast::http; + + if (url->GetPath().size() < 3 || url->GetPath().size() > 4) return false; - if (request.RequestMethod != "GET") + if (request.method() != http::verb::get) return false; - Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[2]); + Type::Ptr type = FilterUtility::TypeFromPluralName(url->GetPath()[2]); if (!type) { HttpUtility::SendJsonError(response, params, 400, "Invalid type specified."); @@ -114,10 +108,10 @@ bool TemplateQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& params->Set("type", type->GetName()); - if (request.RequestUrl->GetPath().size() >= 4) { + if (url->GetPath().size() >= 4) { String attr = type->GetName(); boost::algorithm::to_lower(attr); - params->Set(attr, request.RequestUrl->GetPath()[3]); + params->Set(attr, url->GetPath()[3]); } std::vector objs; @@ -135,7 +129,7 @@ bool TemplateQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& { "results", new Array(std::move(objs)) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); return true; diff --git a/lib/remote/templatequeryhandler.hpp b/lib/remote/templatequeryhandler.hpp index a70188b2a..503bc8560 100644 --- a/lib/remote/templatequeryhandler.hpp +++ b/lib/remote/templatequeryhandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef TEMPLATEQUERYHANDLER_H #define TEMPLATEQUERYHANDLER_H @@ -30,8 +13,16 @@ class TemplateQueryHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(TemplateQueryHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; }; } diff --git a/lib/remote/typequeryhandler.cpp b/lib/remote/typequeryhandler.cpp index fe4703f60..4e8265398 100644 --- a/lib/remote/typequeryhandler.cpp +++ b/lib/remote/typequeryhandler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/typequeryhandler.hpp" #include "remote/httputility.hpp" @@ -63,12 +46,23 @@ public: } }; -bool TypeQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool TypeQueryHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() > 3) + namespace http = boost::beast::http; + + if (url->GetPath().size() > 3) return false; - if (request.RequestMethod != "GET") + if (request.method() != http::verb::get) return false; QueryDescription qd; @@ -81,8 +75,8 @@ bool TypeQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& requ params->Set("type", "Type"); - if (request.RequestUrl->GetPath().size() >= 3) - params->Set("name", request.RequestUrl->GetPath()[2]); + if (url->GetPath().size() >= 3) + params->Set("name", url->GetPath()[2]); std::vector objs; @@ -155,7 +149,7 @@ bool TypeQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& requ { "results", new Array(std::move(results)) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); return true; diff --git a/lib/remote/typequeryhandler.hpp b/lib/remote/typequeryhandler.hpp index 11e533efd..5489cb232 100644 --- a/lib/remote/typequeryhandler.hpp +++ b/lib/remote/typequeryhandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef TYPEQUERYHANDLER_H #define TYPEQUERYHANDLER_H @@ -30,8 +13,16 @@ class TypeQueryHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(TypeQueryHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; }; } diff --git a/lib/remote/url-characters.hpp b/lib/remote/url-characters.hpp index 5e1c722b0..3cc492198 100644 --- a/lib/remote/url-characters.hpp +++ b/lib/remote/url-characters.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef URL_CHARACTERS_H #define URL_CHARACTERS_H diff --git a/lib/remote/url.cpp b/lib/remote/url.cpp index f6c84ece1..e87628eed 100644 --- a/lib/remote/url.cpp +++ b/lib/remote/url.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/array.hpp" #include "base/utility.hpp" @@ -138,33 +121,11 @@ const std::vector& Url::GetPath() const return m_Path; } -const std::map >& Url::GetQuery() const +const std::vector>& Url::GetQuery() const { return m_Query; } -String Url::GetQueryElement(const String& name) const -{ - auto it = m_Query.find(name); - - if (it == m_Query.end()) - return String(); - - return it->second.back(); -} - -const std::vector& Url::GetQueryElements(const String& name) const -{ - auto it = m_Query.find(name); - - if (it == m_Query.end()) { - static std::vector emptyVector; - return emptyVector; - } - - return it->second; -} - String Url::GetFragment() const { return m_Fragment; @@ -200,7 +161,7 @@ void Url::SetPath(const std::vector& path) m_Path = path; } -void Url::SetQuery(const std::map >& query) +void Url::SetQuery(const std::vector>& query) { m_Query = query; } @@ -212,16 +173,7 @@ void Url::SetArrayFormatUseBrackets(bool useBrackets) void Url::AddQueryElement(const String& name, const String& value) { - auto it = m_Query.find(name); - if (it == m_Query.end()) { - m_Query[name] = std::vector { value }; - } else - m_Query[name].push_back(value); -} - -void Url::SetQueryElements(const String& name, const std::vector& values) -{ - m_Query[name] = values; + m_Query.emplace_back(name, value); } void Url::SetFragment(const String& fragment) { @@ -255,38 +207,16 @@ String Url::Format(bool onlyPathAndQuery, bool printCredentials) const if (!m_Query.empty()) { typedef std::pair > kv_pair; - for (const kv_pair& kv : m_Query) { + for (const auto& kv : m_Query) { String key = Utility::EscapeString(kv.first, ACQUERY_ENCODE, false); if (param.IsEmpty()) param = "?"; else param += "&"; - // Just one (or one empty) value - if (kv.second.size() == 1) { - param += key; - param += kv.second[0].IsEmpty() ? - String() : "=" + Utility::EscapeString(kv.second[0], ACQUERY_ENCODE, false); - continue; - } - - // Array - String temp; - for (const String& s : kv.second) { - if (!temp.IsEmpty()) - temp += "&"; - - temp += key; - - if (m_ArrayFormatUseBrackets) { - if (kv.second.size() > 1) - temp += "[]"; - } - - if (!s.IsEmpty()) - temp += "=" + Utility::EscapeString(s, ACQUERY_ENCODE, false); - } - param += temp; + param += key; + param += kv.second.IsEmpty() ? + String() : "=" + Utility::EscapeString(kv.second, ACQUERY_ENCODE, false); } } @@ -408,14 +338,7 @@ bool Url::ParseQuery(const String& query) if (!ValidateToken(key, ACQUERY)) return false; - key = Utility::UnescapeString(key); - - auto it = m_Query.find(key); - - if (it == m_Query.end()) { - m_Query[key] = std::vector { std::move(value) }; - } else - m_Query[key].emplace_back(std::move(value)); + m_Query.emplace_back(Utility::UnescapeString(key), std::move(value)); } return true; diff --git a/lib/remote/url.hpp b/lib/remote/url.hpp index 20386980c..6012b2f3e 100644 --- a/lib/remote/url.hpp +++ b/lib/remote/url.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef URL_H #define URL_H @@ -26,6 +9,7 @@ #include "base/array.hpp" #include "base/value.hpp" #include +#include #include namespace icinga @@ -53,9 +37,7 @@ public: String GetHost() const; String GetPort() const; const std::vector& GetPath() const; - const std::map >& GetQuery() const; - String GetQueryElement(const String& name) const; - const std::vector& GetQueryElements(const String& name) const; + const std::vector>& GetQuery() const; String GetFragment() const; void SetScheme(const String& scheme); @@ -64,11 +46,10 @@ public: void SetHost(const String& host); void SetPort(const String& port); void SetPath(const std::vector& path); - void SetQuery(const std::map >& query); + void SetQuery(const std::vector>& query); void SetArrayFormatUseBrackets(bool useBrackets = true); void AddQueryElement(const String& name, const String& query); - void SetQueryElements(const String& name, const std::vector& query); void SetFragment(const String& fragment); private: @@ -78,7 +59,7 @@ private: String m_Host; String m_Port; std::vector m_Path; - std::map > m_Query; + std::vector> m_Query; bool m_ArrayFormatUseBrackets; String m_Fragment; diff --git a/lib/remote/variablequeryhandler.cpp b/lib/remote/variablequeryhandler.cpp index 4a36fc8ce..aef896e6f 100644 --- a/lib/remote/variablequeryhandler.cpp +++ b/lib/remote/variablequeryhandler.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/variablequeryhandler.hpp" #include "remote/httputility.hpp" @@ -24,6 +7,7 @@ #include "base/scriptglobal.hpp" #include "base/logger.hpp" #include "base/serializer.hpp" +#include "base/namespace.hpp" #include using namespace icinga; @@ -48,10 +32,10 @@ public: const std::function& addTarget) const override { { - Dictionary::Ptr globals = ScriptGlobal::GetGlobals(); + Namespace::Ptr globals = ScriptGlobal::GetGlobals(); ObjectLock olock(globals); - for (const Dictionary::Pair& kv : globals) { - addTarget(GetTargetForVar(kv.first, kv.second)); + for (const Namespace::Pair& kv : globals) { + addTarget(GetTargetForVar(kv.first, kv.second->Get())); } } } @@ -72,12 +56,23 @@ public: } }; -bool VariableQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) +bool VariableQueryHandler::HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server +) { - if (request.RequestUrl->GetPath().size() > 3) + namespace http = boost::beast::http; + + if (url->GetPath().size() > 3) return false; - if (request.RequestMethod != "GET") + if (request.method() != http::verb::get) return false; QueryDescription qd; @@ -87,8 +82,8 @@ bool VariableQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& params->Set("type", "Variable"); - if (request.RequestUrl->GetPath().size() >= 3) - params->Set("variable", request.RequestUrl->GetPath()[2]); + if (url->GetPath().size() >= 3) + params->Set("variable", url->GetPath()[2]); std::vector objs; @@ -115,7 +110,7 @@ bool VariableQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& { "results", new Array(std::move(results)) } }); - response.SetStatus(200, "OK"); + response.result(http::status::ok); HttpUtility::SendJsonBody(response, params, result); return true; diff --git a/lib/remote/variablequeryhandler.hpp b/lib/remote/variablequeryhandler.hpp index 6ce4dacc1..48e73be35 100644 --- a/lib/remote/variablequeryhandler.hpp +++ b/lib/remote/variablequeryhandler.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef VARIABLEQUERYHANDLER_H #define VARIABLEQUERYHANDLER_H @@ -30,8 +13,16 @@ class VariableQueryHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(VariableQueryHandler); - bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, - HttpResponse& response, const Dictionary::Ptr& params) override; + bool HandleRequest( + AsioTlsStream& stream, + const ApiUser::Ptr& user, + boost::beast::http::request& request, + const Url::Ptr& url, + boost::beast::http::response& response, + const Dictionary::Ptr& params, + boost::asio::yield_context& yc, + HttpServerConnection& server + ) override; }; } diff --git a/lib/remote/zone.cpp b/lib/remote/zone.cpp index ea6e007bd..5ae1468c1 100644 --- a/lib/remote/zone.cpp +++ b/lib/remote/zone.cpp @@ -1,25 +1,9 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "remote/zone.hpp" #include "remote/zone-ti.cpp" #include "remote/jsonrpcconnection.hpp" +#include "base/array.hpp" #include "base/objectlock.hpp" #include "base/logger.hpp" @@ -33,6 +17,9 @@ void Zone::OnAllConfigLoaded() m_Parent = Zone::GetByName(GetParentRaw()); + if (m_Parent && m_Parent->IsGlobal()) + BOOST_THROW_EXCEPTION(ScriptError("Zone '" + GetName() + "' can not have a global zone as parent.", GetDebugInfo())); + Zone::Ptr zone = m_Parent; int levels = 0; @@ -86,11 +73,21 @@ std::set Zone::GetEndpoints() const return result; } -std::vector Zone::GetAllParents() const +std::vector Zone::GetAllParentsRaw() const { return m_AllParents; } +Array::Ptr Zone::GetAllParents() const +{ + auto result (new Array); + + for (auto& parent : m_AllParents) + result->Add(parent->GetName()); + + return result; +} + bool Zone::CanAccessObject(const ConfigObject::Ptr& object) { Zone::Ptr object_zone; diff --git a/lib/remote/zone.hpp b/lib/remote/zone.hpp index 6193dc026..897b18e96 100644 --- a/lib/remote/zone.hpp +++ b/lib/remote/zone.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ZONE_H #define ZONE_H @@ -40,7 +23,8 @@ public: Zone::Ptr GetParent() const; std::set GetEndpoints() const; - std::vector GetAllParents() const; + std::vector GetAllParentsRaw() const; + Array::Ptr GetAllParents() const override; bool CanAccessObject(const ConfigObject::Ptr& object); bool IsChildOf(const Zone::Ptr& zone); diff --git a/lib/remote/zone.ti b/lib/remote/zone.ti index ad7bb8d50..53fd4e609 100644 --- a/lib/remote/zone.ti +++ b/lib/remote/zone.ti @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" @@ -34,6 +17,9 @@ class Zone : ConfigObject [config] array(name(Endpoint)) endpoints (EndpointsRaw); [config] bool global; + [no_user_modify, no_storage] array(Value) all_parents { + get; + }; }; } diff --git a/mkdocs.yml b/mkdocs.yml index 8fa7bcfec..aaee8d5c7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,9 +3,9 @@ docs_dir: doc dev_addr: 0.0.0.0:8000 pages: - 'About Icinga 2': '01-about.md' - - 'Getting Started': '02-getting-started.md' + - 'Installation': '02-installation.md' - 'Monitoring Basics': '03-monitoring-basics.md' - - 'Configuring Icinga 2': '04-configuring-icinga-2.md' + - 'Configuration': '04-configuration.md' - 'Service Monitoring': '05-service-monitoring.md' - 'Distributed Monitoring': '06-distributed-monitoring.md' - 'Agent Based Monitoring': '07-agent-based-monitoring.md' diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index c908a89e4..27fddecff 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ add_executable(check_nscp_api check_nscp_api.cpp @@ -24,7 +9,6 @@ add_executable(check_nscp_api target_link_libraries(check_nscp_api ${base_DEPS}) set_target_properties ( check_nscp_api PROPERTIES - INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2 DEFINE_SYMBOL I2_PLUGINS_BUILD FOLDER Plugins) @@ -47,7 +31,6 @@ if (WIN32) set_target_properties( thresholds PROPERTIES - INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2 FOLDER Plugins ) @@ -65,7 +48,6 @@ if (WIN32) set_target_properties( ${check_OUT} PROPERTIES - INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2 DEFINE_SYMBOL I2_PLUGINS_BUILD FOLDER Plugins ) diff --git a/plugins/README.md b/plugins/README.md deleted file mode 100644 index f524f31ab..000000000 --- a/plugins/README.md +++ /dev/null @@ -1,51 +0,0 @@ -## Icinga 2 plugins for Windows - -This collection of plugins is intended to provide basic functionality checks on windows machines. -They (mostly) conform to the [nagios developer guidelines](https://nagios-plugins.org/doc/guidelines.html), -returning adequate exit codes and printing a pertinent string with performance data. - - -### Intallation - -The plugins are installed as part of Icinga 2. - - -### Requirements - -- Boost 1.41.0 -- Windows Vista, Windows Server 2008 or newer - - -### Usage - -Call a plugin with the "--help" option to receive information about its usage. -Most of them don't need any parameters to but all of them have a -w (warning) and -c (critical) option. -Those accept, if not otherwise specified, value or percentage based thresholds or threshold ranges. - -A few examples: -*./check_command.exe -w 12 -c !60%* -Adds a warning threshold of 12 and an inversed critical threshold of 60% - -*./check_command.exe -w ![20%-80%] -c [0%-40%]* -The warning threshold is outside of 20% to 80% and the critical threshold is the range from 0% to 40%. -A critical state always overwrites a warning state, meaning the check would be critical with a value of 30%. - - -### License - -Icinga 2 -Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) - -This program is free software; you can redistribute it and/or -modify it under the tems 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. diff --git a/plugins/check_disk.cpp b/plugins/check_disk.cpp index b0e898f54..ac475b560 100644 --- a/plugins/check_disk.cpp +++ b/plugins/check_disk.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "plugins/thresholds.hpp" #include @@ -382,7 +365,7 @@ static int printOutput(printInfoStruct& printInfo, std::vector& vDrives) wsDrives.push_back(it->name + L" " + removeZero(it->free) + L" " + unit + L" (" + removeZero(std::round(it->free / it->cap * 100.0)) + L"%); "); - wsPerf.push_back(L" " + it->name + L"=" + removeZero(it->free) + unit + L";" + + wsPerf.push_back(L" '" + it->name + L"'=" + removeZero(it->free) + unit + L";" + printInfo.warn.pString(it->cap) + L";" + printInfo.crit.pString(it->cap) + L";0;" + removeZero(it->cap)); diff --git a/plugins/check_load.cpp b/plugins/check_load.cpp index 81f2ecfdf..563c34788 100644 --- a/plugins/check_load.cpp +++ b/plugins/check_load.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "plugins/thresholds.hpp" #include @@ -171,7 +154,7 @@ static int printOutput(printInfoStruct& printInfo) break; } - std::wcout << " " << printInfo.load << L"% | load=" << printInfo.load << L"%;" + std::wcout << " " << printInfo.load << L"% | 'load'=" << printInfo.load << L"%;" << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";0;100" << '\n'; diff --git a/plugins/check_memory.cpp b/plugins/check_memory.cpp index 46c65649e..146144532 100644 --- a/plugins/check_memory.cpp +++ b/plugins/check_memory.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "plugins/thresholds.hpp" #include @@ -150,7 +133,11 @@ static int parseArguments(int ac, WCHAR ** av, po::variables_map& vm, printInfoS } } - printInfo.showUsed = vm.count("show-used") > 0; + if (vm.count("show-used")) { + printInfo.showUsed = true; + printInfo.warn.legal = true; + printInfo.crit.legal = true; + } return -1; } @@ -160,7 +147,7 @@ static int printOutput(printInfoStruct& printInfo) if (l_Debug) std::wcout << L"Constructing output string" << '\n'; - state state; + state state = OK; std::wcout << L"MEMORY "; @@ -171,25 +158,22 @@ static int printOutput(printInfoStruct& printInfo) else currentValue = printInfo.tRam - printInfo.aRam; - if (printInfo.warn.rend(currentValue, printInfo.tRam)) { + if (printInfo.warn.rend(currentValue, printInfo.tRam)) state = WARNING; - std::wcout << L"WARNING"; - } else if (printInfo.crit.rend(currentValue, printInfo.tRam)) { + + if (printInfo.crit.rend(currentValue, printInfo.tRam)) state = CRITICAL; - std::wcout << L"CRITICAL"; - } else { - state = OK; - std::wcout << L"OK"; - } + + std::wcout << stateToString(state); if (!printInfo.showUsed) std::wcout << " - " << printInfo.percentFree << L"% free"; else std::wcout << " - " << 100 - printInfo.percentFree << L"% used"; - std::wcout << "| memory=" << currentValue << BunitStr(printInfo.unit) << L";" + std::wcout << "| 'memory'=" << currentValue << BunitStr(printInfo.unit) << L";" << printInfo.warn.pString(printInfo.tRam) << L";" << printInfo.crit.pString(printInfo.tRam) - << L";0;" << printInfo.tRam; + << L";0;" << printInfo.tRam << '\n'; return state; } diff --git a/plugins/check_network.cpp b/plugins/check_network.cpp index e17d7b503..e21607a09 100644 --- a/plugins/check_network.cpp +++ b/plugins/check_network.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #define WIN32_LEAN_AND_MEAN @@ -120,11 +103,6 @@ static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoSt L"warn is VALUE is inside the range spanned by THR1 and THR2\n\n" L"-w ![THR1-THR2]\n" L"warn if VALUE is outside the range spanned by THR1 and THR2\n\n" - L"-w THRESHOLD%%\n" - L"if the plugin accepts percentage based thresholds those will be used.\n" - L"Does nothing if the plugin does not accept percentages, or only uses\n" - L"percentage thresholds. Ranges can be used with \"%%\", but both range values need\n" - L"to end with a percentage sign.\n\n" L"All of these options work with the critical threshold \"-c\" too." , progName); std::cout << '\n'; @@ -190,7 +168,7 @@ static int printOutput(printInfoStruct& printInfo, const std::vector continue; } else { boost::algorithm::replace_all(wsFriendlyName, "'", "''"); - tss << L"\'" << wsFriendlyName << L"_in\'=" << it->BytesInSec << L"B \'" << wsFriendlyName << L"_out\'=" << it->BytesOutSec << L"B "; + tss << L"'" << wsFriendlyName << L"_in'=" << it->BytesInSec << L"B '" << wsFriendlyName << L"_out'=" << it->BytesOutSec << L"B "; } } @@ -214,7 +192,8 @@ static int printOutput(printInfoStruct& printInfo, const std::vector } std::wcout << " " << tIn + tOut << L"B/s | " - << L"network=" << tIn + tOut << L"B;" << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";" << L"0; " + << L"'network'=" << tIn + tOut << L"B;" << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";" << L"0; " + << L"'network_in'=" << tIn << L"B 'network_out'=" << tOut << L"B " << tss.str() << '\n'; return state; diff --git a/plugins/check_nscp_api.cpp b/plugins/check_nscp_api.cpp index 333b217ab..1339ccbac 100644 --- a/plugins/check_nscp_api.cpp +++ b/plugins/check_nscp_api.cpp @@ -1,33 +1,31 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ -#define VERSION "1.0.1" +#include "icinga-version.h" /* include VERSION */ -#include "remote/httpclientconnection.hpp" -#include "remote/httprequest.hpp" -#include "remote/url-characters.hpp" +// ensure to include base first +#include "base/i2-base.hpp" #include "base/application.hpp" #include "base/json.hpp" #include "base/string.hpp" +#include "base/logger.hpp" #include "base/exception.hpp" +#include "base/utility.hpp" +#include "base/defer.hpp" +#include "base/io-engine.hpp" +#include "base/stream.hpp" +#include "base/tcpsocket.hpp" /* include global icinga::Connect */ +#include "base/tlsstream.hpp" +#include "base/base64.hpp" +#include "remote/url.hpp" +#include #include #include +#include +#include +#include +#include +#include +#include #include using namespace icinga; @@ -35,96 +33,17 @@ namespace po = boost::program_options; static bool l_Debug; -/* - * This function is called by an 'HttpRequest' once the server answers. After doing a short check on the 'response' it - * decodes it to a Dictionary and then tells 'QueryEndpoint()' that it's done - */ -static void ResultHttpCompletionCallback(const HttpRequest& request, HttpResponse& response, bool& ready, - boost::condition_variable& cv, boost::mutex& mtx, Dictionary::Ptr& result) -{ - String body; - char buffer[1024]; - size_t count; - - while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0) - body += String(buffer, buffer + count); - - if (l_Debug) { - std::cout << "Received answer\n" - << "\tHTTP code: " << response.StatusCode << "\n" - << "\tHTTP message: '" << response.StatusMessage << "'\n" - << "\tHTTP body: '" << body << "'.\n"; - } - - // Only try to decode the body if the 'HttpRequest' was successful - if (response.StatusCode != 200) - result = Dictionary::Ptr(); - else - result = JsonDecode(body); - - // Unlock our mutex, set ready and notify 'QueryEndpoint()' - boost::mutex::scoped_lock lock(mtx); - ready = true; - cv.notify_all(); -} - -/* - * This function takes all the information required to query an nscp instance on - * 'host':'port' with 'password'. The String 'endpoint' contains the specific - * query name and all the arguments formatted as an URL. - */ -static Dictionary::Ptr QueryEndpoint(const String& host, const String& port, const String& password, - const String& endpoint) -{ - HttpClientConnection::Ptr m_Connection = new HttpClientConnection(host, port, true); - - try { - bool ready = false; - boost::condition_variable cv; - boost::mutex mtx; - Dictionary::Ptr result; - std::shared_ptr req = m_Connection->NewRequest(); - req->RequestMethod = "GET"; - - // Url() will call Utillity::UnescapeString() which will thrown an exception if it finds a lonely % - req->RequestUrl = new Url(endpoint); - - // NSClient++ uses `time=1m&time=5m` instead of `time[]=1m&time[]=5m` - req->RequestUrl->SetArrayFormatUseBrackets(false); - - req->AddHeader("password", password); - if (l_Debug) - std::cout << "Sending request to 'https://" << host << ":" << port << req->RequestUrl->Format(false, false) << "'\n"; - - // Submits the request. The 'ResultHttpCompletionCallback' is called once the HttpRequest receives an answer, - // which then sets 'ready' to true - m_Connection->SubmitRequest(req, std::bind(ResultHttpCompletionCallback, _1, _2, - boost::ref(ready), boost::ref(cv), boost::ref(mtx), boost::ref(result))); - - // We need to spinlock here because our 'HttpRequest' works asynchronous - boost::mutex::scoped_lock lock(mtx); - while (!ready) { - cv.wait(lock); - } - - return result; - } - catch (const std::exception& ex) { - // Exceptions should only happen in extreme edge cases we can't recover from - std::cout << "Caught exception: " << DiagnosticInformation(ex, false) << '\n'; - return Dictionary::Ptr(); - } -} - -/* - * Takes a Dictionary 'result' and constructs an icinga compliant output string. - * If 'result' is not in the expected format it returns 3 ("UNKNOWN") and prints an informative, icinga compliant, - * output string. +/** + * Prints an Icinga plugin API compliant output, including error handling. + * + * @param result + * + * @return Status code for exit() */ static int FormatOutput(const Dictionary::Ptr& result) { if (!result) { - std::cout << "UNKNOWN: No data received.\n"; + std::cerr << "UNKNOWN: No data received.\n"; return 3; } @@ -133,54 +52,59 @@ static int FormatOutput(const Dictionary::Ptr& result) Array::Ptr payloads = result->Get("payload"); if (!payloads) { - std::cout << "UNKNOWN: Answer format error: Answer is missing 'payload'.\n"; + std::cerr << "UNKNOWN: Answer format error: Answer is missing 'payload'.\n"; return 3; } if (payloads->GetLength() == 0) { - std::cout << "UNKNOWN: Answer format error: 'payload' was empty.\n"; + std::cerr << "UNKNOWN: Answer format error: 'payload' was empty.\n"; return 3; } if (payloads->GetLength() > 1) { - std::cout << "UNKNOWN: Answer format error: Multiple payloads are not supported."; + std::cerr << "UNKNOWN: Answer format error: Multiple payloads are not supported."; return 3; } Dictionary::Ptr payload; + try { payload = payloads->Get(0); } catch (const std::exception&) { - std::cout << "UNKNOWN: Answer format error: 'payload' was not a Dictionary.\n"; + std::cerr << "UNKNOWN: Answer format error: 'payload' was not a Dictionary.\n"; return 3; } Array::Ptr lines; + try { lines = payload->Get("lines"); } catch (const std::exception&) { - std::cout << "UNKNOWN: Answer format error: 'payload' is missing 'lines'.\n"; + std::cerr << "UNKNOWN: Answer format error: 'payload' is missing 'lines'.\n"; return 3; } if (!lines) { - std::cout << "UNKNOWN: Answer format error: 'lines' is Null.\n"; + std::cerr << "UNKNOWN: Answer format error: 'lines' is Null.\n"; return 3; } std::stringstream ssout; + ObjectLock olock(lines); for (const Value& vline : lines) { Dictionary::Ptr line; + try { line = vline; } catch (const std::exception&) { - std::cout << "UNKNOWN: Answer format error: 'lines' entry was not a Dictionary.\n"; + std::cerr << "UNKNOWN: Answer format error: 'lines' entry was not a Dictionary.\n"; return 3; } + if (!line) { - std::cout << "UNKNOWN: Answer format error: 'lines' entry was Null.\n"; + std::cerr << "UNKNOWN: Answer format error: 'lines' entry was Null.\n"; return 3; } @@ -192,11 +116,17 @@ static int FormatOutput(const Dictionary::Ptr& result) } Array::Ptr perfs = line->Get("perf"); + ObjectLock olock(perfs); for (const Dictionary::Ptr& perf : perfs) { ssout << "'" << perf->Get("alias") << "'="; - Dictionary::Ptr values = perf->Contains("int_value") ? perf->Get("int_value") : perf->Get("float_value"); + + Dictionary::Ptr values = perf->Get("float_value"); + + if (perf->Contains("int_value")) + values = perf->Get("int_value"); + ssout << values->Get("value") << values->Get("unit") << ';' << values->Get("warning") << ';' << values->Get("critical"); if (values->Contains("minimum") || values->Contains("maximum")) { @@ -215,24 +145,283 @@ static int FormatOutput(const Dictionary::Ptr& result) ssout << '\n'; } - //TODO: Fix - String state = static_cast(payload->Get("result")).ToUpper(); - int creturn = state == "OK" ? 0 : - state == "WARNING" ? 1 : - state == "CRITICAL" ? 2 : - state == "UNKNOWN" ? 3 : 4; + std::map stateMap = { + { "OK", 0 }, + { "WARNING", 1}, + { "CRITICAL", 2}, + { "UNKNOWN", 3} + }; - if (creturn == 4) { - std::cout << "check_nscp UNKNOWN Answer format error: 'result' was not a known state.\n"; + String state = static_cast(payload->Get("result")).ToUpper(); + + auto it = stateMap.find(state); + + if (it == stateMap.end()) { + std::cerr << "UNKNOWN Answer format error: 'result' was not a known state.\n"; return 3; } std::cout << ssout.rdbuf(); - return creturn; + + return it->second; } -/* - * Process arguments, initialize environment and shut down gracefully. +/** + * Connects to host:port and performs a TLS shandshake + * + * @param host To connect to. + * @param port To connect to. + * + * @returns AsioTlsStream pointer for future HTTP connections. + */ +static std::shared_ptr Connect(const String& host, const String& port) +{ + std::shared_ptr sslContext; + + try { + sslContext = MakeAsioSslContext(Empty, Empty, Empty); //TODO: Add support for cert, key, ca parameters + } catch(const std::exception& ex) { + Log(LogCritical, "DebugConsole") + << "Cannot make SSL context: " << ex.what(); + throw; + } + + std::shared_ptr stream = std::make_shared(IoEngine::Get().GetIoContext(), *sslContext, host); + + try { + icinga::Connect(stream->lowest_layer(), host, port); + } catch (const std::exception& ex) { + Log(LogWarning, "DebugConsole") + << "Cannot connect to REST API on host '" << host << "' port '" << port << "': " << ex.what(); + throw; + } + + auto& tlsStream (stream->next_layer()); + + try { + tlsStream.handshake(tlsStream.client); + } catch (const std::exception& ex) { + Log(LogWarning, "DebugConsole") + << "TLS handshake with host '" << host << "' failed: " << ex.what(); + throw; + } + + return std::move(stream); +} + +static const char l_ReasonToInject[2] = {' ', 'X'}; + +template +static inline +boost::asio::mutable_buffer GetFirstNonZeroBuffer(const MutableBufferSequence& mbs) +{ + namespace asio = boost::asio; + + auto end (asio::buffer_sequence_end(mbs)); + + for (auto current (asio::buffer_sequence_begin(mbs)); current != end; ++current) { + asio::mutable_buffer buf (*current); + + if (buf.size() > 0u) { + return std::move(buf); + } + } + + return {}; +} + +/** + * Workaround for . + */ +template +class HttpResponseReasonInjector +{ +public: + inline HttpResponseReasonInjector(SyncReadStream& stream) + : m_Stream(stream), m_ReasonHasBeenInjected(false), m_StashedData(nullptr) + { + } + + HttpResponseReasonInjector(const HttpResponseReasonInjector&) = delete; + HttpResponseReasonInjector(HttpResponseReasonInjector&&) = delete; + HttpResponseReasonInjector& operator=(const HttpResponseReasonInjector&) = delete; + HttpResponseReasonInjector& operator=(HttpResponseReasonInjector&&) = delete; + + template + size_t read_some(const MutableBufferSequence& mbs) + { + boost::system::error_code ec; + size_t amount = read_some(mbs, ec); + + if (ec) { + throw boost::system::system_error(ec); + } + + return amount; + } + + template + size_t read_some(const MutableBufferSequence& mbs, boost::system::error_code& ec) + { + auto mb (GetFirstNonZeroBuffer(mbs)); + + if (m_StashedData) { + size_t amount = 0; + auto end ((char*)mb.data() + mb.size()); + + for (auto current ((char*)mb.data()); current < end; ++current) { + *current = *m_StashedData; + + ++m_StashedData; + ++amount; + + if (m_StashedData == (char*)m_StashedDataBuf + (sizeof(m_StashedDataBuf) / sizeof(m_StashedDataBuf[0]))) { + m_StashedData = nullptr; + break; + } + } + + return amount; + } + + size_t amount = m_Stream.read_some(mb, ec); + + if (!ec && !m_ReasonHasBeenInjected) { + auto end ((char*)mb.data() + amount); + + for (auto current ((char*)mb.data()); current < end; ++current) { + if (*current == '\r') { + auto last (end - 1); + + for (size_t i = sizeof(l_ReasonToInject) / sizeof(l_ReasonToInject[0]); i;) { + m_StashedDataBuf[--i] = *last; + + if (last > current) { + memmove(current + 1, current, last - current); + } + + *current = l_ReasonToInject[i]; + } + + m_ReasonHasBeenInjected = true; + m_StashedData = m_StashedDataBuf; + + break; + } + } + } + + return amount; + } + +private: + SyncReadStream& m_Stream; + bool m_ReasonHasBeenInjected; + char m_StashedDataBuf[sizeof(l_ReasonToInject) / sizeof(l_ReasonToInject[0])]; + char* m_StashedData; +}; + +/** + * Queries the given endpoint and host:port and retrieves data. + * + * @param host To connect to. + * @param port To connect to. + * @param password For auth header (required). + * @param endpoint Caller must construct the full endpoint including the command query. + * + * @return Dictionary de-serialized from JSON data. + */ + +static Dictionary::Ptr FetchData(const String& host, const String& port, const String& password, + const String& endpoint) +{ + namespace beast = boost::beast; + namespace http = beast::http; + + std::shared_ptr tlsStream; + + try { + tlsStream = Connect(host, port); + } catch (const std::exception& ex) { + std::cerr << "Connection error: " << ex.what(); + throw ex; + } + + Url::Ptr url; + + try { + url = new Url(endpoint); + } catch (const std::exception& ex) { + std::cerr << "URL error: " << ex.what(); + throw ex; + } + + url->SetScheme("https"); + url->SetHost(host); + url->SetPort(port); + + // NSClient++ uses `time=1m&time=5m` instead of `time[]=1m&time[]=5m` + url->SetArrayFormatUseBrackets(false); + + http::request request (http::verb::get, std::string(url->Format(true)), 10); + + request.set(http::field::user_agent, "Icinga/check_nscp_api/" + String(VERSION)); + request.set(http::field::host, host + ":" + port); + + request.set(http::field::accept, "application/json"); + request.set("password", password); + + if (l_Debug) { + std::cout << "Sending request to " << url->Format(false, false) << "'.\n"; + } + + try { + http::write(*tlsStream, request); + tlsStream->flush(); + } catch (const std::exception& ex) { + std::cerr << "Cannot write HTTP request to REST API at URL '" << url->Format(false, false) << "': " << ex.what(); + throw ex; + } + + beast::flat_buffer buffer; + http::parser p; + + try { + HttpResponseReasonInjector reasonInjector (*tlsStream); + http::read(reasonInjector, buffer, p); + } catch (const std::exception &ex) { + BOOST_THROW_EXCEPTION(ScriptError(String("Error reading HTTP response data: ") + ex.what())); + } + + String body (std::move(p.get().body())); + + if (l_Debug) + std::cout << "Received body from NSCP: '" << body << "'." << std::endl; + + // Add some rudimentary error handling. + if (body.IsEmpty()) { + String message = "No body received. Ensure that connection parameters are good and check the NSCP logs."; + BOOST_THROW_EXCEPTION(ScriptError(message)); + } + + Dictionary::Ptr jsonResponse; + + try { + jsonResponse = JsonDecode(body); + } catch (const std::exception& ex) { + String message = "Cannot parse JSON response body '" + body + "', error: " + ex.what(); + BOOST_THROW_EXCEPTION(ScriptError(message)); + } + + return jsonResponse; +} + +/** + * Main function + * + * @param argc + * @param argv + * @return exit code */ int main(int argc, char **argv) { @@ -282,6 +471,12 @@ int main(int argc, char **argv) l_Debug = vm.count("debug") > 0; + // Initialize logger + if (l_Debug) + Logger::SetConsoleLogSeverity(LogDebug); + else + Logger::SetConsoleLogSeverity(LogWarning); + // Create the URL string and escape certain characters since Url() follows RFC 3986 String endpoint = "/query/" + vm["query"].as(); if (!vm.count("arguments")) @@ -301,11 +496,15 @@ int main(int argc, char **argv) } } - // This needs to happen for HttpRequest to work - Application::InitializeBase(); + Dictionary::Ptr result; - Dictionary::Ptr result = QueryEndpoint(vm["host"].as(), vm["port"].as(), - vm["password"].as(), endpoint); + try { + result = FetchData(vm["host"].as(), vm["port"].as(), + vm["password"].as(), endpoint); + } catch (const std::exception& ex) { + std::cerr << "UNKNOWN - " << ex.what(); + exit(3); + } // Application::Exit() is the clean way to exit after calling InitializeBase() Application::Exit(FormatOutput(result)); diff --git a/plugins/check_perfmon.cpp b/plugins/check_perfmon.cpp index 7e415f1cd..0f94b12dc 100644 --- a/plugins/check_perfmon.cpp +++ b/plugins/check_perfmon.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "plugins/thresholds.hpp" #include @@ -297,7 +280,11 @@ bool QueryPerfData(printInfoStruct& pI) if (FAILED(status)) goto die; - status = PdhAddCounter(hQuery, pI.wsFullPath.c_str(), NULL, &hCounter); + status = PdhAddEnglishCounter(hQuery, pI.wsFullPath.c_str(), NULL, &hCounter); + + if (FAILED(status)) + status = PdhAddCounter(hQuery, pI.wsFullPath.c_str(), NULL, &hCounter); + if (FAILED(status)) goto die; @@ -330,7 +317,7 @@ bool QueryPerfData(printInfoStruct& pI) pI.dValue = pDisplayValues[0].FmtValue.longValue; break; case (PDH_FMT_LARGE): - pI.dValue = pDisplayValues[0].FmtValue.largeValue; + pI.dValue = (double) pDisplayValues[0].FmtValue.largeValue; break; default: pI.dValue = pDisplayValues[0].FmtValue.doubleValue; @@ -352,26 +339,27 @@ static int printOutput(const po::variables_map& vm, printInfoStruct& pi) std::wstringstream wssPerfData; if (vm.count("perf-syntax")) - wssPerfData << "\"" << vm["perf-syntax"].as() << "\"="; + wssPerfData << "'" << vm["perf-syntax"].as() << "'="; else - wssPerfData << "\"" << pi.wsFullPath << "\"="; + wssPerfData << "'" << pi.wsFullPath << "'="; wssPerfData << pi.dValue << ';' << pi.tWarn.pString() << ';' << pi.tCrit.pString() << ";;"; if (pi.tCrit.rend(pi.dValue)) { - std::wcout << "PERFMON CRITICAL \"" << (vm.count("perf-syntax") ? vm["perf-syntax"].as() : pi.wsFullPath) - << "\" = " << pi.dValue << " | " << wssPerfData.str() << '\n'; + std::wcout << "PERFMON CRITICAL for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as() : pi.wsFullPath) + << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n"; return 2; } if (pi.tWarn.rend(pi.dValue)) { - std::wcout << "PERFMON WARNING \"" << (vm.count("perf-syntax") ? vm["perf-syntax"].as() : pi.wsFullPath) - << "\" = " << pi.dValue << " | " << wssPerfData.str() << '\n'; + std::wcout << "PERFMON WARNING for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as() : pi.wsFullPath) + << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n"; return 1; } - std::wcout << "PERFMON OK \"" << (vm.count("perf-syntax") ? vm["perf-syntax"].as() : pi.wsFullPath) - << "\" = " << pi.dValue << " | " << wssPerfData.str() << '\n'; + std::wcout << "PERFMON OK for '" << (vm.count("perf-syntax") ? vm["perf-syntax"].as() : pi.wsFullPath) + << "' = " << pi.dValue << " | " << wssPerfData.str() << "\n"; + return 0; } diff --git a/plugins/check_ping.cpp b/plugins/check_ping.cpp index 8be087a8f..c918d9272 100644 --- a/plugins/check_ping.cpp +++ b/plugins/check_ping.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN //else winsock will be included with windows.h and conflict with winsock2 @@ -264,7 +247,7 @@ static bool resolveHostname(const std::wstring& hostname, bool ipv6, std::wstrin DWORD ret = GetAddrInfoW(hostname.c_str(), NULL, &hints, &result); if (ret) { - std::cout << "Failed to resolve hostname. Winsock Error Code: " << ret << '\n'; + std::wcout << L"Failed to resolve hostname. Error " << ret << L": " << formatErrorInfo(ret) << L"\n"; return false; } @@ -362,7 +345,7 @@ static int check_ping4(const printInfoStruct& pi, response& response) QueryPerformanceCounter(&timer2); if (((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart) < pi.timeout) - Sleep(pi.timeout - ((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart)); + Sleep((DWORD) (pi.timeout - ((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart))); } while (--num); if (l_Debug) @@ -417,82 +400,80 @@ static int check_ping6(const printInfoStruct& pi, response& response) HANDLE hIcmp = Icmp6CreateFile(); if (hIcmp == INVALID_HANDLE_VALUE) { - goto die; + printErrorInfo(GetLastError()); + + if (hIcmp) + IcmpCloseHandle(hIcmp); + + if (repBuf) + delete reinterpret_cast(repBuf); + + return 3; + } else { + IP_OPTION_INFORMATION ipInfo = { 30, 0, 0, 0, NULL }; + + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + + do { + LARGE_INTEGER timer1; + QueryPerformanceCounter(&timer1); + + if (l_Debug) + std::wcout << L"Sending Icmp echo" << '\n'; + + if (!Icmp6SendEcho2(hIcmp, NULL, NULL, NULL, &ipSource6, &ipDest6, + NULL, 0, &ipInfo, repBuf, dwRepSize, pi.timeout)) { + response.dropped++; + if (l_Debug) + std::wcout << L"Dropped: Response was 0" << '\n'; + continue; + } + + if (l_Debug) + std::wcout << "Ping recieved" << '\n'; + + Icmp6ParseReplies(repBuf, dwRepSize); + + ICMPV6_ECHO_REPLY *pEchoReply = static_cast(repBuf); + + if (pEchoReply->Status != IP_SUCCESS) { + response.dropped++; + if (l_Debug) + std::wcout << L"Dropped: echo reply status " << pEchoReply->Status << '\n'; + continue; + } + + rtt += pEchoReply->RoundTripTime; + + if (l_Debug) + std::wcout << L"Recorded rtt of " << pEchoReply->RoundTripTime << '\n'; + + if (response.pMin == 0 || pEchoReply->RoundTripTime < response.pMin) + response.pMin = pEchoReply->RoundTripTime; + else if (pEchoReply->RoundTripTime > response.pMax) + response.pMax = pEchoReply->RoundTripTime; + + LARGE_INTEGER timer2; + QueryPerformanceCounter(&timer2); + + if (((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart) < pi.timeout) + Sleep((DWORD) (pi.timeout - ((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart))); + } while (--num); + + if (l_Debug) + std::wcout << L"All pings sent. Cleaning up and returning" << '\n'; + + if (hIcmp) + IcmpCloseHandle(hIcmp); + + if (repBuf) + delete reinterpret_cast(repBuf); + + response.avg = ((double)rtt / pi.num); + + return -1; } - - IP_OPTION_INFORMATION ipInfo = { 30, 0, 0, 0, NULL }; - - LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); - - do { - LARGE_INTEGER timer1; - QueryPerformanceCounter(&timer1); - - if (l_Debug) - std::wcout << L"Sending Icmp echo" << '\n'; - - if (!Icmp6SendEcho2(hIcmp, NULL, NULL, NULL, &ipSource6, &ipDest6, - NULL, 0, &ipInfo, repBuf, dwRepSize, pi.timeout)) { - response.dropped++; - if (l_Debug) - std::wcout << L"Dropped: Response was 0" << '\n'; - continue; - } - - if (l_Debug) - std::wcout << "Ping recieved" << '\n'; - - Icmp6ParseReplies(repBuf, dwRepSize); - - ICMPV6_ECHO_REPLY *pEchoReply = static_cast(repBuf); - - if (pEchoReply->Status != IP_SUCCESS) { - response.dropped++; - if (l_Debug) - std::wcout << L"Dropped: echo reply status " << pEchoReply->Status << '\n'; - continue; - } - - rtt += pEchoReply->RoundTripTime; - - if (l_Debug) - std::wcout << L"Recorded rtt of " << pEchoReply->RoundTripTime << '\n'; - - if (response.pMin == 0 || pEchoReply->RoundTripTime < response.pMin) - response.pMin = pEchoReply->RoundTripTime; - else if (pEchoReply->RoundTripTime > response.pMax) - response.pMax = pEchoReply->RoundTripTime; - - LARGE_INTEGER timer2; - QueryPerformanceCounter(&timer2); - - if (((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart) < pi.timeout) - Sleep(pi.timeout - ((timer2.QuadPart - timer1.QuadPart) * 1000 / frequency.QuadPart)); - } while (--num); - - if (l_Debug) - std::wcout << L"All pings sent. Cleaning up and returning" << '\n'; - - if (hIcmp) - IcmpCloseHandle(hIcmp); - - if (repBuf) - delete reinterpret_cast(repBuf); - - response.avg = ((double)rtt / pi.num); - - return -1; -die: - printErrorInfo(GetLastError()); - - if (hIcmp) - IcmpCloseHandle(hIcmp); - - if (repBuf) - delete reinterpret_cast(repBuf); - - return 3; } int wmain(int argc, WCHAR **argv) diff --git a/plugins/check_procs.cpp b/plugins/check_procs.cpp index ee7fd3261..44e2483ef 100644 --- a/plugins/check_procs.cpp +++ b/plugins/check_procs.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "plugins/thresholds.hpp" #include diff --git a/plugins/check_service.cpp b/plugins/check_service.cpp index e6e12d74e..cd0cf1462 100644 --- a/plugins/check_service.cpp +++ b/plugins/check_service.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "plugins/thresholds.hpp" #include @@ -60,8 +43,10 @@ static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoSt parser .options(desc) .style( - po::command_line_style::unix_style | - po::command_line_style::allow_long_disguise) + po::command_line_style::unix_style & + ~po::command_line_style::allow_guessing | + po::command_line_style::allow_long_disguise + ) .run(), vm); vm.notify(); @@ -124,7 +109,7 @@ static int printOutput(const printInfoStruct& printInfo) state state = OK; if (!printInfo.ServiceState) { - std::wcout << L"SERVICE CRITICAL NOTFOUND | service=" << printInfo.ServiceState << ";;;1;7" << '\n'; + std::wcout << L"SERVICE CRITICAL NOT FOUND | 'service'=" << printInfo.ServiceState << ";;;1;7" << '\n'; return 3; } @@ -133,13 +118,13 @@ static int printOutput(const printInfoStruct& printInfo) switch (state) { case OK: - std::wcout << L"SERVICE \"" << printInfo.service << "\" OK RUNNING | service=4;;;1;7" << '\n'; + std::wcout << L"SERVICE \"" << printInfo.service << "\" OK RUNNING | 'service'=4;;;1;7" << '\n'; break; case WARNING: - std::wcout << L"SERVICE \"" << printInfo.service << "\" WARNING NOT RUNNING | service=" << printInfo.ServiceState << ";;;1;7" << '\n'; + std::wcout << L"SERVICE \"" << printInfo.service << "\" WARNING NOT RUNNING | 'service'=" << printInfo.ServiceState << ";;;1;7" << '\n'; break; case CRITICAL: - std::wcout << L"SERVICE \"" << printInfo.service << "\" CRITICAL NOT RUNNING | service=" << printInfo.ServiceState << ";;;1;7" << '\n'; + std::wcout << L"SERVICE \"" << printInfo.service << "\" CRITICAL NOT RUNNING | 'service'=" << printInfo.ServiceState << ";;;1;7" << '\n'; break; } @@ -184,7 +169,7 @@ static std::wstring getServiceByDescription(const std::wstring& description) EnumServicesStatus(hSCM, SERVICE_WIN32 | SERVICE_DRIVER, SERVICE_STATE_ALL, lpServices, pcbBytesNeeded, &pcbBytesNeeded, &lpServicesReturned, &lpResumeHandle); - for (int index = 0; index < lpServicesReturned; index++) { + for (decltype(lpServicesReturned) index = 0; index < lpServicesReturned; index++) { LPWSTR lpCurrent = lpServices[index].lpServiceName; if (l_Debug) { diff --git a/plugins/check_swap.cpp b/plugins/check_swap.cpp index d405a094f..dc08f3b3b 100644 --- a/plugins/check_swap.cpp +++ b/plugins/check_swap.cpp @@ -1,27 +1,11 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "plugins/thresholds.hpp" #include #include #include -#include +#include +#include #define VERSION 1.0 @@ -35,10 +19,29 @@ struct printInfoStruct double aSwap; double percentFree; Bunit unit = BunitMB; + bool showUsed; +}; + +struct pageFileInfo +{ + SIZE_T totalSwap; + SIZE_T availableSpwap; }; static bool l_Debug; +BOOL EnumPageFilesProc(LPVOID pContext, PENUM_PAGE_FILE_INFORMATION pPageFileInfo, LPCWSTR lpFilename) { + std::vector* pageFile = static_cast*>(pContext); + SYSTEM_INFO systemInfo; + + GetSystemInfo(&systemInfo); + + // pPageFileInfo output is in pages, we need to multiply it by the page size + pageFile->push_back({ pPageFileInfo->TotalSize * systemInfo.dwPageSize, (pPageFileInfo->TotalSize - pPageFileInfo->TotalInUse) * systemInfo.dwPageSize }); + + return TRUE; +} + static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo) { WCHAR namePath[MAX_PATH]; @@ -54,6 +57,7 @@ static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoSt ("warning,w", po::wvalue(), "Warning threshold") ("critical,c", po::wvalue(), "Critical threshold") ("unit,u", po::wvalue(), "The unit to use for display (default MB)") + ("show-used,U", "Show used swap instead of the free swap") ; po::wcommand_line_parser parser(ac, av); @@ -148,6 +152,12 @@ static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoSt } } + if (vm.count("show-used")) { + printInfo.showUsed = true; + printInfo.warn.legal = true; + printInfo.crit.legal = true; + } + return -1; } @@ -158,46 +168,55 @@ static int printOutput(printInfoStruct& printInfo) state state = OK; - if (printInfo.warn.rend(printInfo.aSwap, printInfo.tSwap)) + std::wcout << L"SWAP "; + + double currentValue; + + if (!printInfo.showUsed) + currentValue = printInfo.aSwap; + else + currentValue = printInfo.tSwap - printInfo.aSwap; + + if (printInfo.warn.rend(currentValue, printInfo.tSwap)) state = WARNING; - if (printInfo.crit.rend(printInfo.aSwap, printInfo.tSwap)) + if (printInfo.crit.rend(currentValue, printInfo.tSwap)) state = CRITICAL; - switch (state) { - case OK: - std::wcout << L"SWAP OK - " << printInfo.percentFree << L"% free | swap=" << printInfo.aSwap << BunitStr(printInfo.unit) << L";" - << printInfo.warn.pString(printInfo.tSwap) << L";" << printInfo.crit.pString(printInfo.tSwap) - << L";0;" << printInfo.tSwap << '\n'; - break; - case WARNING: - std::wcout << L"SWAP WARNING - " << printInfo.percentFree << L"% free | swap=" << printInfo.aSwap << BunitStr(printInfo.unit) << L";" - << printInfo.warn.pString(printInfo.tSwap) << L";" << printInfo.crit.pString(printInfo.tSwap) - << L";0;" << printInfo.tSwap << '\n'; - break; - case CRITICAL: - std::wcout << L"SWAP CRITICAL - " << printInfo.percentFree << L"% free | swap=" << printInfo.aSwap << BunitStr(printInfo.unit) << L";" - << printInfo.warn.pString(printInfo.tSwap) << L";" << printInfo.crit.pString(printInfo.tSwap) - << L";0;" << printInfo.tSwap << '\n'; - break; - } + std::wcout << stateToString(state) << " - "; + + if (!printInfo.showUsed) + std::wcout << printInfo.percentFree << L"% free "; + else + std::wcout << 100 - printInfo.percentFree << L"% used "; + + std::wcout << "| 'swap'=" << currentValue << BunitStr(printInfo.unit) << L";" + << printInfo.warn.pString(printInfo.tSwap) << L";" << printInfo.crit.pString(printInfo.tSwap) + << L";0;" << printInfo.tSwap << '\n'; return state; } static int check_swap(printInfoStruct& printInfo) { - MEMORYSTATUSEX MemBuf; - MemBuf.dwLength = sizeof(MemBuf); + // Needs explicit cast: http://msinilo.pl/blog2/post/p1348/ + PENUM_PAGE_FILE_CALLBACKW pageFileCallback = (PENUM_PAGE_FILE_CALLBACKW)EnumPageFilesProc; + std::vector pageFiles; - if (!GlobalMemoryStatusEx(&MemBuf)) { + if(!EnumPageFilesW(pageFileCallback, &pageFiles)) { printErrorInfo(); return 3; } - printInfo.tSwap = round(MemBuf.ullTotalPageFile / pow(1024.0, printInfo.unit)); - printInfo.aSwap = round(MemBuf.ullAvailPageFile / pow(1024.0, printInfo.unit)); - printInfo.percentFree = 100.0 * MemBuf.ullAvailPageFile / MemBuf.ullTotalPageFile; + for (int i = 0; i < pageFiles.size(); i++) { + printInfo.tSwap += round(pageFiles.at(i).totalSwap / pow(1024.0, printInfo.unit)); + printInfo.aSwap += round(pageFiles.at(i).availableSpwap / pow(1024.0, printInfo.unit)); + } + + if (printInfo.aSwap > 0 && printInfo.tSwap > 0) + printInfo.percentFree = 100.0 * printInfo.aSwap / printInfo.tSwap; + else + printInfo.percentFree = 0; return -1; } diff --git a/plugins/check_update.cpp b/plugins/check_update.cpp index 31d98e44b..2711d9330 100644 --- a/plugins/check_update.cpp +++ b/plugins/check_update.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "plugins/thresholds.hpp" #include @@ -33,11 +16,11 @@ namespace po = boost::program_options; struct printInfoStruct { - bool warn{false}; - bool crit{false}; + int warn{0}; + int crit{0}; LONG numUpdates{0}; - bool important{false}; - bool reboot{false}; + bool ignoreReboot{false}; + int reboot{0}; bool careForCanRequest{false}; }; @@ -55,9 +38,10 @@ static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoSt ("help,h", "Print help message and exit") ("version,V", "Print version and exit") ("debug,d", "Verbose/Debug output") - ("warning,w", "Warn if there are important updates available") - ("critical,c", "Critical if there are important updates that require a reboot") + ("warning,w", po::value(), "Number of updates to trigger a warning.") + ("critical,c", po::value(), "Number of updates to trigger a critical.") ("possible-reboot", "Treat \"update may need reboot\" as \"update needs reboot\"") + ("no-reboot-critical", "Do not automatically return critical if an update requiring reboot is present.") ; po::wcommand_line_parser parser(ac, av); @@ -87,36 +71,37 @@ static int parseArguments(int ac, WCHAR **av, po::variables_map& vm, printInfoSt L"\nAfter some time, it will then output a string like this one:\n\n" L"\tUPDATE WARNING 8 | updates=8;1;1;0\n\n" L"\"UPDATE\" being the type of the check, \"WARNING\" the returned status\n" - L"and \"8\" is the number of important updates updates.\n" + L"and \"8\" is the number of important updates.\n" L"The performance data is found behind the \"|\", in order:\n" L"returned value, warning threshold, critical threshold, minimal value and,\n" - L"if applicable, the maximal value. Performance data will only be displayed when\n" - L"you set at least one threshold\n\n" + L"if applicable, the maximal value.\n\n" L"An update counts as important when it is part of the Security- or\n" L"CriticalUpdates group.\n" L"Consult the msdn on WSUS Classification GUIDs for more information.\n" L"%s' exit codes denote the following:\n" L" 0\tOK,\n\tNo Thresholds were broken or the programs check part was not executed\n" L" 1\tWARNING,\n\tThe warning, but not the critical threshold was broken\n" - L" 2\tCRITICAL,\n\tThe critical threshold was broken\n" + L" 2\tCRITICAL,\n\tThe critical threshold was broken or an update required reboot.\n" L" 3\tUNKNOWN, \n\tThe program experienced an internal or input error\n\n" - L"%s works different from other plugins in that you do not set thresholds\n" - L"but only activate them. Using \"-w\" triggers warning state if there are not\n" - L"installed and non-optional updates. \"-c\" triggers critical if there are\n" - L"non-optional updates that require a reboot.\n" + L"If a warning threshold is set but not a critical threshold, the critical\n" + L"threshold will be set to one greater than the set warning threshold.\n\n" L"The \"possible-reboot\" option is not recommended since this true for nearly\n" L"every update." - , progName, progName); + , progName); std::cout << '\n'; return 0; } if (vm.count("version")) { std::cout << "Version: " << VERSION << '\n'; return 0; } - - printInfo.warn = vm.count("warning") > 0; - printInfo.crit = vm.count("critical") > 0; + if(vm.count("warning")) + printInfo.warn = vm["warning"].as(); + if (vm.count("critical")) + printInfo.crit = vm["critical"].as(); + else if (vm.count("warning")) + printInfo.crit = printInfo.warn + 1; printInfo.careForCanRequest = vm.count("possible-reboot") > 0; + printInfo.ignoreReboot = vm.count("no-reboot-critical") > 0; l_Debug = vm.count("debug") > 0; @@ -131,10 +116,10 @@ static int printOutput(const printInfoStruct& printInfo) state state = OK; std::wstring output = L"UPDATE "; - if (printInfo.important) + if (printInfo.numUpdates >= printInfo.warn && printInfo.warn) state = WARNING; - if (printInfo.reboot) + if ((printInfo.reboot && !printInfo.ignoreReboot) || (printInfo.numUpdates >= printInfo.crit && printInfo.crit)) state = CRITICAL; switch (state) { @@ -148,8 +133,13 @@ static int printOutput(const printInfoStruct& printInfo) output.append(L"CRITICAL "); break; } - - std::wcout << output << printInfo.numUpdates << L" | update=" << printInfo.numUpdates << L";" + output.append(std::to_wstring(printInfo.numUpdates)); + if (printInfo.reboot) { + output.append(L"; "); + output.append(std::to_wstring(printInfo.reboot)); + output.append(L" NEED REBOOT "); + } + std::wcout << output << L" | 'update'=" << printInfo.numUpdates << L";" << printInfo.warn << L";" << printInfo.crit << L";0;" << '\n'; return state; @@ -214,15 +204,16 @@ static int check_update(printInfoStruct& printInfo) pUpdate->get_InstallationBehavior(&pIbehav); pIbehav->get_RebootBehavior(&updateReboot); if (updateReboot == irbAlwaysRequiresReboot) { - printInfo.reboot = true; + printInfo.reboot++; if (l_Debug) std::wcout << L"It requires reboot" << '\n'; continue; } - if (printInfo.careForCanRequest && updateReboot == irbCanRequestReboot) + if (printInfo.careForCanRequest && updateReboot == irbCanRequestReboot) { if (l_Debug) std::wcout << L"It requires reboot" << '\n'; - printInfo.reboot = true; + printInfo.reboot++; + } } if (l_Debug) diff --git a/plugins/check_uptime.cpp b/plugins/check_uptime.cpp index 038625ade..93d540af2 100644 --- a/plugins/check_uptime.cpp +++ b/plugins/check_uptime.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "plugins/thresholds.hpp" #include @@ -33,6 +16,7 @@ struct printInfoStruct threshold warn; threshold crit; long long time; + long long timeInSeconds; Tunit unit; }; @@ -159,26 +143,26 @@ static int printOutput(printInfoStruct& printInfo) state state = OK; - if (printInfo.warn.rend(printInfo.time)) + if (printInfo.warn.rend((double) printInfo.time)) state = WARNING; - if (printInfo.crit.rend(printInfo.time)) + if (printInfo.crit.rend((double) printInfo.time)) state = CRITICAL; switch (state) { case OK: - std::wcout << L"UPTIME OK " << printInfo.time << TunitStr(printInfo.unit) << L" | uptime=" << printInfo.time - << TunitStr(printInfo.unit) << L";" << printInfo.warn.pString() << L";" - << printInfo.crit.pString() << L";0;" << '\n'; + std::wcout << L"UPTIME OK " << printInfo.time << TunitStr(printInfo.unit) << L" | 'uptime'=" << printInfo.timeInSeconds + << "s" << L";" << printInfo.warn.toSeconds(printInfo.unit).pString() << L";" + << printInfo.crit.toSeconds(printInfo.unit).pString() << L";0;" << '\n'; break; case WARNING: - std::wcout << L"UPTIME WARNING " << printInfo.time << TunitStr(printInfo.unit) << L" | uptime=" << printInfo.time - << TunitStr(printInfo.unit) << L";" << printInfo.warn.pString() << L";" - << printInfo.crit.pString() << L";0;" << '\n'; + std::wcout << L"UPTIME WARNING " << printInfo.time << TunitStr(printInfo.unit) << L" | 'uptime'=" << printInfo.timeInSeconds + << "s" << L";" << printInfo.warn.toSeconds(printInfo.unit).pString() << L";" + << printInfo.crit.toSeconds(printInfo.unit).pString() << L";0;" << '\n'; break; case CRITICAL: - std::wcout << L"UPTIME CRITICAL " << printInfo.time << TunitStr(printInfo.unit) << L" | uptime=" << printInfo.time - << TunitStr(printInfo.unit) << L";" << printInfo.warn.pString() << L";" - << printInfo.crit.pString() << L";0;" << '\n'; + std::wcout << L"UPTIME CRITICAL " << printInfo.time << TunitStr(printInfo.unit) << L" | 'uptime'=" << printInfo.timeInSeconds + << "s" << L";" << printInfo.warn.toSeconds(printInfo.unit).pString() << L";" + << printInfo.crit.toSeconds(printInfo.unit).pString() << L";0;" << '\n'; break; } @@ -209,6 +193,9 @@ static void getUptime(printInfoStruct& printInfo) printInfo.time = uptime.count(); break; } + + // For the Performance Data we need the time in seconds + printInfo.timeInSeconds = boost::chrono::duration_cast(uptime).count(); } int wmain(int argc, WCHAR **argv) diff --git a/plugins/check_users.cpp b/plugins/check_users.cpp index 993a40027..9193551db 100644 --- a/plugins/check_users.cpp +++ b/plugins/check_users.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "plugins/thresholds.hpp" #include @@ -151,15 +134,15 @@ static int printOutput(printInfoStruct& printInfo) switch (state) { case OK: - std::wcout << L"USERS OK " << printInfo.users << L" User(s) logged in | users=" << printInfo.users << L";" + std::wcout << L"USERS OK " << printInfo.users << L" User(s) logged in | 'users'=" << printInfo.users << L";" << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";0;" << '\n'; break; case WARNING: - std::wcout << L"USERS WARNING " << printInfo.users << L" User(s) logged in | users=" << printInfo.users << L";" + std::wcout << L"USERS WARNING " << printInfo.users << L" User(s) logged in | 'users'=" << printInfo.users << L";" << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";0;" << '\n'; break; case CRITICAL: - std::wcout << L"USERS CRITICAL " << printInfo.users << L" User(s) logged in | users=" << printInfo.users << L";" + std::wcout << L"USERS CRITICAL " << printInfo.users << L" User(s) logged in | 'users'=" << printInfo.users << L";" << printInfo.warn.pString() << L";" << printInfo.crit.pString() << L";0;" << '\n'; break; } diff --git a/plugins/thresholds.cpp b/plugins/thresholds.cpp index f80e7d634..bdd67ee95 100644 --- a/plugins/thresholds.cpp +++ b/plugins/thresholds.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "plugins/thresholds.hpp" #include @@ -28,6 +11,13 @@ threshold::threshold() : set(false) {} +threshold::threshold(const double v, const double c, bool l , bool p ) { + lower = v; + upper = c; + legal = l; + perc = p; +} + threshold::threshold(const std::wstring& stri) { if (stri.empty()) @@ -126,6 +116,35 @@ std::wstring threshold::pString(const double max) return s; } +threshold threshold::toSeconds(const Tunit& fromUnit) { + if (!set) + return *this; + + double lowerAbs = lower; + double upperAbs = upper; + + switch (fromUnit) { + case TunitMS: + lowerAbs = lowerAbs / 1000; + upperAbs = upperAbs / 1000; + break; + case TunitS: + lowerAbs = lowerAbs ; + upperAbs = upperAbs ; + break; + case TunitM: + lowerAbs = lowerAbs * 60; + upperAbs = upperAbs * 60; + break; + case TunitH: + lowerAbs = lowerAbs * 60 * 60; + upperAbs = upperAbs * 60 * 60; + break; + } + + return threshold(lowerAbs, upperAbs, legal, perc); +} + std::wstring removeZero(double val) { std::wstring ret = boost::lexical_cast(val); @@ -223,7 +242,35 @@ void printErrorInfo(unsigned long err) LPWSTR mBuf = NULL; if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&mBuf, 0, NULL)) - std::wcout << "Failed to format error message, last error was: " << err << '\n'; - else + std::wcout << "Failed to format error message, last error was: " << err << '\n'; + else { + boost::trim_right(std::wstring(mBuf)); std::wcout << mBuf << std::endl; + } } + +std::wstring formatErrorInfo(unsigned long err) { + std::wostringstream out; + if (!err) + err = GetLastError(); + LPWSTR mBuf = NULL; + if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&mBuf, 0, NULL)) + out << "Failed to format error message, last error was: " << err; + else { + std::wstring tempOut = std::wstring(mBuf); + boost::trim_right(tempOut); + out << tempOut; + } + + return out.str(); +} + +std::wstring stateToString(const state& state) { + switch (state) { + case OK: return L"OK"; + case WARNING: return L"WARNING"; + case CRITICAL: return L"CRITICAL"; + default: return L"UNKNOWN"; + } +} \ No newline at end of file diff --git a/plugins/thresholds.hpp b/plugins/thresholds.hpp index 1a63e5a4d..4c47ddb7d 100644 --- a/plugins/thresholds.hpp +++ b/plugins/thresholds.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef THRESHOLDS_H #define THRESHOLDS_H @@ -62,6 +45,7 @@ public: // returns a printable string of the threshold std::wstring pString(const double max = 100.0); + threshold toSeconds(const Tunit& fromUnit); }; std::wstring removeZero(double); @@ -73,5 +57,8 @@ Tunit parseTUnit(const std::wstring&); std::wstring TunitStr(const Tunit&); void printErrorInfo(unsigned long err = 0); +std::wstring formatErrorInfo(unsigned long err); + +std::wstring stateToString(const state&); #endif /* THRESHOLDS_H */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 29a16646f..bfcd83ffd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ include(BoostTestTargets) @@ -28,6 +13,7 @@ set(base_test_SOURCES base-match.cpp base-netstring.cpp base-object.cpp + base-object-packer.cpp base-serialize.cpp base-shellescape.cpp base-stacktrace.cpp @@ -35,6 +21,7 @@ set(base_test_SOURCES base-string.cpp base-timer.cpp base-type.cpp + base-utility.cpp base-value.cpp config-ops.cpp icinga-checkresult.cpp @@ -43,7 +30,6 @@ set(base_test_SOURCES icinga-notification.cpp icinga-perfdata.cpp remote-url.cpp - remote-user.cpp ${base_OBJS} $ $ @@ -73,6 +59,8 @@ add_boost_test(base base_convert/tostring base_convert/tobool base_dictionary/construct + base_dictionary/initializer1 + base_dictionary/initializer2 base_dictionary/get1 base_dictionary/get2 base_dictionary/foreach @@ -81,7 +69,16 @@ add_boost_test(base base_dictionary/json base_fifo/construct base_fifo/io + base_json/encode + base_json/decode base_json/invalid1 + base_object_packer/pack_null + base_object_packer/pack_false + base_object_packer/pack_true + base_object_packer/pack_number + base_object_packer/pack_string + base_object_packer/pack_array + base_object_packer/pack_object base_match/tolong base_netstring/netstring base_object/construct @@ -111,6 +108,11 @@ add_boost_test(base base_type/assign base_type/byname base_type/instantiate + base_utility/parse_version + base_utility/compare_version + base_utility/comparepasswords_works + base_utility/comparepasswords_issafe + base_utility/validateutf8 base_value/scalar base_value/convert base_value/format @@ -124,10 +126,12 @@ add_boost_test(base icinga_checkresult/service_3attempts icinga_checkresult/host_flapping_notification icinga_checkresult/service_flapping_notification + icinga_notification/strings icinga_notification/state_filter icinga_notification/type_filter icinga_macros/simple icinga_legacytimeperiod/simple + icinga_legacytimeperiod/advanced icinga_perfdata/empty icinga_perfdata/simple icinga_perfdata/quotes @@ -142,7 +146,6 @@ add_boost_test(base remote_url/get_and_set remote_url/format remote_url/illegal_legal_strings - api_user/password ) if(ICINGA2_WITH_LIVESTATUS) diff --git a/test/base-array.cpp b/test/base-array.cpp index 74bbd692c..33e54e8f1 100644 --- a/test/base-array.cpp +++ b/test/base-array.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/array.hpp" #include "base/objectlock.hpp" diff --git a/test/base-base64.cpp b/test/base-base64.cpp index eb9c2af01..f9e6aece7 100644 --- a/test/base-base64.cpp +++ b/test/base-base64.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/base64.hpp" #include diff --git a/test/base-convert.cpp b/test/base-convert.cpp index 5f32cc392..17805ea5c 100644 --- a/test/base-convert.cpp +++ b/test/base-convert.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/convert.hpp" #include "base/object.hpp" @@ -33,6 +16,8 @@ BOOST_AUTO_TEST_CASE(tolong) BOOST_CHECK_THROW(Convert::ToLong("7a"), boost::exception); BOOST_CHECK(Convert::ToLong(Value(-7)) == -7); + + BOOST_CHECK(Convert::ToLong(3.141386593) == 3); } BOOST_AUTO_TEST_CASE(todouble) diff --git a/test/base-dictionary.cpp b/test/base-dictionary.cpp index 92146c879..cd074e1a9 100644 --- a/test/base-dictionary.cpp +++ b/test/base-dictionary.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/dictionary.hpp" #include "base/objectlock.hpp" @@ -32,6 +15,28 @@ BOOST_AUTO_TEST_CASE(construct) BOOST_CHECK(dictionary); } +BOOST_AUTO_TEST_CASE(initializer1) +{ + DictionaryData dict; + + dict.emplace_back("test1", "Gin-o-clock"); + + Dictionary::Ptr dictionary = new Dictionary(std::move(dict)); + + Value test1; + test1 = dictionary->Get("test1"); + BOOST_CHECK(test1 == "Gin-o-clock"); +} + +BOOST_AUTO_TEST_CASE(initializer2) +{ + Dictionary::Ptr dictionary = new Dictionary({ {"test1", "Gin-for-the-win"} }); + + Value test1; + test1 = dictionary->Get("test1"); + BOOST_CHECK(test1 == "Gin-for-the-win"); +} + BOOST_AUTO_TEST_CASE(get1) { Dictionary::Ptr dictionary = new Dictionary(); diff --git a/test/base-fifo.cpp b/test/base-fifo.cpp index eb67cdda4..5ecf1ac7d 100644 --- a/test/base-fifo.cpp +++ b/test/base-fifo.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/fifo.hpp" #include "base/objectlock.hpp" diff --git a/test/base-json.cpp b/test/base-json.cpp index e7054c776..271d4f771 100644 --- a/test/base-json.cpp +++ b/test/base-json.cpp @@ -1,31 +1,99 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/dictionary.hpp" +#include "base/namespace.hpp" +#include "base/array.hpp" #include "base/objectlock.hpp" #include "base/json.hpp" +#include #include using namespace icinga; BOOST_AUTO_TEST_SUITE(base_json) +BOOST_AUTO_TEST_CASE(encode) +{ + Dictionary::Ptr input (new Dictionary({ + { "array", new Array({ new Namespace() }) }, + { "false", false }, + { "float", -1.25 }, + { "int", -42 }, + { "null", Value() }, + { "string", "LF\nTAB\tAUml\xC3\xA4Ill\xC3" }, + { "true", true }, + { "uint", 23u } + })); + + String output (R"EOF({ + "array": [ + {} + ], + "false": false, + "float": -1.25, + "int": -42.0, + "null": null, + "string": "LF\nTAB\tAUml\u00e4Ill\ufffd", + "true": true, + "uint": 23.0 +})EOF"); + + BOOST_CHECK(JsonEncode(input, true) == output); + + boost::algorithm::replace_all(output, " ", ""); + boost::algorithm::replace_all(output, "\n", ""); + + BOOST_CHECK(JsonEncode(input, false) == output); +} + +BOOST_AUTO_TEST_CASE(decode) +{ + String input (R"EOF({ + "array": [ + {} + ], + "false": false, + "float": -1.25, + "int": -42.0, + "null": null, + "string": "LF\nTAB\tAUmlIll", + "true": true, + "uint": 23.0 +})EOF"); + + boost::algorithm::replace_all(input, "AUml", "AUml\xC3\xA4"); + boost::algorithm::replace_all(input, "Ill", "Ill\xC3"); + + auto output ((Dictionary::Ptr)JsonDecode(input)); + BOOST_CHECK(output->GetKeys() == std::vector({"array", "false", "float", "int", "null", "string", "true", "uint"})); + + auto array ((Array::Ptr)output->Get("array")); + BOOST_CHECK(array->GetLength() == 1u); + + auto array0 ((Dictionary::Ptr)array->Get(0)); + BOOST_CHECK(array0->GetKeys() == std::vector()); + + auto fAlse (output->Get("false")); + BOOST_CHECK(fAlse.IsBoolean() && !fAlse.ToBool()); + + auto fLoat (output->Get("float")); + BOOST_CHECK(fLoat.IsNumber() && fLoat.Get() == -1.25); + + auto iNt (output->Get("int")); + BOOST_CHECK(iNt.IsNumber() && iNt.Get() == -42.0); + + BOOST_CHECK(output->Get("null").IsEmpty()); + + auto string (output->Get("string")); + BOOST_CHECK(string.IsString() && string.Get() == "LF\nTAB\tAUml\xC3\xA4Ill\xEF\xBF\xBD"); + + auto tRue (output->Get("true")); + BOOST_CHECK(tRue.IsBoolean() && tRue.ToBool()); + + auto uint (output->Get("uint")); + BOOST_CHECK(uint.IsNumber() && uint.Get() == 23.0); +} + BOOST_AUTO_TEST_CASE(invalid1) { BOOST_CHECK_THROW(JsonDecode("\"1.7"), std::exception); diff --git a/test/base-match.cpp b/test/base-match.cpp index 5c3b44dfc..7fad3cba8 100644 --- a/test/base-match.cpp +++ b/test/base-match.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/utility.hpp" #include diff --git a/test/base-netstring.cpp b/test/base-netstring.cpp index 8e477fcc0..faa7eb56f 100644 --- a/test/base-netstring.cpp +++ b/test/base-netstring.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/netstring.hpp" #include "base/fifo.hpp" diff --git a/test/base-object-packer.cpp b/test/base-object-packer.cpp new file mode 100644 index 000000000..b84705d14 --- /dev/null +++ b/test/base-object-packer.cpp @@ -0,0 +1,264 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/object-packer.hpp" +#include "base/value.hpp" +#include "base/string.hpp" +#include "base/array.hpp" +#include "base/dictionary.hpp" +#include +#include +#include +#include +#include + +using namespace icinga; + +#if CHAR_MIN != 0 +union CharU2SConverter +{ + CharU2SConverter() + { + s = 0; + } + + unsigned char u; + signed char s; +}; +#endif + +/** + * Avoid implementation-defined overflows during unsigned to signed casts + */ +static inline char UIntToByte(unsigned i) +{ +#if CHAR_MIN == 0 + return i; +#else + CharU2SConverter converter; + + converter.u = i; + return converter.s; +#endif +} + +#if CHAR_MIN != 0 +union CharS2UConverter +{ + CharS2UConverter() + { + u = 0; + } + + unsigned char u; + signed char s; +}; +#endif + +/** + * Avoid implementation-defined underflows during signed to unsigned casts + */ +static inline unsigned ByteToUInt(char c) +{ +#if CHAR_MIN == 0 + return c; +#else + CharS2UConverter converter; + + converter.s = c; + return converter.u; +#endif +} + +/** + * Compare the expected output with the actual output + */ +static inline bool ComparePackObjectResult(const String& actualOutput, const std::initializer_list& out) +{ + if (actualOutput.GetLength() != out.size()) + return false; + + auto actualOutputPos = actualOutput.Begin(); + for (auto byte : out) { + if (*actualOutputPos != UIntToByte(byte)) + return false; + + ++actualOutputPos; + } + + return true; +} + +/** + * Pack the given input and compare with the expected output + */ +static inline bool AssertPackObjectResult(Value in, std::initializer_list out) +{ + auto actualOutput = PackObject(in); + bool equal = ComparePackObjectResult(actualOutput, out); + + if (!equal) { + std::ostringstream buf; + buf << std::setw(2) << std::setfill('0') << std::setbase(16); + + buf << "--- "; + for (int c : out) { + buf << c; + } + buf << std::endl; + + buf << "+++ "; + for (char c : actualOutput) { + buf << ByteToUInt(c); + } + buf << std::endl; + + BOOST_TEST_MESSAGE(buf.str()); + } + + return equal; +} + +BOOST_AUTO_TEST_SUITE(base_object_packer) + +BOOST_AUTO_TEST_CASE(pack_null) +{ + BOOST_CHECK(AssertPackObjectResult(Empty, {0})); +} + +BOOST_AUTO_TEST_CASE(pack_false) +{ + BOOST_CHECK(AssertPackObjectResult(false, {1})); +} + +BOOST_AUTO_TEST_CASE(pack_true) +{ + BOOST_CHECK(AssertPackObjectResult(true, {2})); +} + +BOOST_AUTO_TEST_CASE(pack_number) +{ + BOOST_CHECK(AssertPackObjectResult(42.125, { + // type + 3, + // IEEE 754 + 64, 69, 16, 0, 0, 0, 0, 0 + })); +} + +BOOST_AUTO_TEST_CASE(pack_string) +{ + BOOST_CHECK(AssertPackObjectResult( + String( + // ASCII (1 to 127) + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + // some keyboard-independent non-ASCII unicode characters + "áéíóú" + ), + { + // type + 4, + // length + 0, 0, 0, 0, 0, 0, 0, 137, + // ASCII + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + // UTF-8 + 195, 161, 195, 169, 195, 173, 195, 179, 195, 186 + } + )); +} + +BOOST_AUTO_TEST_CASE(pack_array) +{ + BOOST_CHECK(AssertPackObjectResult( + (Array::Ptr)new Array({Empty, false, true, 42.125, "foobar"}), + { + // type + 5, + // length + 0, 0, 0, 0, 0, 0, 0, 5, + // Empty + 0, + // false + 1, + // true + 2, + // 42.125 + 3, + 64, 69, 16, 0, 0, 0, 0, 0, + // "foobar" + 4, + 0, 0, 0, 0, 0, 0, 0, 6, + 102, 111, 111, 98, 97, 114 + } + )); +} + +BOOST_AUTO_TEST_CASE(pack_object) +{ + BOOST_CHECK(AssertPackObjectResult( + (Dictionary::Ptr)new Dictionary({ + {"null", Empty}, + {"false", false}, + {"true", true}, + {"42.125", 42.125}, + {"foobar", "foobar"}, + {"[]", (Array::Ptr)new Array()} + }), + { + // type + 6, + // length + 0, 0, 0, 0, 0, 0, 0, 6, + // "42.125" + 0, 0, 0, 0, 0, 0, 0, 6, + 52, 50, 46, 49, 50, 53, + // 42.125 + 3, + 64, 69, 16, 0, 0, 0, 0, 0, + // "[]" + 0, 0, 0, 0, 0, 0, 0, 2, + 91, 93, + // (Array::Ptr)new Array() + 5, + 0, 0, 0, 0, 0, 0, 0, 0, + // "false" + 0, 0, 0, 0, 0, 0, 0, 5, + 102, 97, 108, 115, 101, + // false + 1, + // "foobar" + 0, 0, 0, 0, 0, 0, 0, 6, + 102, 111, 111, 98, 97, 114, + // "foobar" + 4, + 0, 0, 0, 0, 0, 0, 0, 6, + 102, 111, 111, 98, 97, 114, + // "null" + 0, 0, 0, 0, 0, 0, 0, 4, + 110, 117, 108, 108, + // Empty + 0, + // "true" + 0, 0, 0, 0, 0, 0, 0, 4, + 116, 114, 117, 101, + // true + 2 + } + )); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/base-object.cpp b/test/base-object.cpp index e81c5bb38..fb3c2b3d6 100644 --- a/test/base-object.cpp +++ b/test/base-object.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/object.hpp" #include "base/value.hpp" diff --git a/test/base-serialize.cpp b/test/base-serialize.cpp index 7f7d21523..3293f86ab 100644 --- a/test/base-serialize.cpp +++ b/test/base-serialize.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/perfdatavalue.hpp" #include "base/dictionary.hpp" diff --git a/test/base-shellescape.cpp b/test/base-shellescape.cpp index 51754465c..1eb0eaef5 100644 --- a/test/base-shellescape.cpp +++ b/test/base-shellescape.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/utility.hpp" #include diff --git a/test/base-stacktrace.cpp b/test/base-stacktrace.cpp index cad4903c9..349c2364a 100644 --- a/test/base-stacktrace.cpp +++ b/test/base-stacktrace.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/stacktrace.hpp" #include diff --git a/test/base-stream.cpp b/test/base-stream.cpp index 79d70a850..34a93a2bf 100644 --- a/test/base-stream.cpp +++ b/test/base-stream.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/stdiostream.hpp" #include "base/string.hpp" diff --git a/test/base-string.cpp b/test/base-string.cpp index 5d790bbcb..835b1a643 100644 --- a/test/base-string.cpp +++ b/test/base-string.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/string.hpp" #include diff --git a/test/base-timer.cpp b/test/base-timer.cpp index 2cbd519b2..d3a4a0040 100644 --- a/test/base-timer.cpp +++ b/test/base-timer.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/timer.hpp" #include "base/utility.hpp" @@ -39,16 +22,17 @@ BOOST_AUTO_TEST_CASE(interval) BOOST_CHECK(timer->GetInterval() == 1.5); } -static void Callback(int *counter) +int counter = 0; + +static void Callback(const Timer * const&) { - (*counter)++; + counter++; } BOOST_AUTO_TEST_CASE(invoke) { - int counter; Timer::Ptr timer = new Timer(); - timer->OnTimerExpired.connect(std::bind(&Callback, &counter)); + timer->OnTimerExpired.connect(&Callback); timer->SetInterval(1); counter = 0; @@ -61,9 +45,8 @@ BOOST_AUTO_TEST_CASE(invoke) BOOST_AUTO_TEST_CASE(scope) { - int counter; Timer::Ptr timer = new Timer(); - timer->OnTimerExpired.connect(std::bind(&Callback, &counter)); + timer->OnTimerExpired.connect(&Callback); timer->SetInterval(1); counter = 0; diff --git a/test/base-type.cpp b/test/base-type.cpp index 9013f9f7c..21bcf439d 100644 --- a/test/base-type.cpp +++ b/test/base-type.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/perfdatavalue.hpp" #include "base/dictionary.hpp" diff --git a/test/base-utility.cpp b/test/base-utility.cpp new file mode 100644 index 000000000..e25179229 --- /dev/null +++ b/test/base-utility.cpp @@ -0,0 +1,78 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/utility.hpp" +#include +#include + +using namespace icinga; + +BOOST_AUTO_TEST_SUITE(base_utility) + +BOOST_AUTO_TEST_CASE(parse_version) +{ + BOOST_CHECK(Utility::ParseVersion("2.11.0-0.rc1.1") == "2.11.0"); + BOOST_CHECK(Utility::ParseVersion("v2.10.5") == "2.10.5"); + BOOST_CHECK(Utility::ParseVersion("r2.11.1") == "2.11.1"); + BOOST_CHECK(Utility::ParseVersion("v2.11.0-rc1-58-g7c1f716da") == "2.11.0"); + + BOOST_CHECK(Utility::ParseVersion("v2.11butactually3.0") == "v2.11butactually3.0"); +} + +BOOST_AUTO_TEST_CASE(compare_version) +{ + BOOST_CHECK(Utility::CompareVersion("2.10.5", Utility::ParseVersion("v2.10.4")) < 0); + BOOST_CHECK(Utility::CompareVersion("2.11.0", Utility::ParseVersion("2.11.0-0")) == 0); + BOOST_CHECK(Utility::CompareVersion("2.10.5", Utility::ParseVersion("2.11.0-0.rc1.1")) > 0); +} + +BOOST_AUTO_TEST_CASE(comparepasswords_works) +{ + BOOST_CHECK(Utility::ComparePasswords("", "")); + + BOOST_CHECK(!Utility::ComparePasswords("x", "")); + BOOST_CHECK(!Utility::ComparePasswords("", "x")); + + BOOST_CHECK(Utility::ComparePasswords("x", "x")); + BOOST_CHECK(!Utility::ComparePasswords("x", "y")); + + BOOST_CHECK(Utility::ComparePasswords("abcd", "abcd")); + BOOST_CHECK(!Utility::ComparePasswords("abc", "abcd")); + BOOST_CHECK(!Utility::ComparePasswords("abcde", "abcd")); +} + +BOOST_AUTO_TEST_CASE(comparepasswords_issafe) +{ + using std::chrono::duration_cast; + using std::chrono::microseconds; + using std::chrono::steady_clock; + + String a, b; + + a.Append(200000001, 'a'); + b.Append(200000002, 'b'); + + auto start1 (steady_clock::now()); + + Utility::ComparePasswords(a, a); + + auto duration1 (steady_clock::now() - start1); + + auto start2 (steady_clock::now()); + + Utility::ComparePasswords(a, b); + + auto duration2 (steady_clock::now() - start2); + + double diff = (double)duration_cast(duration1).count() / (double)duration_cast(duration2).count(); + BOOST_WARN(0.9 <= diff && diff <= 1.1); +} + +BOOST_AUTO_TEST_CASE(validateutf8) +{ + BOOST_CHECK(Utility::ValidateUTF8("") == ""); + BOOST_CHECK(Utility::ValidateUTF8("a") == "a"); + BOOST_CHECK(Utility::ValidateUTF8("\xC3") == "\xEF\xBF\xBD"); + BOOST_CHECK(Utility::ValidateUTF8("\xC3\xA4") == "\xC3\xA4"); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/base-value.cpp b/test/base-value.cpp index f47f37d84..950290e12 100644 --- a/test/base-value.cpp +++ b/test/base-value.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/value.hpp" #include diff --git a/test/config-ops.cpp b/test/config-ops.cpp index c47ab0305..dfbef2530 100644 --- a/test/config-ops.cpp +++ b/test/config-ops.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/configcompiler.hpp" #include "base/exception.hpp" diff --git a/test/icinga-checkable-fixture.cpp b/test/icinga-checkable-fixture.cpp index 3743fd382..67fab1be2 100644 --- a/test/icinga-checkable-fixture.cpp +++ b/test/icinga-checkable-fixture.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "cli/daemonutility.hpp" #include "base/application.hpp" diff --git a/test/icinga-checkable-flapping.cpp b/test/icinga-checkable-flapping.cpp index 256c7b517..bc3056425 100644 --- a/test/icinga-checkable-flapping.cpp +++ b/test/icinga-checkable-flapping.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/host.hpp" #include diff --git a/test/icinga-checkresult.cpp b/test/icinga-checkresult.cpp index fb024ce31..7db7fffb3 100644 --- a/test/icinga-checkresult.cpp +++ b/test/icinga-checkresult.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/host.hpp" #include diff --git a/test/icinga-legacytimeperiod.cpp b/test/icinga-legacytimeperiod.cpp index f178934c9..c7eb51b06 100644 --- a/test/icinga-legacytimeperiod.cpp +++ b/test/icinga-legacytimeperiod.cpp @@ -1,23 +1,12 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ +#include "base/utility.hpp" #include "icinga/legacytimeperiod.hpp" +#include +#include +#include +#include +#include #include using namespace icinga; @@ -60,47 +49,230 @@ BOOST_GLOBAL_FIXTURE(GlobalTimezoneFixture); BOOST_AUTO_TEST_CASE(simple) { - tm beg, end, ref; + tm tm_beg, tm_end, tm_ref; + String timestamp; + boost::posix_time::ptime begin; + boost::posix_time::ptime end; + boost::posix_time::ptime expectedBegin; + boost::posix_time::ptime expectedEnd; + //----------------------------------------------------- // check parsing of "YYYY-MM-DD" specs - LegacyTimePeriod::ParseTimeSpec("2016-01-01", &beg, &end, &ref); - BOOST_CHECK_EQUAL(mktime(&beg), (time_t) 1451606400); - BOOST_CHECK_EQUAL(mktime(&end), (time_t) 1451692800); + timestamp = "2016-01-01"; - LegacyTimePeriod::ParseTimeSpec("2015-12-31", &beg, &end, &ref); - BOOST_CHECK_EQUAL(mktime(&beg), (time_t) 1451520000); - BOOST_CHECK_EQUAL(mktime(&end), (time_t) 1451606400); + expectedBegin = boost::posix_time::ptime(boost::gregorian::date(2016, 1, 1), boost::posix_time::time_duration(0, 0, 0)); - BOOST_CHECK_THROW(LegacyTimePeriod::ParseTimeSpec("2015-12-32", &beg, &end, &ref), + expectedEnd = boost::posix_time::ptime(boost::gregorian::date(2016, 1, 2), boost::posix_time::time_duration(0, 0, 0)); + + // Run test + LegacyTimePeriod::ParseTimeSpec(timestamp, &tm_beg, &tm_end, &tm_ref); + + // Compare times + begin = boost::posix_time::ptime_from_tm(tm_beg); + end = boost::posix_time::ptime_from_tm(tm_end); + + BOOST_CHECK_EQUAL(begin, expectedBegin); + BOOST_CHECK_EQUAL(end, expectedEnd); + + //----------------------------------------------------- + timestamp = "2015-12-31"; + + expectedBegin = boost::posix_time::ptime(boost::gregorian::date(2015, 12, 31), boost::posix_time::time_duration(0, 0, 0)); + + expectedEnd = boost::posix_time::ptime(boost::gregorian::date(2016, 1, 1), boost::posix_time::time_duration(0, 0, 0)); + + // Run test + LegacyTimePeriod::ParseTimeSpec(timestamp, &tm_beg, &tm_end, &tm_ref); + + // Compare times + begin = boost::posix_time::ptime_from_tm(tm_beg); + end = boost::posix_time::ptime_from_tm(tm_end); + + BOOST_CHECK_EQUAL(begin, expectedBegin); + BOOST_CHECK_EQUAL(end, expectedEnd); + + //----------------------------------------------------- + // Break things forcefully + BOOST_CHECK_THROW(LegacyTimePeriod::ParseTimeSpec("2015-12-32", &tm_beg, &tm_end, &tm_ref), std::invalid_argument); - BOOST_CHECK_THROW(LegacyTimePeriod::ParseTimeSpec("2015-28-01", &beg, &end, &ref), + BOOST_CHECK_THROW(LegacyTimePeriod::ParseTimeSpec("2015-28-01", &tm_beg, &tm_end, &tm_ref), std::invalid_argument); + //----------------------------------------------------- // check parsing of "day X" and "day -X" specs - ref.tm_year = 2016 - 1900; - ref.tm_mon = 1; - LegacyTimePeriod::ParseTimeSpec("day 2", &beg, &end, &ref); - BOOST_CHECK_EQUAL(mktime(&beg), (time_t) 1454371200); // 2016-02-02 - BOOST_CHECK_EQUAL(mktime(&end), (time_t) 1454457600); // 2016-02-03 + timestamp = "day 2"; + tm_ref.tm_year = 2016 - 1900; + tm_ref.tm_mon = 2 - 1; - ref.tm_year = 2018 - 1900; - ref.tm_mon = 11; - LegacyTimePeriod::ParseTimeSpec("day 31", &beg, &end, &ref); - BOOST_CHECK_EQUAL(mktime(&beg), (time_t) 1546214400); // 2018-12-31 - BOOST_CHECK_EQUAL(mktime(&end), (time_t) 1546300800); // 2019-01-01 + expectedBegin = boost::posix_time::ptime(boost::gregorian::date(2016, 2, 2), boost::posix_time::time_duration(0, 0, 0)); - ref.tm_year = 2012 - 1900; - ref.tm_mon = 6; - LegacyTimePeriod::ParseTimeSpec("day -1", &beg, &end, &ref); - BOOST_CHECK_EQUAL(mktime(&beg), (time_t) 1343692800); // 2012-07-31 - BOOST_CHECK_EQUAL(mktime(&end), (time_t) 1343779200); // 2012-08-01 + expectedEnd = boost::posix_time::ptime(boost::gregorian::date(2016, 2, 3), boost::posix_time::time_duration(0, 0, 0)); - ref.tm_year = 2016 - 1900; // leap year - ref.tm_mon = 1; - LegacyTimePeriod::ParseTimeSpec("day -1", &beg, &end, &ref); - BOOST_CHECK_EQUAL(mktime(&beg), (time_t) 1456704000); // 2016-02-29 - BOOST_CHECK_EQUAL(mktime(&end), (time_t) 1456790400); // 2016-03-01 + // Run Tests + LegacyTimePeriod::ParseTimeSpec(timestamp, &tm_beg, &tm_end, &tm_ref); + + // Compare times + begin = boost::posix_time::ptime_from_tm(tm_beg); + end = boost::posix_time::ptime_from_tm(tm_end); + + BOOST_CHECK_EQUAL(begin, expectedBegin); + BOOST_CHECK_EQUAL(end, expectedEnd); + + //----------------------------------------------------- + timestamp = "day 31"; + tm_ref.tm_year = 2018 - 1900; + tm_ref.tm_mon = 12 - 1; + + expectedBegin = boost::posix_time::ptime(boost::gregorian::date(2018, 12, 31), boost::posix_time::time_duration(0, 0, 0)); + + expectedEnd = boost::posix_time::ptime(boost::gregorian::date(2019, 1, 1), boost::posix_time::time_duration(0, 0, 0)); + + // Run Tests + LegacyTimePeriod::ParseTimeSpec(timestamp, &tm_beg, &tm_end, &tm_ref); + + // Compare times + begin = boost::posix_time::ptime_from_tm(tm_beg); + end = boost::posix_time::ptime_from_tm(tm_end); + + BOOST_CHECK_EQUAL(begin, expectedBegin); + BOOST_CHECK_EQUAL(end, expectedEnd); + + //----------------------------------------------------- + // Last day of the month + timestamp = "day -1"; + tm_ref.tm_year = 2012 - 1900; + tm_ref.tm_mon = 7 - 1; + + expectedBegin = boost::posix_time::ptime(boost::gregorian::date(2012, 7, 31), boost::posix_time::time_duration(0, 0, 0)); + + expectedEnd = boost::posix_time::ptime(boost::gregorian::date(2012, 8, 1), boost::posix_time::time_duration(0, 0, 0)); + + // Run Tests + LegacyTimePeriod::ParseTimeSpec(timestamp, &tm_beg, &tm_end, &tm_ref); + + // Compare times + begin = boost::posix_time::ptime_from_tm(tm_beg); + end = boost::posix_time::ptime_from_tm(tm_end); + + BOOST_CHECK_EQUAL(begin, expectedBegin); + BOOST_CHECK_EQUAL(end, expectedEnd); + + //----------------------------------------------------- + // Third last day of the month + timestamp = "day -3"; + tm_ref.tm_year = 2019 - 1900; + tm_ref.tm_mon = 7 - 1; + + expectedBegin = boost::posix_time::ptime(boost::gregorian::date(2019, 7, 29), boost::posix_time::time_duration(0, 0, 0)); + + expectedEnd = boost::posix_time::ptime(boost::gregorian::date(2019, 7, 30), boost::posix_time::time_duration(0, 0, 0)); + + // Run Tests + LegacyTimePeriod::ParseTimeSpec(timestamp, &tm_beg, &tm_end, &tm_ref); + + // Compare times + begin = boost::posix_time::ptime_from_tm(tm_beg); + end = boost::posix_time::ptime_from_tm(tm_end); + + BOOST_CHECK_EQUAL(begin, expectedBegin); + BOOST_CHECK_EQUAL(end, expectedEnd); + + //----------------------------------------------------- + // Leap year with the last day of the month + timestamp = "day -1"; + tm_ref.tm_year = 2016 - 1900; // leap year + tm_ref.tm_mon = 2 - 1; + + expectedBegin = boost::posix_time::ptime(boost::gregorian::date(2016, 2, 29), boost::posix_time::time_duration(0, 0, 0)); + + expectedEnd = boost::posix_time::ptime(boost::gregorian::date(2016, 3, 1), boost::posix_time::time_duration(0, 0, 0)); + + // Run Tests + LegacyTimePeriod::ParseTimeSpec("day -1", &tm_beg, &tm_end, &tm_ref); + + // Compare times + begin = boost::posix_time::ptime_from_tm(tm_beg); + end = boost::posix_time::ptime_from_tm(tm_end); + + BOOST_CHECK_EQUAL(begin, expectedBegin); + BOOST_CHECK_EQUAL(end, expectedEnd); +} + +BOOST_AUTO_TEST_CASE(advanced) +{ + tm tm_beg, tm_end, tm_ref; + String timestamp; + boost::posix_time::ptime begin; + boost::posix_time::ptime end; + boost::posix_time::ptime expectedBegin; + boost::posix_time::ptime expectedEnd; + + //----------------------------------------------------- + // 2019-05-06 where Icinga celebrates 10 years #monitoringlove + // 2019-05-06 22:00:00 - 2019-05-07 06:00:00 + timestamp = "22:00-06:00"; + tm_ref.tm_year = 2019 - 1900; + tm_ref.tm_mon = 5 - 1; + tm_ref.tm_mday = 6; + + expectedBegin = boost::posix_time::ptime(boost::gregorian::date(2019, 5, 6), boost::posix_time::time_duration(22, 0, 0)); + + expectedEnd = boost::posix_time::ptime(boost::gregorian::date(2019, 5, 7), boost::posix_time::time_duration(6, 0, 0)); + + // Run test + LegacyTimePeriod::ProcessTimeRangeRaw(timestamp, &tm_ref, &tm_beg, &tm_end); + + // Compare times + begin = boost::posix_time::ptime_from_tm(tm_beg); + end = boost::posix_time::ptime_from_tm(tm_end); + + BOOST_CHECK_EQUAL(begin, expectedBegin); + BOOST_CHECK_EQUAL(end, expectedEnd); + + //----------------------------------------------------- + // 2019-05-06 Icinga is unleashed. + // 09:00:00 - 17:00:00 + timestamp = "09:00-17:00"; + tm_ref.tm_year = 2009 - 1900; + tm_ref.tm_mon = 5 - 1; + tm_ref.tm_mday = 6; + + expectedBegin = boost::posix_time::ptime(boost::gregorian::date(2009, 5, 6), boost::posix_time::time_duration(9, 0, 0)); + + expectedEnd = boost::posix_time::ptime(boost::gregorian::date(2009, 5, 6), boost::posix_time::time_duration(17, 0, 0)); + + // Run test + LegacyTimePeriod::ProcessTimeRangeRaw(timestamp, &tm_ref, &tm_beg, &tm_end); + + // Compare times + begin = boost::posix_time::ptime_from_tm(tm_beg); + end = boost::posix_time::ptime_from_tm(tm_end); + + BOOST_CHECK_EQUAL(begin, expectedBegin); + BOOST_CHECK_EQUAL(end, expectedEnd); + + //----------------------------------------------------- + // At our first Icinga Camp in SFO 2014 at GitHub HQ, we partied all night long with an overflow. + // 2014-09-24 09:00:00 - 2014-09-25 06:00:00 + timestamp = "09:00-30:00"; + tm_ref.tm_year = 2014 - 1900; + tm_ref.tm_mon = 9 - 1; + tm_ref.tm_mday = 24; + + expectedBegin = boost::posix_time::ptime(boost::gregorian::date(2014, 9, 24), boost::posix_time::time_duration(9, 0, 0)); + + expectedEnd = boost::posix_time::ptime(boost::gregorian::date(2014, 9, 25), boost::posix_time::time_duration(6, 0, 0)); + + // Run test + LegacyTimePeriod::ProcessTimeRangeRaw(timestamp, &tm_ref, &tm_beg, &tm_end); + + // Compare times + begin = boost::posix_time::ptime_from_tm(tm_beg); + end = boost::posix_time::ptime_from_tm(tm_end); + + BOOST_CHECK_EQUAL(begin, expectedBegin); + BOOST_CHECK_EQUAL(end, expectedEnd); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/icinga-macros.cpp b/test/icinga-macros.cpp index 94f067600..e7c789c40 100644 --- a/test/icinga-macros.cpp +++ b/test/icinga-macros.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/macroprocessor.hpp" #include diff --git a/test/icinga-notification.cpp b/test/icinga-notification.cpp index 09373a952..5cf3f49e8 100644 --- a/test/icinga-notification.cpp +++ b/test/icinga-notification.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/notification.hpp" #include @@ -25,6 +8,21 @@ using namespace icinga; BOOST_AUTO_TEST_SUITE(icinga_notification) +BOOST_AUTO_TEST_CASE(strings) +{ + // States + BOOST_CHECK("OK" == Notification::NotificationServiceStateToString(ServiceOK)); + BOOST_CHECK("Critical" == Notification::NotificationServiceStateToString(ServiceCritical)); + BOOST_CHECK("Up" == Notification::NotificationHostStateToString(HostUp)); + + // Types + BOOST_CHECK("DowntimeStart" == Notification::NotificationTypeToString(NotificationDowntimeStart)); + BOOST_CHECK("Problem" == Notification::NotificationTypeToString(NotificationProblem)); + + // Compat + BOOST_CHECK("DOWNTIMECANCELLED" == Notification::NotificationTypeToStringCompat(NotificationDowntimeRemoved)); +} + BOOST_AUTO_TEST_CASE(state_filter) { unsigned long fstate; diff --git a/test/icinga-perfdata.cpp b/test/icinga-perfdata.cpp index bdd5ca281..b0fa1ee98 100644 --- a/test/icinga-perfdata.cpp +++ b/test/icinga-perfdata.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/perfdatavalue.hpp" #include "icinga/pluginutility.hpp" diff --git a/test/icingaapplication-fixture.cpp b/test/icingaapplication-fixture.cpp index bb3b1c710..80fa4bfd8 100644 --- a/test/icingaapplication-fixture.cpp +++ b/test/icingaapplication-fixture.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** -* Icinga 2 * -* Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * -* * -* 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. * -******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icingaapplication-fixture.hpp" diff --git a/test/icingaapplication-fixture.hpp b/test/icingaapplication-fixture.hpp index d79836b8b..23f4c9c81 100644 --- a/test/icingaapplication-fixture.hpp +++ b/test/icingaapplication-fixture.hpp @@ -1,21 +1,4 @@ -/****************************************************************************** -* Icinga 2 * -* Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * -* * -* 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. * -******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef ICINGAAPPLICATION_FIXTURE_H #define ICINGAAPPLICATION_FIXTURE_H diff --git a/test/livestatus-fixture.cpp b/test/livestatus-fixture.cpp index c016ffe95..aaa0e073a 100644 --- a/test/livestatus-fixture.cpp +++ b/test/livestatus-fixture.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "config/configcompiler.hpp" #include "config/configitem.hpp" diff --git a/test/livestatus.cpp b/test/livestatus.cpp index bb74ffc30..6aafa3be3 100644 --- a/test/livestatus.cpp +++ b/test/livestatus.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "livestatus/livestatusquery.hpp" #include "base/application.hpp" diff --git a/test/remote-url.cpp b/test/remote-url.cpp index 60df7c130..36b79894e 100644 --- a/test/remote-url.cpp +++ b/test/remote-url.cpp @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/array.hpp" #include "remote/url.hpp" @@ -53,31 +36,50 @@ BOOST_AUTO_TEST_CASE(get_and_set) BOOST_CHECK(url->Format(false, true) == "ftp://Horst:Seehofer@koenigreich.bayern:1918/path/to/m%C3%BCnchen"); - std::map > m; - std::vector v1 { "hip", "hip", "hurra" }; - std::vector v2 { "äü^ä+#ül-" }; - std::vector v3 { "1", "2" }; - m.insert(std::make_pair("shout", v1)); - m.insert(std::make_pair("sonderzeichen", v2)); - url->SetQuery(m); - url->SetQueryElements("count", v3); + url->SetQuery({ + {"shout", "hip"}, + {"shout", "hip"}, + {"shout", "hurra"}, + {"sonderzeichen", "äü^ä+#ül-"} + }); url->AddQueryElement("count", "3"); - std::map > mn = url->GetQuery(); - BOOST_CHECK(mn["shout"][0] == v1[0]); - BOOST_CHECK(mn["sonderzeichen"][0] == v2[0]); - BOOST_CHECK(mn["count"][2] == "3"); + auto mn (url->GetQuery()); + + BOOST_CHECK(mn.size() == 5); + + BOOST_CHECK(mn[0].first == "shout"); + BOOST_CHECK(mn[0].second == "hip"); + + BOOST_CHECK(mn[1].first == "shout"); + BOOST_CHECK(mn[1].second == "hip"); + + BOOST_CHECK(mn[2].first == "shout"); + BOOST_CHECK(mn[2].second == "hurra"); + + BOOST_CHECK(mn[3].first == "sonderzeichen"); + BOOST_CHECK(mn[3].second == "äü^ä+#ül-"); + + BOOST_CHECK(mn[4].first == "count"); + BOOST_CHECK(mn[4].second == "3"); } BOOST_AUTO_TEST_CASE(parameters) { - Url::Ptr url = new Url("https://icinga.com/hya/?rain=karl&rair=robert&foo[]=bar"); + Url::Ptr url = new Url("https://icinga.com/hya/?rair=robert&rain=karl&foo[]=bar"); - BOOST_CHECK(url->GetQueryElement("rair") == "robert"); - BOOST_CHECK(url->GetQueryElement("rain") == "karl"); - std::vector test = url->GetQueryElements("foo"); - BOOST_CHECK(test.size() == 1); - BOOST_CHECK(test[0] == "bar"); + auto query (url->GetQuery()); + + BOOST_CHECK(query.size() == 3); + + BOOST_CHECK(query[0].first == "rair"); + BOOST_CHECK(query[0].second == "robert"); + + BOOST_CHECK(query[1].first == "rain"); + BOOST_CHECK(query[1].second == "karl"); + + BOOST_CHECK(query[2].first == "foo"); + BOOST_CHECK(query[2].second == "bar"); } BOOST_AUTO_TEST_CASE(format) diff --git a/test/remote-user.cpp b/test/remote-user.cpp deleted file mode 100644 index f43816a19..000000000 --- a/test/remote-user.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * - * as published by the Free Software Foundation; either version 2 * - * of the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software Foundation * - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ******************************************************************************/ - -#include "remote/apiuser.hpp" -#include "base/tlsutility.hpp" -#include - -#include - -using namespace icinga; - -BOOST_AUTO_TEST_SUITE(api_user) - -BOOST_AUTO_TEST_CASE(password) -{ - ApiUser::Ptr user = new ApiUser(); - String passwd = RandomString(16); - String salt = RandomString(8); - user->SetPasswordHash(CreateHashedPasswordString(passwd, salt)); - user->OnConfigLoaded(); - user->OnAllConfigLoaded(); - user->Start(); - - BOOST_CHECK(user->GetPasswordHash() != passwd); - - Dictionary::Ptr passwdd = user->GetPasswordDict(); - - BOOST_CHECK(passwdd); - BOOST_CHECK(passwdd->Get("salt") == salt); - BOOST_CHECK(ComparePassword(passwdd->Get("password"), passwd, salt)); - BOOST_CHECK(!ComparePassword(passwdd->Get("password"), "wrong password uwu!", salt)); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/test-runner.cpp b/test/test-runner.cpp index 4ed6aa30a..fac41ea70 100644 --- a/test/test-runner.cpp +++ b/test/test-runner.cpp @@ -1,22 +1,21 @@ -/****************************************************************************** -* Icinga 2 * -* Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * -* * -* 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. * -******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ -#define BOOST_TEST_MAIN +#define BOOST_TEST_MODULE icinga2 +#define BOOST_TEST_NO_MAIN +#define BOOST_TEST_ALTERNATIVE_INIT_API #include +#include +#include + +int BOOST_TEST_CALL_DECL +main(int argc, char **argv) +{ + std::_Exit(boost::unit_test::unit_test_main(init_unit_test, argc, argv)); + return EXIT_FAILURE; +} + +#ifdef _WIN32 +#include +#include +#endif /* _WIN32 */ diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 7ccccb5f9..fea750f1d 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -1,26 +1,7 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ add_subdirectory(mmatch) -if(NOT YAJL_FOUND) - add_subdirectory(yajl) -endif() - if(UNIX OR CYGWIN) add_subdirectory(execvpe) endif() diff --git a/third-party/cmake/FindMySQL.cmake b/third-party/cmake/FindMySQL.cmake index 201a98259..d3a6e7f2a 100644 --- a/third-party/cmake/FindMySQL.cmake +++ b/third-party/cmake/FindMySQL.cmake @@ -36,15 +36,21 @@ FIND_PATH(MYSQL_INCLUDE_DIR mysql.h $ENV{MYSQL_DIR}/include /usr/include/mysql /usr/local/include/mysql + /usr/include/mariadb + /usr/local/include/mariadb /opt/mysql/mysql/include /opt/mysql/mysql/include/mysql /opt/mysql/include + /opt/mariadb/include/mysql /opt/local/include/mysql5 /usr/local/mysql/include /usr/local/mysql/include/mysql ${_macports_include_dirs} $ENV{ProgramFiles}/MySQL/*/include - $ENV{SystemDrive}/MySQL/*/include) + $ENV{SystemDrive}/MySQL/*/include + $ENV{ProgramFiles}/MariaDB*/include/mysql + $ENV{SystemDrive}/MariaDB*/include/mysql +) UNSET(_macports_include_dirs) diff --git a/third-party/execvpe/CMakeLists.txt b/third-party/execvpe/CMakeLists.txt index 4be31bc1b..4dda40a93 100644 --- a/third-party/execvpe/CMakeLists.txt +++ b/third-party/execvpe/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ set(execvpe_SOURCES execvpe.c execvpe.h diff --git a/third-party/execvpe/execvpe.h b/third-party/execvpe/execvpe.h index c2230cad7..5e32d66b6 100644 --- a/third-party/execvpe/execvpe.h +++ b/third-party/execvpe/execvpe.h @@ -1,21 +1,4 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) * - * * - * 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. * - ******************************************************************************/ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #ifndef EXECVPE_H #define EXECVPE_H diff --git a/third-party/mmatch/CMakeLists.txt b/third-party/mmatch/CMakeLists.txt index 70bb6f1c2..f48e0739f 100644 --- a/third-party/mmatch/CMakeLists.txt +++ b/third-party/mmatch/CMakeLists.txt @@ -1,19 +1,4 @@ -# Icinga 2 -# Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) -# -# 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. +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ set(mmatch_SOURCES mmatch.c mmatch.h diff --git a/third-party/nlohmann_json/LICENSE b/third-party/nlohmann_json/LICENSE new file mode 100644 index 000000000..8b0f7002e --- /dev/null +++ b/third-party/nlohmann_json/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013-2018 Niels Lohmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third-party/nlohmann_json/json.hpp b/third-party/nlohmann_json/json.hpp new file mode 100644 index 000000000..c9af0bed3 --- /dev/null +++ b/third-party/nlohmann_json/json.hpp @@ -0,0 +1,20406 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.5.0 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2018 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef NLOHMANN_JSON_HPP +#define NLOHMANN_JSON_HPP + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 5 +#define NLOHMANN_JSON_VERSION_PATCH 0 + +#include // all_of, find, for_each +#include // assert +#include // and, not, or +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#include // istream, ostream +#include // random_access_iterator_tag +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap + +// #include +#ifndef NLOHMANN_JSON_FWD_HPP +#define NLOHMANN_JSON_FWD_HPP + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer> +class basic_json; + +/*! +@brief JSON Pointer + +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. + +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + +@since version 2.0.0 +*/ +template +class json_pointer; + +/*! +@brief default JSON class + +This type is the default specialization of the @ref basic_json class which +uses the standard template types. + +@since version 1.0.0 +*/ +using json = basic_json<>; +} // namespace nlohmann + +#endif + +// #include + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define JSON_DEPRECATED __declspec(deprecated) +#else + #define JSON_DEPRECATED +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// manual branch prediction +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_LIKELY(x) __builtin_expect(!!(x), 1) + #define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else + #define JSON_LIKELY(x) x + #define JSON_UNLIKELY(x) x +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// #include + + +#include // not +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type + +namespace nlohmann +{ +namespace detail +{ +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using uncvref_t = typename std::remove_cv::type>::type; + +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +template +struct merge_and_renumber; + +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; + +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; + +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; + +template +using index_sequence_for = make_index_sequence; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // not +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval + +// #include + +// #include + + +#include // random_access_iterator_tag + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} +} + +// #include + +// #include + + +#include + +// #include + + +// http://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + void operator=(nonesuch const&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template