mirror of
https://github.com/Icinga/icinga2.git
synced 2025-07-26 23:24:09 +02:00
Merge branch 'master' into feature/v1-actions-execute-command-8034
This commit is contained in:
commit
bb851b0558
21
.github/workflows/docker.yml
vendored
Normal file
21
.github/workflows/docker.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
name: Docker image
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request: {}
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- published
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Docker image
|
||||||
|
uses: Icinga/docker-icinga2@master
|
||||||
|
env:
|
||||||
|
INPUT_TOKEN: '${{ github.token }}'
|
||||||
|
DOCKER_HUB_PASSWORD: '${{ secrets.DOCKER_HUB_PERSONAL_TOKEN }}'
|
92
.github/workflows/packages.yml
vendored
92
.github/workflows/packages.yml
vendored
@ -11,6 +11,7 @@ jobs:
|
|||||||
name: .deb
|
name: .deb
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
distro:
|
distro:
|
||||||
- name: debian
|
- name: debian
|
||||||
@ -19,9 +20,6 @@ jobs:
|
|||||||
- name: debian
|
- name: debian
|
||||||
codename: stretch
|
codename: stretch
|
||||||
has32bit: true
|
has32bit: true
|
||||||
- name: debian
|
|
||||||
codename: jessie
|
|
||||||
has32bit: true
|
|
||||||
- name: ubuntu
|
- name: ubuntu
|
||||||
codename: focal
|
codename: focal
|
||||||
has32bit: false
|
has32bit: false
|
||||||
@ -63,7 +61,8 @@ jobs:
|
|||||||
uses: actions/cache@v1
|
uses: actions/cache@v1
|
||||||
with:
|
with:
|
||||||
path: deb-icinga2/ccache
|
path: deb-icinga2/ccache
|
||||||
key: '${{ matrix.distro.name }}/${{ matrix.distro.codename }}-ccache'
|
key: |-
|
||||||
|
${{ matrix.distro.name }}/${{ matrix.distro.codename }}-ccache-${{ hashFiles('deb-icinga2/ccache') }}
|
||||||
|
|
||||||
- name: Binary x64
|
- name: Binary x64
|
||||||
run: |
|
run: |
|
||||||
@ -113,16 +112,11 @@ jobs:
|
|||||||
-e ICINGA_BUILD_TYPE=snapshot \
|
-e ICINGA_BUILD_TYPE=snapshot \
|
||||||
registry.icinga.com/build-docker/${{ matrix.distro.name }}/${{ matrix.distro.codename }}:x86 \
|
registry.icinga.com/build-docker/${{ matrix.distro.name }}/${{ matrix.distro.codename }}:x86 \
|
||||||
icinga-build-test
|
icinga-build-test
|
||||||
|
|
||||||
- name: Artifacts
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: '${{ matrix.distro.name }}-${{ matrix.distro.codename }}-packages'
|
|
||||||
path: deb-icinga2/build
|
|
||||||
rpm:
|
rpm:
|
||||||
name: .rpm
|
name: .rpm
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
distro:
|
distro:
|
||||||
- name: centos
|
- name: centos
|
||||||
@ -197,7 +191,8 @@ jobs:
|
|||||||
uses: actions/cache@v1
|
uses: actions/cache@v1
|
||||||
with:
|
with:
|
||||||
path: rpm-icinga2/ccache
|
path: rpm-icinga2/ccache
|
||||||
key: '${{ matrix.distro.name }}/${{ matrix.distro.release }}-ccache'
|
key: |-
|
||||||
|
${{ matrix.distro.name }}/${{ matrix.distro.release }}-ccache-${{ hashFiles('rpm-icinga2/ccache') }}
|
||||||
|
|
||||||
- name: Binary
|
- name: Binary
|
||||||
if: "steps.vars.outputs.CAN_BUILD == 'true'"
|
if: "steps.vars.outputs.CAN_BUILD == 'true'"
|
||||||
@ -228,10 +223,75 @@ jobs:
|
|||||||
-e ICINGA_BUILD_TYPE=snapshot \
|
-e ICINGA_BUILD_TYPE=snapshot \
|
||||||
registry.icinga.com/build-docker/${{ matrix.distro.name }}/${{ matrix.distro.release }} \
|
registry.icinga.com/build-docker/${{ matrix.distro.name }}/${{ matrix.distro.release }} \
|
||||||
icinga-build-test
|
icinga-build-test
|
||||||
|
raspbian:
|
||||||
|
name: Raspbian
|
||||||
|
|
||||||
- name: Artifacts
|
strategy:
|
||||||
if: "steps.vars.outputs.CAN_BUILD == 'true'"
|
fail-fast: false
|
||||||
uses: actions/upload-artifact@v1
|
matrix:
|
||||||
|
codename:
|
||||||
|
- buster
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout HEAD
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: qemu-user-static
|
||||||
|
run: |
|
||||||
|
set -exo pipefail
|
||||||
|
sudo apt-get update
|
||||||
|
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y qemu-user-static
|
||||||
|
|
||||||
|
- name: raspbian-icinga2
|
||||||
|
run: |
|
||||||
|
set -exo pipefail
|
||||||
|
git clone https://git.icinga.com/packaging/raspbian-icinga2.git
|
||||||
|
chmod o+w raspbian-icinga2
|
||||||
|
|
||||||
|
- name: Restore/backup ccache
|
||||||
|
id: ccache
|
||||||
|
uses: actions/cache@v1
|
||||||
with:
|
with:
|
||||||
name: '${{ matrix.distro.name }}-${{ matrix.distro.release }}-packages'
|
path: raspbian-icinga2/ccache
|
||||||
path: rpm-icinga2/build
|
key: |-
|
||||||
|
raspbian/${{ matrix.codename }}-ccache-${{ hashFiles('raspbian-icinga2/ccache') }}
|
||||||
|
|
||||||
|
- name: Binary
|
||||||
|
run: |
|
||||||
|
set -exo pipefail
|
||||||
|
git checkout -B master
|
||||||
|
if [ -e raspbian-icinga2/ccache ]; then
|
||||||
|
chmod -R o+w raspbian-icinga2/ccache
|
||||||
|
fi
|
||||||
|
docker run --rm \
|
||||||
|
-v "$(pwd)/raspbian-icinga2:/raspbian-icinga2" \
|
||||||
|
-v "$(pwd)/.git:/icinga2.git:ro" \
|
||||||
|
-w /raspbian-icinga2 \
|
||||||
|
-e ICINGA_BUILD_PROJECT=icinga2 \
|
||||||
|
-e ICINGA_BUILD_TYPE=snapshot \
|
||||||
|
-e UPSTREAM_GIT_URL=file:///icinga2.git \
|
||||||
|
-e ICINGA_BUILD_DEB_DEFAULT_ARCH=armhf \
|
||||||
|
registry.icinga.com/build-docker/raspbian/${{ matrix.codename }} \
|
||||||
|
icinga-build-package
|
||||||
|
|
||||||
|
# Setting up icinga2-bin (2.12.0+rc1.25.g5d1c82a3d.20200526.0754+buster-0) ...
|
||||||
|
# enabling default icinga2 features
|
||||||
|
# qemu:handle_cpu_signal received signal outside vCPU context @ pc=0x6015c75c
|
||||||
|
# qemu:handle_cpu_signal received signal outside vCPU context @ pc=0x6015c75c
|
||||||
|
# qemu:handle_cpu_signal received signal outside vCPU context @ pc=0x600016ea
|
||||||
|
# dpkg: error processing package icinga2-bin (--configure):
|
||||||
|
# installed icinga2-bin package post-installation script subprocess returned error exit status 127
|
||||||
|
#
|
||||||
|
# - name: Test
|
||||||
|
# run: |
|
||||||
|
# set -exo pipefail
|
||||||
|
# docker run --rm \
|
||||||
|
# -v "$(pwd)/raspbian-icinga2:/raspbian-icinga2" \
|
||||||
|
# -w /raspbian-icinga2 \
|
||||||
|
# -e ICINGA_BUILD_PROJECT=icinga2 \
|
||||||
|
# -e ICINGA_BUILD_TYPE=snapshot \
|
||||||
|
# -e ICINGA_BUILD_DEB_DEFAULT_ARCH=armhf \
|
||||||
|
# registry.icinga.com/build-docker/raspbian/${{ matrix.codename }} \
|
||||||
|
# icinga-build-test
|
||||||
|
7
.mailmap
7
.mailmap
@ -12,6 +12,7 @@ Alexander A. Klimov <alexander.klimov@icinga.com> <alexander.klimov@icinga.com>
|
|||||||
<jason.young@velaspan.com> <jyoung15@gmail.com>
|
<jason.young@velaspan.com> <jyoung15@gmail.com>
|
||||||
<jo.goossens@hosted-power.com> <sales@hosted-power.com>
|
<jo.goossens@hosted-power.com> <sales@hosted-power.com>
|
||||||
<johannes.meyer@icinga.com> <johannes.meyer@netways.de>
|
<johannes.meyer@icinga.com> <johannes.meyer@netways.de>
|
||||||
|
<julian.brost@icinga.com> <julian@0x4a42.net>
|
||||||
<lars.engels@0x20.net> <lars@0x20.net>
|
<lars.engels@0x20.net> <lars@0x20.net>
|
||||||
<lennart.betz@icinga.com> <lennart.betz@netways.de>
|
<lennart.betz@icinga.com> <lennart.betz@netways.de>
|
||||||
<marius@graylog.com> <marius@torch.sh>
|
<marius@graylog.com> <marius@torch.sh>
|
||||||
@ -24,6 +25,7 @@ Alexander A. Klimov <alexander.klimov@icinga.com> <alexander.klimov@icinga.com>
|
|||||||
<thomas.widhalm@icinga.com> <thomas.widhalm@netways.de>
|
<thomas.widhalm@icinga.com> <thomas.widhalm@netways.de>
|
||||||
<thomas.widhalm@icinga.com> <widhalmt@widhalmt.or.at>
|
<thomas.widhalm@icinga.com> <widhalmt@widhalmt.or.at>
|
||||||
<tobias.vonderkrone@profitbricks.com> <tobias@vonderkrone.info>
|
<tobias.vonderkrone@profitbricks.com> <tobias@vonderkrone.info>
|
||||||
|
Alex <alexp710@hotmail.com> <alexp710@hotmail.com>
|
||||||
Carsten Köbke <carsten.koebke@gmx.de> Carsten Koebke <carsten.koebke@koebbes.de>
|
Carsten Köbke <carsten.koebke@gmx.de> Carsten Koebke <carsten.koebke@koebbes.de>
|
||||||
Claudio Kuenzler <ck@claudiokuenzler.com>
|
Claudio Kuenzler <ck@claudiokuenzler.com>
|
||||||
Diana Flach <diana.flach@icinga.com> <crunsher@bamberg.ccc.de>
|
Diana Flach <diana.flach@icinga.com> <crunsher@bamberg.ccc.de>
|
||||||
@ -32,15 +34,20 @@ Diana Flach <diana.flach@icinga.com> <jean-marcel.flach@netways.de>
|
|||||||
Diana Flach <diana.flach@icinga.com> Jean Flach <jean-marcel.flach@icinga.com>
|
Diana Flach <diana.flach@icinga.com> Jean Flach <jean-marcel.flach@icinga.com>
|
||||||
Dolf Schimmel <dolf@transip.nl> <dolf@dolfschimmel.nl>
|
Dolf Schimmel <dolf@transip.nl> <dolf@dolfschimmel.nl>
|
||||||
Gunnar Beutner <gunnar.beutner@icinga.com> <icinga@net-icinga2.adm.netways.de>
|
Gunnar Beutner <gunnar.beutner@icinga.com> <icinga@net-icinga2.adm.netways.de>
|
||||||
|
Henrik Triem <henrik.triem@icinga.com> <henrik.triem@netways.de>
|
||||||
Henrik Triem <henrik.triem@icinga.com> Henrik Triem <43344334+htriem@users.noreply.github.com>
|
Henrik Triem <henrik.triem@icinga.com> Henrik Triem <43344334+htriem@users.noreply.github.com>
|
||||||
<henrik.triem@icinga.com> <Henrik.Triem@icinga.com>
|
<henrik.triem@icinga.com> <Henrik.Triem@icinga.com>
|
||||||
Jens Schanz <jens.schanz@mueller.de> <mail@jensschanz.de>
|
Jens Schanz <jens.schanz@mueller.de> <mail@jensschanz.de>
|
||||||
Jens Schanz <jens.schanz@mueller.de> Schanz, Jens <jens.schanz@mueller.de>
|
Jens Schanz <jens.schanz@mueller.de> Schanz, Jens <jens.schanz@mueller.de>
|
||||||
|
Kálmán „KAMI” Szalai <kami911@gmail.com> <kami911@gmail.com>
|
||||||
Marianne Spiller <github@spiller.me>
|
Marianne Spiller <github@spiller.me>
|
||||||
Markus Waldmüller <markus.waldmueller@netways.de>
|
Markus Waldmüller <markus.waldmueller@netways.de>
|
||||||
Michael Insel <mcktr55@gmail.com> <mcktr55@gmail.com>
|
Michael Insel <mcktr55@gmail.com> <mcktr55@gmail.com>
|
||||||
Michael Insel <mcktr55@gmail.com> <michael@email.de>
|
Michael Insel <mcktr55@gmail.com> <michael@email.de>
|
||||||
Michael Insel <mcktr55@gmail.com> <michael@insel.email>
|
Michael Insel <mcktr55@gmail.com> <michael@insel.email>
|
||||||
nemtrif <ntrifunovic@hotmail.com> <nemtrif@users.noreply.github.com>
|
nemtrif <ntrifunovic@hotmail.com> <nemtrif@users.noreply.github.com>
|
||||||
|
nemtrif <ntrifunovic@hotmail.com> <ntrifunovic@hotmail.com>
|
||||||
Robin O'Brien <robin@labs.epiuse.com> <robinjohnobrien@gmail.com>
|
Robin O'Brien <robin@labs.epiuse.com> <robinjohnobrien@gmail.com>
|
||||||
|
Roman Gerhardt <roman.gerhardt@cbc-x.com> <roman.gerhardt@cbc-x.com>
|
||||||
|
Sebastian Chrostek <sebastian@chrostek.net> <sebastian@chrostek.net>
|
||||||
Thomas Gelf <thomas.gelf@icinga.com> <thomas@gelf.net>
|
Thomas Gelf <thomas.gelf@icinga.com> <thomas@gelf.net>
|
||||||
|
17
AUTHORS
17
AUTHORS
@ -23,6 +23,7 @@ Arnd Hannemann <arnd@arndnet.de>
|
|||||||
Assaf Flatto <assaf@aikilinux.com>
|
Assaf Flatto <assaf@aikilinux.com>
|
||||||
azthec <azthec@users.noreply.github.com>
|
azthec <azthec@users.noreply.github.com>
|
||||||
BarbUk <julien.virey@gmail.com>
|
BarbUk <julien.virey@gmail.com>
|
||||||
|
Bård Dahlmo-Lerbæk <bard.dahlmo-lerbaek@skatteetaten.no>
|
||||||
Bas Couwenberg <sebastic@xs4all.nl>
|
Bas Couwenberg <sebastic@xs4all.nl>
|
||||||
bascarsija <bascarsija.dev@gmail.com>
|
bascarsija <bascarsija.dev@gmail.com>
|
||||||
Bastian Guse <bguse@nocopy.de>
|
Bastian Guse <bguse@nocopy.de>
|
||||||
@ -36,7 +37,6 @@ Brendan Jurd <direvus@gmail.com>
|
|||||||
Brian De Wolf <git@bldewolf.com>
|
Brian De Wolf <git@bldewolf.com>
|
||||||
Brian Dockter <specus@gmail.com>
|
Brian Dockter <specus@gmail.com>
|
||||||
Bruno Lingner <mail@hugo.ro>
|
Bruno Lingner <mail@hugo.ro>
|
||||||
Bård Dahlmo-Lerbæk <bard.dahlmo-lerbaek@skatteetaten.no>
|
|
||||||
Carlos Cesario <carloscesario@gmail.com>
|
Carlos Cesario <carloscesario@gmail.com>
|
||||||
Carsten Köbke <carsten.koebke@gmx.de>
|
Carsten Köbke <carsten.koebke@gmx.de>
|
||||||
Chris Boot <crb@tiger-computing.co.uk>
|
Chris Boot <crb@tiger-computing.co.uk>
|
||||||
@ -48,7 +48,6 @@ Christian Lehmann <christian_lehmann@gmx.de>
|
|||||||
Christian Loos <cloos@netsandbox.de>
|
Christian Loos <cloos@netsandbox.de>
|
||||||
Christian Schmidt <github@chsc.dk>
|
Christian Schmidt <github@chsc.dk>
|
||||||
Christopher Schirner <schinken@bamberg.ccc.de>
|
Christopher Schirner <schinken@bamberg.ccc.de>
|
||||||
chrostek <sebastian@chrostek.net>
|
|
||||||
Claudio Bilotta <bilottalove@gmail.com>
|
Claudio Bilotta <bilottalove@gmail.com>
|
||||||
Claudio Kuenzler <ck@claudiokuenzler.com>
|
Claudio Kuenzler <ck@claudiokuenzler.com>
|
||||||
Conrad Clement <cclement@printeron.com>
|
Conrad Clement <cclement@printeron.com>
|
||||||
@ -72,6 +71,7 @@ Edgar Fuß <ef@math.uni-bonn.de>
|
|||||||
Eduard Güldner <eduard.gueldner@gmail.com>
|
Eduard Güldner <eduard.gueldner@gmail.com>
|
||||||
Edvin Seferovic <edvin@seferovic.net>
|
Edvin Seferovic <edvin@seferovic.net>
|
||||||
Elias Ohm <eohm@novomind.com>
|
Elias Ohm <eohm@novomind.com>
|
||||||
|
Élie Bouttier <elie@bouttier.eu>
|
||||||
Eric Lippmann <eric.lippmann@icinga.com>
|
Eric Lippmann <eric.lippmann@icinga.com>
|
||||||
Evgeni Golov <evgeni@golov.de>
|
Evgeni Golov <evgeni@golov.de>
|
||||||
Ewoud Kohl van Wijngaarden <ewoud@kohlvanwijngaarden.nl>
|
Ewoud Kohl van Wijngaarden <ewoud@kohlvanwijngaarden.nl>
|
||||||
@ -80,13 +80,11 @@ fbachmann <bachmann.f@gmail.com>
|
|||||||
Federico Cuello <federico.cuello@sociomantic.com>
|
Federico Cuello <federico.cuello@sociomantic.com>
|
||||||
Federico Pires <federico.pires@upsight.com>
|
Federico Pires <federico.pires@upsight.com>
|
||||||
Ferdi Gueran <ferdi.gueran@nextevolution.de>
|
Ferdi Gueran <ferdi.gueran@nextevolution.de>
|
||||||
fluxX04 <alexp710@hotmail.com>
|
|
||||||
Francesco Colista <fcolista@alpinelinux.org>
|
Francesco Colista <fcolista@alpinelinux.org>
|
||||||
Gaël Beaudoin <gaboo@gaboo.org>
|
Gaël Beaudoin <gaboo@gaboo.org>
|
||||||
Georg Faerber <georg@riseup.net>
|
Georg Faerber <georg@riseup.net>
|
||||||
Georg Haas <hax404foogit@hax404.de>
|
Georg Haas <hax404foogit@hax404.de>
|
||||||
Gerd von Egidy <gerd@egidy.de>
|
Gerd von Egidy <gerd@egidy.de>
|
||||||
Gerhardt Roman <roman.gerhardt@cbc-x.com>
|
|
||||||
gitmopp <mopp@gmx.net>
|
gitmopp <mopp@gmx.net>
|
||||||
Glauco Vinicius <gl4uc0@gmail.com>
|
Glauco Vinicius <gl4uc0@gmail.com>
|
||||||
Greg Hewgill <greg@hewgill.com>
|
Greg Hewgill <greg@hewgill.com>
|
||||||
@ -98,7 +96,6 @@ Harald Laabs <github@dasr.de>
|
|||||||
Heike Jurzik <icinga@huhnix.org>
|
Heike Jurzik <icinga@huhnix.org>
|
||||||
Hendrik Röder <hendrik.biz@gmail.com>
|
Hendrik Röder <hendrik.biz@gmail.com>
|
||||||
Henrik Triem <henrik.triem@icinga.com>
|
Henrik Triem <henrik.triem@icinga.com>
|
||||||
htriem <henrik.triem@netways.de>
|
|
||||||
Ian Kelling <ian@iankelling.org>
|
Ian Kelling <ian@iankelling.org>
|
||||||
Ildar Hizbulin <hizel@vyborg.ru>
|
Ildar Hizbulin <hizel@vyborg.ru>
|
||||||
Irina Kaprizkina <ikapriz@gmail.com>
|
Irina Kaprizkina <ikapriz@gmail.com>
|
||||||
@ -115,6 +112,7 @@ Jens Link <jenslink@quux.de>
|
|||||||
Jens Schanz <jens.schanz@mueller.de>
|
Jens Schanz <jens.schanz@mueller.de>
|
||||||
Jeon Sang Wan <maxswjeon@naver.com>
|
Jeon Sang Wan <maxswjeon@naver.com>
|
||||||
Jeremy Armstrong <lepubel@gmail.com>
|
Jeremy Armstrong <lepubel@gmail.com>
|
||||||
|
Jérôme Drouet <jerome.drouet@gmail.com>
|
||||||
Jesse Morgan <morgajel@gmail.com>
|
Jesse Morgan <morgajel@gmail.com>
|
||||||
Jo Goossens <jo.goossens@hosted-power.com>
|
Jo Goossens <jo.goossens@hosted-power.com>
|
||||||
Johannes Meyer <johannes.meyer@icinga.com>
|
Johannes Meyer <johannes.meyer@icinga.com>
|
||||||
@ -122,15 +120,13 @@ Jonas Meurer <jonas@freesources.org>
|
|||||||
Jordi van Scheijen <jordi.vanscheijen@solvinity.com>
|
Jordi van Scheijen <jordi.vanscheijen@solvinity.com>
|
||||||
Joseph L. Casale <jcasale@activenetwerx.com>
|
Joseph L. Casale <jcasale@activenetwerx.com>
|
||||||
jre3brg <jorge.rebelo@pt.bosch.com>
|
jre3brg <jorge.rebelo@pt.bosch.com>
|
||||||
Julian Brost <julian@0x4a42.net>
|
Julian Brost <julian.brost@icinga.com>
|
||||||
Jérôme Drouet <jerome.drouet@gmail.com>
|
|
||||||
K0nne <34264690+K0nne@users.noreply.github.com>
|
K0nne <34264690+K0nne@users.noreply.github.com>
|
||||||
Kai Goller <kai.goller@netways.de>
|
Kai Goller <kai.goller@netways.de>
|
||||||
|
Kálmán „KAMI” Szalai <kami911@gmail.com>
|
||||||
kiba <zombie32@gmail.com>
|
kiba <zombie32@gmail.com>
|
||||||
Konstantin Kelemen <konstantin@kel.mn>
|
Konstantin Kelemen <konstantin@kel.mn>
|
||||||
krishna <gskrishna44@gmail.com>
|
krishna <gskrishna44@gmail.com>
|
||||||
Kálmán Szalai - KAMI <kami911@gmail.com>
|
|
||||||
Kálmán „KAMI” Szalai <kami911@gmail.com>
|
|
||||||
Lars Engels <lars.engels@0x20.net>
|
Lars Engels <lars.engels@0x20.net>
|
||||||
Lars Krüger <krueger-lars@web.de>
|
Lars Krüger <krueger-lars@web.de>
|
||||||
Leah Oswald <mail@leahoswald.de>
|
Leah Oswald <mail@leahoswald.de>
|
||||||
@ -182,7 +178,6 @@ Mirco Bauer <meebey@meebey.net>
|
|||||||
Mirko Nardin <mirko.nardin@gmx.net>
|
Mirko Nardin <mirko.nardin@gmx.net>
|
||||||
mocruz <mocruz@theworkshop.com>
|
mocruz <mocruz@theworkshop.com>
|
||||||
Muhammad Mominul Huque <nahidbinbaten1995@gmail.com>
|
Muhammad Mominul Huque <nahidbinbaten1995@gmail.com>
|
||||||
Nemanja Trifunovic <ntrifunovic@hotmail.com>
|
|
||||||
nemtrif <ntrifunovic@hotmail.com>
|
nemtrif <ntrifunovic@hotmail.com>
|
||||||
Nicolai <nbuchwitz@users.noreply.github.com>
|
Nicolai <nbuchwitz@users.noreply.github.com>
|
||||||
Nicolas Limage <github@xephon.org>
|
Nicolas Limage <github@xephon.org>
|
||||||
@ -260,6 +255,6 @@ Winfried Angele <winfried.angele@gmail.com>
|
|||||||
Wolfgang Nieder <wnd@gmx.net>
|
Wolfgang Nieder <wnd@gmx.net>
|
||||||
Yannick Charton <tontonitch-pro@yahoo.fr>
|
Yannick Charton <tontonitch-pro@yahoo.fr>
|
||||||
Yohan Jarosz <yohanjarosz@yahoo.fr>
|
Yohan Jarosz <yohanjarosz@yahoo.fr>
|
||||||
|
Yonas Habteab <yonas.habteab@icinga.com>
|
||||||
Zachary McGibbon <zachary.mcgibbon@gmail.com>
|
Zachary McGibbon <zachary.mcgibbon@gmail.com>
|
||||||
Zoltan Nagy <abesto@abesto.net>
|
Zoltan Nagy <abesto@abesto.net>
|
||||||
Élie Bouttier <elie@bouttier.eu>
|
|
||||||
|
116
CHANGELOG.md
116
CHANGELOG.md
@ -7,6 +7,122 @@ documentation before upgrading to a new release.
|
|||||||
|
|
||||||
Released closed milestones can be found on [GitHub](https://github.com/Icinga/icinga2/milestones?state=closed).
|
Released closed milestones can be found on [GitHub](https://github.com/Icinga/icinga2/milestones?state=closed).
|
||||||
|
|
||||||
|
## 2.12.0 (2020-08-05)
|
||||||
|
|
||||||
|
[Issue and PRs](https://github.com/Icinga/icinga2/issues?utf8=%E2%9C%93&q=milestone%3A2.12.0)
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
Upgrading docs: https://icinga.com/docs/icinga2/snapshot/doc/16-upgrading-icinga-2/#upgrading-to-v212
|
||||||
|
|
||||||
|
Thanks to all contributors:
|
||||||
|
[Ant1x](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3AAnt1x+milestone%3A2.12.0),
|
||||||
|
[azthec](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aazthec+milestone%3A2.12.0),
|
||||||
|
[baurmatt](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Abaurmatt+milestone%3A2.12.0),
|
||||||
|
[bootc](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Abootc+milestone%3A2.12.0),
|
||||||
|
[Foxeronie](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3AFoxeronie+milestone%3A2.12.0),
|
||||||
|
[ggzengel](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aggzengel+milestone%3A2.12.0),
|
||||||
|
[islander](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aislander+milestone%3A2.12.0),
|
||||||
|
[joni1993](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Ajoni1993+milestone%3A2.12.0),
|
||||||
|
[KAMI911](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3AKAMI911+milestone%3A2.12.0),
|
||||||
|
[mcktr](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Amcktr+milestone%3A2.12.0),
|
||||||
|
[MichalMMac](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3AMichalMMac+milestone%3A2.12.0),
|
||||||
|
[sebastic](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Asebastic+milestone%3A2.12.0),
|
||||||
|
[sthen](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Asthen+milestone%3A2.12.0),
|
||||||
|
[unki](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Aunki+milestone%3A2.12.0),
|
||||||
|
[vigiroux](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Avigiroux+milestone%3A2.12.0),
|
||||||
|
[wopfel](https://github.com/Icinga/icinga2/pulls?q=is%3Apr+author%3Awopfel+milestone%3A2.12.0)
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
|
||||||
|
* Deprecate Windows plugins in favor of our
|
||||||
|
[PowerShell plugins](https://github.com/Icinga/icinga-powershell-plugins) #8071
|
||||||
|
* Deprecate Livestatus #8051
|
||||||
|
* Refuse acknowledging an already acknowledged checkable #7695
|
||||||
|
* Config lexer: complain on EOF in heredocs, i.e. `{{{abc<EOF>` #7541
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* Core
|
||||||
|
* Implement new database backend: Icinga DB #7571
|
||||||
|
* Re-send notifications previously suppressed by their time periods #7816
|
||||||
|
* API
|
||||||
|
* Host/Service: Add `acknowledgement_last_change` and `next_update` attributes #7881 #7534
|
||||||
|
* Improve error message for POST queries #7681
|
||||||
|
* /v1/actions/remove-comment: let users specify themselves #7646
|
||||||
|
* /v1/actions/remove-downtime: let users specify themselves #7645
|
||||||
|
* /v1/config/stages: Add 'activate' parameter #7535
|
||||||
|
* CLI
|
||||||
|
* Add `pki verify` command for better TLS certificate troubleshooting #7843
|
||||||
|
* Add OpenSSL version to 'Build' section in --version #7833
|
||||||
|
* Improve experience with 'Node Setup for Agents/Satellite' #7835
|
||||||
|
* DSL
|
||||||
|
* Add `get_template()` and `get_templates()` #7632
|
||||||
|
* `MacroProcessor::ResolveArguments()`: skip null argument values #7567
|
||||||
|
* Fix crash due to dependency apply rule with `ignore_on_error` and non-existing parent #7538
|
||||||
|
* Introduce ternary operator (`x ? y : z`) #7442
|
||||||
|
* LegacyTimePeriod: support specifying seconds #7439
|
||||||
|
* Add support for Lambda Closures (`() use(x) => x and () use(x) => { return x }`) #7417
|
||||||
|
* ITL
|
||||||
|
* Add notemp parameter to oracle health #7748
|
||||||
|
* Add extended checks options to snmp-interface command template #7602
|
||||||
|
* Add file age check for Windows command definition #7540
|
||||||
|
* Docs
|
||||||
|
* Development: Update debugging instructions #7867
|
||||||
|
* Add new API clients #7859
|
||||||
|
* Clarify CRITICAL vs. UNKNOWN #7665
|
||||||
|
* Explicitly explain how to disable freshness checks #7664
|
||||||
|
* Update installation for RHEL/CentOS 8 and SLES 15 #7640
|
||||||
|
* Add Powershell example to validate the certificate #7603
|
||||||
|
* Misc
|
||||||
|
* Don't send `event::Heartbeat` to unauthenticated peers #7747
|
||||||
|
* OpenTsdbWriter: Add custom tag support #7357
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
* Core
|
||||||
|
* Fix JSON-RPC crashes #7532 #7737
|
||||||
|
* Fix zone definitions in zones #7546
|
||||||
|
* Fix deadlock during start on OpenBSD #7739
|
||||||
|
* Consider PENDING not a problem #7685
|
||||||
|
* Fix zombie processes after reload #7606
|
||||||
|
* Don't wait for checks to finish during reload #7894
|
||||||
|
* Cluster
|
||||||
|
* Fix segfault during heartbeat timeout with clients not yet signed #7970
|
||||||
|
* Make the config update process mutually exclusive (Prevents file system race conditions) #7936
|
||||||
|
* Fix `check_timeout` not being forwarded to agent command endpoints #7861
|
||||||
|
* Config sync: Use a more friendly message when configs are equal and don't need a reload #7811
|
||||||
|
* Fix open connections when agent waits for CA approval #7686
|
||||||
|
* Consider a JsonRpcConnection alive on a single byte of TLS payload, not only on a whole message #7836
|
||||||
|
* Send JsonRpcConnection heartbeat every 20s instead of 10s #8102
|
||||||
|
* Use JsonRpcConnection heartbeat only to update connection liveness (m\_Seen) #8142
|
||||||
|
* Fix TLS context not being updated on signed certificate messages on agents #7654
|
||||||
|
* API
|
||||||
|
* Close connections w/o successful TLS handshakes after 10s #7809
|
||||||
|
* Handle permission exceptions soon enough, returning 404 #7528
|
||||||
|
* SELinux
|
||||||
|
* Fix safe-reload #7858
|
||||||
|
* Allow direct SMTP notifications #7749
|
||||||
|
* Windows
|
||||||
|
* Terminate check processes with UNKNOWN state on timeout #7788
|
||||||
|
* Ensure that log replay files are properly renamed #7767
|
||||||
|
* Metrics
|
||||||
|
* Graphite/OpenTSDB: Ensure that reconnect failure is detected #7765
|
||||||
|
* Always send 0 as value for thresholds #7696
|
||||||
|
* Scripts
|
||||||
|
* Fix notification scripts to stay compatible with Dash #7706
|
||||||
|
* Fix bash line continuation in mail-host-notification.sh #7701
|
||||||
|
* Fix notification scripts string comparison #7647
|
||||||
|
* Service and host mail-notifications: Add line-breaks to very long output #6822
|
||||||
|
* Set correct UTF-8 email subject header (RFC1342) #6369
|
||||||
|
* Misc
|
||||||
|
* DSL: Fix segfault due to passing null as custom function to `Array#{sort,map,reduce,filter,any,all}()` #8053
|
||||||
|
* CLI: `pki save-cert`: allow to specify --key and --cert for backwards compatibility #7995
|
||||||
|
* Catch exception when trusted cert is not readable during node setup on agent/satellite #7838
|
||||||
|
* CheckCommand ssl: Fix wrong parameter `-N` #7741
|
||||||
|
* Code quality fixes
|
||||||
|
* Small documentation fixes
|
||||||
|
|
||||||
## 2.12.0 RC1 (2020-03-13)
|
## 2.12.0 RC1 (2020-03-13)
|
||||||
|
|
||||||
[Issue and PRs](https://github.com/Icinga/icinga2/issues?utf8=%E2%9C%93&q=milestone%3A2.12.0)
|
[Issue and PRs](https://github.com/Icinga/icinga2/issues?utf8=%E2%9C%93&q=milestone%3A2.12.0)
|
||||||
|
@ -119,7 +119,9 @@ endif()
|
|||||||
# NuGet on Windows requires a semantic versioning, example: 2.10.4.123 (only 4 element, only numeric)
|
# 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 "-([0-9]+).*$" ".\\1" ICINGA2_VERSION_SAFE "${ICINGA2_VERSION}")
|
||||||
string(REGEX REPLACE "-[^\\.]*(.*)$" "\\1" ICINGA2_VERSION_SAFE "${ICINGA2_VERSION_SAFE}")
|
string(REGEX REPLACE "-[^\\.]*(.*)$" "\\1" ICINGA2_VERSION_SAFE "${ICINGA2_VERSION_SAFE}")
|
||||||
message(STATUS "ICINGA2_VERSION_SAFE=${ICINGA2_VERSION_SAFE}")
|
string(REGEX REPLACE "^([0-9]+\\.[0-9]+\\.[0-9]+)[\\.]?[0-9]*" "\\1" CHOCO_VERSION_SHORT "${ICINGA2_VERSION_SAFE}")
|
||||||
|
|
||||||
|
message(STATUS "ICINGA2_VERSION_SAFE=${ICINGA2_VERSION_SAFE} CHOCO_VERSION_SHORT=${CHOCO_VERSION_SHORT}")
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(Boost_USE_STATIC_LIBS ON)
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
|
71
RELEASE.md
71
RELEASE.md
@ -5,7 +5,6 @@
|
|||||||
- [1. Preparations](#preparations)
|
- [1. Preparations](#preparations)
|
||||||
- [1.1. Issues](#issues)
|
- [1.1. Issues](#issues)
|
||||||
- [1.2. Backport Commits](#backport-commits)
|
- [1.2. Backport Commits](#backport-commits)
|
||||||
- [1.3. Authors](#authors)
|
|
||||||
- [2. Version](#version)
|
- [2. Version](#version)
|
||||||
- [3. Changelog](#changelog)
|
- [3. Changelog](#changelog)
|
||||||
- [4. Git Tag](#git-tag)
|
- [4. Git Tag](#git-tag)
|
||||||
@ -15,7 +14,7 @@
|
|||||||
- [6. Build Server](#build-infrastructure)
|
- [6. Build Server](#build-infrastructure)
|
||||||
- [7. Release Tests](#release-tests)
|
- [7. Release Tests](#release-tests)
|
||||||
- [8. GitHub Release](#github-release)
|
- [8. GitHub Release](#github-release)
|
||||||
- [9. Chocolatey](#chocolatey)
|
- [9. Docker](#docker)
|
||||||
- [10. Post Release](#post-release)
|
- [10. Post Release](#post-release)
|
||||||
- [10.1. Online Documentation](#online-documentation)
|
- [10.1. Online Documentation](#online-documentation)
|
||||||
- [10.2. Announcement](#announcement)
|
- [10.2. Announcement](#announcement)
|
||||||
@ -49,14 +48,6 @@ Check issues at https://github.com/Icinga/icinga2
|
|||||||
For minor versions you need to manually backports any and all commits from the
|
For minor versions you need to manually backports any and all commits from the
|
||||||
master branch which should be part of this release.
|
master branch which should be part of this release.
|
||||||
|
|
||||||
### Authors <a id="authors"></a>
|
|
||||||
|
|
||||||
Update the [.mailmap](.mailmap) and [AUTHORS](AUTHORS) files:
|
|
||||||
|
|
||||||
```
|
|
||||||
git checkout master
|
|
||||||
git log --use-mailmap | grep '^Author:' | cut -f2- -d' ' | sort -f | uniq > AUTHORS
|
|
||||||
```
|
|
||||||
|
|
||||||
## Version <a id="version"></a>
|
## Version <a id="version"></a>
|
||||||
|
|
||||||
@ -88,7 +79,7 @@ git tag -s -m "Version $VERSION" v$VERSION
|
|||||||
Push the tag:
|
Push the tag:
|
||||||
|
|
||||||
```
|
```
|
||||||
git push --tags
|
git push origin v$VERSION
|
||||||
```
|
```
|
||||||
|
|
||||||
**For major releases:** Create a new `support` branch:
|
**For major releases:** Create a new `support` branch:
|
||||||
@ -112,7 +103,7 @@ cd $HOME/dev/icinga/packaging
|
|||||||
### RPM Packages <a id="rpm-packages"></a>
|
### RPM Packages <a id="rpm-packages"></a>
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone git@git.icinga.com:icinga/rpm-icinga2.git && cd rpm-icinga2
|
git clone git@git.icinga.com:packaging/rpm-icinga2.git && cd rpm-icinga2
|
||||||
```
|
```
|
||||||
|
|
||||||
### DEB Packages <a id="deb-packages"></a>
|
### DEB Packages <a id="deb-packages"></a>
|
||||||
@ -124,39 +115,39 @@ git clone git@git.icinga.com:packaging/deb-icinga2.git && cd deb-icinga2
|
|||||||
#### Raspbian Packages
|
#### Raspbian Packages
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone git@git.icinga.com:icinga/raspbian-icinga2.git && cd raspbian-icinga2
|
git clone git@git.icinga.com:packaging/raspbian-icinga2.git && cd raspbian-icinga2
|
||||||
```
|
```
|
||||||
|
|
||||||
### Windows Packages
|
### Windows Packages
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone git@git.icinga.com:icinga/windows-icinga2.git && cd windows-icinga2
|
git clone git@git.icinga.com:packaging/windows-icinga2.git && cd windows-icinga2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Branch Workflow
|
### Branch Workflow
|
||||||
|
|
||||||
Checkout `master` and create a new branch.
|
For each support branch in this repo (e.g. support/2.12), there exists a corresponding branch in the packaging repos
|
||||||
|
(e.g. 2.12). Each package revision is a tagged commit on these branches. When doing a major release, create the new
|
||||||
* For releases use x.x[.x] as branch name (e.g. 2.11 or 2.11.1)
|
branch, otherweise switch to the existing one.
|
||||||
* For releases with revision use x.x.x-n (e.g. 2.11.0-2)
|
|
||||||
|
|
||||||
|
|
||||||
### Switch Build Type
|
### Switch Build Type
|
||||||
|
|
||||||
Edit file `.gitlab-ci.yml` and comment variable `ICINGA_BUILD_TYPE` out.
|
Ensure that `ICINGA_BUILD_TYPE` is set to `release` in `.gitlab-ci.yml`. This should only be necessary after creating a
|
||||||
|
new branch.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
variables:
|
variables:
|
||||||
...
|
...
|
||||||
#ICINGA_BUILD_TYPE: snapshot
|
ICINGA_BUILD_TYPE: release
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Commit the change.
|
Commit the change.
|
||||||
|
|
||||||
```
|
```
|
||||||
git commit -av -m "Switch build type for $VERSION-1"
|
git commit -av -m "Switch build type for 2.13"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### RPM Release Preparations
|
#### RPM Release Preparations
|
||||||
@ -186,6 +177,16 @@ icinga2 (2.11.0-1) icinga; urgency=medium
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### Windows Release Preparations
|
||||||
|
|
||||||
|
Update the file `.gitlab-ci.yml`:
|
||||||
|
|
||||||
|
```
|
||||||
|
sed -i "s/^ UPSTREAM_GIT_BRANCH: .*/ UPSTREAM_GIT_BRANCH: v$VERSION/g" .gitlab-ci.yml
|
||||||
|
sed -i "s/^ ICINGA_FORCE_VERSION: .*/ ICINGA_FORCE_VERSION: v$VERSION/g" .gitlab-ci.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Release Commit
|
### Release Commit
|
||||||
|
|
||||||
Commit the changes and push the branch.
|
Commit the changes and push the branch.
|
||||||
@ -300,24 +301,28 @@ The release body should contain a short changelog, with links
|
|||||||
into the roadmap, changelog and blogpost.
|
into the roadmap, changelog and blogpost.
|
||||||
|
|
||||||
|
|
||||||
## Chocolatey <a id="chocolatey"></a>
|
## Docker <a id="docker"></a>
|
||||||
|
|
||||||
Navigate to the git repository on your Windows box which
|
> Only for final versions (not for RCs).
|
||||||
already has chocolatey installed. Pull/checkout the release.
|
|
||||||
|
|
||||||
Create the nupkg package (or use the one generated on https://packages.icinga.com/windows):
|
Once the release has been published on GitHub, wait for its
|
||||||
|
[GitHub actions](https://github.com/Icinga/icinga2/actions) to complete.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
cpack
|
VERSION=2.12.1
|
||||||
```
|
|
||||||
|
|
||||||
Fetch the API key from https://chocolatey.org/account and use the `choco push`
|
TAGS=(2.12)
|
||||||
command line.
|
#TAGS=(2.12 2 latest)
|
||||||
|
|
||||||
```
|
docker pull icinga/icinga2:$VERSION
|
||||||
choco apikey --key xxx --source https://push.chocolatey.org/
|
|
||||||
|
|
||||||
choco push Icinga2-v2.11.0.nupkg --source https://push.chocolatey.org/
|
for t in "${TAGS[@]}"; do
|
||||||
|
docker tag icinga/icinga2:$VERSION icinga/icinga2:$t
|
||||||
|
done
|
||||||
|
|
||||||
|
for t in "${TAGS[@]}"; do
|
||||||
|
docker push icinga/icinga2:$t
|
||||||
|
done
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
---
|
---
|
||||||
version: 2.11.0.dev.{build}
|
version: 2.11.0.dev.{build}
|
||||||
|
|
||||||
os: Visual Studio 2017
|
os: Visual Studio 2019
|
||||||
platform: x64
|
platform: x64
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
BITS: 64
|
BITS: 64
|
||||||
CMAKE_BUILD_TYPE: Debug
|
CMAKE_BUILD_TYPE: Debug
|
||||||
CMAKE_GENERATOR: "Visual Studio 15 2017 Win64"
|
CMAKE_GENERATOR: "Visual Studio 16 2019"
|
||||||
|
CMAKE_GENERATOR_PLATFORM: x64
|
||||||
# https://www.appveyor.com/docs/windows-images-software/#boost
|
# https://www.appveyor.com/docs/windows-images-software/#boost
|
||||||
BOOST_ROOT: 'C:\Libraries\boost_1_67_0'
|
BOOST_ROOT: 'C:\Libraries\boost_1_71_0'
|
||||||
BOOST_LIBRARYDIR: 'C:\Libraries\boost_1_67_0\lib64-msvc-14.1'
|
BOOST_LIBRARYDIR: 'C:\Libraries\boost_1_71_0\lib64-msvc-14.2'
|
||||||
# https://www.appveyor.com/docs/windows-images-software/#tools
|
# https://www.appveyor.com/docs/windows-images-software/#tools
|
||||||
OPENSSL_ROOT_DIR: 'C:\OpenSSL-v111-Win64'
|
OPENSSL_ROOT_DIR: 'C:\OpenSSL-v111-Win64'
|
||||||
BISON_BINARY: 'C:\ProgramData\chocolatey\lib\winflexbison3\tools\win_bison.exe'
|
BISON_BINARY: 'C:\ProgramData\chocolatey\lib\winflexbison3\tools\win_bison.exe'
|
||||||
|
@ -1,14 +1,6 @@
|
|||||||
# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
|
# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
find_program(CHOCO_BINARY choco)
|
|
||||||
|
|
||||||
configure_file(icinga2.nuspec.cmake icinga2.nuspec)
|
configure_file(icinga2.nuspec.cmake icinga2.nuspec)
|
||||||
configure_file(chocolateyInstall.ps1.cmake chocolateyInstall.ps1)
|
configure_file(chocolateyInstall.ps1.template.cmake chocolateyInstall.ps1.template)
|
||||||
|
|
||||||
add_custom_target(choco-pkg ALL
|
|
||||||
COMMAND choco pack
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/icinga2.${ICINGA2_VERSION_SAFE}.nupkg ${CMAKE_CURRENT_BINARY_DIR}/icinga2.nupkg
|
|
||||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/icinga2.nuspec ${CMAKE_CURRENT_BINARY_DIR}/chocolateyInstall.ps1 chocolateyUninstall.ps1
|
|
||||||
)
|
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
$packageName = 'icinga2'
|
|
||||||
$installerType = 'msi'
|
|
||||||
$url32 = 'https://packages.icinga.com/windows/Icinga2-v${ICINGA2_VERSION_SAFE}-x86.msi'
|
|
||||||
$url64 = 'https://packages.icinga.com/windows/Icinga2-v${ICINGA2_VERSION_SAFE}-x86_64.msi'
|
|
||||||
$silentArgs = '/qn /norestart'
|
|
||||||
$validExitCodes = @(0)
|
|
||||||
|
|
||||||
Install-ChocolateyPackage "$packageName" "$installerType" "$silentArgs" "$url32" "$url64" -validExitCodes $validExitCodes
|
|
20
choco/chocolateyInstall.ps1.template.cmake
Normal file
20
choco/chocolateyInstall.ps1.template.cmake
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
$packageName= 'icinga2'
|
||||||
|
$toolsDir = "$(Split-Path -Parent $MyInvocation.MyCommand.Definition)"
|
||||||
|
$url = 'https://packages.icinga.com/windows/Icinga2-v${CHOCO_VERSION_SHORT}-x86.msi'
|
||||||
|
$url64 = 'https://packages.icinga.com/windows/Icinga2-v${CHOCO_VERSION_SHORT}-x86_64.msi'
|
||||||
|
|
||||||
|
$packageArgs = @{
|
||||||
|
packageName = $packageName
|
||||||
|
fileType = 'msi'
|
||||||
|
url = $url
|
||||||
|
url64bit = $url64
|
||||||
|
silentArgs = "/qn /norestart"
|
||||||
|
validExitCodes= @(0)
|
||||||
|
softwareName = 'Icinga 2*'
|
||||||
|
checksum = '%CHOCO_32BIT_CHECKSUM%'
|
||||||
|
checksumType = 'sha256'
|
||||||
|
checksum64 = '%CHOCO_64BIT_CHECKSUM%'
|
||||||
|
checksumType64= 'sha256'
|
||||||
|
}
|
||||||
|
|
||||||
|
Install-ChocolateyPackage @packageArgs
|
@ -10,17 +10,20 @@
|
|||||||
<authors>Icinga GmbH</authors>
|
<authors>Icinga GmbH</authors>
|
||||||
<owners>Icinga GmbH</owners>
|
<owners>Icinga GmbH</owners>
|
||||||
<summary>icinga2 - Monitoring Agent for Windows</summary>
|
<summary>icinga2 - Monitoring Agent for Windows</summary>
|
||||||
<description>Icinga 2 is an open source monitoring platform which notifies users about host and service outages.</description>
|
<description>Icinga is an open source monitoring platform which notifies users about host and service outages.</description>
|
||||||
<projectUrl>https://icinga.com/</projectUrl>
|
<projectUrl>https://icinga.com/</projectUrl>
|
||||||
<tags>icinga2 agent monitoring admin</tags>
|
<tags>icinga2 icinga agent monitoring admin</tags>
|
||||||
<licenseUrl>https://icinga.com/resources/faq/</licenseUrl>
|
<licenseUrl>https://github.com/Icinga/icinga2/blob/master/COPYING</licenseUrl>
|
||||||
<releaseNotes>https://github.com/Icinga/icinga2/blob/master/ChangeLog</releaseNotes>
|
<releaseNotes>https://github.com/Icinga/icinga2/blob/master/ChangeLog</releaseNotes>
|
||||||
<docsUrl>https://docs.icinga.com/icinga2/</docsUrl>
|
<docsUrl>https://icinga.com/docs/icinga2/latest/</docsUrl>
|
||||||
<bugTrackerUrl>https://github.com/Icinga/icinga2/issues</bugTrackerUrl>
|
<bugTrackerUrl>https://github.com/Icinga/icinga2/issues</bugTrackerUrl>
|
||||||
<packageSourceUrl>https://github.com/Icinga/icinga2</packageSourceUrl>
|
<packageSourceUrl>https://github.com/Icinga/icinga2</packageSourceUrl>
|
||||||
<projectSourceUrl>https://github.com/Icinga/icinga2</projectSourceUrl>
|
<projectSourceUrl>https://github.com/Icinga/icinga2</projectSourceUrl>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<iconUrl>https://icinga.com/wp-content/uploads/2015/05/icinga_icon_128x128.png</iconUrl>
|
<iconUrl>https://raw.githubusercontent.com/Icinga/icinga2/master/icinga-app/icinga.ico</iconUrl>
|
||||||
|
<dependencies>
|
||||||
|
<dependency id='netfx-4.6.2' />
|
||||||
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="${CMAKE_CURRENT_BINARY_DIR}/chocolateyInstall.ps1" target="tools" />
|
<file src="${CMAKE_CURRENT_BINARY_DIR}/chocolateyInstall.ps1" target="tools" />
|
||||||
|
@ -607,50 +607,16 @@ $ nano /etc/icinga2/conf.d/templates.conf
|
|||||||
Icinga 2 can be used with Icinga Web 2 and a variety of modules.
|
Icinga 2 can be used with Icinga Web 2 and a variety of modules.
|
||||||
This chapter explains how to set up Icinga Web 2.
|
This chapter explains how to set up Icinga Web 2.
|
||||||
|
|
||||||
Either Icinga DB or the DB IDO (Database Icinga Data Output) feature for Icinga 2 takes care of
|
The DB IDO (Database Icinga Data Output) feature for Icinga 2 takes care of
|
||||||
exporting all configuration and status information into a database.
|
exporting all configuration and status information into a database.
|
||||||
|
|
||||||
Please choose whether to install [Icinga DB](02-installation.md#configuring-icinga-db) (MySQL only)
|
> **Note**
|
||||||
or DB IDO ([MySQL](02-installation.md#configuring-db-ido-mysql) or
|
>
|
||||||
[PostgreSQL](02-installation.md#configuring-db-ido-postgresql)).
|
> We're currently working on a new data backend called Icinga DB.
|
||||||
It's recommended to use the newer Icinga DB feature, if you don't need PostgreSQL.
|
> If you want to try the latest release candidate skip to
|
||||||
|
> the [Icinga DB Chapter](02-installation.md#icingadb).
|
||||||
### Configuring Icinga DB <a id="configuring-icinga-db"></a>
|
> Please keep in mind, that this version is not ready for use in
|
||||||
|
> production and currently only supports MySQL.
|
||||||
First, make sure to setup Icinga DB itself and its database backends (Redis and MySQL) by following the [installation instructions](https://icinga.com/docs/icingadb/latest/doc/02-Installation/).
|
|
||||||
|
|
||||||
#### Enabling the Icinga DB feature <a id="enabling-icinga-db"></a>
|
|
||||||
|
|
||||||
Icinga 2 provides a configuration file that is installed in
|
|
||||||
`/etc/icinga2/features-available/icingadb.conf`. You can update
|
|
||||||
the Redis credentials in this file.
|
|
||||||
|
|
||||||
All available attributes are explained in the
|
|
||||||
[IcingaDB object](09-object-types.md#objecttype-icingadb)
|
|
||||||
chapter.
|
|
||||||
|
|
||||||
You can enable the `icingadb` feature configuration file using
|
|
||||||
`icinga2 feature enable`:
|
|
||||||
|
|
||||||
```
|
|
||||||
# icinga2 feature enable icingadb
|
|
||||||
Module 'icingadb' was enabled.
|
|
||||||
Make sure to restart Icinga 2 for these changes to take effect.
|
|
||||||
```
|
|
||||||
|
|
||||||
Restart Icinga 2.
|
|
||||||
|
|
||||||
```
|
|
||||||
systemctl restart icinga2
|
|
||||||
```
|
|
||||||
|
|
||||||
Alpine Linux:
|
|
||||||
|
|
||||||
```
|
|
||||||
rc-service icinga2 restart
|
|
||||||
```
|
|
||||||
|
|
||||||
Continue with the [webserver setup](02-installation.md#icinga2-user-interface-webserver).
|
|
||||||
|
|
||||||
### Configuring DB IDO MySQL <a id="configuring-db-ido-mysql"></a>
|
### Configuring DB IDO MySQL <a id="configuring-db-ido-mysql"></a>
|
||||||
|
|
||||||
@ -1168,3 +1134,49 @@ PostgreSQL:
|
|||||||
|
|
||||||
* [Documentation](https://www.postgresql.org/docs/9.3/static/backup.html)
|
* [Documentation](https://www.postgresql.org/docs/9.3/static/backup.html)
|
||||||
|
|
||||||
|
## Icinga DB <a id="icingadb"></a>
|
||||||
|
|
||||||
|
Icinga DB is a new data backend currently in development.
|
||||||
|
It's purpose is to synchronise data between Icinga 2 (Redis) and Icinga Web 2 (MySQL), some day replacing the IDO.
|
||||||
|
Don't worry, we won't drop support on the IDO any time soon.
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
> Icinga DB is not ready to be used in production
|
||||||
|
> and should only be used for testing purposes.
|
||||||
|
|
||||||
|
### Configuring Icinga DB <a id="configuring-icinga-db"></a>
|
||||||
|
|
||||||
|
First, make sure to setup Icinga DB itself and its database backends (Redis and MySQL) by following the [installation instructions](https://icinga.com/docs/icingadb/latest/doc/02-Installation/).
|
||||||
|
|
||||||
|
#### Enabling the Icinga DB feature <a id="enabling-icinga-db"></a>
|
||||||
|
|
||||||
|
Icinga 2 provides a configuration file that is installed in
|
||||||
|
`/etc/icinga2/features-available/icingadb.conf`. You can update
|
||||||
|
the Redis credentials in this file.
|
||||||
|
|
||||||
|
All available attributes are explained in the
|
||||||
|
[IcingaDB object](09-object-types.md#objecttype-icingadb)
|
||||||
|
chapter.
|
||||||
|
|
||||||
|
You can enable the `icingadb` feature configuration file using
|
||||||
|
`icinga2 feature enable`:
|
||||||
|
|
||||||
|
```
|
||||||
|
# icinga2 feature enable icingadb
|
||||||
|
Module 'icingadb' was enabled.
|
||||||
|
Make sure to restart Icinga 2 for these changes to take effect.
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart Icinga 2.
|
||||||
|
|
||||||
|
```
|
||||||
|
systemctl restart icinga2
|
||||||
|
```
|
||||||
|
|
||||||
|
Alpine Linux:
|
||||||
|
|
||||||
|
```
|
||||||
|
rc-service icinga2 restart
|
||||||
|
```
|
||||||
|
|
||||||
|
Continue with the [webserver setup](02-installation.md#icinga2-user-interface-webserver).
|
||||||
|
@ -791,7 +791,7 @@ after the installation. Select the check box to proceed.
|
|||||||
#### Agent Setup on Windows: Configuration Wizard <a id="distributed-monitoring-setup-agent-windows-configuration-wizard"></a>
|
#### Agent Setup on Windows: Configuration Wizard <a id="distributed-monitoring-setup-agent-windows-configuration-wizard"></a>
|
||||||
|
|
||||||
On a fresh installation the setup wizard guides you through the initial configuration.
|
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).
|
It also provides a mechanism to send a certificate request to the [CSR signing master](06-distributed-monitoring.md#distributed-monitoring-setup-sign-certificates-master).
|
||||||
|
|
||||||
The following configuration details are required:
|
The following configuration details are required:
|
||||||
|
|
||||||
|
@ -1661,6 +1661,11 @@ require the [CompatLogger](09-object-types.md#objecttype-compatlogger) feature e
|
|||||||
pointing to the log files using the `compat_log_path` configuration attribute.
|
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).
|
This configuration object is available as [livestatus feature](14-features.md#setting-up-livestatus).
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
>
|
||||||
|
> This feature is DEPRECATED and will be removed in future releases.
|
||||||
|
> Check the [roadmap](https://github.com/Icinga/icinga2/milestones).
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -1503,6 +1503,13 @@ uptime_since | **Optional.** Show last boot in yyyy-mm-dd HH:MM:SS format (ou
|
|||||||
|
|
||||||
## Windows Plugins for Icinga 2 <a id="windows-plugins"></a>
|
## Windows Plugins for Icinga 2 <a id="windows-plugins"></a>
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
>
|
||||||
|
> These plugins are DEPRECATED in favor of our
|
||||||
|
> [PowerShell Plugins](https://github.com/Icinga/icinga-powershell-plugins)
|
||||||
|
> and will be removed in a future release.
|
||||||
|
> Check the [roadmap](https://github.com/Icinga/icinga2/milestones).
|
||||||
|
|
||||||
To allow a basic monitoring of Windows clients Icinga 2 comes with a set of Windows only plugins. While trying to mirror the functionalities of their linux cousins from the monitoring-plugins package, the differences between Windows and Linux are too big to be able use the same CheckCommands for both systems.
|
To allow a basic monitoring of Windows clients Icinga 2 comes with a set of Windows only plugins. While trying to mirror the functionalities of their linux cousins from the monitoring-plugins package, the differences between Windows and Linux are too big to be able use the same CheckCommands for both systems.
|
||||||
|
|
||||||
A check-commands-windows.conf comes with Icinga 2, it assumes that the Windows Plugins are installed in the PluginDir set in your constants.conf. To enable them the following include directive is needed in you icinga2.conf:
|
A check-commands-windows.conf comes with Icinga 2, it assumes that the Windows Plugins are installed in the PluginDir set in your constants.conf. To enable them the following include directive is needed in you icinga2.conf:
|
||||||
|
@ -915,210 +915,6 @@ is running on.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Livestatus <a id="setting-up-livestatus"></a>
|
|
||||||
|
|
||||||
The [MK Livestatus](https://mathias-kettner.de/checkmk_livestatus.html) project
|
|
||||||
implements a query protocol that lets users query their Icinga instance for
|
|
||||||
status information. It can also be used to send commands.
|
|
||||||
|
|
||||||
The Livestatus component that is distributed as part of Icinga 2 is a
|
|
||||||
re-implementation of the Livestatus protocol which is compatible with MK
|
|
||||||
Livestatus.
|
|
||||||
|
|
||||||
> **Tip**
|
|
||||||
>
|
|
||||||
> Only install the Livestatus feature if your web interface or addon requires
|
|
||||||
> you to do so.
|
|
||||||
> [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
|
|
||||||
in the [Livestatus Schema](24-appendix.md#schema-livestatus) section.
|
|
||||||
|
|
||||||
You can enable Livestatus using icinga2 feature enable:
|
|
||||||
|
|
||||||
```
|
|
||||||
# icinga2 feature enable livestatus
|
|
||||||
```
|
|
||||||
|
|
||||||
After that you will have to restart Icinga 2:
|
|
||||||
|
|
||||||
```
|
|
||||||
# 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
|
|
||||||
```
|
|
||||||
|
|
||||||
The Debian packages use `nagios` as the user and group name. Make sure to change `icingacmd` to
|
|
||||||
`nagios` if you're using Debian.
|
|
||||||
|
|
||||||
Change `www-data` to the user you're using to run queries.
|
|
||||||
|
|
||||||
In order to use the historical tables provided by the livestatus feature (for example, the
|
|
||||||
`log` table) you need to have the `CompatLogger` feature enabled. By default these logs
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
### Livestatus Sockets <a id="livestatus-sockets"></a>
|
|
||||||
|
|
||||||
Other to the Icinga 1.x Addon, Icinga 2 supports two socket types
|
|
||||||
|
|
||||||
* Unix socket (default)
|
|
||||||
* TCP socket
|
|
||||||
|
|
||||||
Details on the configuration can be found in the [LivestatusListener](09-object-types.md#objecttype-livestatuslistener)
|
|
||||||
object configuration.
|
|
||||||
|
|
||||||
### Livestatus GET Queries <a id="livestatus-get-queries"></a>
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
>
|
|
||||||
> All Livestatus queries require an additional empty line as query end identifier.
|
|
||||||
> The `nc` tool (`netcat`) provides the `-U` parameter to communicate using
|
|
||||||
> a unix socket.
|
|
||||||
|
|
||||||
There also is a Perl module available in CPAN for accessing the Livestatus socket
|
|
||||||
programmatically: [Monitoring::Livestatus](http://search.cpan.org/~nierlein/Monitoring-Livestatus-0.74/)
|
|
||||||
|
|
||||||
|
|
||||||
Example using the unix socket:
|
|
||||||
|
|
||||||
```
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# cat servicegroups <<EOF
|
|
||||||
GET servicegroups
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
(cat servicegroups; sleep 1) | netcat 127.0.0.1 6558
|
|
||||||
```
|
|
||||||
|
|
||||||
### Livestatus COMMAND Queries <a id="livestatus-command-queries"></a>
|
|
||||||
|
|
||||||
A list of available external commands and their parameters can be found [here](24-appendix.md#external-commands-list-detail)
|
|
||||||
|
|
||||||
```
|
|
||||||
$ echo -e 'COMMAND <externalcommandstring>' | netcat 127.0.0.1 6558
|
|
||||||
```
|
|
||||||
|
|
||||||
### Livestatus Filters <a id="livestatus-filters"></a>
|
|
||||||
|
|
||||||
and, or, negate
|
|
||||||
|
|
||||||
Operator | Negate | Description
|
|
||||||
----------|----------|-------------
|
|
||||||
= | != | Equality
|
|
||||||
~ | !~ | Regex match
|
|
||||||
=~ | !=~ | Equality ignoring case
|
|
||||||
~~ | !~~ | Regex ignoring case
|
|
||||||
< | | Less than
|
|
||||||
> | | Greater than
|
|
||||||
<= | | Less than or equal
|
|
||||||
>= | | Greater than or equal
|
|
||||||
|
|
||||||
|
|
||||||
### Livestatus Stats <a id="livestatus-stats"></a>
|
|
||||||
|
|
||||||
Schema: "Stats: aggregatefunction aggregateattribute"
|
|
||||||
|
|
||||||
Aggregate Function | Description
|
|
||||||
-------------------|--------------
|
|
||||||
sum |
|
|
||||||
min |
|
|
||||||
max |
|
|
||||||
avg | sum / count
|
|
||||||
std | standard deviation
|
|
||||||
suminv | sum (1 / value)
|
|
||||||
avginv | suminv / count
|
|
||||||
count | ordinary default for any stats query if not aggregate function defined
|
|
||||||
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
### Livestatus Output <a id="livestatus-output"></a>
|
|
||||||
|
|
||||||
* CSV
|
|
||||||
|
|
||||||
CSV output uses two levels of array separators: The members array separator
|
|
||||||
is a comma (1st level) while extra info and host|service relation separator
|
|
||||||
is a pipe (2nd level).
|
|
||||||
|
|
||||||
Separators can be set using ASCII codes like:
|
|
||||||
|
|
||||||
```
|
|
||||||
Separators: 10 59 44 124
|
|
||||||
```
|
|
||||||
|
|
||||||
* JSON
|
|
||||||
|
|
||||||
Default separators.
|
|
||||||
|
|
||||||
### Livestatus Error Codes <a id="livestatus-error-codes"></a>
|
|
||||||
|
|
||||||
Code | Description
|
|
||||||
----------|--------------
|
|
||||||
200 | OK
|
|
||||||
404 | Table does not exist
|
|
||||||
452 | Exception on query
|
|
||||||
|
|
||||||
### Livestatus Tables <a id="livestatus-tables"></a>
|
|
||||||
|
|
||||||
Table | Join |Description
|
|
||||||
--------------|-----------|----------------------------
|
|
||||||
hosts | | host config and status attributes, services counter
|
|
||||||
hostgroups | | hostgroup config, status attributes and host/service counters
|
|
||||||
services | hosts | service config and status attributes
|
|
||||||
servicegroups | | servicegroup config, status attributes and service counters
|
|
||||||
contacts | | contact config and status attributes
|
|
||||||
contactgroups | | contact config, members
|
|
||||||
commands | | command name and line
|
|
||||||
status | | programstatus, config and stats
|
|
||||||
comments | services | status attributes
|
|
||||||
downtimes | services | status attributes
|
|
||||||
timeperiods | | name and is inside flag
|
|
||||||
endpoints | | config and status attributes
|
|
||||||
log | services, hosts, contacts, commands | parses [compatlog](09-object-types.md#objecttype-compatlogger) and shows log attributes
|
|
||||||
statehist | hosts, services | parses [compatlog](09-object-types.md#objecttype-compatlogger) and aggregates state change attributes
|
|
||||||
hostsbygroup | hostgroups | host attributes grouped by hostgroup and its attributes
|
|
||||||
servicesbygroup | servicegroups | service attributes grouped by servicegroup and its attributes
|
|
||||||
servicesbyhostgroup | hostgroups | service attributes grouped by hostgroup and its attributes
|
|
||||||
|
|
||||||
The `commands` table is populated with `CheckCommand`, `EventCommand` and `NotificationCommand` objects.
|
|
||||||
|
|
||||||
A detailed list on the available table attributes can be found in the [Livestatus Schema documentation](24-appendix.md#schema-livestatus).
|
|
||||||
|
|
||||||
|
|
||||||
## Deprecated Features <a id="deprecated-features"></a>
|
## Deprecated Features <a id="deprecated-features"></a>
|
||||||
|
|
||||||
@ -1237,3 +1033,212 @@ object CheckResultReader "reader" {
|
|||||||
spool_dir = "/data/check-results"
|
spool_dir = "/data/check-results"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Livestatus <a id="setting-up-livestatus"></a>
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
>
|
||||||
|
> This feature is DEPRECATED and will be removed in future releases.
|
||||||
|
> Check the [roadmap](https://github.com/Icinga/icinga2/milestones).
|
||||||
|
|
||||||
|
The [MK Livestatus](https://mathias-kettner.de/checkmk_livestatus.html) project
|
||||||
|
implements a query protocol that lets users query their Icinga instance for
|
||||||
|
status information. It can also be used to send commands.
|
||||||
|
|
||||||
|
The Livestatus component that is distributed as part of Icinga 2 is a
|
||||||
|
re-implementation of the Livestatus protocol which is compatible with MK
|
||||||
|
Livestatus.
|
||||||
|
|
||||||
|
> **Tip**
|
||||||
|
>
|
||||||
|
> Only install the Livestatus feature if your web interface or addon requires
|
||||||
|
> you to do so.
|
||||||
|
> [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
|
||||||
|
in the [Livestatus Schema](24-appendix.md#schema-livestatus) section.
|
||||||
|
|
||||||
|
You can enable Livestatus using icinga2 feature enable:
|
||||||
|
|
||||||
|
```
|
||||||
|
# icinga2 feature enable livestatus
|
||||||
|
```
|
||||||
|
|
||||||
|
After that you will have to restart Icinga 2:
|
||||||
|
|
||||||
|
```
|
||||||
|
# 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
|
||||||
|
```
|
||||||
|
|
||||||
|
The Debian packages use `nagios` as the user and group name. Make sure to change `icingacmd` to
|
||||||
|
`nagios` if you're using Debian.
|
||||||
|
|
||||||
|
Change `www-data` to the user you're using to run queries.
|
||||||
|
|
||||||
|
In order to use the historical tables provided by the livestatus feature (for example, the
|
||||||
|
`log` table) you need to have the `CompatLogger` feature enabled. By default these logs
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Livestatus Sockets <a id="livestatus-sockets"></a>
|
||||||
|
|
||||||
|
Other to the Icinga 1.x Addon, Icinga 2 supports two socket types
|
||||||
|
|
||||||
|
* Unix socket (default)
|
||||||
|
* TCP socket
|
||||||
|
|
||||||
|
Details on the configuration can be found in the [LivestatusListener](09-object-types.md#objecttype-livestatuslistener)
|
||||||
|
object configuration.
|
||||||
|
|
||||||
|
#### Livestatus GET Queries <a id="livestatus-get-queries"></a>
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
>
|
||||||
|
> All Livestatus queries require an additional empty line as query end identifier.
|
||||||
|
> The `nc` tool (`netcat`) provides the `-U` parameter to communicate using
|
||||||
|
> a unix socket.
|
||||||
|
|
||||||
|
There also is a Perl module available in CPAN for accessing the Livestatus socket
|
||||||
|
programmatically: [Monitoring::Livestatus](http://search.cpan.org/~nierlein/Monitoring-Livestatus-0.74/)
|
||||||
|
|
||||||
|
|
||||||
|
Example using the unix socket:
|
||||||
|
|
||||||
|
```
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# cat servicegroups <<EOF
|
||||||
|
GET servicegroups
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
(cat servicegroups; sleep 1) | netcat 127.0.0.1 6558
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Livestatus COMMAND Queries <a id="livestatus-command-queries"></a>
|
||||||
|
|
||||||
|
A list of available external commands and their parameters can be found [here](24-appendix.md#external-commands-list-detail)
|
||||||
|
|
||||||
|
```
|
||||||
|
$ echo -e 'COMMAND <externalcommandstring>' | netcat 127.0.0.1 6558
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Livestatus Filters <a id="livestatus-filters"></a>
|
||||||
|
|
||||||
|
and, or, negate
|
||||||
|
|
||||||
|
Operator | Negate | Description
|
||||||
|
----------|----------|-------------
|
||||||
|
= | != | Equality
|
||||||
|
~ | !~ | Regex match
|
||||||
|
=~ | !=~ | Equality ignoring case
|
||||||
|
~~ | !~~ | Regex ignoring case
|
||||||
|
< | | Less than
|
||||||
|
> | | Greater than
|
||||||
|
<= | | Less than or equal
|
||||||
|
>= | | Greater than or equal
|
||||||
|
|
||||||
|
|
||||||
|
#### Livestatus Stats <a id="livestatus-stats"></a>
|
||||||
|
|
||||||
|
Schema: "Stats: aggregatefunction aggregateattribute"
|
||||||
|
|
||||||
|
Aggregate Function | Description
|
||||||
|
-------------------|--------------
|
||||||
|
sum |
|
||||||
|
min |
|
||||||
|
max |
|
||||||
|
avg | sum / count
|
||||||
|
std | standard deviation
|
||||||
|
suminv | sum (1 / value)
|
||||||
|
avginv | suminv / count
|
||||||
|
count | ordinary default for any stats query if not aggregate function defined
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Livestatus Output <a id="livestatus-output"></a>
|
||||||
|
|
||||||
|
* CSV
|
||||||
|
|
||||||
|
CSV output uses two levels of array separators: The members array separator
|
||||||
|
is a comma (1st level) while extra info and host|service relation separator
|
||||||
|
is a pipe (2nd level).
|
||||||
|
|
||||||
|
Separators can be set using ASCII codes like:
|
||||||
|
|
||||||
|
```
|
||||||
|
Separators: 10 59 44 124
|
||||||
|
```
|
||||||
|
|
||||||
|
* JSON
|
||||||
|
|
||||||
|
Default separators.
|
||||||
|
|
||||||
|
#### Livestatus Error Codes <a id="livestatus-error-codes"></a>
|
||||||
|
|
||||||
|
Code | Description
|
||||||
|
----------|--------------
|
||||||
|
200 | OK
|
||||||
|
404 | Table does not exist
|
||||||
|
452 | Exception on query
|
||||||
|
|
||||||
|
#### Livestatus Tables <a id="livestatus-tables"></a>
|
||||||
|
|
||||||
|
Table | Join |Description
|
||||||
|
--------------|-----------|----------------------------
|
||||||
|
hosts | | host config and status attributes, services counter
|
||||||
|
hostgroups | | hostgroup config, status attributes and host/service counters
|
||||||
|
services | hosts | service config and status attributes
|
||||||
|
servicegroups | | servicegroup config, status attributes and service counters
|
||||||
|
contacts | | contact config and status attributes
|
||||||
|
contactgroups | | contact config, members
|
||||||
|
commands | | command name and line
|
||||||
|
status | | programstatus, config and stats
|
||||||
|
comments | services | status attributes
|
||||||
|
downtimes | services | status attributes
|
||||||
|
timeperiods | | name and is inside flag
|
||||||
|
endpoints | | config and status attributes
|
||||||
|
log | services, hosts, contacts, commands | parses [compatlog](09-object-types.md#objecttype-compatlogger) and shows log attributes
|
||||||
|
statehist | hosts, services | parses [compatlog](09-object-types.md#objecttype-compatlogger) and aggregates state change attributes
|
||||||
|
hostsbygroup | hostgroups | host attributes grouped by hostgroup and its attributes
|
||||||
|
servicesbygroup | servicegroups | service attributes grouped by servicegroup and its attributes
|
||||||
|
servicesbyhostgroup | hostgroups | service attributes grouped by hostgroup and its attributes
|
||||||
|
|
||||||
|
The `commands` table is populated with `CheckCommand`, `EventCommand` and `NotificationCommand` objects.
|
||||||
|
|
||||||
|
A detailed list on the available table attributes can be found in the [Livestatus Schema documentation](24-appendix.md#schema-livestatus).
|
||||||
|
@ -1400,6 +1400,40 @@ Message updates will be dropped when:
|
|||||||
* Checkable does not exist.
|
* Checkable does not exist.
|
||||||
* Origin endpoint's zone is not allowed to access this checkable.
|
* Origin endpoint's zone is not allowed to access this checkable.
|
||||||
|
|
||||||
|
#### event::SetLastCheckStarted <a id="technical-concepts-json-rpc-messages-event-setlastcheckstarted"></a>
|
||||||
|
|
||||||
|
> Location: `clusterevents.cpp`
|
||||||
|
|
||||||
|
##### Message Body
|
||||||
|
|
||||||
|
Key | Value
|
||||||
|
----------|---------
|
||||||
|
jsonrpc | 2.0
|
||||||
|
method | event::SetLastCheckStarted
|
||||||
|
params | Dictionary
|
||||||
|
|
||||||
|
##### Params
|
||||||
|
|
||||||
|
Key | Type | Description
|
||||||
|
---------------------|-----------|------------------
|
||||||
|
host | String | Host name
|
||||||
|
service | String | Service name
|
||||||
|
last\_check\_started | Timestamp | Last check's start time as UNIX timestamp.
|
||||||
|
|
||||||
|
##### Functions
|
||||||
|
|
||||||
|
Event Sender: `Checkable::OnLastCheckStartedChanged`
|
||||||
|
Event Receiver: `LastCheckStartedChangedAPIHandler`
|
||||||
|
|
||||||
|
##### 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 <a id="technical-concepts-json-rpc-messages-event-setsupressednotifications"></a>
|
#### event::SuppressedNotifications <a id="technical-concepts-json-rpc-messages-event-setsupressednotifications"></a>
|
||||||
|
|
||||||
> Location: `clusterevents.cpp`
|
> Location: `clusterevents.cpp`
|
||||||
@ -1434,6 +1468,39 @@ Message updates will be dropped when:
|
|||||||
* Checkable does not exist.
|
* Checkable does not exist.
|
||||||
* Origin endpoint's zone is not allowed to access this checkable.
|
* Origin endpoint's zone is not allowed to access this checkable.
|
||||||
|
|
||||||
|
#### event::SetSuppressedNotificationTypes <a id="technical-concepts-json-rpc-messages-event-setsuppressednotificationtypes"></a>
|
||||||
|
|
||||||
|
> Location: `clusterevents.cpp`
|
||||||
|
|
||||||
|
##### Message Body
|
||||||
|
|
||||||
|
Key | Value
|
||||||
|
----------|---------
|
||||||
|
jsonrpc | 2.0
|
||||||
|
method | event::SetSuppressedNotificationTypes
|
||||||
|
params | Dictionary
|
||||||
|
|
||||||
|
##### Params
|
||||||
|
|
||||||
|
Key | Type | Description
|
||||||
|
-------------------------|--------|------------------
|
||||||
|
notification | String | Notification name
|
||||||
|
supressed\_notifications | Number | Bitmask for suppressed notifications.
|
||||||
|
|
||||||
|
##### Functions
|
||||||
|
|
||||||
|
Event Sender: `Notification::OnSuppressedNotificationsChanged`
|
||||||
|
Event Receiver: `SuppressedNotificationTypesChangedAPIHandler`
|
||||||
|
|
||||||
|
##### 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 notification.
|
||||||
|
|
||||||
|
|
||||||
#### event::SetNextNotification <a id="technical-concepts-json-rpc-messages-event-setnextnotification"></a>
|
#### event::SetNextNotification <a id="technical-concepts-json-rpc-messages-event-setnextnotification"></a>
|
||||||
|
|
||||||
|
@ -1789,8 +1789,7 @@ 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 installation requires ~9GB disk space. [Download](https://www.visualstudio.com/downloads/)
|
||||||
the web installer and start the installation.
|
the web installer and start the installation.
|
||||||
|
|
||||||
Note: Both Visual Studio 2017 and 2019 are covered here. Older versions
|
Note: Only Visual Studio 2019 is covered here. Older versions are not supported.
|
||||||
are not supported.
|
|
||||||
|
|
||||||
You need a free Microsoft account to download and also store your preferences.
|
You need a free Microsoft account to download and also store your preferences.
|
||||||
|
|
||||||
@ -1883,7 +1882,6 @@ Icinga needs the development header and library files from the Boost library.
|
|||||||
|
|
||||||
Visual Studio translates into the following compiler versions:
|
Visual Studio translates into the following compiler versions:
|
||||||
|
|
||||||
- `msvc-14.1` = Visual Studio 2017
|
|
||||||
- `msvc-14.2` = Visual Studio 2019
|
- `msvc-14.2` = Visual Studio 2019
|
||||||
|
|
||||||
##### Pre-built Binaries
|
##### Pre-built Binaries
|
||||||
@ -1963,9 +1961,11 @@ CMake uses CPack and NSIS to create the setup executable including all binaries
|
|||||||
in addition to setup dialogues and configuration. Therefore we’ll need to install [NSIS](http://nsis.sourceforge.net/Download)
|
in addition to setup dialogues and configuration. Therefore we’ll need to install [NSIS](http://nsis.sourceforge.net/Download)
|
||||||
first.
|
first.
|
||||||
|
|
||||||
We also need to install the Windows Installer XML (WIX) toolset.
|
We also need to install the Windows Installer XML (WIX) toolset. This has .NET 3.5 as a dependency which might need a
|
||||||
|
reboot of the system which is not handled properly by Chocolatey. Therefore install it first and reboot when asked.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
Enable-WindowsOptionalFeature -FeatureName "NetFx3" -Online
|
||||||
choco install -y wixtoolset
|
choco install -y wixtoolset
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1993,7 +1993,6 @@ Build Icinga with specific CMake variables. This generates a new Visual Studio p
|
|||||||
|
|
||||||
Visual Studio translates into the following:
|
Visual Studio translates into the following:
|
||||||
|
|
||||||
- `msvc-14.1` = Visual Studio 2017
|
|
||||||
- `msvc-14.2` = Visual Studio 2019
|
- `msvc-14.2` = Visual Studio 2019
|
||||||
|
|
||||||
You need to specify the previously installed component paths.
|
You need to specify the previously installed component paths.
|
||||||
@ -2001,7 +2000,7 @@ You need to specify the previously installed component paths.
|
|||||||
Variable | Value | Description
|
Variable | Value | Description
|
||||||
----------------------|----------------------------------------------------------------------|-------------------------------------------------------
|
----------------------|----------------------------------------------------------------------|-------------------------------------------------------
|
||||||
`BOOST_ROOT` | `C:\local\boost_1_71_0` | Root path where you've extracted and compiled Boost.
|
`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`.
|
`BOOST_LIBRARYDIR` | Binary: `C:\local\boost_1_71_0\lib64-msvc-14.2`, 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.
|
`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.
|
`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_MYSQL` | OFF | Requires extra setup for MySQL if set to `ON`. Not supported for client setups.
|
||||||
@ -2027,9 +2026,8 @@ cd %HOMEPATH%\source\repos\icinga2
|
|||||||
|
|
||||||
The debug MSI package is located in the `debug` directory.
|
The debug MSI package is located in the `debug` directory.
|
||||||
|
|
||||||
If you did not follow the above steps with Boost binaries
|
If you did not follow the above steps with Boost binaries and OpenSSL
|
||||||
and OpenSSL paths, or using VS 2017, you can still modify
|
paths, you can still modify the environment variables.
|
||||||
the environment variables.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$env:CMAKE_GENERATOR='Visual Studio 16 2019'
|
$env:CMAKE_GENERATOR='Visual Studio 16 2019'
|
||||||
|
@ -26,12 +26,10 @@ getent group $ICINGA2_GROUP >/dev/null 2>&1 || (echo "Icinga group '$ICINGA2_GRO
|
|||||||
getent group $ICINGA2_COMMAND_GROUP >/dev/null 2>&1 || (echo "Icinga command group '$ICINGA2_COMMAND_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_INIT_RUN_DIR" ]; then
|
if [ ! -e "$ICINGA2_INIT_RUN_DIR" ]; then
|
||||||
mkdir "$ICINGA2_INIT_RUN_DIR"
|
mkdir -m 755 "$ICINGA2_INIT_RUN_DIR"
|
||||||
mkdir "$ICINGA2_INIT_RUN_DIR"/cmd
|
mkdir -m 2750 "$ICINGA2_INIT_RUN_DIR"/cmd
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chmod 755 "$ICINGA2_INIT_RUN_DIR"
|
|
||||||
chmod 2750 "$ICINGA2_INIT_RUN_DIR"/cmd
|
|
||||||
chown -R $ICINGA2_USER:$ICINGA2_COMMAND_GROUP "$ICINGA2_INIT_RUN_DIR"
|
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"
|
test -e "$ICINGA2_LOG_DIR" || install -m 750 -o $ICINGA2_USER -g $ICINGA2_COMMAND_GROUP -d "$ICINGA2_LOG_DIR"
|
||||||
|
@ -64,6 +64,7 @@ set(base_SOURCES
|
|||||||
shared-object.hpp
|
shared-object.hpp
|
||||||
singleton.hpp
|
singleton.hpp
|
||||||
socket.cpp socket.hpp
|
socket.cpp socket.hpp
|
||||||
|
spinlock.cpp spinlock.hpp
|
||||||
stacktrace.cpp stacktrace.hpp
|
stacktrace.cpp stacktrace.hpp
|
||||||
statsfunction.hpp
|
statsfunction.hpp
|
||||||
stdiostream.cpp stdiostream.hpp
|
stdiostream.cpp stdiostream.hpp
|
||||||
|
@ -342,7 +342,9 @@ void Application::RunEventLoop()
|
|||||||
ConfigObject::StopObjects();
|
ConfigObject::StopObjects();
|
||||||
Application::GetInstance()->OnShutdown();
|
Application::GetInstance()->OnShutdown();
|
||||||
|
|
||||||
UninitializeBase();
|
#ifdef I2_DEBUG
|
||||||
|
UninitializeBase(); // Inspired from Exit()
|
||||||
|
#endif /* I2_DEBUG */
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::IsShuttingDown()
|
bool Application::IsShuttingDown()
|
||||||
|
@ -83,6 +83,7 @@ static Array::Ptr ArraySort(const std::vector<Value>& args)
|
|||||||
std::sort(arr->Begin(), arr->End());
|
std::sort(arr->Begin(), arr->End());
|
||||||
} else {
|
} else {
|
||||||
Function::Ptr function = args[0];
|
Function::Ptr function = args[0];
|
||||||
|
REQUIRE_NOT_NULL(function);
|
||||||
|
|
||||||
if (vframe->Sandboxed && !function->IsSideEffectFree())
|
if (vframe->Sandboxed && !function->IsSideEffectFree())
|
||||||
BOOST_THROW_EXCEPTION(ScriptError("Sort function must be side-effect free."));
|
BOOST_THROW_EXCEPTION(ScriptError("Sort function must be side-effect free."));
|
||||||
@ -123,6 +124,7 @@ static Array::Ptr ArrayMap(const Function::Ptr& function)
|
|||||||
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
|
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
|
||||||
Array::Ptr self = static_cast<Array::Ptr>(vframe->Self);
|
Array::Ptr self = static_cast<Array::Ptr>(vframe->Self);
|
||||||
REQUIRE_NOT_NULL(self);
|
REQUIRE_NOT_NULL(self);
|
||||||
|
REQUIRE_NOT_NULL(function);
|
||||||
|
|
||||||
if (vframe->Sandboxed && !function->IsSideEffectFree())
|
if (vframe->Sandboxed && !function->IsSideEffectFree())
|
||||||
BOOST_THROW_EXCEPTION(ScriptError("Map function must be side-effect free."));
|
BOOST_THROW_EXCEPTION(ScriptError("Map function must be side-effect free."));
|
||||||
@ -142,6 +144,7 @@ static Value ArrayReduce(const Function::Ptr& function)
|
|||||||
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
|
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
|
||||||
Array::Ptr self = static_cast<Array::Ptr>(vframe->Self);
|
Array::Ptr self = static_cast<Array::Ptr>(vframe->Self);
|
||||||
REQUIRE_NOT_NULL(self);
|
REQUIRE_NOT_NULL(self);
|
||||||
|
REQUIRE_NOT_NULL(function);
|
||||||
|
|
||||||
if (vframe->Sandboxed && !function->IsSideEffectFree())
|
if (vframe->Sandboxed && !function->IsSideEffectFree())
|
||||||
BOOST_THROW_EXCEPTION(ScriptError("Reduce function must be side-effect free."));
|
BOOST_THROW_EXCEPTION(ScriptError("Reduce function must be side-effect free."));
|
||||||
@ -164,6 +167,7 @@ static Array::Ptr ArrayFilter(const Function::Ptr& function)
|
|||||||
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
|
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
|
||||||
Array::Ptr self = static_cast<Array::Ptr>(vframe->Self);
|
Array::Ptr self = static_cast<Array::Ptr>(vframe->Self);
|
||||||
REQUIRE_NOT_NULL(self);
|
REQUIRE_NOT_NULL(self);
|
||||||
|
REQUIRE_NOT_NULL(function);
|
||||||
|
|
||||||
if (vframe->Sandboxed && !function->IsSideEffectFree())
|
if (vframe->Sandboxed && !function->IsSideEffectFree())
|
||||||
BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free."));
|
BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free."));
|
||||||
@ -184,6 +188,7 @@ static bool ArrayAny(const Function::Ptr& function)
|
|||||||
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
|
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
|
||||||
Array::Ptr self = static_cast<Array::Ptr>(vframe->Self);
|
Array::Ptr self = static_cast<Array::Ptr>(vframe->Self);
|
||||||
REQUIRE_NOT_NULL(self);
|
REQUIRE_NOT_NULL(self);
|
||||||
|
REQUIRE_NOT_NULL(function);
|
||||||
|
|
||||||
if (vframe->Sandboxed && !function->IsSideEffectFree())
|
if (vframe->Sandboxed && !function->IsSideEffectFree())
|
||||||
BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free."));
|
BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free."));
|
||||||
@ -202,6 +207,7 @@ static bool ArrayAll(const Function::Ptr& function)
|
|||||||
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
|
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
|
||||||
Array::Ptr self = static_cast<Array::Ptr>(vframe->Self);
|
Array::Ptr self = static_cast<Array::Ptr>(vframe->Self);
|
||||||
REQUIRE_NOT_NULL(self);
|
REQUIRE_NOT_NULL(self);
|
||||||
|
REQUIRE_NOT_NULL(function);
|
||||||
|
|
||||||
if (vframe->Sandboxed && !function->IsSideEffectFree())
|
if (vframe->Sandboxed && !function->IsSideEffectFree())
|
||||||
BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free."));
|
BOOST_THROW_EXCEPTION(ScriptError("Filter function must be side-effect free."));
|
||||||
|
@ -144,3 +144,11 @@ void AsioConditionVariable::Wait(boost::asio::yield_context yc)
|
|||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
m_Timer.async_wait(yc[ec]);
|
m_Timer.async_wait(yc[ec]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Timeout::Cancel()
|
||||||
|
{
|
||||||
|
m_Cancelled.store(true);
|
||||||
|
|
||||||
|
boost::system::error_code ec;
|
||||||
|
m_Timer.cancel(ec);
|
||||||
|
}
|
||||||
|
@ -6,10 +6,12 @@
|
|||||||
#include "base/exception.hpp"
|
#include "base/exception.hpp"
|
||||||
#include "base/lazy-init.hpp"
|
#include "base/lazy-init.hpp"
|
||||||
#include "base/logger.hpp"
|
#include "base/logger.hpp"
|
||||||
|
#include "base/shared-object.hpp"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <boost/exception/all.hpp>
|
#include <boost/exception/all.hpp>
|
||||||
@ -153,6 +155,56 @@ private:
|
|||||||
boost::asio::deadline_timer m_Timer;
|
boost::asio::deadline_timer m_Timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I/O timeout emulator
|
||||||
|
*
|
||||||
|
* @ingroup base
|
||||||
|
*/
|
||||||
|
class Timeout : public SharedObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DECLARE_PTR_TYPEDEFS(Timeout);
|
||||||
|
|
||||||
|
template<class Executor, class TimeoutFromNow, class OnTimeout>
|
||||||
|
Timeout(boost::asio::io_context& io, Executor& executor, TimeoutFromNow timeoutFromNow, OnTimeout onTimeout)
|
||||||
|
: m_Timer(io)
|
||||||
|
{
|
||||||
|
Ptr keepAlive (this);
|
||||||
|
|
||||||
|
m_Cancelled.store(false);
|
||||||
|
m_Timer.expires_from_now(std::move(timeoutFromNow));
|
||||||
|
|
||||||
|
IoEngine::SpawnCoroutine(executor, [this, keepAlive, onTimeout](boost::asio::yield_context yc) {
|
||||||
|
if (m_Cancelled.load()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::system::error_code ec;
|
||||||
|
|
||||||
|
m_Timer.async_wait(yc[ec]);
|
||||||
|
|
||||||
|
if (ec) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_Cancelled.load()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto f (onTimeout);
|
||||||
|
f(std::move(yc));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cancel();
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::asio::deadline_timer m_Timer;
|
||||||
|
std::atomic<bool> m_Cancelled;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* IO_ENGINE_H */
|
#endif /* IO_ENGINE_H */
|
||||||
|
22
lib/base/spinlock.cpp
Normal file
22
lib/base/spinlock.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* Icinga 2 | (c) 2020 Icinga GmbH | GPLv2+ */
|
||||||
|
|
||||||
|
#include "base/spinlock.hpp"
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
using namespace icinga;
|
||||||
|
|
||||||
|
void SpinLock::lock()
|
||||||
|
{
|
||||||
|
while (m_Locked.test_and_set(std::memory_order_acquire)) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpinLock::try_lock()
|
||||||
|
{
|
||||||
|
return !m_Locked.test_and_set(std::memory_order_acquire);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpinLock::unlock()
|
||||||
|
{
|
||||||
|
m_Locked.clear(std::memory_order_release);
|
||||||
|
}
|
35
lib/base/spinlock.hpp
Normal file
35
lib/base/spinlock.hpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* Icinga 2 | (c) 2020 Icinga GmbH | GPLv2+ */
|
||||||
|
|
||||||
|
#ifndef SPINLOCK_H
|
||||||
|
#define SPINLOCK_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace icinga
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A spin lock.
|
||||||
|
*
|
||||||
|
* @ingroup base
|
||||||
|
*/
|
||||||
|
class SpinLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SpinLock() = default;
|
||||||
|
SpinLock(const SpinLock&) = delete;
|
||||||
|
SpinLock& operator=(const SpinLock&) = delete;
|
||||||
|
SpinLock(SpinLock&&) = delete;
|
||||||
|
SpinLock& operator=(SpinLock&&) = delete;
|
||||||
|
|
||||||
|
void lock();
|
||||||
|
bool try_lock();
|
||||||
|
void unlock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic_flag m_Locked = ATOMIC_FLAG_INIT;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* SPINLOCK_H */
|
@ -41,6 +41,8 @@ void StreamLogger::FlushLogTimerHandler()
|
|||||||
|
|
||||||
void StreamLogger::Flush()
|
void StreamLogger::Flush()
|
||||||
{
|
{
|
||||||
|
ObjectLock oLock (this);
|
||||||
|
|
||||||
if (m_Stream)
|
if (m_Stream)
|
||||||
m_Stream->flush();
|
m_Stream->flush();
|
||||||
}
|
}
|
||||||
|
@ -9,17 +9,53 @@
|
|||||||
#include "base/stream.hpp"
|
#include "base/stream.hpp"
|
||||||
#include "base/tlsutility.hpp"
|
#include "base/tlsutility.hpp"
|
||||||
#include "base/fifo.hpp"
|
#include "base/fifo.hpp"
|
||||||
|
#include "base/utility.hpp"
|
||||||
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <boost/asio/buffered_stream.hpp>
|
#include <boost/asio/buffered_stream.hpp>
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/asio/spawn.hpp>
|
||||||
#include <boost/asio/ssl/context.hpp>
|
#include <boost/asio/ssl/context.hpp>
|
||||||
#include <boost/asio/ssl/stream.hpp>
|
#include <boost/asio/ssl/stream.hpp>
|
||||||
|
|
||||||
namespace icinga
|
namespace icinga
|
||||||
{
|
{
|
||||||
|
|
||||||
|
template<class ARS>
|
||||||
|
class SeenStream : public ARS
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<class... Args>
|
||||||
|
SeenStream(Args&&... args) : ARS(std::forward<Args>(args)...)
|
||||||
|
{
|
||||||
|
m_Seen.store(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
auto async_read_some(Args&&... args) -> decltype(((ARS*)nullptr)->async_read_some(std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
{
|
||||||
|
auto seen (m_Seen.load());
|
||||||
|
|
||||||
|
if (seen) {
|
||||||
|
*seen = Utility::GetTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((ARS*)this)->async_read_some(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SetSeen(double* seen)
|
||||||
|
{
|
||||||
|
m_Seen.store(seen);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic<double*> m_Seen;
|
||||||
|
};
|
||||||
|
|
||||||
struct UnbufferedAsioTlsStreamParams
|
struct UnbufferedAsioTlsStreamParams
|
||||||
{
|
{
|
||||||
boost::asio::io_context& IoContext;
|
boost::asio::io_context& IoContext;
|
||||||
@ -27,14 +63,14 @@ struct UnbufferedAsioTlsStreamParams
|
|||||||
const String& Hostname;
|
const String& Hostname;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> AsioTcpTlsStream;
|
typedef SeenStream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> AsioTcpTlsStream;
|
||||||
|
|
||||||
class UnbufferedAsioTlsStream : public AsioTcpTlsStream
|
class UnbufferedAsioTlsStream : public AsioTcpTlsStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline
|
inline
|
||||||
UnbufferedAsioTlsStream(UnbufferedAsioTlsStreamParams& init)
|
UnbufferedAsioTlsStream(UnbufferedAsioTlsStreamParams& init)
|
||||||
: stream(init.IoContext, init.SslContext), m_VerifyOK(true), m_Hostname(init.Hostname)
|
: AsioTcpTlsStream(init.IoContext, init.SslContext), m_VerifyOK(true), m_Hostname(init.Hostname)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,8 +52,6 @@ public:
|
|||||||
static String DirName(const String& path);
|
static String DirName(const String& path);
|
||||||
static String BaseName(const String& path);
|
static String BaseName(const String& path);
|
||||||
|
|
||||||
static String GetEnv(const String& key);
|
|
||||||
|
|
||||||
static void NullDeleter(void *);
|
static void NullDeleter(void *);
|
||||||
|
|
||||||
static double GetTime();
|
static double GetTime();
|
||||||
|
@ -14,9 +14,9 @@ using namespace icinga;
|
|||||||
std::atomic<int> WorkQueue::m_NextID(1);
|
std::atomic<int> WorkQueue::m_NextID(1);
|
||||||
boost::thread_specific_ptr<WorkQueue *> l_ThreadWorkQueue;
|
boost::thread_specific_ptr<WorkQueue *> l_ThreadWorkQueue;
|
||||||
|
|
||||||
WorkQueue::WorkQueue(size_t maxItems, int threadCount)
|
WorkQueue::WorkQueue(size_t maxItems, int threadCount, LogSeverity statsLogLevel)
|
||||||
: m_ID(m_NextID++), m_ThreadCount(threadCount), m_MaxItems(maxItems),
|
: m_ID(m_NextID++), m_ThreadCount(threadCount), m_MaxItems(maxItems),
|
||||||
m_TaskStats(15 * 60)
|
m_TaskStats(15 * 60), m_StatsLogLevel(statsLogLevel)
|
||||||
{
|
{
|
||||||
/* Initialize logger. */
|
/* Initialize logger. */
|
||||||
m_StatusTimerTimeout = Utility::GetTime();
|
m_StatusTimerTimeout = Utility::GetTime();
|
||||||
@ -216,7 +216,7 @@ void WorkQueue::StatusTimerHandler()
|
|||||||
|
|
||||||
/* Log if there are pending items, or 5 minute timeout is reached. */
|
/* Log if there are pending items, or 5 minute timeout is reached. */
|
||||||
if (pending > 0 || m_StatusTimerTimeout < now) {
|
if (pending > 0 || m_StatusTimerTimeout < now) {
|
||||||
Log(LogInformation, "WorkQueue")
|
Log(m_StatsLogLevel, "WorkQueue")
|
||||||
<< "#" << m_ID << " (" << m_Name << ") "
|
<< "#" << m_ID << " (" << m_Name << ") "
|
||||||
<< "items: " << pending << ", "
|
<< "items: " << pending << ", "
|
||||||
<< "rate: " << std::setw(2) << GetTaskCount(60) / 60.0 << "/s "
|
<< "rate: " << std::setw(2) << GetTaskCount(60) / 60.0 << "/s "
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "base/i2-base.hpp"
|
#include "base/i2-base.hpp"
|
||||||
#include "base/timer.hpp"
|
#include "base/timer.hpp"
|
||||||
#include "base/ringbuffer.hpp"
|
#include "base/ringbuffer.hpp"
|
||||||
|
#include "base/logger.hpp"
|
||||||
#include <boost/thread/thread.hpp>
|
#include <boost/thread/thread.hpp>
|
||||||
#include <boost/thread/mutex.hpp>
|
#include <boost/thread/mutex.hpp>
|
||||||
#include <boost/thread/condition_variable.hpp>
|
#include <boost/thread/condition_variable.hpp>
|
||||||
@ -52,7 +53,7 @@ class WorkQueue
|
|||||||
public:
|
public:
|
||||||
typedef std::function<void (boost::exception_ptr)> ExceptionCallback;
|
typedef std::function<void (boost::exception_ptr)> ExceptionCallback;
|
||||||
|
|
||||||
WorkQueue(size_t maxItems = 0, int threadCount = 1);
|
WorkQueue(size_t maxItems = 0, int threadCount = 1, LogSeverity statsLogLevel = LogInformation);
|
||||||
~WorkQueue();
|
~WorkQueue();
|
||||||
|
|
||||||
void SetName(const String& name);
|
void SetName(const String& name);
|
||||||
@ -129,6 +130,7 @@ private:
|
|||||||
std::vector<boost::exception_ptr> m_Exceptions;
|
std::vector<boost::exception_ptr> m_Exceptions;
|
||||||
Timer::Ptr m_StatusTimer;
|
Timer::Ptr m_StatusTimer;
|
||||||
double m_StatusTimerTimeout;
|
double m_StatusTimerTimeout;
|
||||||
|
LogSeverity m_StatsLogLevel;
|
||||||
|
|
||||||
RingBuffer m_TaskStats;
|
RingBuffer m_TaskStats;
|
||||||
size_t m_PendingTasks{0};
|
size_t m_PendingTasks{0};
|
||||||
|
@ -74,30 +74,6 @@ void CheckerComponent::Stop(bool runtimeRemoved)
|
|||||||
m_CV.notify_all();
|
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_ResultTimer->Stop();
|
||||||
m_Thread.join();
|
m_Thread.join();
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "base/defer.hpp"
|
#include "base/defer.hpp"
|
||||||
#include "base/logger.hpp"
|
#include "base/logger.hpp"
|
||||||
#include "base/application.hpp"
|
#include "base/application.hpp"
|
||||||
|
#include "base/process.hpp"
|
||||||
#include "base/timer.hpp"
|
#include "base/timer.hpp"
|
||||||
#include "base/utility.hpp"
|
#include "base/utility.hpp"
|
||||||
#include "base/exception.hpp"
|
#include "base/exception.hpp"
|
||||||
@ -397,6 +398,10 @@ static void UmbrellaSignalHandler(int num, siginfo_t *info, void*)
|
|||||||
static void WorkerSignalHandler(int num, siginfo_t *info, void*)
|
static void WorkerSignalHandler(int num, siginfo_t *info, void*)
|
||||||
{
|
{
|
||||||
switch (num) {
|
switch (num) {
|
||||||
|
case SIGUSR1:
|
||||||
|
// Catches SIGUSR1 as long as the actual handler (logrotate)
|
||||||
|
// has not been installed not to let SIGUSR1 terminate the process
|
||||||
|
break;
|
||||||
case SIGUSR2:
|
case SIGUSR2:
|
||||||
if (info->si_pid == 0 || info->si_pid == l_UmbrellaPid) {
|
if (info->si_pid == 0 || info->si_pid == l_UmbrellaPid) {
|
||||||
// The umbrella process allowed us to continue working beyond config validation
|
// The umbrella process allowed us to continue working beyond config validation
|
||||||
@ -489,6 +494,7 @@ static pid_t StartUnixWorker(const std::vector<std::string>& configs, bool close
|
|||||||
sa.sa_sigaction = &WorkerSignalHandler;
|
sa.sa_sigaction = &WorkerSignalHandler;
|
||||||
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||||
|
|
||||||
|
(void)sigaction(SIGUSR1, &sa, nullptr);
|
||||||
(void)sigaction(SIGUSR2, &sa, nullptr);
|
(void)sigaction(SIGUSR2, &sa, nullptr);
|
||||||
(void)sigaction(SIGINT, &sa, nullptr);
|
(void)sigaction(SIGINT, &sa, nullptr);
|
||||||
(void)sigaction(SIGTERM, &sa, nullptr);
|
(void)sigaction(SIGTERM, &sa, nullptr);
|
||||||
@ -504,6 +510,14 @@ static pid_t StartUnixWorker(const std::vector<std::string>& configs, bool close
|
|||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Process::InitializeSpawnHelper();
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
Log(LogCritical, "cli")
|
||||||
|
<< "Failed to initialize process spawn helper after forking (child): " << DiagnosticInformation(ex);
|
||||||
|
_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
_exit(RunWorker(configs, closeConsoleLog, stderrFile));
|
_exit(RunWorker(configs, closeConsoleLog, stderrFile));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
@ -776,6 +790,17 @@ int DaemonCommand::Run(const po::variables_map& vm, const std::vector<std::strin
|
|||||||
<< "Waited for " << Utility::FormatDuration(Utility::GetTime() - start) << " on old process to exit.";
|
<< "Waited for " << Utility::FormatDuration(Utility::GetTime() - start) << " on old process to exit.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int info;;) {
|
||||||
|
auto pid (waitpid(-1, &info, WNOHANG));
|
||||||
|
|
||||||
|
if (pid < 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(LogNotice, "cli")
|
||||||
|
<< "Reaped child process " << pid << ".";
|
||||||
|
}
|
||||||
|
|
||||||
// Old instance shut down, allow the new one to continue working beyond config validation
|
// Old instance shut down, allow the new one to continue working beyond config validation
|
||||||
(void)kill(nextWorker, SIGUSR2);
|
(void)kill(nextWorker, SIGUSR2);
|
||||||
|
|
||||||
|
@ -29,6 +29,10 @@ void PKISaveCertCommand::InitParameters(boost::program_options::options_descript
|
|||||||
("trustedcert", po::value<std::string>(), "Trusted certificate file path (output)")
|
("trustedcert", po::value<std::string>(), "Trusted certificate file path (output)")
|
||||||
("host", po::value<std::string>(), "Parent Icinga instance to fetch the public TLS certificate from")
|
("host", po::value<std::string>(), "Parent Icinga instance to fetch the public TLS certificate from")
|
||||||
("port", po::value<std::string>()->default_value("5665"), "Icinga 2 port");
|
("port", po::value<std::string>()->default_value("5665"), "Icinga 2 port");
|
||||||
|
|
||||||
|
hiddenDesc.add_options()
|
||||||
|
("key", po::value<std::string>())
|
||||||
|
("cert", po::value<std::string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<String> PKISaveCertCommand::GetArgumentSuggestions(const String& argument, const String& word) const
|
std::vector<String> PKISaveCertCommand::GetArgumentSuggestions(const String& argument, const String& word) const
|
||||||
|
@ -76,6 +76,13 @@ void DbConnection::Resume()
|
|||||||
m_CleanUpTimer->SetInterval(60);
|
m_CleanUpTimer->SetInterval(60);
|
||||||
m_CleanUpTimer->OnTimerExpired.connect(std::bind(&DbConnection::CleanUpHandler, this));
|
m_CleanUpTimer->OnTimerExpired.connect(std::bind(&DbConnection::CleanUpHandler, this));
|
||||||
m_CleanUpTimer->Start();
|
m_CleanUpTimer->Start();
|
||||||
|
|
||||||
|
m_LogStatsTimeout = 0;
|
||||||
|
|
||||||
|
m_LogStatsTimer = new Timer();
|
||||||
|
m_LogStatsTimer->SetInterval(10);
|
||||||
|
m_LogStatsTimer->OnTimerExpired.connect([this](const Timer * const&) { LogStatsHandler(); });
|
||||||
|
m_LogStatsTimer->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbConnection::Pause()
|
void DbConnection::Pause()
|
||||||
@ -145,7 +152,7 @@ void DbConnection::UpdateProgramStatus()
|
|||||||
DbQuery query1;
|
DbQuery query1;
|
||||||
query1.Table = "programstatus";
|
query1.Table = "programstatus";
|
||||||
query1.IdColumn = "programstatus_id";
|
query1.IdColumn = "programstatus_id";
|
||||||
query1.Type = DbQueryInsert | DbQueryUpdate;
|
query1.Type = DbQueryInsert | DbQueryDelete;
|
||||||
query1.Category = DbCatProgramStatus;
|
query1.Category = DbCatProgramStatus;
|
||||||
|
|
||||||
query1.Fields = new Dictionary({
|
query1.Fields = new Dictionary({
|
||||||
@ -172,7 +179,7 @@ void DbConnection::UpdateProgramStatus()
|
|||||||
{ "instance_id", 0 } /* DbConnection class fills in real ID */
|
{ "instance_id", 0 } /* DbConnection class fills in real ID */
|
||||||
});
|
});
|
||||||
|
|
||||||
query1.Priority = PriorityHigh;
|
query1.Priority = PriorityImmediate;
|
||||||
queries.emplace_back(std::move(query1));
|
queries.emplace_back(std::move(query1));
|
||||||
|
|
||||||
DbQuery query2;
|
DbQuery query2;
|
||||||
@ -236,6 +243,38 @@ void DbConnection::CleanUpHandler()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DbConnection::LogStatsHandler()
|
||||||
|
{
|
||||||
|
if (!GetConnected() || IsPaused())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto pending = m_PendingQueries.load();
|
||||||
|
|
||||||
|
auto now = Utility::GetTime();
|
||||||
|
bool timeoutReached = m_LogStatsTimeout < now;
|
||||||
|
|
||||||
|
if (pending == 0u && !timeoutReached) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto output = round(m_OutputQueries.CalculateRate(now, 10));
|
||||||
|
|
||||||
|
if (pending < output * 5 && !timeoutReached) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto input = round(m_InputQueries.CalculateRate(now, 10));
|
||||||
|
|
||||||
|
Log(LogInformation, GetReflectionType()->GetName())
|
||||||
|
<< "Pending queries: " << pending << " (Input: " << input
|
||||||
|
<< "/s; Output: " << output << "/s)";
|
||||||
|
|
||||||
|
/* Reschedule next log entry in 5 minutes. */
|
||||||
|
if (timeoutReached) {
|
||||||
|
m_LogStatsTimeout = now + 60 * 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DbConnection::CleanUpExecuteQuery(const String&, const String&, double)
|
void DbConnection::CleanUpExecuteQuery(const String&, const String&, double)
|
||||||
{
|
{
|
||||||
/* Default handler does nothing. */
|
/* Default handler does nothing. */
|
||||||
@ -446,7 +485,7 @@ void DbConnection::UpdateAllObjects()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
|
for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
|
||||||
UpdateObject(object);
|
m_QueryQueue.Enqueue([this, object](){ UpdateObject(object); }, PriorityHigh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -507,3 +546,15 @@ int DbConnection::GetSessionToken()
|
|||||||
{
|
{
|
||||||
return Application::GetStartTime();
|
return Application::GetStartTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DbConnection::IncreasePendingQueries(int count)
|
||||||
|
{
|
||||||
|
m_PendingQueries.fetch_add(count);
|
||||||
|
m_InputQueries.InsertValue(Utility::GetTime(), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DbConnection::DecreasePendingQueries(int count)
|
||||||
|
{
|
||||||
|
m_PendingQueries.fetch_sub(count);
|
||||||
|
m_OutputQueries.InsertValue(Utility::GetTime(), count);
|
||||||
|
}
|
||||||
|
@ -92,6 +92,11 @@ protected:
|
|||||||
|
|
||||||
static int GetSessionToken();
|
static int GetSessionToken();
|
||||||
|
|
||||||
|
void IncreasePendingQueries(int count);
|
||||||
|
void DecreasePendingQueries(int count);
|
||||||
|
|
||||||
|
WorkQueue m_QueryQueue{10000000, 1, LogNotice};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_IDCacheValid{false};
|
bool m_IDCacheValid{false};
|
||||||
std::map<std::pair<DbType::Ptr, DbReference>, String> m_ConfigHashes;
|
std::map<std::pair<DbType::Ptr, DbReference>, String> m_ConfigHashes;
|
||||||
@ -101,8 +106,12 @@ private:
|
|||||||
std::set<DbObject::Ptr> m_ConfigUpdates;
|
std::set<DbObject::Ptr> m_ConfigUpdates;
|
||||||
std::set<DbObject::Ptr> m_StatusUpdates;
|
std::set<DbObject::Ptr> m_StatusUpdates;
|
||||||
Timer::Ptr m_CleanUpTimer;
|
Timer::Ptr m_CleanUpTimer;
|
||||||
|
Timer::Ptr m_LogStatsTimer;
|
||||||
|
|
||||||
|
double m_LogStatsTimeout;
|
||||||
|
|
||||||
void CleanUpHandler();
|
void CleanUpHandler();
|
||||||
|
void LogStatsHandler();
|
||||||
|
|
||||||
static Timer::Ptr m_ProgramStatusTimer;
|
static Timer::Ptr m_ProgramStatusTimer;
|
||||||
static boost::once_flag m_OnceFlag;
|
static boost::once_flag m_OnceFlag;
|
||||||
@ -112,6 +121,10 @@ private:
|
|||||||
mutable boost::mutex m_StatsMutex;
|
mutable boost::mutex m_StatsMutex;
|
||||||
RingBuffer m_QueryStats{15 * 60};
|
RingBuffer m_QueryStats{15 * 60};
|
||||||
bool m_ActiveChangedHandler{false};
|
bool m_ActiveChangedHandler{false};
|
||||||
|
|
||||||
|
RingBuffer m_InputQueries{10};
|
||||||
|
RingBuffer m_OutputQueries{10};
|
||||||
|
Atomic<uint_fast64_t> m_PendingQueries{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct database_error : virtual std::exception, virtual boost::exception { };
|
struct database_error : virtual std::exception, virtual boost::exception { };
|
||||||
|
@ -111,7 +111,6 @@ void DbObject::SendConfigUpdateHeavy(const Dictionary::Ptr& configFields)
|
|||||||
{
|
{
|
||||||
/* update custom var config and status */
|
/* update custom var config and status */
|
||||||
SendVarsConfigUpdateHeavy();
|
SendVarsConfigUpdateHeavy();
|
||||||
SendVarsStatusUpdate();
|
|
||||||
|
|
||||||
/* config attributes */
|
/* config attributes */
|
||||||
if (!configFields)
|
if (!configFields)
|
||||||
@ -245,6 +244,22 @@ void DbObject::SendVarsConfigUpdateHeavy()
|
|||||||
{ "instance_id", 0 } /* DbConnection class fills in real ID */
|
{ "instance_id", 0 } /* DbConnection class fills in real ID */
|
||||||
});
|
});
|
||||||
queries.emplace_back(std::move(query3));
|
queries.emplace_back(std::move(query3));
|
||||||
|
|
||||||
|
DbQuery query4;
|
||||||
|
query4.Table = "customvariablestatus";
|
||||||
|
query4.Type = DbQueryInsert;
|
||||||
|
query4.Category = DbCatState;
|
||||||
|
|
||||||
|
query4.Fields = new Dictionary({
|
||||||
|
{ "varname", kv.first },
|
||||||
|
{ "varvalue", value },
|
||||||
|
{ "is_json", is_json },
|
||||||
|
{ "status_update_time", DbValue::FromTimestamp(Utility::GetTime()) },
|
||||||
|
{ "object_id", obj },
|
||||||
|
{ "instance_id", 0 } /* DbConnection class fills in real ID */
|
||||||
|
});
|
||||||
|
|
||||||
|
queries.emplace_back(std::move(query4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,8 +356,12 @@ String HostDbObject::CalculateConfigHash(const Dictionary::Ptr& configFields) co
|
|||||||
|
|
||||||
Array::Ptr groups = host->GetGroups();
|
Array::Ptr groups = host->GetGroups();
|
||||||
|
|
||||||
if (groups)
|
if (groups) {
|
||||||
|
groups = groups->ShallowClone();
|
||||||
|
ObjectLock oLock (groups);
|
||||||
|
std::sort(groups->Begin(), groups->End());
|
||||||
hashData += DbObject::HashValue(groups);
|
hashData += DbObject::HashValue(groups);
|
||||||
|
}
|
||||||
|
|
||||||
ArrayData parents;
|
ArrayData parents;
|
||||||
|
|
||||||
|
@ -308,8 +308,12 @@ String ServiceDbObject::CalculateConfigHash(const Dictionary::Ptr& configFields)
|
|||||||
|
|
||||||
Array::Ptr groups = service->GetGroups();
|
Array::Ptr groups = service->GetGroups();
|
||||||
|
|
||||||
if (groups)
|
if (groups) {
|
||||||
|
groups = groups->ShallowClone();
|
||||||
|
ObjectLock oLock (groups);
|
||||||
|
std::sort(groups->Begin(), groups->End());
|
||||||
hashData += DbObject::HashValue(groups);
|
hashData += DbObject::HashValue(groups);
|
||||||
|
}
|
||||||
|
|
||||||
ArrayData dependencies;
|
ArrayData dependencies;
|
||||||
|
|
||||||
|
@ -150,8 +150,12 @@ String UserDbObject::CalculateConfigHash(const Dictionary::Ptr& configFields) co
|
|||||||
|
|
||||||
Array::Ptr groups = user->GetGroups();
|
Array::Ptr groups = user->GetGroups();
|
||||||
|
|
||||||
if (groups)
|
if (groups) {
|
||||||
|
groups = groups->ShallowClone();
|
||||||
|
ObjectLock oLock (groups);
|
||||||
|
std::sort(groups->Begin(), groups->End());
|
||||||
hashData += DbObject::HashValue(groups);
|
hashData += DbObject::HashValue(groups);
|
||||||
|
}
|
||||||
|
|
||||||
return SHA256(hashData);
|
return SHA256(hashData);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "base/configtype.hpp"
|
#include "base/configtype.hpp"
|
||||||
#include "base/exception.hpp"
|
#include "base/exception.hpp"
|
||||||
#include "base/statsfunction.hpp"
|
#include "base/statsfunction.hpp"
|
||||||
|
#include "base/defer.hpp"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
using namespace icinga;
|
using namespace icinga;
|
||||||
@ -175,6 +176,8 @@ void IdoMysqlConnection::InternalNewTransaction()
|
|||||||
if (!GetConnected())
|
if (!GetConnected())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
IncreasePendingQueries(2);
|
||||||
|
|
||||||
AsyncQuery("COMMIT");
|
AsyncQuery("COMMIT");
|
||||||
AsyncQuery("BEGIN");
|
AsyncQuery("BEGIN");
|
||||||
}
|
}
|
||||||
@ -472,7 +475,7 @@ void IdoMysqlConnection::FinishConnect(double startTime)
|
|||||||
{
|
{
|
||||||
AssertOnWorkQueue();
|
AssertOnWorkQueue();
|
||||||
|
|
||||||
if (!GetConnected())
|
if (!GetConnected() || IsPaused())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FinishAsyncQueries();
|
FinishAsyncQueries();
|
||||||
@ -510,11 +513,6 @@ void IdoMysqlConnection::AsyncQuery(const String& query, const std::function<voi
|
|||||||
*/
|
*/
|
||||||
aq.Callback = callback;
|
aq.Callback = callback;
|
||||||
m_AsyncQueries.emplace_back(std::move(aq));
|
m_AsyncQueries.emplace_back(std::move(aq));
|
||||||
|
|
||||||
if (m_AsyncQueries.size() > 25000) {
|
|
||||||
FinishAsyncQueries();
|
|
||||||
InternalNewTransaction();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IdoMysqlConnection::FinishAsyncQueries()
|
void IdoMysqlConnection::FinishAsyncQueries()
|
||||||
@ -524,12 +522,29 @@ void IdoMysqlConnection::FinishAsyncQueries()
|
|||||||
|
|
||||||
std::vector<IdoAsyncQuery>::size_type offset = 0;
|
std::vector<IdoAsyncQuery>::size_type offset = 0;
|
||||||
|
|
||||||
|
// This will be executed if there is a problem with executing the queries,
|
||||||
|
// at which point this function throws an exception and the queries should
|
||||||
|
// not be listed as still pending in the queue.
|
||||||
|
Defer decreaseQueries ([this, &offset, &queries]() {
|
||||||
|
auto lostQueries = queries.size() - offset;
|
||||||
|
|
||||||
|
if (lostQueries > 0) {
|
||||||
|
DecreasePendingQueries(lostQueries);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
while (offset < queries.size()) {
|
while (offset < queries.size()) {
|
||||||
std::ostringstream querybuf;
|
std::ostringstream querybuf;
|
||||||
|
|
||||||
std::vector<IdoAsyncQuery>::size_type count = 0;
|
std::vector<IdoAsyncQuery>::size_type count = 0;
|
||||||
size_t num_bytes = 0;
|
size_t num_bytes = 0;
|
||||||
|
|
||||||
|
Defer decreaseQueries ([this, &offset, &count]() {
|
||||||
|
offset += count;
|
||||||
|
DecreasePendingQueries(count);
|
||||||
|
m_UncommittedAsyncQueries += count;
|
||||||
|
});
|
||||||
|
|
||||||
for (std::vector<IdoAsyncQuery>::size_type i = offset; i < queries.size(); i++) {
|
for (std::vector<IdoAsyncQuery>::size_type i = offset; i < queries.size(); i++) {
|
||||||
const IdoAsyncQuery& aq = queries[i];
|
const IdoAsyncQuery& aq = queries[i];
|
||||||
|
|
||||||
@ -608,8 +623,13 @@ void IdoMysqlConnection::FinishAsyncQueries()
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
offset += count;
|
if (m_UncommittedAsyncQueries > 25000) {
|
||||||
|
m_UncommittedAsyncQueries = 0;
|
||||||
|
|
||||||
|
Query("COMMIT");
|
||||||
|
Query("BEGIN");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -617,6 +637,9 @@ IdoMysqlResult IdoMysqlConnection::Query(const String& query)
|
|||||||
{
|
{
|
||||||
AssertOnWorkQueue();
|
AssertOnWorkQueue();
|
||||||
|
|
||||||
|
IncreasePendingQueries(1);
|
||||||
|
Defer decreaseQueries ([this]() { DecreasePendingQueries(1); });
|
||||||
|
|
||||||
/* finish all async queries to maintain the right order for queries */
|
/* finish all async queries to maintain the right order for queries */
|
||||||
FinishAsyncQueries();
|
FinishAsyncQueries();
|
||||||
|
|
||||||
@ -770,6 +793,7 @@ void IdoMysqlConnection::InternalActivateObject(const DbObject::Ptr& dbobj)
|
|||||||
SetObjectID(dbobj, GetLastInsertID());
|
SetObjectID(dbobj, GetLastInsertID());
|
||||||
} else {
|
} else {
|
||||||
qbuf << "UPDATE " + GetTablePrefix() + "objects SET is_active = 1 WHERE object_id = " << static_cast<long>(dbref);
|
qbuf << "UPDATE " + GetTablePrefix() + "objects SET is_active = 1 WHERE object_id = " << static_cast<long>(dbref);
|
||||||
|
IncreasePendingQueries(1);
|
||||||
AsyncQuery(qbuf.str());
|
AsyncQuery(qbuf.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -804,6 +828,7 @@ void IdoMysqlConnection::InternalDeactivateObject(const DbObject::Ptr& dbobj)
|
|||||||
|
|
||||||
std::ostringstream qbuf;
|
std::ostringstream qbuf;
|
||||||
qbuf << "UPDATE " + GetTablePrefix() + "objects SET is_active = 0 WHERE object_id = " << static_cast<long>(dbref);
|
qbuf << "UPDATE " + GetTablePrefix() + "objects SET is_active = 0 WHERE object_id = " << static_cast<long>(dbref);
|
||||||
|
IncreasePendingQueries(1);
|
||||||
AsyncQuery(qbuf.str());
|
AsyncQuery(qbuf.str());
|
||||||
|
|
||||||
/* Note that we're _NOT_ clearing the db refs via SetReference/SetConfigUpdate/SetStatusUpdate
|
/* Note that we're _NOT_ clearing the db refs via SetReference/SetConfigUpdate/SetStatusUpdate
|
||||||
@ -893,6 +918,7 @@ void IdoMysqlConnection::ExecuteQuery(const DbQuery& query)
|
|||||||
<< "Scheduling execute query task, type " << query.Type << ", table '" << query.Table << "'.";
|
<< "Scheduling execute query task, type " << query.Type << ", table '" << query.Table << "'.";
|
||||||
#endif /* I2_DEBUG */
|
#endif /* I2_DEBUG */
|
||||||
|
|
||||||
|
IncreasePendingQueries(1);
|
||||||
m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalExecuteQuery, this, query, -1), query.Priority, true);
|
m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalExecuteQuery, this, query, -1), query.Priority, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -909,6 +935,7 @@ void IdoMysqlConnection::ExecuteMultipleQueries(const std::vector<DbQuery>& quer
|
|||||||
<< "Scheduling multiple execute query task, type " << queries[0].Type << ", table '" << queries[0].Table << "'.";
|
<< "Scheduling multiple execute query task, type " << queries[0].Type << ", table '" << queries[0].Table << "'.";
|
||||||
#endif /* I2_DEBUG */
|
#endif /* I2_DEBUG */
|
||||||
|
|
||||||
|
IncreasePendingQueries(queries.size());
|
||||||
m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalExecuteMultipleQueries, this, queries), queries[0].Priority, true);
|
m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalExecuteMultipleQueries, this, queries), queries[0].Priority, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -948,11 +975,16 @@ void IdoMysqlConnection::InternalExecuteMultipleQueries(const std::vector<DbQuer
|
|||||||
{
|
{
|
||||||
AssertOnWorkQueue();
|
AssertOnWorkQueue();
|
||||||
|
|
||||||
if (IsPaused())
|
if (IsPaused()) {
|
||||||
|
DecreasePendingQueries(queries.size());
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!GetConnected())
|
if (!GetConnected()) {
|
||||||
|
DecreasePendingQueries(queries.size());
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for (const DbQuery& query : queries) {
|
for (const DbQuery& query : queries) {
|
||||||
ASSERT(query.Type == DbQueryNewTransaction || query.Category != DbCatInvalid);
|
ASSERT(query.Type == DbQueryNewTransaction || query.Category != DbCatInvalid);
|
||||||
@ -979,23 +1011,32 @@ void IdoMysqlConnection::InternalExecuteQuery(const DbQuery& query, int typeOver
|
|||||||
{
|
{
|
||||||
AssertOnWorkQueue();
|
AssertOnWorkQueue();
|
||||||
|
|
||||||
if (IsPaused())
|
if (IsPaused()) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!GetConnected())
|
if (!GetConnected()) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (query.Type == DbQueryNewTransaction) {
|
if (query.Type == DbQueryNewTransaction) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
InternalNewTransaction();
|
InternalNewTransaction();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check whether we're allowed to execute the query first */
|
/* check whether we're allowed to execute the query first */
|
||||||
if (GetCategoryFilter() != DbCatEverything && (query.Category & GetCategoryFilter()) == 0)
|
if (GetCategoryFilter() != DbCatEverything && (query.Category & GetCategoryFilter()) == 0) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (query.Object && query.Object->GetObject()->GetExtension("agent_check").ToBool())
|
if (query.Object && query.Object->GetObject()->GetExtension("agent_check").ToBool()) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* check if there are missing object/insert ids and re-enqueue the query */
|
/* check if there are missing object/insert ids and re-enqueue the query */
|
||||||
if (!CanExecuteQuery(query)) {
|
if (!CanExecuteQuery(query)) {
|
||||||
@ -1066,6 +1107,7 @@ void IdoMysqlConnection::InternalExecuteQuery(const DbQuery& query, int typeOver
|
|||||||
if ((type & DbQueryInsert) && (type & DbQueryDelete)) {
|
if ((type & DbQueryInsert) && (type & DbQueryDelete)) {
|
||||||
std::ostringstream qdel;
|
std::ostringstream qdel;
|
||||||
qdel << "DELETE FROM " << GetTablePrefix() << query.Table << where.str();
|
qdel << "DELETE FROM " << GetTablePrefix() << query.Table << where.str();
|
||||||
|
IncreasePendingQueries(1);
|
||||||
AsyncQuery(qdel.str());
|
AsyncQuery(qdel.str());
|
||||||
|
|
||||||
type = DbQueryInsert;
|
type = DbQueryInsert;
|
||||||
@ -1150,6 +1192,7 @@ void IdoMysqlConnection::FinishExecuteQuery(const DbQuery& query, int type, bool
|
|||||||
<< "Rescheduling DELETE/INSERT query: Upsert UPDATE did not affect rows, type " << type << ", table '" << query.Table << "'.";
|
<< "Rescheduling DELETE/INSERT query: Upsert UPDATE did not affect rows, type " << type << ", table '" << query.Table << "'.";
|
||||||
#endif /* I2_DEBUG */
|
#endif /* I2_DEBUG */
|
||||||
|
|
||||||
|
IncreasePendingQueries(1);
|
||||||
m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalExecuteQuery, this, query, DbQueryDelete | DbQueryInsert), query.Priority);
|
m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalExecuteQuery, this, query, DbQueryDelete | DbQueryInsert), query.Priority);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -1178,6 +1221,7 @@ void IdoMysqlConnection::CleanUpExecuteQuery(const String& table, const String&
|
|||||||
<< time_column << "'. max_age is set to '" << max_age << "'.";
|
<< time_column << "'. max_age is set to '" << max_age << "'.";
|
||||||
#endif /* I2_DEBUG */
|
#endif /* I2_DEBUG */
|
||||||
|
|
||||||
|
IncreasePendingQueries(1);
|
||||||
m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalCleanUpExecuteQuery, this, table, time_column, max_age), PriorityLow, true);
|
m_QueryQueue.Enqueue(std::bind(&IdoMysqlConnection::InternalCleanUpExecuteQuery, this, table, time_column, max_age), PriorityLow, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1185,11 +1229,15 @@ void IdoMysqlConnection::InternalCleanUpExecuteQuery(const String& table, const
|
|||||||
{
|
{
|
||||||
AssertOnWorkQueue();
|
AssertOnWorkQueue();
|
||||||
|
|
||||||
if (IsPaused())
|
if (IsPaused()) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!GetConnected())
|
if (!GetConnected()) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AsyncQuery("DELETE FROM " + GetTablePrefix() + table + " WHERE instance_id = " +
|
AsyncQuery("DELETE FROM " + GetTablePrefix() + table + " WHERE instance_id = " +
|
||||||
Convert::ToString(static_cast<long>(m_InstanceID)) + " AND " + time_column +
|
Convert::ToString(static_cast<long>(m_InstanceID)) + " AND " + time_column +
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "base/timer.hpp"
|
#include "base/timer.hpp"
|
||||||
#include "base/workqueue.hpp"
|
#include "base/workqueue.hpp"
|
||||||
#include "base/library.hpp"
|
#include "base/library.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace icinga
|
namespace icinga
|
||||||
{
|
{
|
||||||
@ -54,8 +55,6 @@ protected:
|
|||||||
private:
|
private:
|
||||||
DbReference m_InstanceID;
|
DbReference m_InstanceID;
|
||||||
|
|
||||||
WorkQueue m_QueryQueue{10000000};
|
|
||||||
|
|
||||||
Library m_Library;
|
Library m_Library;
|
||||||
std::unique_ptr<MysqlInterface, MysqlInterfaceDeleter> m_Mysql;
|
std::unique_ptr<MysqlInterface, MysqlInterfaceDeleter> m_Mysql;
|
||||||
|
|
||||||
@ -64,6 +63,7 @@ private:
|
|||||||
unsigned int m_MaxPacketSize;
|
unsigned int m_MaxPacketSize;
|
||||||
|
|
||||||
std::vector<IdoAsyncQuery> m_AsyncQueries;
|
std::vector<IdoAsyncQuery> m_AsyncQueries;
|
||||||
|
uint_fast32_t m_UncommittedAsyncQueries = 0;
|
||||||
|
|
||||||
Timer::Ptr m_ReconnectTimer;
|
Timer::Ptr m_ReconnectTimer;
|
||||||
Timer::Ptr m_TxTimer;
|
Timer::Ptr m_TxTimer;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "base/exception.hpp"
|
#include "base/exception.hpp"
|
||||||
#include "base/context.hpp"
|
#include "base/context.hpp"
|
||||||
#include "base/statsfunction.hpp"
|
#include "base/statsfunction.hpp"
|
||||||
|
#include "base/defer.hpp"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
using namespace icinga;
|
using namespace icinga;
|
||||||
@ -137,6 +138,7 @@ void IdoPgsqlConnection::Disconnect()
|
|||||||
if (!GetConnected())
|
if (!GetConnected())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
IncreasePendingQueries(1);
|
||||||
Query("COMMIT");
|
Query("COMMIT");
|
||||||
|
|
||||||
m_Pgsql->finish(m_Connection);
|
m_Pgsql->finish(m_Connection);
|
||||||
@ -166,6 +168,7 @@ void IdoPgsqlConnection::InternalNewTransaction()
|
|||||||
if (!GetConnected())
|
if (!GetConnected())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
IncreasePendingQueries(2);
|
||||||
Query("COMMIT");
|
Query("COMMIT");
|
||||||
Query("BEGIN");
|
Query("BEGIN");
|
||||||
}
|
}
|
||||||
@ -191,6 +194,7 @@ void IdoPgsqlConnection::Reconnect()
|
|||||||
if (GetConnected()) {
|
if (GetConnected()) {
|
||||||
/* Check if we're really still connected */
|
/* Check if we're really still connected */
|
||||||
try {
|
try {
|
||||||
|
IncreasePendingQueries(1);
|
||||||
Query("SELECT 1");
|
Query("SELECT 1");
|
||||||
return;
|
return;
|
||||||
} catch (const std::exception&) {
|
} catch (const std::exception&) {
|
||||||
@ -260,10 +264,13 @@ void IdoPgsqlConnection::Reconnect()
|
|||||||
/* explicitely require legacy mode for string escaping in PostgreSQL >= 9.1
|
/* explicitely require legacy mode for string escaping in PostgreSQL >= 9.1
|
||||||
* changing standard_conforming_strings to on by default
|
* changing standard_conforming_strings to on by default
|
||||||
*/
|
*/
|
||||||
if (m_Pgsql->serverVersion(m_Connection) >= 90100)
|
if (m_Pgsql->serverVersion(m_Connection) >= 90100) {
|
||||||
|
IncreasePendingQueries(1);
|
||||||
result = Query("SET standard_conforming_strings TO off");
|
result = Query("SET standard_conforming_strings TO off");
|
||||||
|
}
|
||||||
|
|
||||||
String dbVersionName = "idoutils";
|
String dbVersionName = "idoutils";
|
||||||
|
IncreasePendingQueries(1);
|
||||||
result = Query("SELECT version FROM " + GetTablePrefix() + "dbversion WHERE name=E'" + Escape(dbVersionName) + "'");
|
result = Query("SELECT version FROM " + GetTablePrefix() + "dbversion WHERE name=E'" + Escape(dbVersionName) + "'");
|
||||||
|
|
||||||
Dictionary::Ptr row = FetchRow(result, 0);
|
Dictionary::Ptr row = FetchRow(result, 0);
|
||||||
@ -295,10 +302,12 @@ void IdoPgsqlConnection::Reconnect()
|
|||||||
|
|
||||||
String instanceName = GetInstanceName();
|
String instanceName = GetInstanceName();
|
||||||
|
|
||||||
|
IncreasePendingQueries(1);
|
||||||
result = Query("SELECT instance_id FROM " + GetTablePrefix() + "instances WHERE instance_name = E'" + Escape(instanceName) + "'");
|
result = Query("SELECT instance_id FROM " + GetTablePrefix() + "instances WHERE instance_name = E'" + Escape(instanceName) + "'");
|
||||||
row = FetchRow(result, 0);
|
row = FetchRow(result, 0);
|
||||||
|
|
||||||
if (!row) {
|
if (!row) {
|
||||||
|
IncreasePendingQueries(1);
|
||||||
Query("INSERT INTO " + GetTablePrefix() + "instances (instance_name, instance_description) VALUES (E'" + Escape(instanceName) + "', E'" + Escape(GetInstanceDescription()) + "')");
|
Query("INSERT INTO " + GetTablePrefix() + "instances (instance_name, instance_description) VALUES (E'" + Escape(instanceName) + "', E'" + Escape(GetInstanceDescription()) + "')");
|
||||||
m_InstanceID = GetSequenceValue(GetTablePrefix() + "instances", "instance_id");
|
m_InstanceID = GetSequenceValue(GetTablePrefix() + "instances", "instance_id");
|
||||||
} else {
|
} else {
|
||||||
@ -310,6 +319,7 @@ void IdoPgsqlConnection::Reconnect()
|
|||||||
/* we have an endpoint in a cluster setup, so decide if we can proceed here */
|
/* we have an endpoint in a cluster setup, so decide if we can proceed here */
|
||||||
if (my_endpoint && GetHAMode() == HARunOnce) {
|
if (my_endpoint && GetHAMode() == HARunOnce) {
|
||||||
/* get the current endpoint writing to programstatus table */
|
/* get the current endpoint writing to programstatus table */
|
||||||
|
IncreasePendingQueries(1);
|
||||||
result = Query("SELECT UNIX_TIMESTAMP(status_update_time) AS status_update_time, endpoint_name FROM " +
|
result = Query("SELECT UNIX_TIMESTAMP(status_update_time) AS status_update_time, endpoint_name FROM " +
|
||||||
GetTablePrefix() + "programstatus WHERE instance_id = " + Convert::ToString(m_InstanceID));
|
GetTablePrefix() + "programstatus WHERE instance_id = " + Convert::ToString(m_InstanceID));
|
||||||
row = FetchRow(result, 0);
|
row = FetchRow(result, 0);
|
||||||
@ -372,12 +382,14 @@ void IdoPgsqlConnection::Reconnect()
|
|||||||
<< "PGSQL IDO instance id: " << static_cast<long>(m_InstanceID) << " (schema version: '" + version + "')"
|
<< "PGSQL IDO instance id: " << static_cast<long>(m_InstanceID) << " (schema version: '" + version + "')"
|
||||||
<< (!sslMode.IsEmpty() ? ", sslmode='" + sslMode + "'" : "");
|
<< (!sslMode.IsEmpty() ? ", sslmode='" + sslMode + "'" : "");
|
||||||
|
|
||||||
|
IncreasePendingQueries(1);
|
||||||
Query("BEGIN");
|
Query("BEGIN");
|
||||||
|
|
||||||
/* update programstatus table */
|
/* update programstatus table */
|
||||||
UpdateProgramStatus();
|
UpdateProgramStatus();
|
||||||
|
|
||||||
/* record connection */
|
/* record connection */
|
||||||
|
IncreasePendingQueries(1);
|
||||||
Query("INSERT INTO " + GetTablePrefix() + "conninfo " +
|
Query("INSERT INTO " + GetTablePrefix() + "conninfo " +
|
||||||
"(instance_id, connect_time, last_checkin_time, agent_name, agent_version, connect_type, data_start_time) VALUES ("
|
"(instance_id, connect_time, last_checkin_time, agent_name, agent_version, connect_type, data_start_time) VALUES ("
|
||||||
+ Convert::ToString(static_cast<long>(m_InstanceID)) + ", NOW(), NOW(), E'icinga2 db_ido_pgsql', E'" + Escape(Application::GetAppVersion())
|
+ Convert::ToString(static_cast<long>(m_InstanceID)) + ", NOW(), NOW(), E'icinga2 db_ido_pgsql', E'" + Escape(Application::GetAppVersion())
|
||||||
@ -388,6 +400,7 @@ void IdoPgsqlConnection::Reconnect()
|
|||||||
|
|
||||||
std::ostringstream q1buf;
|
std::ostringstream q1buf;
|
||||||
q1buf << "SELECT object_id, objecttype_id, name1, name2, is_active FROM " + GetTablePrefix() + "objects WHERE instance_id = " << static_cast<long>(m_InstanceID);
|
q1buf << "SELECT object_id, objecttype_id, name1, name2, is_active FROM " + GetTablePrefix() + "objects WHERE instance_id = " << static_cast<long>(m_InstanceID);
|
||||||
|
IncreasePendingQueries(1);
|
||||||
result = Query(q1buf.str());
|
result = Query(q1buf.str());
|
||||||
|
|
||||||
std::vector<DbObject::Ptr> activeDbObjs;
|
std::vector<DbObject::Ptr> activeDbObjs;
|
||||||
@ -442,6 +455,7 @@ void IdoPgsqlConnection::FinishConnect(double startTime)
|
|||||||
<< "Finished reconnecting to '" << GetName() << "' database '" << GetDatabase() << "' in "
|
<< "Finished reconnecting to '" << GetName() << "' database '" << GetDatabase() << "' in "
|
||||||
<< std::setw(2) << Utility::GetTime() - startTime << " second(s).";
|
<< std::setw(2) << Utility::GetTime() - startTime << " second(s).";
|
||||||
|
|
||||||
|
IncreasePendingQueries(2);
|
||||||
Query("COMMIT");
|
Query("COMMIT");
|
||||||
Query("BEGIN");
|
Query("BEGIN");
|
||||||
}
|
}
|
||||||
@ -455,6 +469,7 @@ void IdoPgsqlConnection::ClearTablesBySession()
|
|||||||
|
|
||||||
void IdoPgsqlConnection::ClearTableBySession(const String& table)
|
void IdoPgsqlConnection::ClearTableBySession(const String& table)
|
||||||
{
|
{
|
||||||
|
IncreasePendingQueries(1);
|
||||||
Query("DELETE FROM " + GetTablePrefix() + table + " WHERE instance_id = " +
|
Query("DELETE FROM " + GetTablePrefix() + table + " WHERE instance_id = " +
|
||||||
Convert::ToString(static_cast<long>(m_InstanceID)) + " AND session_token <> " +
|
Convert::ToString(static_cast<long>(m_InstanceID)) + " AND session_token <> " +
|
||||||
Convert::ToString(GetSessionToken()));
|
Convert::ToString(GetSessionToken()));
|
||||||
@ -464,6 +479,8 @@ IdoPgsqlResult IdoPgsqlConnection::Query(const String& query)
|
|||||||
{
|
{
|
||||||
AssertOnWorkQueue();
|
AssertOnWorkQueue();
|
||||||
|
|
||||||
|
Defer decreaseQueries ([this]() { DecreasePendingQueries(1); });
|
||||||
|
|
||||||
Log(LogDebug, "IdoPgsqlConnection")
|
Log(LogDebug, "IdoPgsqlConnection")
|
||||||
<< "Query: " << query;
|
<< "Query: " << query;
|
||||||
|
|
||||||
@ -512,6 +529,7 @@ DbReference IdoPgsqlConnection::GetSequenceValue(const String& table, const Stri
|
|||||||
{
|
{
|
||||||
AssertOnWorkQueue();
|
AssertOnWorkQueue();
|
||||||
|
|
||||||
|
IncreasePendingQueries(1);
|
||||||
IdoPgsqlResult result = Query("SELECT CURRVAL(pg_get_serial_sequence(E'" + Escape(table) + "', E'" + Escape(column) + "')) AS id");
|
IdoPgsqlResult result = Query("SELECT CURRVAL(pg_get_serial_sequence(E'" + Escape(table) + "', E'" + Escape(column) + "')) AS id");
|
||||||
|
|
||||||
Dictionary::Ptr row = FetchRow(result, 0);
|
Dictionary::Ptr row = FetchRow(result, 0);
|
||||||
@ -601,10 +619,12 @@ void IdoPgsqlConnection::InternalActivateObject(const DbObject::Ptr& dbobj)
|
|||||||
<< "E'" << Escape(dbobj->GetName1()) << "', 1)";
|
<< "E'" << Escape(dbobj->GetName1()) << "', 1)";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IncreasePendingQueries(1);
|
||||||
Query(qbuf.str());
|
Query(qbuf.str());
|
||||||
SetObjectID(dbobj, GetSequenceValue(GetTablePrefix() + "objects", "object_id"));
|
SetObjectID(dbobj, GetSequenceValue(GetTablePrefix() + "objects", "object_id"));
|
||||||
} else {
|
} else {
|
||||||
qbuf << "UPDATE " + GetTablePrefix() + "objects SET is_active = 1 WHERE object_id = " << static_cast<long>(dbref);
|
qbuf << "UPDATE " + GetTablePrefix() + "objects SET is_active = 1 WHERE object_id = " << static_cast<long>(dbref);
|
||||||
|
IncreasePendingQueries(1);
|
||||||
Query(qbuf.str());
|
Query(qbuf.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -631,6 +651,7 @@ void IdoPgsqlConnection::InternalDeactivateObject(const DbObject::Ptr& dbobj)
|
|||||||
|
|
||||||
std::ostringstream qbuf;
|
std::ostringstream qbuf;
|
||||||
qbuf << "UPDATE " + GetTablePrefix() + "objects SET is_active = 0 WHERE object_id = " << static_cast<long>(dbref);
|
qbuf << "UPDATE " + GetTablePrefix() + "objects SET is_active = 0 WHERE object_id = " << static_cast<long>(dbref);
|
||||||
|
IncreasePendingQueries(1);
|
||||||
Query(qbuf.str());
|
Query(qbuf.str());
|
||||||
|
|
||||||
/* Note that we're _NOT_ clearing the db refs via SetReference/SetConfigUpdate/SetStatusUpdate
|
/* Note that we're _NOT_ clearing the db refs via SetReference/SetConfigUpdate/SetStatusUpdate
|
||||||
@ -715,6 +736,7 @@ void IdoPgsqlConnection::ExecuteQuery(const DbQuery& query)
|
|||||||
|
|
||||||
ASSERT(query.Category != DbCatInvalid);
|
ASSERT(query.Category != DbCatInvalid);
|
||||||
|
|
||||||
|
IncreasePendingQueries(1);
|
||||||
m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalExecuteQuery, this, query, -1), query.Priority, true);
|
m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalExecuteQuery, this, query, -1), query.Priority, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,6 +748,7 @@ void IdoPgsqlConnection::ExecuteMultipleQueries(const std::vector<DbQuery>& quer
|
|||||||
if (queries.empty())
|
if (queries.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
IncreasePendingQueries(queries.size());
|
||||||
m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalExecuteMultipleQueries, this, queries), queries[0].Priority, true);
|
m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalExecuteMultipleQueries, this, queries), queries[0].Priority, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -765,11 +788,15 @@ void IdoPgsqlConnection::InternalExecuteMultipleQueries(const std::vector<DbQuer
|
|||||||
{
|
{
|
||||||
AssertOnWorkQueue();
|
AssertOnWorkQueue();
|
||||||
|
|
||||||
if (IsPaused())
|
if (IsPaused()) {
|
||||||
|
DecreasePendingQueries(queries.size());
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!GetConnected())
|
if (!GetConnected()) {
|
||||||
|
DecreasePendingQueries(queries.size());
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const DbQuery& query : queries) {
|
for (const DbQuery& query : queries) {
|
||||||
ASSERT(query.Type == DbQueryNewTransaction || query.Category != DbCatInvalid);
|
ASSERT(query.Type == DbQueryNewTransaction || query.Category != DbCatInvalid);
|
||||||
@ -789,23 +816,32 @@ void IdoPgsqlConnection::InternalExecuteQuery(const DbQuery& query, int typeOver
|
|||||||
{
|
{
|
||||||
AssertOnWorkQueue();
|
AssertOnWorkQueue();
|
||||||
|
|
||||||
if (IsPaused())
|
if (IsPaused()) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!GetConnected())
|
if (!GetConnected()) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (query.Type == DbQueryNewTransaction) {
|
if (query.Type == DbQueryNewTransaction) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
InternalNewTransaction();
|
InternalNewTransaction();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check whether we're allowed to execute the query first */
|
/* check whether we're allowed to execute the query first */
|
||||||
if (GetCategoryFilter() != DbCatEverything && (query.Category & GetCategoryFilter()) == 0)
|
if (GetCategoryFilter() != DbCatEverything && (query.Category & GetCategoryFilter()) == 0) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (query.Object && query.Object->GetObject()->GetExtension("agent_check").ToBool())
|
if (query.Object && query.Object->GetObject()->GetExtension("agent_check").ToBool()) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* check if there are missing object/insert ids and re-enqueue the query */
|
/* check if there are missing object/insert ids and re-enqueue the query */
|
||||||
if (!CanExecuteQuery(query)) {
|
if (!CanExecuteQuery(query)) {
|
||||||
@ -862,6 +898,7 @@ void IdoPgsqlConnection::InternalExecuteQuery(const DbQuery& query, int typeOver
|
|||||||
if ((type & DbQueryInsert) && (type & DbQueryDelete)) {
|
if ((type & DbQueryInsert) && (type & DbQueryDelete)) {
|
||||||
std::ostringstream qdel;
|
std::ostringstream qdel;
|
||||||
qdel << "DELETE FROM " << GetTablePrefix() << query.Table << where.str();
|
qdel << "DELETE FROM " << GetTablePrefix() << query.Table << where.str();
|
||||||
|
IncreasePendingQueries(1);
|
||||||
Query(qdel.str());
|
Query(qdel.str());
|
||||||
|
|
||||||
type = DbQueryInsert;
|
type = DbQueryInsert;
|
||||||
@ -929,6 +966,7 @@ void IdoPgsqlConnection::InternalExecuteQuery(const DbQuery& query, int typeOver
|
|||||||
Query(qbuf.str());
|
Query(qbuf.str());
|
||||||
|
|
||||||
if (upsert && GetAffectedRows() == 0) {
|
if (upsert && GetAffectedRows() == 0) {
|
||||||
|
IncreasePendingQueries(1);
|
||||||
InternalExecuteQuery(query, DbQueryDelete | DbQueryInsert);
|
InternalExecuteQuery(query, DbQueryDelete | DbQueryInsert);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -959,6 +997,7 @@ void IdoPgsqlConnection::CleanUpExecuteQuery(const String& table, const String&
|
|||||||
if (IsPaused())
|
if (IsPaused())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
IncreasePendingQueries(1);
|
||||||
m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalCleanUpExecuteQuery, this, table, time_column, max_age), PriorityLow, true);
|
m_QueryQueue.Enqueue(std::bind(&IdoPgsqlConnection::InternalCleanUpExecuteQuery, this, table, time_column, max_age), PriorityLow, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -966,8 +1005,10 @@ void IdoPgsqlConnection::InternalCleanUpExecuteQuery(const String& table, const
|
|||||||
{
|
{
|
||||||
AssertOnWorkQueue();
|
AssertOnWorkQueue();
|
||||||
|
|
||||||
if (!GetConnected())
|
if (!GetConnected()) {
|
||||||
|
DecreasePendingQueries(1);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Query("DELETE FROM " + GetTablePrefix() + table + " WHERE instance_id = " +
|
Query("DELETE FROM " + GetTablePrefix() + table + " WHERE instance_id = " +
|
||||||
Convert::ToString(static_cast<long>(m_InstanceID)) + " AND " + time_column +
|
Convert::ToString(static_cast<long>(m_InstanceID)) + " AND " + time_column +
|
||||||
@ -977,6 +1018,7 @@ void IdoPgsqlConnection::InternalCleanUpExecuteQuery(const String& table, const
|
|||||||
void IdoPgsqlConnection::FillIDCache(const DbType::Ptr& type)
|
void IdoPgsqlConnection::FillIDCache(const DbType::Ptr& type)
|
||||||
{
|
{
|
||||||
String query = "SELECT " + type->GetIDColumn() + " AS object_id, " + type->GetTable() + "_id, config_hash FROM " + GetTablePrefix() + type->GetTable() + "s";
|
String query = "SELECT " + type->GetIDColumn() + " AS object_id, " + type->GetTable() + "_id, config_hash FROM " + GetTablePrefix() + type->GetTable() + "s";
|
||||||
|
IncreasePendingQueries(1);
|
||||||
IdoPgsqlResult result = Query(query);
|
IdoPgsqlResult result = Query(query);
|
||||||
|
|
||||||
Dictionary::Ptr row;
|
Dictionary::Ptr row;
|
||||||
|
@ -48,8 +48,6 @@ protected:
|
|||||||
private:
|
private:
|
||||||
DbReference m_InstanceID;
|
DbReference m_InstanceID;
|
||||||
|
|
||||||
WorkQueue m_QueryQueue{1000000};
|
|
||||||
|
|
||||||
Library m_Library;
|
Library m_Library;
|
||||||
std::unique_ptr<PgsqlInterface, PgsqlInterfaceDeleter> m_Pgsql;
|
std::unique_ptr<PgsqlInterface, PgsqlInterfaceDeleter> m_Pgsql;
|
||||||
|
|
||||||
|
@ -358,9 +358,12 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrig
|
|||||||
SetLastCheckResult(cr);
|
SetLastCheckResult(cr);
|
||||||
|
|
||||||
if (GetProblem() != wasProblem) {
|
if (GetProblem() != wasProblem) {
|
||||||
for (auto& service : host->GetServices()) {
|
auto services = host->GetServices();
|
||||||
|
olock.Unlock();
|
||||||
|
for (auto& service : services) {
|
||||||
Service::OnHostProblemChanged(service, cr, origin);
|
Service::OnHostProblemChanged(service, cr, origin);
|
||||||
}
|
}
|
||||||
|
olock.Lock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,6 +517,8 @@ void Checkable::ExecuteCheck()
|
|||||||
double scheduled_start = GetNextCheck();
|
double scheduled_start = GetNextCheck();
|
||||||
double before_check = Utility::GetTime();
|
double before_check = Utility::GetTime();
|
||||||
|
|
||||||
|
SetLastCheckStarted(Utility::GetTime());
|
||||||
|
|
||||||
/* This calls SetNextCheck() which updates the CheckerComponent's idle/pending
|
/* This calls SetNextCheck() which updates the CheckerComponent's idle/pending
|
||||||
* queues and ensures that checks are not fired multiple times. ProcessCheckResult()
|
* queues and ensures that checks are not fired multiple times. ProcessCheckResult()
|
||||||
* is called too late. See #6421.
|
* is called too late. See #6421.
|
||||||
|
@ -147,25 +147,7 @@ static void FireSuppressedNotifications(Checkable* checkable)
|
|||||||
|
|
||||||
for (auto type : {NotificationProblem, NotificationRecovery, NotificationFlappingStart, NotificationFlappingEnd}) {
|
for (auto type : {NotificationProblem, NotificationRecovery, NotificationFlappingStart, NotificationFlappingEnd}) {
|
||||||
if (suppressed_types & type) {
|
if (suppressed_types & type) {
|
||||||
bool still_applies;
|
bool still_applies = checkable->NotificationReasonApplies(type);
|
||||||
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) {
|
if (still_applies) {
|
||||||
bool still_suppressed;
|
bool still_suppressed;
|
||||||
@ -185,28 +167,8 @@ static void FireSuppressedNotifications(Checkable* checkable)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!still_suppressed && checkable->GetEnableActiveChecks()) {
|
if (!still_suppressed && !checkable->IsLikelyToBeCheckedSoon()) {
|
||||||
/* If e.g. the downtime just ended, but the service is still not ok, we would re-send the stashed problem notification.
|
Checkable::OnNotificationsRequested(checkable, type, checkable->GetLastCheckResult(), "", "", nullptr);
|
||||||
* 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;
|
subtract |= type;
|
||||||
}
|
}
|
||||||
@ -241,3 +203,62 @@ void Checkable::FireSuppressedNotifications(const Timer * const&)
|
|||||||
::FireSuppressedNotifications(service.get());
|
::FireSuppressedNotifications(service.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether sending a notification of type type right now would represent *this' current state correctly.
|
||||||
|
*
|
||||||
|
* @param type The type of notification to send (or not to send).
|
||||||
|
*
|
||||||
|
* @return Whether to send the notification.
|
||||||
|
*/
|
||||||
|
bool Checkable::NotificationReasonApplies(NotificationType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case NotificationProblem:
|
||||||
|
{
|
||||||
|
auto cr (GetLastCheckResult());
|
||||||
|
return cr && !IsStateOK(cr->GetState()) && GetStateType() == StateTypeHard;
|
||||||
|
}
|
||||||
|
case NotificationRecovery:
|
||||||
|
{
|
||||||
|
auto cr (GetLastCheckResult());
|
||||||
|
return cr && IsStateOK(cr->GetState());
|
||||||
|
}
|
||||||
|
case NotificationFlappingStart:
|
||||||
|
return IsFlapping();
|
||||||
|
case NotificationFlappingEnd:
|
||||||
|
return !IsFlapping();
|
||||||
|
default:
|
||||||
|
VERIFY(!"Checkable#NotificationReasonStillApplies(): given type not implemented");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* E.g. we're going to re-send a stashed problem notification as *this is still not ok.
|
||||||
|
* But if the next check result recovers *this soon, we would send a recovery notification soon after the problem one.
|
||||||
|
* This is not desired, especially for lots of checkables 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 *this and we drop the stashed notification.
|
||||||
|
*
|
||||||
|
* @return Whether *this is likely to be checked soon
|
||||||
|
*/
|
||||||
|
bool Checkable::IsLikelyToBeCheckedSoon()
|
||||||
|
{
|
||||||
|
if (!GetEnableActiveChecks()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// One minute unless the check interval is too short so the next check will always run during the next minute.
|
||||||
|
auto threshold (GetCheckInterval() - 10);
|
||||||
|
|
||||||
|
if (threshold > 60) {
|
||||||
|
threshold = 60;
|
||||||
|
} else if (threshold < 0) {
|
||||||
|
threshold = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetNextCheck() <= Utility::GetTime() + threshold;
|
||||||
|
}
|
||||||
|
@ -66,6 +66,14 @@ void Checkable::Start(bool runtimeCreated)
|
|||||||
{
|
{
|
||||||
double now = Utility::GetTime();
|
double now = Utility::GetTime();
|
||||||
|
|
||||||
|
{
|
||||||
|
auto cr (GetLastCheckResult());
|
||||||
|
|
||||||
|
if (GetLastCheckStarted() > (cr ? cr->GetExecutionEnd() : 0.0)) {
|
||||||
|
SetNextCheck(GetLastCheckStarted());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (GetNextCheck() < now + 60) {
|
if (GetNextCheck() < now + 60) {
|
||||||
double delta = std::min(GetCheckInterval(), 60.0);
|
double delta = std::min(GetCheckInterval(), 60.0);
|
||||||
delta *= (double)std::rand() / RAND_MAX;
|
delta *= (double)std::rand() / RAND_MAX;
|
||||||
|
@ -174,6 +174,9 @@ public:
|
|||||||
void ValidateRetryInterval(const Lazy<double>& lvalue, const ValidationUtils& value) final;
|
void ValidateRetryInterval(const Lazy<double>& lvalue, const ValidationUtils& value) final;
|
||||||
void ValidateMaxCheckAttempts(const Lazy<int>& lvalue, const ValidationUtils& value) final;
|
void ValidateMaxCheckAttempts(const Lazy<int>& lvalue, const ValidationUtils& value) final;
|
||||||
|
|
||||||
|
bool NotificationReasonApplies(NotificationType type);
|
||||||
|
bool IsLikelyToBeCheckedSoon();
|
||||||
|
|
||||||
static void IncreasePendingChecks();
|
static void IncreasePendingChecks();
|
||||||
static void DecreasePendingChecks();
|
static void DecreasePendingChecks();
|
||||||
static int GetPendingChecks();
|
static int GetPendingChecks();
|
||||||
|
@ -90,6 +90,8 @@ abstract class Checkable : CustomVarObject
|
|||||||
[config] String icon_image_alt;
|
[config] String icon_image_alt;
|
||||||
|
|
||||||
[state] Timestamp next_check;
|
[state] Timestamp next_check;
|
||||||
|
[state, no_user_view, no_user_modify] Timestamp last_check_started;
|
||||||
|
|
||||||
[state] int check_attempt {
|
[state] int check_attempt {
|
||||||
default {{{ return 1; }}}
|
default {{{ return 1; }}}
|
||||||
};
|
};
|
||||||
|
@ -24,7 +24,9 @@ INITIALIZE_ONCE(&ClusterEvents::StaticInitialize);
|
|||||||
|
|
||||||
REGISTER_APIFUNCTION(CheckResult, event, &ClusterEvents::CheckResultAPIHandler);
|
REGISTER_APIFUNCTION(CheckResult, event, &ClusterEvents::CheckResultAPIHandler);
|
||||||
REGISTER_APIFUNCTION(SetNextCheck, event, &ClusterEvents::NextCheckChangedAPIHandler);
|
REGISTER_APIFUNCTION(SetNextCheck, event, &ClusterEvents::NextCheckChangedAPIHandler);
|
||||||
|
REGISTER_APIFUNCTION(SetLastCheckStarted, event, &ClusterEvents::LastCheckStartedChangedAPIHandler);
|
||||||
REGISTER_APIFUNCTION(SetSuppressedNotifications, event, &ClusterEvents::SuppressedNotificationsChangedAPIHandler);
|
REGISTER_APIFUNCTION(SetSuppressedNotifications, event, &ClusterEvents::SuppressedNotificationsChangedAPIHandler);
|
||||||
|
REGISTER_APIFUNCTION(SetSuppressedNotificationTypes, event, &ClusterEvents::SuppressedNotificationTypesChangedAPIHandler);
|
||||||
REGISTER_APIFUNCTION(SetNextNotification, event, &ClusterEvents::NextNotificationChangedAPIHandler);
|
REGISTER_APIFUNCTION(SetNextNotification, event, &ClusterEvents::NextNotificationChangedAPIHandler);
|
||||||
REGISTER_APIFUNCTION(SetForceNextCheck, event, &ClusterEvents::ForceNextCheckChangedAPIHandler);
|
REGISTER_APIFUNCTION(SetForceNextCheck, event, &ClusterEvents::ForceNextCheckChangedAPIHandler);
|
||||||
REGISTER_APIFUNCTION(SetForceNextNotification, event, &ClusterEvents::ForceNextNotificationChangedAPIHandler);
|
REGISTER_APIFUNCTION(SetForceNextNotification, event, &ClusterEvents::ForceNextNotificationChangedAPIHandler);
|
||||||
@ -41,7 +43,9 @@ void ClusterEvents::StaticInitialize()
|
|||||||
{
|
{
|
||||||
Checkable::OnNewCheckResult.connect(&ClusterEvents::CheckResultHandler);
|
Checkable::OnNewCheckResult.connect(&ClusterEvents::CheckResultHandler);
|
||||||
Checkable::OnNextCheckChanged.connect(&ClusterEvents::NextCheckChangedHandler);
|
Checkable::OnNextCheckChanged.connect(&ClusterEvents::NextCheckChangedHandler);
|
||||||
|
Checkable::OnLastCheckStartedChanged.connect(&ClusterEvents::LastCheckStartedChangedHandler);
|
||||||
Checkable::OnSuppressedNotificationsChanged.connect(&ClusterEvents::SuppressedNotificationsChangedHandler);
|
Checkable::OnSuppressedNotificationsChanged.connect(&ClusterEvents::SuppressedNotificationsChangedHandler);
|
||||||
|
Notification::OnSuppressedNotificationsChanged.connect(&ClusterEvents::SuppressedNotificationTypesChangedHandler);
|
||||||
Notification::OnNextNotificationChanged.connect(&ClusterEvents::NextNotificationChangedHandler);
|
Notification::OnNextNotificationChanged.connect(&ClusterEvents::NextNotificationChangedHandler);
|
||||||
Checkable::OnForceNextCheckChanged.connect(&ClusterEvents::ForceNextCheckChangedHandler);
|
Checkable::OnForceNextCheckChanged.connect(&ClusterEvents::ForceNextCheckChangedHandler);
|
||||||
Checkable::OnForceNextNotificationChanged.connect(&ClusterEvents::ForceNextNotificationChangedHandler);
|
Checkable::OnForceNextNotificationChanged.connect(&ClusterEvents::ForceNextNotificationChangedHandler);
|
||||||
@ -236,6 +240,68 @@ Value ClusterEvents::NextCheckChangedAPIHandler(const MessageOrigin::Ptr& origin
|
|||||||
return Empty;
|
return Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClusterEvents::LastCheckStartedChangedHandler(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("last_check_started", checkable->GetLastCheckStarted());
|
||||||
|
|
||||||
|
Dictionary::Ptr message = new Dictionary();
|
||||||
|
message->Set("jsonrpc", "2.0");
|
||||||
|
message->Set("method", "event::SetLastCheckStarted");
|
||||||
|
message->Set("params", params);
|
||||||
|
|
||||||
|
listener->RelayMessage(origin, checkable, message, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value ClusterEvents::LastCheckStartedChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
|
||||||
|
{
|
||||||
|
Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
|
||||||
|
|
||||||
|
if (!endpoint) {
|
||||||
|
Log(LogNotice, "ClusterEvents")
|
||||||
|
<< "Discarding 'last_check_started 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 'last_check_started changed' message for checkable '" << checkable->GetName()
|
||||||
|
<< "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
|
||||||
|
return Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkable->SetLastCheckStarted(params->Get("last_check_started"), false, origin);
|
||||||
|
|
||||||
|
return Empty;
|
||||||
|
}
|
||||||
|
|
||||||
void ClusterEvents::SuppressedNotificationsChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
|
void ClusterEvents::SuppressedNotificationsChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
|
||||||
{
|
{
|
||||||
ApiListener::Ptr listener = ApiListener::GetInstance();
|
ApiListener::Ptr listener = ApiListener::GetInstance();
|
||||||
@ -298,6 +364,52 @@ Value ClusterEvents::SuppressedNotificationsChangedAPIHandler(const MessageOrigi
|
|||||||
return Empty;
|
return Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClusterEvents::SuppressedNotificationTypesChangedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin)
|
||||||
|
{
|
||||||
|
ApiListener::Ptr listener = ApiListener::GetInstance();
|
||||||
|
|
||||||
|
if (!listener)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Dictionary::Ptr params = new Dictionary();
|
||||||
|
params->Set("notification", notification->GetName());
|
||||||
|
params->Set("suppressed_notifications", notification->GetSuppressedNotifications());
|
||||||
|
|
||||||
|
Dictionary::Ptr message = new Dictionary();
|
||||||
|
message->Set("jsonrpc", "2.0");
|
||||||
|
message->Set("method", "event::SetSuppressedNotificationTypes");
|
||||||
|
message->Set("params", params);
|
||||||
|
|
||||||
|
listener->RelayMessage(origin, notification, message, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value ClusterEvents::SuppressedNotificationTypesChangedAPIHandler(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto notification (Notification::GetByName(params->Get("notification")));
|
||||||
|
|
||||||
|
if (!notification)
|
||||||
|
return Empty;
|
||||||
|
|
||||||
|
if (origin->FromZone && !origin->FromZone->CanAccessObject(notification)) {
|
||||||
|
Log(LogNotice, "ClusterEvents")
|
||||||
|
<< "Discarding 'suppressed notification types changed' message for notification '" << notification->GetName()
|
||||||
|
<< "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
|
||||||
|
return Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
notification->SetSuppressedNotifications(params->Get("suppressed_notifications"), false, origin);
|
||||||
|
|
||||||
|
return Empty;
|
||||||
|
}
|
||||||
|
|
||||||
void ClusterEvents::NextNotificationChangedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin)
|
void ClusterEvents::NextNotificationChangedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin)
|
||||||
{
|
{
|
||||||
ApiListener::Ptr listener = ApiListener::GetInstance();
|
ApiListener::Ptr listener = ApiListener::GetInstance();
|
||||||
|
@ -26,9 +26,15 @@ public:
|
|||||||
static void NextCheckChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin);
|
static void NextCheckChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin);
|
||||||
static Value NextCheckChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
|
static Value NextCheckChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
|
||||||
|
|
||||||
|
static void LastCheckStartedChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin);
|
||||||
|
static Value LastCheckStartedChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
|
||||||
|
|
||||||
static void SuppressedNotificationsChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin);
|
static void SuppressedNotificationsChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin);
|
||||||
static Value SuppressedNotificationsChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
|
static Value SuppressedNotificationsChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
|
||||||
|
|
||||||
|
static void SuppressedNotificationTypesChangedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin);
|
||||||
|
static Value SuppressedNotificationTypesChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
|
||||||
|
|
||||||
static void NextNotificationChangedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin);
|
static void NextNotificationChangedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin);
|
||||||
static Value NextNotificationChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
|
static Value NextNotificationChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
|
||||||
|
|
||||||
|
@ -345,10 +345,19 @@ void Downtime::RemoveDowntime(const String& id, bool cancelled, bool expired, co
|
|||||||
reason = "<unknown>";
|
reason = "<unknown>";
|
||||||
}
|
}
|
||||||
|
|
||||||
Log(LogInformation, "Downtime")
|
Log msg (LogInformation, "Downtime");
|
||||||
<< "Removed downtime '" << downtime->GetName() << "' from checkable '"
|
|
||||||
<< downtime->GetCheckable()->GetName() << "' (Reason: " << reason << ").";
|
|
||||||
|
|
||||||
|
msg << "Removed downtime '" << downtime->GetName() << "' from checkable";
|
||||||
|
|
||||||
|
{
|
||||||
|
auto checkable (downtime->GetCheckable());
|
||||||
|
|
||||||
|
if (checkable) {
|
||||||
|
msg << " '" << checkable->GetName() << "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg << " (Reason: " << reason << ").";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Downtime::CanBeTriggered()
|
bool Downtime::CanBeTriggered()
|
||||||
|
@ -513,8 +513,6 @@ Value MacroProcessor::ResolveArguments(const Value& command, const Dictionary::P
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
arg.SkipValue = arg.SkipValue || arg.AValue.GetType() == ValueEmpty;
|
|
||||||
|
|
||||||
args.emplace_back(std::move(arg));
|
args.emplace_back(std::move(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,6 +234,39 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe
|
|||||||
Log(LogNotice, "Notification")
|
Log(LogNotice, "Notification")
|
||||||
<< "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '" << notificationName
|
<< "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '" << notificationName
|
||||||
<< "': not in timeperiod '" << tp->GetName() << "'";
|
<< "': not in timeperiod '" << tp->GetName() << "'";
|
||||||
|
|
||||||
|
if (!reminder) {
|
||||||
|
switch (type) {
|
||||||
|
case NotificationProblem:
|
||||||
|
case NotificationRecovery:
|
||||||
|
case NotificationFlappingStart:
|
||||||
|
case NotificationFlappingEnd:
|
||||||
|
{
|
||||||
|
/* If a non-reminder notification was suppressed, but just because of its time period,
|
||||||
|
* stash it into a notification types bitmask for maybe re-sending later.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ObjectLock olock (this);
|
||||||
|
int suppressedTypesBefore (GetSuppressedNotifications());
|
||||||
|
int suppressedTypesAfter (suppressedTypesBefore | type);
|
||||||
|
|
||||||
|
for (int conflict : {NotificationProblem | NotificationRecovery, NotificationFlappingStart | NotificationFlappingEnd}) {
|
||||||
|
/* E.g. problem and recovery notifications neutralize each other. */
|
||||||
|
|
||||||
|
if ((suppressedTypesAfter & conflict) == conflict) {
|
||||||
|
suppressedTypesAfter &= ~conflict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suppressedTypesAfter != suppressedTypesBefore) {
|
||||||
|
SetSuppressedNotifications(suppressedTypesAfter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
; // Cheating the compiler on "5 enumeration values not handled in switch"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +86,10 @@ class Notification : CustomVarObject < NotificationNameComposer
|
|||||||
[state] int notification_number;
|
[state] int notification_number;
|
||||||
[state] Timestamp last_problem_notification;
|
[state] Timestamp last_problem_notification;
|
||||||
|
|
||||||
|
[state, no_user_view, no_user_modify] int suppressed_notifications {
|
||||||
|
default {{{ return 0; }}}
|
||||||
|
};
|
||||||
|
|
||||||
[config, navigation] name(Endpoint) command_endpoint (CommandEndpointRaw) {
|
[config, navigation] name(Endpoint) command_endpoint (CommandEndpointRaw) {
|
||||||
navigate {{{
|
navigate {{{
|
||||||
return Endpoint::GetByName(GetCommandEndpointRaw());
|
return Endpoint::GetByName(GetCommandEndpointRaw());
|
||||||
|
@ -56,6 +56,69 @@ void NotificationComponent::Stop(bool runtimeRemoved)
|
|||||||
ObjectImpl<NotificationComponent>::Stop(runtimeRemoved);
|
ObjectImpl<NotificationComponent>::Stop(runtimeRemoved);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void SubtractSuppressedNotificationTypes(const Notification::Ptr& notification, int types)
|
||||||
|
{
|
||||||
|
ObjectLock olock (notification);
|
||||||
|
|
||||||
|
int suppressedTypesBefore (notification->GetSuppressedNotifications());
|
||||||
|
int suppressedTypesAfter (suppressedTypesBefore & ~types);
|
||||||
|
|
||||||
|
if (suppressedTypesAfter != suppressedTypesBefore) {
|
||||||
|
notification->SetSuppressedNotifications(suppressedTypesAfter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void FireSuppressedNotifications(const Notification::Ptr& notification)
|
||||||
|
{
|
||||||
|
int suppressedTypes (notification->GetSuppressedNotifications());
|
||||||
|
if (!suppressedTypes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int subtract = 0;
|
||||||
|
auto checkable (notification->GetCheckable());
|
||||||
|
|
||||||
|
for (auto type : {NotificationProblem, NotificationRecovery, NotificationFlappingStart, NotificationFlappingEnd}) {
|
||||||
|
if ((suppressedTypes & type) && !checkable->NotificationReasonApplies(type)) {
|
||||||
|
subtract |= type;
|
||||||
|
suppressedTypes &= ~type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suppressedTypes) {
|
||||||
|
auto tp (notification->GetPeriod());
|
||||||
|
|
||||||
|
if ((!tp || tp->IsInside(Utility::GetTime())) && !checkable->IsLikelyToBeCheckedSoon()) {
|
||||||
|
for (auto type : {NotificationProblem, NotificationRecovery, NotificationFlappingStart, NotificationFlappingEnd}) {
|
||||||
|
if (!(suppressedTypes & type))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto notificationName (notification->GetName());
|
||||||
|
|
||||||
|
Log(LogNotice, "NotificationComponent")
|
||||||
|
<< "Attempting to re-send previously suppressed notification '" << notificationName << "'.";
|
||||||
|
|
||||||
|
subtract |= type;
|
||||||
|
SubtractSuppressedNotificationTypes(notification, subtract);
|
||||||
|
subtract = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
notification->BeginExecuteNotification(type, checkable->GetLastCheckResult(), false, false);
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
Log(LogWarning, "NotificationComponent")
|
||||||
|
<< "Exception occurred during notification for object '"
|
||||||
|
<< notificationName << "': " << DiagnosticInformation(ex, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subtract) {
|
||||||
|
SubtractSuppressedNotificationTypes(notification, subtract);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Periodically sends notifications.
|
* Periodically sends notifications.
|
||||||
*
|
*
|
||||||
@ -104,37 +167,41 @@ void NotificationComponent::NotificationTimerHandler()
|
|||||||
bool reachable = checkable->IsReachable(DependencyNotification);
|
bool reachable = checkable->IsReachable(DependencyNotification);
|
||||||
|
|
||||||
if (reachable) {
|
if (reachable) {
|
||||||
Array::Ptr unstashedNotifications = new Array();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
auto stashedNotifications (notification->GetStashedNotifications());
|
Array::Ptr unstashedNotifications = new Array();
|
||||||
ObjectLock olock(stashedNotifications);
|
|
||||||
|
|
||||||
stashedNotifications->CopyTo(unstashedNotifications);
|
{
|
||||||
stashedNotifications->Clear();
|
auto stashedNotifications (notification->GetStashedNotifications());
|
||||||
}
|
ObjectLock olock(stashedNotifications);
|
||||||
|
|
||||||
ObjectLock olock(unstashedNotifications);
|
stashedNotifications->CopyTo(unstashedNotifications);
|
||||||
|
stashedNotifications->Clear();
|
||||||
|
}
|
||||||
|
|
||||||
for (Dictionary::Ptr unstashedNotification : unstashedNotifications) {
|
ObjectLock olock(unstashedNotifications);
|
||||||
try {
|
|
||||||
Log(LogNotice, "NotificationComponent")
|
|
||||||
<< "Attempting to send stashed notification '" << notificationName << "'.";
|
|
||||||
|
|
||||||
notification->BeginExecuteNotification(
|
for (Dictionary::Ptr unstashedNotification : unstashedNotifications) {
|
||||||
(NotificationType)(int)unstashedNotification->Get("type"),
|
try {
|
||||||
(CheckResult::Ptr)unstashedNotification->Get("cr"),
|
Log(LogNotice, "NotificationComponent")
|
||||||
(bool)unstashedNotification->Get("force"),
|
<< "Attempting to send stashed notification '" << notificationName << "'.";
|
||||||
(bool)unstashedNotification->Get("reminder"),
|
|
||||||
(String)unstashedNotification->Get("author"),
|
notification->BeginExecuteNotification(
|
||||||
(String)unstashedNotification->Get("text")
|
(NotificationType)(int)unstashedNotification->Get("type"),
|
||||||
);
|
(CheckResult::Ptr)unstashedNotification->Get("cr"),
|
||||||
} catch (const std::exception& ex) {
|
(bool)unstashedNotification->Get("force"),
|
||||||
Log(LogWarning, "NotificationComponent")
|
(bool)unstashedNotification->Get("reminder"),
|
||||||
<< "Exception occurred during notification for object '"
|
(String)unstashedNotification->Get("author"),
|
||||||
<< notificationName << "': " << DiagnosticInformation(ex, false);
|
(String)unstashedNotification->Get("text")
|
||||||
|
);
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
Log(LogWarning, "NotificationComponent")
|
||||||
|
<< "Exception occurred during notification for object '"
|
||||||
|
<< notificationName << "': " << DiagnosticInformation(ex, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FireSuppressedNotifications(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notification->GetInterval() <= 0 && notification->GetNoMoreNotifications()) {
|
if (notification->GetInterval() <= 0 && notification->GetNoMoreNotifications()) {
|
||||||
@ -165,6 +232,10 @@ void NotificationComponent::NotificationTimerHandler()
|
|||||||
if ((service && service->GetState() == ServiceOK) || (!service && host->GetState() == HostUp))
|
if ((service && service->GetState() == ServiceOK) || (!service && host->GetState() == HostUp))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* Don't send reminder notifications before initial ones. */
|
||||||
|
if (checkable->GetSuppressedNotifications() & NotificationProblem)
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Skip in runtime filters. */
|
/* Skip in runtime filters. */
|
||||||
if (!reachable || checkable->IsInDowntime() || checkable->IsAcknowledged() || checkable->IsFlapping())
|
if (!reachable || checkable->IsInDowntime() || checkable->IsAcknowledged() || checkable->IsFlapping())
|
||||||
continue;
|
continue;
|
||||||
|
@ -20,7 +20,7 @@ using namespace icinga;
|
|||||||
|
|
||||||
REGISTER_APIFUNCTION(Update, config, &ApiListener::ConfigUpdateHandler);
|
REGISTER_APIFUNCTION(Update, config, &ApiListener::ConfigUpdateHandler);
|
||||||
|
|
||||||
boost::mutex ApiListener::m_ConfigSyncStageLock;
|
SpinLock ApiListener::m_ConfigSyncStageLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entrypoint for updating all authoritative configs from /etc/zones.d, packages, etc.
|
* Entrypoint for updating all authoritative configs from /etc/zones.d, packages, etc.
|
||||||
@ -312,7 +312,16 @@ Value ApiListener::ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const D
|
|||||||
return Empty;
|
return Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread([origin, params]() { HandleConfigUpdate(origin, params); }).detach();
|
std::thread([origin, params, listener]() {
|
||||||
|
try {
|
||||||
|
listener->HandleConfigUpdate(origin, params);
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
auto msg ("Exception during config sync: " + DiagnosticInformation(ex));
|
||||||
|
|
||||||
|
Log(LogCritical, "ApiListener") << msg;
|
||||||
|
listener->UpdateLastFailedZonesStageValidation(msg);
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
return Empty;
|
return Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +330,7 @@ void ApiListener::HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dic
|
|||||||
/* Only one transaction is allowed, concurrent message handlers need to wait.
|
/* Only one transaction is allowed, concurrent message handlers need to wait.
|
||||||
* This affects two parent endpoints sending the config in the same moment.
|
* This affects two parent endpoints sending the config in the same moment.
|
||||||
*/
|
*/
|
||||||
auto lock (Shared<boost::mutex::scoped_lock>::Make(m_ConfigSyncStageLock));
|
auto lock (Shared<std::unique_lock<SpinLock>>::Make(m_ConfigSyncStageLock));
|
||||||
|
|
||||||
String apiZonesStageDir = GetApiZonesStageDir();
|
String apiZonesStageDir = GetApiZonesStageDir();
|
||||||
String fromEndpointName = origin->FromClient->GetEndpoint()->GetName();
|
String fromEndpointName = origin->FromClient->GetEndpoint()->GetName();
|
||||||
@ -534,6 +543,7 @@ void ApiListener::HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dic
|
|||||||
Log(LogInformation, "ApiListener")
|
Log(LogInformation, "ApiListener")
|
||||||
<< "Received configuration updates (" << count << ") from endpoint '" << fromEndpointName
|
<< "Received configuration updates (" << count << ") from endpoint '" << fromEndpointName
|
||||||
<< "' are equal to production, skipping validation and reload.";
|
<< "' are equal to production, skipping validation and reload.";
|
||||||
|
ClearLastFailedZonesStageValidation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -618,7 +628,7 @@ void ApiListener::TryActivateZonesStageCallback(const ProcessResult& pr,
|
|||||||
*
|
*
|
||||||
* @param relativePaths Required for later file operations in the callback. Provides the zone name plus path in a list.
|
* @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<String>& relativePaths, const Shared<boost::mutex::scoped_lock>::Ptr& lock)
|
void ApiListener::AsyncTryActivateZonesStage(const std::vector<String>& relativePaths, const Shared<std::unique_lock<SpinLock>>::Ptr& lock)
|
||||||
{
|
{
|
||||||
VERIFY(Application::GetArgC() >= 1);
|
VERIFY(Application::GetArgC() >= 1);
|
||||||
|
|
||||||
|
@ -560,32 +560,19 @@ void ApiListener::NewClientHandlerInternal(
|
|||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
|
|
||||||
{
|
{
|
||||||
struct DoneHandshake
|
Timeout::Ptr handshakeTimeout (new Timeout(
|
||||||
{
|
strand->context(),
|
||||||
bool Done = false;
|
*strand,
|
||||||
};
|
boost::posix_time::microseconds(intmax_t(Configuration::TlsHandshakeTimeout * 1000000)),
|
||||||
|
[strand, client](asio::yield_context yc) {
|
||||||
auto doneHandshake (Shared<DoneHandshake>::Make());
|
boost::system::error_code ec;
|
||||||
|
|
||||||
IoEngine::SpawnCoroutine(*strand, [strand, client, doneHandshake](asio::yield_context yc) {
|
|
||||||
namespace sys = boost::system;
|
|
||||||
|
|
||||||
{
|
|
||||||
boost::asio::deadline_timer timer (strand->context());
|
|
||||||
timer.expires_from_now(boost::posix_time::microseconds(intmax_t(Configuration::TlsHandshakeTimeout * 1000000)));
|
|
||||||
|
|
||||||
sys::error_code ec;
|
|
||||||
timer.async_wait(yc[ec]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!doneHandshake->Done) {
|
|
||||||
sys::error_code ec;
|
|
||||||
client->lowest_layer().cancel(ec);
|
client->lowest_layer().cancel(ec);
|
||||||
}
|
}
|
||||||
});
|
));
|
||||||
|
|
||||||
sslConn.async_handshake(role == RoleClient ? sslConn.client : sslConn.server, yc[ec]);
|
sslConn.async_handshake(role == RoleClient ? sslConn.client : sslConn.server, yc[ec]);
|
||||||
doneHandshake->Done = true;
|
|
||||||
|
handshakeTimeout->Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ec) {
|
if (ec) {
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "base/configobject.hpp"
|
#include "base/configobject.hpp"
|
||||||
#include "base/process.hpp"
|
#include "base/process.hpp"
|
||||||
#include "base/shared.hpp"
|
#include "base/shared.hpp"
|
||||||
|
#include "base/spinlock.hpp"
|
||||||
#include "base/timer.hpp"
|
#include "base/timer.hpp"
|
||||||
#include "base/workqueue.hpp"
|
#include "base/workqueue.hpp"
|
||||||
#include "base/tcpsocket.hpp"
|
#include "base/tcpsocket.hpp"
|
||||||
@ -22,6 +23,7 @@
|
|||||||
#include <boost/asio/spawn.hpp>
|
#include <boost/asio/spawn.hpp>
|
||||||
#include <boost/asio/ssl/context.hpp>
|
#include <boost/asio/ssl/context.hpp>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <mutex>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
namespace icinga
|
namespace icinga
|
||||||
@ -115,7 +117,7 @@ public:
|
|||||||
|
|
||||||
/* filesync */
|
/* filesync */
|
||||||
static Value ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
|
static Value ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
|
||||||
static void HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
|
void HandleConfigUpdate(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
|
||||||
|
|
||||||
/* configsync */
|
/* configsync */
|
||||||
static void ConfigUpdateObjectHandler(const ConfigObject::Ptr& object, const Value& cookie);
|
static void ConfigUpdateObjectHandler(const ConfigObject::Ptr& object, const Value& cookie);
|
||||||
@ -216,7 +218,7 @@ private:
|
|||||||
void RemoveStatusFile();
|
void RemoveStatusFile();
|
||||||
|
|
||||||
/* filesync */
|
/* filesync */
|
||||||
static boost::mutex m_ConfigSyncStageLock;
|
static SpinLock m_ConfigSyncStageLock;
|
||||||
|
|
||||||
void SyncLocalZoneDirs() const;
|
void SyncLocalZoneDirs() const;
|
||||||
void SyncLocalZoneDir(const Zone::Ptr& zone) const;
|
void SyncLocalZoneDir(const Zone::Ptr& zone) const;
|
||||||
@ -230,7 +232,7 @@ private:
|
|||||||
|
|
||||||
static void TryActivateZonesStageCallback(const ProcessResult& pr,
|
static void TryActivateZonesStageCallback(const ProcessResult& pr,
|
||||||
const std::vector<String>& relativePaths);
|
const std::vector<String>& relativePaths);
|
||||||
static void AsyncTryActivateZonesStage(const std::vector<String>& relativePaths, const Shared<boost::mutex::scoped_lock>::Ptr& lock);
|
static void AsyncTryActivateZonesStage(const std::vector<String>& relativePaths, const Shared<std::unique_lock<SpinLock>>::Ptr& lock);
|
||||||
|
|
||||||
static String GetChecksum(const String& content);
|
static String GetChecksum(const String& content);
|
||||||
static bool CheckConfigChange(const ConfigDirInformation& oldConfig, const ConfigDirInformation& newConfig);
|
static bool CheckConfigChange(const ConfigDirInformation& oldConfig, const ConfigDirInformation& newConfig);
|
||||||
|
@ -15,47 +15,34 @@ using namespace icinga;
|
|||||||
|
|
||||||
REGISTER_APIFUNCTION(Heartbeat, event, &JsonRpcConnection::HeartbeatAPIHandler);
|
REGISTER_APIFUNCTION(Heartbeat, event, &JsonRpcConnection::HeartbeatAPIHandler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We still send a heartbeat without timeout here
|
||||||
|
* to keep the m_Seen variable up to date. This is to keep the
|
||||||
|
* cluster connection alive when there isn't much going on.
|
||||||
|
*/
|
||||||
|
|
||||||
void JsonRpcConnection::HandleAndWriteHeartbeats(boost::asio::yield_context yc)
|
void JsonRpcConnection::HandleAndWriteHeartbeats(boost::asio::yield_context yc)
|
||||||
{
|
{
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
m_HeartbeatTimer.expires_from_now(boost::posix_time::seconds(10));
|
m_HeartbeatTimer.expires_from_now(boost::posix_time::seconds(20));
|
||||||
m_HeartbeatTimer.async_wait(yc[ec]);
|
m_HeartbeatTimer.async_wait(yc[ec]);
|
||||||
|
|
||||||
if (m_ShuttingDown) {
|
if (m_ShuttingDown) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_NextHeartbeat != 0 && m_NextHeartbeat < Utility::GetTime()) {
|
SendMessageInternal(new Dictionary({
|
||||||
Log(LogWarning, "JsonRpcConnection")
|
{ "jsonrpc", "2.0" },
|
||||||
<< "Client for endpoint '" << m_Endpoint->GetName() << "' has requested "
|
{ "method", "event::Heartbeat" },
|
||||||
<< "heartbeat message but hasn't responded in time. Closing connection.";
|
{ "params", new Dictionary() }
|
||||||
|
}));
|
||||||
Disconnect();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Endpoint) {
|
|
||||||
SendMessageInternal(new Dictionary({
|
|
||||||
{ "jsonrpc", "2.0" },
|
|
||||||
{ "method", "event::Heartbeat" },
|
|
||||||
{ "params", new Dictionary({
|
|
||||||
{ "timeout", 120 }
|
|
||||||
}) }
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Value JsonRpcConnection::HeartbeatAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
|
Value JsonRpcConnection::HeartbeatAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
|
||||||
{
|
{
|
||||||
Value vtimeout = params->Get("timeout");
|
|
||||||
|
|
||||||
if (!vtimeout.IsEmpty()) {
|
|
||||||
origin->FromClient->m_NextHeartbeat = Utility::GetTime() + vtimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Empty;
|
return Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +60,8 @@ void JsonRpcConnection::Start()
|
|||||||
|
|
||||||
void JsonRpcConnection::HandleIncomingMessages(boost::asio::yield_context yc)
|
void JsonRpcConnection::HandleIncomingMessages(boost::asio::yield_context yc)
|
||||||
{
|
{
|
||||||
|
m_Stream->next_layer().SetSeen(&m_Seen);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
String message;
|
String message;
|
||||||
|
|
||||||
@ -233,8 +235,20 @@ void JsonRpcConnection::Disconnect()
|
|||||||
|
|
||||||
m_Stream->lowest_layer().cancel(ec);
|
m_Stream->lowest_layer().cancel(ec);
|
||||||
|
|
||||||
|
Timeout::Ptr shutdownTimeout (new Timeout(
|
||||||
|
m_IoStrand.context(),
|
||||||
|
m_IoStrand,
|
||||||
|
boost::posix_time::seconds(10),
|
||||||
|
[this, keepAlive](asio::yield_context yc) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
m_Stream->lowest_layer().cancel(ec);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
m_Stream->next_layer().async_shutdown(yc[ec]);
|
m_Stream->next_layer().async_shutdown(yc[ec]);
|
||||||
|
|
||||||
|
shutdownTimeout->Cancel();
|
||||||
|
|
||||||
m_Stream->lowest_layer().shutdown(m_Stream->lowest_layer().shutdown_both, ec);
|
m_Stream->lowest_layer().shutdown(m_Stream->lowest_layer().shutdown_both, ec);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
42
tools/win32/build-choco.ps1
Normal file
42
tools/win32/build-choco.ps1
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
Set-PsDebug -Trace 1
|
||||||
|
|
||||||
|
if(-not (Test-Path "$($env:ProgramData)\chocolatey\choco.exe")) {
|
||||||
|
throw "Could not find Choco executable. Abort."
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path env:ICINGA2_BUILDPATH)) {
|
||||||
|
$env:ICINGA2_BUILDPATH = '.\build'
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path "$($env:ICINGA2_BUILDPATH)\choco\chocolateyInstall.ps1.template")) {
|
||||||
|
throw "Could not find Chocolatey install script template. Abort."
|
||||||
|
}
|
||||||
|
|
||||||
|
$chocoInstallScriptTemplatePath = "$($env:ICINGA2_BUILDPATH)\choco\chocolateyInstall.ps1.template"
|
||||||
|
$chocoInstallScript = Get-Content $chocoInstallScriptTemplatePath
|
||||||
|
|
||||||
|
if(-not (Test-Path "$($env:ICINGA2_BUILDPATH)\*-x86.msi")) {
|
||||||
|
throw "Could not find Icinga 2 32 bit MSI package. Abort."
|
||||||
|
}
|
||||||
|
|
||||||
|
$hashMSIpackage32 = Get-FileHash "$($env:ICINGA2_BUILDPATH)\*-x86.msi"
|
||||||
|
Write-Output "File Hash for 32 bit MSI package: $($hashMSIpackage32.Hash)."
|
||||||
|
|
||||||
|
if(-not (Test-Path "$($env:ICINGA2_BUILDPATH)\*-x86_64.msi")) {
|
||||||
|
throw "Could not find Icinga 2 64 bit MSI package. Abort."
|
||||||
|
}
|
||||||
|
|
||||||
|
$hashMSIpackage64 = Get-FileHash "$($env:ICINGA2_BUILDPATH)\*-x86_64.msi"
|
||||||
|
Write-Output "File Hash for 32 bit MSI package: $($hashMSIpackage64.Hash)"
|
||||||
|
|
||||||
|
$chocoInstallScript = $chocoInstallScript.Replace("%CHOCO_32BIT_CHECKSUM%", "$($hashMSIpackage32.Hash)")
|
||||||
|
$chocoInstallScript = $chocoInstallScript.Replace("%CHOCO_64BIT_CHECKSUM%", "$($hashMSIpackage64.Hash)")
|
||||||
|
Write-Output $chocoInstallScript
|
||||||
|
|
||||||
|
Set-Content -Path "$($env:ICINGA2_BUILDPATH)\choco\chocolateyInstall.ps1" -Value $chocoInstallScript
|
||||||
|
|
||||||
|
cd "$($env:ICINGA2_BUILDPATH)\choco"
|
||||||
|
& "$($env:ProgramData)\chocolatey\choco.exe" "pack"
|
||||||
|
cd "..\.."
|
||||||
|
|
||||||
|
Move-Item -Path "$($env:ICINGA2_BUILDPATH)\choco\*.nupkg" -Destination "$($env:ICINGA2_BUILDPATH)"
|
@ -17,16 +17,19 @@ if (-not ($env:PATH -contains $env:CMAKE_PATH)) {
|
|||||||
$env:PATH = $env:CMAKE_PATH + ';' + $env:PATH
|
$env:PATH = $env:CMAKE_PATH + ';' + $env:PATH
|
||||||
}
|
}
|
||||||
if (-not (Test-Path env:CMAKE_GENERATOR)) {
|
if (-not (Test-Path env:CMAKE_GENERATOR)) {
|
||||||
$env:CMAKE_GENERATOR = 'Visual Studio 15 2017 Win64'
|
$env:CMAKE_GENERATOR = 'Visual Studio 16 2019'
|
||||||
|
}
|
||||||
|
if (-not (Test-Path env:CMAKE_GENERATOR_PLATFORM)) {
|
||||||
|
$env:CMAKE_GENERATOR_PLATFORM = 'x64'
|
||||||
}
|
}
|
||||||
if (-not (Test-Path env:OPENSSL_ROOT_DIR)) {
|
if (-not (Test-Path env:OPENSSL_ROOT_DIR)) {
|
||||||
$env:OPENSSL_ROOT_DIR = 'c:\local\OpenSSL_1_1_1b-Win64'
|
$env:OPENSSL_ROOT_DIR = 'c:\local\OpenSSL_1_1_1h-Win64'
|
||||||
}
|
}
|
||||||
if (-not (Test-Path env:BOOST_ROOT)) {
|
if (-not (Test-Path env:BOOST_ROOT)) {
|
||||||
$env:BOOST_ROOT = 'c:\local\boost_1_69_0-Win64'
|
$env:BOOST_ROOT = 'c:\local\boost_1_71_0-Win64'
|
||||||
}
|
}
|
||||||
if (-not (Test-Path env:BOOST_LIBRARYDIR)) {
|
if (-not (Test-Path env:BOOST_LIBRARYDIR)) {
|
||||||
$env:BOOST_LIBRARYDIR = 'c:\local\boost_1_69_0-Win64\lib64-msvc-14.1'
|
$env:BOOST_LIBRARYDIR = 'c:\local\boost_1_71_0-Win64\lib64-msvc-14.2'
|
||||||
}
|
}
|
||||||
if (-not (Test-Path env:FLEX_BINARY)) {
|
if (-not (Test-Path env:FLEX_BINARY)) {
|
||||||
$env:FLEX_BINARY = 'C:\ProgramData\chocolatey\bin\win_flex.exe'
|
$env:FLEX_BINARY = 'C:\ProgramData\chocolatey\bin\win_flex.exe'
|
||||||
@ -48,7 +51,7 @@ if (Test-Path CMakeCache.txt) {
|
|||||||
|
|
||||||
& cmake.exe "$sourcePath" `
|
& cmake.exe "$sourcePath" `
|
||||||
-DCMAKE_BUILD_TYPE="$env:CMAKE_BUILD_TYPE" `
|
-DCMAKE_BUILD_TYPE="$env:CMAKE_BUILD_TYPE" `
|
||||||
-G "$env:CMAKE_GENERATOR" -DCPACK_GENERATOR=WIX `
|
-G "$env:CMAKE_GENERATOR" -A "$env:CMAKE_GENERATOR_PLATFORM" -DCPACK_GENERATOR=WIX `
|
||||||
-DICINGA2_WITH_MYSQL=OFF -DICINGA2_WITH_PGSQL=OFF `
|
-DICINGA2_WITH_MYSQL=OFF -DICINGA2_WITH_PGSQL=OFF `
|
||||||
-DICINGA2_WITH_LIVESTATUS=OFF -DICINGA2_WITH_COMPAT=OFF `
|
-DICINGA2_WITH_LIVESTATUS=OFF -DICINGA2_WITH_COMPAT=OFF `
|
||||||
-DOPENSSL_ROOT_DIR="$env:OPENSSL_ROOT_DIR" `
|
-DOPENSSL_ROOT_DIR="$env:OPENSSL_ROOT_DIR" `
|
||||||
|
@ -18,7 +18,7 @@ if (-not (Test-Path $BUILD)) {
|
|||||||
if (Test-Path env:VS_INSTALL_PATH) {
|
if (Test-Path env:VS_INSTALL_PATH) {
|
||||||
$VSBASE = $env:VS_INSTALL_PATH
|
$VSBASE = $env:VS_INSTALL_PATH
|
||||||
} else {
|
} else {
|
||||||
$VSBASE = "C:\Program Files (x86)\Microsoft Visual Studio\2017"
|
$VSBASE = "C:\Program Files (x86)\Microsoft Visual Studio\2019"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Test-Path env:BITS) {
|
if (Test-Path env:BITS) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user