Release 20250400 (#5542)

This commit is contained in:
pkippes 2025-04-07 16:55:42 +02:00 committed by GitHub
commit d894389296
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
165 changed files with 36392 additions and 700 deletions

3
.github/CODEOWNERS vendored
View File

@ -21,3 +21,6 @@ tests/** @centreon/owners-robot-e2e
packaging/** @centreon/owners-perl
selinux/** @centreon/owners-pipelines
.github/scripts/pod_spell_check.t @centreon/owners-perl
.gitleaks.toml @centreon/owners-security
.gitleaksignore @centreon/owners-security

View File

@ -18,7 +18,7 @@ runs:
using: 'composite'
steps:
- name: Download Artifacts
uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0
uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9
with:
pattern: ${{ inputs.source_name_pattern }}*
path: ${{ inputs.target_name }}

View File

@ -77,7 +77,7 @@ runs:
- if: ${{ inputs.stability != 'stable' }}
name: Restore packages from cache
uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.${{ steps.parse-distrib.outputs.package_extension }}
key: ${{ inputs.cache_key }}
@ -107,7 +107,7 @@ runs:
}
- name: Download packages from testing
if: ${{ inputs.stability == 'stable' && github.event_name == 'push' && inputs.distrib != 'jammy' }}
if: ${{ inputs.stability == 'stable' && github.event_name == 'push' && (inputs.distrib != 'jammy' || inputs.distrib != 'noble') }}
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
@ -170,7 +170,7 @@ runs:
- name: Publish packages to ${{ inputs.stability }}
if: |
contains(fromJson('["testing", "unstable"]'), inputs.stability) ||
(inputs.stability == 'stable' && github.event_name == 'push' && inputs.distrib != 'jammy')
(inputs.stability == 'stable' && github.event_name == 'push' && (inputs.distrib != 'jammy' || inputs.distrib != 'noble'))
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |

View File

@ -122,7 +122,7 @@ runs:
done
shell: bash
- uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.${{ inputs.package_extension }}
key: ${{ inputs.cache_key }}

View File

@ -54,6 +54,11 @@ runs:
PACKAGE_DISTRIB_NAME="0ubuntu.22.04"
PACKAGE_EXTENSION="deb"
DISTRIB_FAMILY="ubuntu"
elif [[ "${{ inputs.distrib }}" == "noble" ]]; then
PACKAGE_DISTRIB_SEPARATOR="-"
PACKAGE_DISTRIB_NAME="0ubuntu.24.04"
PACKAGE_EXTENSION="deb"
DISTRIB_FAMILY="ubuntu"
else
echo "::error::Distrib ${{ inputs.distrib }} cannot be parsed"
exit 1

View File

@ -66,14 +66,14 @@ runs:
shell: bash
- name: Promote DEB package to stable
if: ${{ contains(fromJSON('["bullseye", "bookworm", "jammy"]'), inputs.distrib) }}
if: ${{ contains(fromJSON('["bullseye", "bookworm", "jammy", "noble"]'), inputs.distrib) }}
run: |
set -eux
echo "[DEBUG] - Distrib: ${{ inputs.distrib }}"
echo "[DEBUG] - Distrib: ${{ inputs.module }}"
if [[ "${{ inputs.distrib }}" == "jammy" ]]; then
if [[ "${{ inputs.distrib }}" == "jammy" || "${{ inputs.distrib }}" == "noble" ]]; then
repo="ubuntu-plugins"
else
repo="apt-plugins"

View File

@ -16,7 +16,7 @@ runs:
steps:
- name: get the cached plugin
uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.${{ inputs.package-extension }}
key: ${{ inputs.cache-key }}
@ -24,4 +24,6 @@ runs:
- name: Install, test and remove plugin
shell: bash
run: python3 .github/scripts/test-all-plugins.py ${{ inputs.package-extension }} ${{ inputs.plugin-list }}
run: |
[[ -f /.venv/bin/activate ]] && source /.venv/bin/activate
python3 .github/scripts/test-all-plugins.py ${{ inputs.package-extension }} ${{ inputs.plugin-list }}

View File

@ -0,0 +1,30 @@
ARG REGISTRY_URL
FROM ${REGISTRY_URL}/ubuntu:noble
RUN bash -e <<EOF
apt-get update
apt-get install -y \
ca-certificates \
git \
openjdk-17-jdk \
wget \
zstd
cd /usr/local/src
wget https://dlcdn.apache.org/maven/maven-3/3.8.8/binaries/apache-maven-3.8.8-bin.tar.gz
tar zxf apache-maven-3.8.8-bin.tar.gz
ln -s /usr/local/src/apache-maven-3.8.8/bin/mvn /usr/bin/mvn
rm -f apache-maven-3.8.8-bin.tar.gz
echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | tee /etc/apt/sources.list.d/goreleaser.list
apt-get update
apt-get install -y nfpm=2.41.0
apt-get clean all
EOF
WORKDIR /src

View File

@ -0,0 +1,72 @@
ARG REGISTRY_URL=docker.io
FROM ${REGISTRY_URL}/ubuntu:noble
ENV DEBIAN_FRONTEND=noninteractive
# fix locale
RUN bash -e <<EOF
apt-get update
apt-get install -y locales
rm -rf /var/lib/apt/lists/*
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
apt-get clean
EOF
ENV LANG=en_US.utf8
RUN bash -e <<EOF
apt-get update
echo 'http://deb.debian.org/debian' | apt-get install -y pbuilder
apt-get install -y \
aptitude \
ca-certificates \
cpanminus \
curl \
debmake \
devscripts \
dh-make \
dh-make-perl \
fakeroot \
gcc \
git \
git-buildpackage \
jq \
libapp-fatpacker-perl \
libcurl4-openssl-dev \
libczmq-dev \
libczmq-dev\
libfile-copy-recursive-perl \
libjson-perl \
libmodule-build-tiny-perl \
libmodule-install-perl \
libssh-dev \
lintian \
python3 \
quilt \
ruby \
uuid-dev \
zstd
cpanm Module::Build::Tiny
cpanm Module::Install
cpanm Crypt::OpenSSL::Guess
gem install fpm
echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | tee /etc/apt/sources.list.d/goreleaser.list
apt-get update
apt-get install -y nfpm=2.41.0
apt-get clean
EOF
COPY .github/patch/fpm-deb.rb.diff /tmp/fpm-deb.rb.diff
# Patch to apply fpm fix for debian package generation while waiting for the official fix to be released (https://github.com/jordansissel/fpm/pull/1947).
RUN patch -i /tmp/fpm-deb.rb.diff $(find / -type f -name "deb.rb") && /bin/rm -rf /tmp/fpm-deb.rb.diff

View File

@ -12,7 +12,7 @@ dnf clean all
dnf install -y python3.11 python3.11-pip
pip3.11 install robotframework robotframework-examples
# Install snmpsim
pip3.11 install snmpsim-lextudio
pip3.11 install snmpsim
# Install node
curl -fsSL https://rpm.nodesource.com/setup_21.x | bash -

View File

@ -12,7 +12,7 @@ dnf clean all
dnf install -y python3.11 python3.11-pip
pip3.11 install robotframework robotframework-examples
# Install snmpsim
pip3.11 install snmpsim-lextudio
pip3.11 install snmpsim
# Install node
curl -fsSL https://rpm.nodesource.com/setup_21.x | bash -

View File

@ -28,7 +28,7 @@ apt-get install -y python3-dev python3-pip
rm -rf /usr/lib/python3.11/EXTERNALLY-MANAGED
pip3 install robotframework robotframework-examples
# Install snmpsim
pip3 install snmpsim-lextudio
pip3 install snmpsim
# Install nodejs
curl -fsSL https://deb.nodesource.com/setup_21.x | bash - &&\

View File

@ -27,7 +27,7 @@ apt-get update
apt-get install -y python3 python3-dev python3-pip
pip3 install robotframework robotframework-examples
# Install snmpsim
pip3 install snmpsim-lextudio
pip3 install snmpsim
# Install nodejs
curl -fsSL https://deb.nodesource.com/setup_21.x | bash - &&\

View File

@ -27,7 +27,7 @@ apt-get update
apt-get install -y python3 python3-dev python3-pip
pip3 install robotframework robotframework-examples
# Install snmpsim
pip3 install snmpsim-lextudio
pip3 install snmpsim
# Install nodejs
curl -fsSL https://deb.nodesource.com/setup_21.x | bash - &&\

View File

@ -0,0 +1,53 @@
ARG REGISTRY_URL=docker.io
FROM ${REGISTRY_URL}/ubuntu:noble
ENV DEBIAN_FRONTEND=noninteractive
# fix locale
RUN bash -e <<EOF
apt-get update
apt-get install -y locales libcurl4-openssl-dev curl wget zstd jq
rm -rf /var/lib/apt/lists/*
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
apt-get clean
EOF
ENV LANG=en_US.utf8
RUN bash -e <<EOF
# Avoid apt to clean packages cache directory
rm -f /etc/apt/apt.conf.d/docker-clean
apt-get update
# Install requirements for python virtual envs
apt-get install -y python3-venv
python3 -m venv .venv
source .venv/bin/activate
# Install Robotframework
apt-get install -y python3 python3-dev python3-pip
pip3 install robotframework robotframework-examples
# Install snmpsim
pip3 install snmpsim
# Install nodejs
curl -fsSL https://deb.nodesource.com/setup_21.x | bash - &&\
apt-get install -y nodejs
# Install mockoon (needs nodejs)
npm install -g -D @mockoon/cli
# Add Centreon plugins repositories
echo "deb https://packages.centreon.com/ubuntu-plugins-testing/ noble main" | tee -a /etc/apt/sources.list.d/centreon-plugins.list
echo "deb https://packages.centreon.com/ubuntu-plugins-unstable/ noble main" | tee -a /etc/apt/sources.list.d/centreon-plugins.list
wget -O- https://apt-key.centreon.com | gpg --dearmor | tee /etc/apt/trusted.gpg.d/centreon.gpg > /dev/null 2>&1
apt-get update
mkdir -p /var/lib/centreon/centplugins/
chmod 777 /var/lib/centreon/centplugins/
apt-get clean
EOF

View File

@ -0,0 +1,37 @@
ARG REGISTRY_URL=docker.io
FROM ${REGISTRY_URL}/ubuntu:noble
ENV DEBIAN_FRONTEND=noninteractive
# fix locale
RUN bash -e <<EOF
apt-get update
apt-get install -y locales libcurl4-openssl-dev curl wget zstd jq gpg
rm -rf /var/lib/apt/lists/*
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
apt-get clean
EOF
ENV LANG=en_US.utf8
RUN bash -e <<EOF
# Add Centreon plugins repositories
echo "deb https://packages.centreon.com/ubuntu-plugins-stable/ noble main" | tee -a /etc/apt/sources.list.d/centreon-plugins.list
echo "deb https://packages.centreon.com/ubuntu-plugins-testing/ noble main" | tee -a /etc/apt/sources.list.d/centreon-plugins.list
echo "deb https://packages.centreon.com/ubuntu-plugins-unstable/ noble main" | tee -a /etc/apt/sources.list.d/centreon-plugins.list
wget -O- https://apt-key.centreon.com | gpg --dearmor | tee /etc/apt/trusted.gpg.d/centreon.gpg > /dev/null 2>&1
apt-get update
apt-get -y install gcc make libtest2-plugin-nowarnings-perl libauthen-radius-perl libconvert-binary-c-perl libcrypt-openssl-rsa-perl libdata-dump-perl libdatetime-format-dateparse-perl libdatetime-format-strptime-perl libdatetime-perl libdbd-mysql-perl libdbd-odbc-perl libdbd-pg-perl libdbd-sybase-perl libdbi-perl libdigest-crc-perl libdigest-md5-perl libdigest-sha-perl libemail-mime-perl libemail-sender-perl libemail-send-smtp-gmail-perl libfilesys-smbclient-perl libhtml-template-perl libio-socket-inet6-perl libio-socket-ip-perl libjmx4perl-perl libjson-maybexs-perl libjson-perl libjson-webtoken-perl libmail-imapclient-perl libmime-base64-perl libmongodb-perl libnet-dhcp-perl libnet-dns-perl libnet-ldap-perl libnet-mqtt-simple-perl libnet-ntp-perl libnet-ssleay-perl libnet-subnet-perl libnet-telnet-perl libnet-tftp-perl libopenwsman-perl libredis-perl librrds-perl libsnmp-perl libsocket-perl libssh-session-perl libtest-www-selenium-perl libtext-csv-perl libtime-hires-perl libtime-parsedate-perl libuuid-perl libxml-libxml-perl libxml-libxml-simple-perl libxml-simple-perl libxml-xpath-perl libzmq-libzmq4-perl perl perl-modules
# this image is used by centreon-perl-libs unit test and centreon-gorgone unit tests.
apt-get -y install libcrypt-openssl-aes-perl libnet-curl-perl libyaml-libyaml-perl libhash-merge-perl libclone-choose-perl libcryptx-perl libjson-xs-perl libjson-pp-perl
apt-get clean
NONINTERACTIVE_TESTING=1 PERL_MM_USE_DEFAULT=1 cpan Test2::Harness UUID
mkdir -p /var/lib/centreon/centplugins/
chmod 777 /var/lib/centreon/centplugins/
EOF

View File

@ -47,7 +47,9 @@ jobs:
- package_extension: deb
image: packaging-plugins-java-jammy
distrib: jammy
- package_extension: deb
image: packaging-plugins-java-noble
distrib: noble
container:
image: ${{ vars.DOCKER_INTERNAL_REGISTRY_URL }}/${{ matrix.image }}
@ -102,7 +104,7 @@ jobs:
stability: ${{ needs.get-environment.outputs.stability }}
- name: Save to cache
uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.${{ matrix.package_extension }}
key: ${{ github.sha }}-${{ github.run_id }}-${{ matrix.package_extension }}-${{ matrix.distrib }}
@ -130,6 +132,8 @@ jobs:
package_extension: deb
- distrib: jammy
package_extension: deb
- distrib: noble
package_extension: deb
name: deliver ${{ matrix.distrib }}
steps:

View File

@ -48,6 +48,9 @@ jobs:
- package_extension: deb
image: packaging-plugins-jammy
distrib: jammy
- package_extension: deb
image: packaging-plugins-noble
distrib: noble
container:
image: ${{ vars.DOCKER_INTERNAL_REGISTRY_URL }}/${{ matrix.image }}
@ -106,6 +109,8 @@ jobs:
package_extension: deb
- distrib: jammy
package_extension: deb
- distrib: noble
package_extension: deb
name: deliver ${{ matrix.distrib }}
steps:

View File

@ -63,6 +63,12 @@ jobs:
- runner: ubuntu-22.04
dockerfile: packaging-plugins-java-jammy
image: packaging-plugins-java-jammy
- runner: ubuntu-22.04
dockerfile: packaging-plugins-noble
image: packaging-plugins-noble
- runner: ubuntu-22.04
dockerfile: packaging-plugins-java-noble
image: packaging-plugins-java-noble
runs-on: ${{ matrix.runner }}
@ -84,9 +90,9 @@ jobs:
username: ${{ secrets.HARBOR_CENTREON_PUSH_USERNAME }}
password: ${{ secrets.HARBOR_CENTREON_PUSH_TOKEN }}
- uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
- uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
with:
file: .github/docker/packaging/Dockerfile.${{ matrix.dockerfile }}
context: .

View File

@ -48,6 +48,10 @@ jobs:
- runner: ubuntu-24.04
dockerfile: jammy
image: jammy
- runner: ubuntu-24.04
dockerfile: noble
image: noble
runs-on: ${{ matrix.runner }}
@ -69,9 +73,9 @@ jobs:
username: ${{ secrets.HARBOR_CENTREON_PUSH_USERNAME }}
password: ${{ secrets.HARBOR_CENTREON_PUSH_TOKEN }}
- uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
- uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
with:
file: .github/docker/testing/Dockerfile.testing-plugins-${{ matrix.dockerfile }}
context: .

View File

@ -48,6 +48,9 @@ jobs:
- runner: ubuntu-22.04
dockerfile: jammy
image: jammy
- runner: ubuntu-22.04
dockerfile: noble
image: noble
runs-on: ${{ matrix.runner }}
@ -69,9 +72,9 @@ jobs:
username: ${{ secrets.HARBOR_CENTREON_PUSH_USERNAME }}
password: ${{ secrets.HARBOR_CENTREON_PUSH_TOKEN }}
- uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
- uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
with:
file: .github/docker/unit-tests/Dockerfile.unit-tests-${{ matrix.dockerfile }}
context: .

View File

@ -134,7 +134,7 @@ jobs:
- if: ${{ steps.has_skip_label.outputs.result == 'true' }}
name: Get push changes
id: get_push_changes
uses: tj-actions/changed-files@d6e91a2266cdb9d62096cebf1e8546899c6aa18f # v45.0.6
uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1
with:
since_last_remote_commit: true
json: true
@ -205,7 +205,7 @@ jobs:
script: |
const getStability = (branchName) => {
switch (true) {
case /(^develop$)|(^dev-\d{2}\.\d{2}\.x$)|(^prepare-release-cloud.*)/.test(branchName):
case /(^develop$)|(^dev-\d{2}\.\d{2}\.x$)/.test(branchName):
return 'unstable';
case /(^release.+)|(^hotfix.+)/.test(branchName):
return 'testing';
@ -250,9 +250,44 @@ jobs:
with:
script: |
const { execSync } = require('child_process');
const fs = require('fs');
let version = '';
if ('${{ inputs.version_file }}'.match(/pom\.xml$/)) {
version = execSync(`grep -m 1 "<version>.*</version>" ${{ inputs.version_file }} | sed 's/.*<version>\\(.*\\)<\\/version>.*/\\1/'`).toString().trim();
} else if ('${{ steps.get_stability.outputs.stability }}' === 'stable') {
const { owner, repo } = context.repo;
// Fetch the most recent tag for plugins
const { data: tags } = await github.rest.repos.listTags({
owner,
repo,
per_page: 10
});
let latestTag = null;
let latestDate = 0;
// Filter tags matching format plugins-YYYYMMDD
for (const tag of tags) {
const match = tag.name.match(/^plugins-(\d{8})$/);
const tagDate = parseInt(match[1], 10);
// ensure we get the true latest tag and not the most recent created
if (tagDate > latestDate) {
latestTag = tag.name;
latestDate = tagDate;
}
}
console.log(`Most recent tag found: ${latestTag}`)
// Get current release tag from .version file
version = fs.readFileSync('.version.plugins', 'utf8').trim();
console.log(`Stable version based on .version.plugins file will be: ${version}`)
} else if ('${{ steps.get_stability.outputs.stability }}' === 'testing') {
const branchName = "${{ github.head_ref || github.ref_name }}";
const matches = branchName.match(/^(?:release|hotfix)-(\d{8})$/);

View File

@ -47,6 +47,9 @@ jobs:
- package_extension: deb
image: packaging-plugins-jammy
distrib: jammy
- package_extension: deb
image: packaging-plugins-noble
distrib: noble
container:
image: ${{ vars.DOCKER_INTERNAL_REGISTRY_URL }}/${{ matrix.image }}
@ -140,6 +143,9 @@ jobs:
package_extension: deb
- distrib: jammy
package_extension: deb
- distrib: noble
package_extension: deb
name: deliver ${{ matrix.distrib }}
steps:

View File

@ -325,7 +325,7 @@ jobs:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
- uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9
with:
name: packages-rpm-${{ matrix.distrib }}
path: ./
@ -336,7 +336,7 @@ jobs:
- run: rpmsign --addsign ./*.rpm
shell: bash
- uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.rpm
key: ${{ github.sha }}-${{ github.run_id }}-rpm-${{ matrix.distrib }}
@ -351,7 +351,7 @@ jobs:
strategy:
fail-fast: false
matrix:
image: [packaging-plugins-bullseye, packaging-plugins-bookworm, packaging-plugins-jammy, packaging-plugins-bullseye-arm64]
image: [packaging-plugins-bullseye, packaging-plugins-bookworm, packaging-plugins-jammy, packaging-plugins-noble, packaging-plugins-bullseye-arm64]
name:
[
"ARGV::Struct",
@ -386,7 +386,7 @@ jobs:
include:
- runner_name: ubuntu-24.04
- arch: amd64
- build_names: "bullseye-amd64,bookworm,jammy"
- build_names: "bullseye-amd64,bookworm,jammy,noble"
- deb_dependencies: ""
- deb_provides: ""
- version: ""
@ -406,6 +406,10 @@ jobs:
distrib: jammy
package_extension: deb
image: packaging-plugins-jammy
- build_name: noble
distrib: noble
package_extension: deb
image: packaging-plugins-noble
- build_name: bullseye-arm64
distrib: bullseye
package_extension: deb
@ -413,7 +417,7 @@ jobs:
arch: arm64
runner_name: centreon-collect-arm64
- name: "Crypt::Argon2"
build_names: "bullseye-amd64,jammy,bullseye-arm64"
build_names: "bullseye-amd64,jammy,noble,bullseye-arm64"
preinstall_cpanlibs: "Dist::Build"
use_dh_make_perl: "false"
no-auto-depends: "true"
@ -424,25 +428,25 @@ jobs:
use_dh_make_perl: "false"
deb_dependencies: "libexporter-tiny-perl libxs-install-perl"
no-auto-depends: "true"
build_names: "bullseye-amd64,bookworm,jammy,bullseye-arm64"
build_names: "bullseye-amd64,bookworm,jammy,noble,bullseye-arm64"
- name: "Device::Modbus::RTU::Client"
build_names: "bookworm"
- name: "Device::Modbus::TCP::Client"
build_names: "bookworm"
- name: "Digest::SHA1"
build_names: "jammy"
build_names: "jammy,noble"
- name: "Libssh::Session"
use_dh_make_perl: "false"
build_names: "bullseye-amd64,bookworm,jammy,bullseye-arm64"
build_names: "bullseye-amd64,bookworm,jammy,noble,bullseye-arm64"
no-auto-depends: "true"
deb_dependencies: "libcarp-assert-perl libdynaloader-functions-perl libexporter-tiny-perl libdevel-overloadinfo-perl libssh-4 libc6"
deb_provides: "libssh-session-perl-dbgsym libssh-session-sftp"
revision: "2"
- name: "Net::Amazon::Signature::V4"
build_names: ["bullseye-amd64", "jammy"]
build_names: ["bullseye-amd64", "jammy", "noble"]
- name: "Net::Curl"
use_dh_make_perl: "false"
build_names: "bullseye-amd64,bookworm,jammy,bullseye-arm64"
build_names: "bullseye-amd64,bookworm,jammy,noble,bullseye-arm64"
no-auto-depends: "true"
deb_dependencies: "libcarp-assert-perl libdynaloader-functions-perl libexporter-tiny-perl libdevel-overloadinfo-perl libcurl4"
deb_provides: "libnet-curl-perl-dbgsym libnet-curl-compat-perl libnet-curl-easy-perl libnet-curl-form-perl libnet-curl-share-perl libnet-curl-multi-perl"
@ -460,7 +464,7 @@ jobs:
use_dh_make_perl: "false"
version: "0.01"
deb_dependencies: "libzmq5"
build_names: "bullseye-amd64,bookworm,jammy,bullseye-arm64"
build_names: "bullseye-amd64,bookworm,jammy,noble,bullseye-arm64"
name: package ${{ matrix.distrib }} ${{ matrix.arch }} ${{ matrix.name }}
container:
image: ${{ vars.DOCKER_INTERNAL_REGISTRY_URL }}/${{ matrix.image }}:latest
@ -572,7 +576,8 @@ jobs:
fi
# Check deb
dpkg-deb --contents $created_package || { echo "Error: dpkg-deb failed for package $created_package"; exit 1; }
dpkg-deb --verbose --contents $created_package || { echo "Error: dpkg-deb failed for package $created_package"; exit 1; }
shell: bash
- if: ${{ steps.check-package-existence.outputs.do_not_build == 'false' && contains(matrix.build_names, matrix.build_name) && matrix.use_dh_make_perl == 'true' }}
@ -617,7 +622,7 @@ jobs:
runs-on: ubuntu-24.04
strategy:
matrix:
distrib: [bullseye, bookworm, jammy]
distrib: [bullseye, bookworm, jammy, noble]
steps:
- name: Merge Artifacts
@ -642,14 +647,14 @@ jobs:
runs-on: ubuntu-24.04
strategy:
matrix:
distrib: [bullseye, bookworm, jammy]
distrib: [bullseye, bookworm, jammy, noble]
steps:
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
- uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9
with:
name: packages-deb-${{ matrix.distrib }}
path: ./
- uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.deb
key: ${{ github.sha }}-${{ github.run_id }}-deb-${{ matrix.distrib }}
@ -685,6 +690,10 @@ jobs:
distrib: jammy
arch: amd64
runner_name: ubuntu-24.04
- package_extension: deb
image: ubuntu:noble
arch: amd64
runner_name: ubuntu-24.04
- package_extension: deb
image: debian:bullseye
distrib: bullseye
@ -737,6 +746,8 @@ jobs:
package_extension: deb
- distrib: jammy
package_extension: deb
- distrib: noble
package_extension: deb
name: deliver ${{ matrix.distrib }}
steps:

View File

@ -63,7 +63,7 @@ jobs:
cp -r ~/rpmbuild/RPMS/x86_64/*.rpm .
shell: bash
- uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.rpm
key: unsigned-${{ github.sha }}-${{ github.run_id }}-rpm-${{ matrix.distrib }}
@ -97,7 +97,7 @@ jobs:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.rpm
key: unsigned-${{ github.sha }}-${{ github.run_id }}-rpm-${{ matrix.distrib }}
@ -108,7 +108,7 @@ jobs:
- run: rpmsign --addsign ./*.rpm
shell: bash
- uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.rpm
key: ${{ github.sha }}-${{ github.run_id }}-rpm-${{ matrix.distrib }}
@ -129,6 +129,9 @@ jobs:
distrib: bookworm
- image: packaging-plugins-jammy
distrib: jammy
- image: packaging-plugins-noble
distrib: noble
name: package ${{ matrix.distrib }}
container:
image: ${{ vars.DOCKER_INTERNAL_REGISTRY_URL }}/${{ matrix.image }}:latest
@ -155,7 +158,7 @@ jobs:
DEB_BUILD_OPTIONS="nocheck nodocs notest noautodbgsym" dh-make-perl make --dist ${{ matrix.distrib }} --verbose --build --version 4.0${{ steps.parse-distrib.outputs.package_distrib_separator }}${{ steps.parse-distrib.outputs.package_distrib_name }} perl-filesys-smbclient/
shell: bash
- uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.deb
key: ${{ github.sha }}-${{ github.run_id }}-deb-${{ matrix.distrib }}
@ -183,6 +186,8 @@ jobs:
package_extension: deb
- distrib: jammy
package_extension: deb
- distrib: noble
package_extension: deb
name: deliver ${{ matrix.distrib }}
steps:

View File

@ -50,6 +50,9 @@ jobs:
- image: packaging-plugins-jammy
distrib: jammy
package_extension: deb
- image: packaging-plugins-noble
distrib: noble
package_extension: deb
runs-on: ubuntu-22.04
@ -142,6 +145,8 @@ jobs:
package_extension: deb
- distrib: jammy
package_extension: deb
- distrib: noble
package_extension: deb
name: deliver ${{ matrix.distrib }}
steps:

View File

@ -55,6 +55,11 @@ jobs:
package_extension: deb
runner: ubuntu-22.04
arch: amd64
- image: packaging-plugins-noble
distrib: noble
package_extension: deb
runner: ubuntu-22.04
arch: amd64
- image: packaging-plugins-bullseye-arm64
distrib: bullseye
package_extension: deb
@ -208,7 +213,7 @@ jobs:
rpm_gpg_signing_passphrase: ${{ secrets.RPM_GPG_SIGNING_PASSPHRASE }}
stability: ${{ needs.get-environment.outputs.stability }}
- uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.${{ matrix.package_extension }}
key: cache-${{ github.sha }}-${{ matrix.package_extension }}-wsman-${{ matrix.distrib }}-${{ matrix.arch }}-${{ github.head_ref || github.ref_name }}
@ -244,6 +249,10 @@ jobs:
- distrib: jammy
package_extension: deb
arch: amd64
- distrib: noble
package_extension: deb
arch: amd64
name: deliver ${{ matrix.distrib }} ${{ matrix.arch }}
steps:

View File

@ -43,7 +43,7 @@ jobs:
shell: bash
- name: Cache vsphere cli sources
uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: vmware-vsphere-cli-distrib
key: ${{ github.sha }}-${{ github.run_id }}-sources-perl-vmware-vsphere
@ -82,6 +82,11 @@ jobs:
distrib: jammy
runner: ubuntu-22.04
arch: amd64
- package_extension: deb
image: packaging-plugins-noble
distrib: noble
runner: ubuntu-22.04
arch: amd64
- package_extension: deb
image: packaging-plugins-bullseye-arm64
distrib: bullseye
@ -103,7 +108,7 @@ jobs:
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Import source files
uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: vmware-vsphere-cli-distrib
key: ${{ github.sha }}-${{ github.run_id }}-sources-perl-vmware-vsphere

View File

@ -57,7 +57,7 @@ jobs:
cp -r ~/rpmbuild/RPMS/x86_64/*.rpm .
shell: bash
- uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.rpm
key: unsigned-${{ github.sha }}-${{ github.run_id }}-rpm-${{ matrix.distrib }}
@ -91,7 +91,7 @@ jobs:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.rpm
key: unsigned-${{ github.sha }}-${{ github.run_id }}-rpm-${{ matrix.distrib }}
@ -102,7 +102,7 @@ jobs:
- run: rpmsign --addsign ./*.rpm
shell: bash
- uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./*.rpm
key: ${{ github.sha }}-${{ github.run_id }}-rpm-${{ matrix.distrib }}

View File

@ -98,7 +98,7 @@ jobs:
strategy:
fail-fast: false
matrix:
image: [unit-tests-alma8, unit-tests-alma9, unit-tests-bullseye, unit-tests-bullseye-arm64, unit-tests-bookworm, unit-tests-jammy]
image: [unit-tests-alma8, unit-tests-alma9, unit-tests-bullseye, unit-tests-bullseye-arm64, unit-tests-bookworm, unit-tests-jammy, unit-tests-noble]
include:
- runner_name: ubuntu-24.04
- package_extension: rpm
@ -120,6 +120,9 @@ jobs:
- package_extension: deb
image: unit-tests-jammy
distrib: jammy
- package_extension: deb
image: unit-tests-noble
distrib: noble
runs-on: ${{ matrix.runner_name }}
container:
@ -168,7 +171,7 @@ jobs:
COMMIT=$(git log -1 HEAD --pretty=format:%h)
perl .github/scripts/plugins-source.container.pl "${{ needs.get-plugins.outputs.plugins }}" "${{ needs.get-environment.outputs.version }} ($COMMIT)"
- uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./build/
key: fatpacked-plugins-${{ github.sha }}-${{ github.run_id }}
@ -206,6 +209,9 @@ jobs:
- package_extension: deb
image: packaging-plugins-jammy
distrib: jammy
- package_extension: deb
image: packaging-plugins-noble
distrib: noble
container:
image: ${{ vars.DOCKER_INTERNAL_REGISTRY_URL }}/${{ matrix.image }}
@ -219,7 +225,7 @@ jobs:
- name: Checkout sources
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./build/
key: fatpacked-plugins-${{ github.sha }}-${{ github.run_id }}
@ -311,7 +317,7 @@ jobs:
strategy:
fail-fast: false
matrix:
image: [testing-plugins-alma8, testing-plugins-alma9, testing-plugins-jammy, testing-plugins-bullseye, testing-plugins-bookworm]
image: [testing-plugins-alma8, testing-plugins-alma9, testing-plugins-jammy, testing-plugins-bullseye, testing-plugins-bookworm, testing-plugins-noble, testing-plugins-bullseye-arm64]
include:
- runner_name: ubuntu-24.04
- package_extension: rpm
@ -329,6 +335,9 @@ jobs:
- package_extension: deb
image: testing-plugins-jammy
distrib: jammy
- package_extension: deb
image: testing-plugins-noble
distrib: noble
- package_extension: deb
image: testing-plugins-bullseye-arm64
distrib: bullseye
@ -385,6 +394,8 @@ jobs:
package_extension: deb
- distrib: jammy
package_extension: deb
- distrib: noble
package_extension: deb
name: deliver ${{ matrix.distrib }}
steps:
@ -412,7 +423,7 @@ jobs:
- name: Checkout sources
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
- uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ./build/
key: fatpacked-plugins-${{ github.sha }}-${{ github.run_id }}

19
.gitleaks.toml Normal file
View File

@ -0,0 +1,19 @@
title = "Gitleaks custom rules"
[extend]
useDefault = true
[allowlist]
paths = [
'''vendor\/''',
'''(.*?)\.rptlibrary''',
'''pnpm-lock\.yaml''',
'''\.gitleaks\.toml$''',
'''(.*?)(jpg|gif|doc|pdf|bin)$'''
]
regexTarget = "match"
regexes = [
'''ABCDEFG1234567890''',
'''s\.aBCD123DEF456GHI789JKL012'''
]

1
.version.plugins Normal file
View File

@ -0,0 +1 @@
20250303

View File

@ -0,0 +1,4 @@
{
"dependencies": [
]
}

View File

@ -0,0 +1,9 @@
{
"pkg_name": "centreon-plugin-Applications-Exense-Step-Restapi",
"pkg_summary": "Centreon Plugin",
"plugin_name": "centreon_exense_step_restapi.pl",
"files": [
"centreon/plugins/script_custom.pm",
"apps/exense/step/restapi/"
]
}

View File

@ -0,0 +1,5 @@
{
"dependencies": [
"perl(DateTime)"
]
}

View File

@ -0,0 +1,4 @@
{
"dependencies": [
]
}

View File

@ -0,0 +1,9 @@
{
"pkg_name": "centreon-plugin-Notification-Foxbox",
"pkg_summary": "Centreon Plugin to send notifications by Foxbox",
"plugin_name": "centreon_notification_foxbox.pl",
"files": [
"centreon/plugins/script_simple.pm",
"notification/foxbox"
]
}

View File

@ -0,0 +1,4 @@
{
"dependencies": [
]
}

View File

@ -0,0 +1,5 @@
{
"dependencies": [
"liburi-encode-perl"
]
}

View File

@ -0,0 +1,9 @@
{
"pkg_name": "centreon-plugin-Notification-Jasminsms-Httpapi",
"pkg_summary": "Centreon Plugin to send notifications by Jasmin SMS HTTP-API",
"plugin_name": "centreon_notification_jasminsms-httpapi.pl",
"files": [
"centreon/plugins/script_custom.pm",
"notification/jasminsms/httpapi"
]
}

View File

@ -0,0 +1,5 @@
{
"dependencies": [
"perl(URI::Encode)"
]
}

View File

@ -0,0 +1,5 @@
{
"dependencies": [
"libjson-perl"
]
}

View File

@ -0,0 +1,9 @@
{
"pkg_name": "centreon-plugin-Notification-Ovhsms",
"pkg_summary": "Centreon Plugin to send notifications by OVH SMS API",
"plugin_name": "centreon_notification_ovhsms.pl",
"files": [
"centreon/plugins/script_simple.pm",
"notification/ovhsms"
]
}

View File

@ -0,0 +1,5 @@
{
"dependencies": [
"perl(JSON::XS)"
]
}

View File

@ -0,0 +1,5 @@
{
"dependencies": [
"libjson-perl"
]
}

View File

@ -0,0 +1,9 @@
{
"pkg_name": "centreon-plugin-Notification-Telegram",
"pkg_summary": "Centreon Plugin to send notifications by Telegram",
"plugin_name": "centreon_notification_telegram.pl",
"files": [
"centreon/plugins/script_simple.pm",
"notification/telegram"
]
}

View File

@ -0,0 +1,5 @@
{
"dependencies": [
"perl(JSON::XS)"
]
}

View File

@ -310,11 +310,11 @@ Filter job type (can be a regexp).
=item B<--filter-start-time>
Filter job with start time greater than current time less value in seconds.
Tolerance value in seconds, to avoid skipping jobs whose start time is earlier than the current time.
=item B<--filter-end-time>
Filter job with end time greater than current time less value in seconds (default: 86400).
Tolerance value in seconds, to avoid skipping jobs whose end time is earlier than the current time (default: 86400).
=item B<--ok-status>

View File

@ -251,6 +251,7 @@ sub cache_backup_job_session {
return $datas;
}
sub cache_repository {
my ($self, %options) = @_;
@ -281,6 +282,24 @@ sub get_backup_job_session {
);
}
sub get_replica_job_session {
my ($self, %options) = @_;
return $self->get_cache_file_response(statefile => 'replica_job_session')
if (defined($self->{option_results}->{cache_use}) && !defined($options{disable_cache}));
my $creation_time = DateTime->now->subtract(seconds => $options{timeframe})->iso8601();
return $self->request_api(
endpoint => '/api/query',
get_param => [
'type=ReplicaJobSession',
'format=Entities',
'filter=CreationTime>=' . $creation_time
]
);
}
sub get_repository {
my ($self, %options) = @_;

View File

@ -229,6 +229,7 @@ sub manage_selection {
my ($self, %options) = @_;
my $jobs_exec = $options{custom}->get_backup_job_session(timeframe => $self->{option_results}->{timeframe});
my $jobs_replica = $options{custom}->get_replica_job_session(timeframe => $self->{option_results}->{timeframe});
my $ctime = time();
@ -278,6 +279,50 @@ sub manage_selection {
$self->{jobs}->{ $job->{JobUid} }->{failed}->{failed}++;
}
}
foreach my $job (@{$jobs_replica->{Entities}->{ReplicaJobSessions}->{ReplicaJobSessions}}) {
next if (defined($self->{option_results}->{filter_uid}) && $self->{option_results}->{filter_uid} ne '' && $job->{JobUid} !~ /$self->{option_results}->{filter_uid}/);
next if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && $job->{JobName} !~ /$self->{option_results}->{filter_name}/);
next if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' && $job->{JobType} !~ /$self->{option_results}->{filter_type}/);
if (!defined($self->{jobs}->{ $job->{JobUid} })) {
$self->{jobs}->{ $job->{JobUid} } = {
name => $job->{JobName},
type => $job->{JobType},
failed => { name => $job->{JobName}, total => 0, failed => 0 }
};
$self->{global}->{detected}++;
}
$job->{CreationTimeUTC} =~ /^(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)/;
my $dt = DateTime->new(year => $1, month => $2, day => $3, hour => $4, minute => $5, second => $6);
my $epoch = $dt->epoch();
if (!defined($self->{jobs}->{ $job->{JobUid} }->{executions}) || $epoch > $self->{jobs}->{ $job->{JobUid} }->{executions}->{last}->{epoch}) {
$self->{jobs}->{ $job->{JobUid} }->{executions}->{last} = {
jobName => $job->{JobName},
started => $job->{CreationTimeUTC},
status => $job->{Result},
epoch => $epoch
};
$self->{jobs}->{ $job->{JobUid} }->{timers} = {
name => $job->{JobName},
lastExecSeconds => $ctime - $epoch,
lastExecHuman => centreon::plugins::misc::change_seconds(value => $ctime - $epoch)
};
if ($job->{State} =~ /Starting|Working|Resuming/i) {
my $duration = $ctime - $epoch;
$self->{jobs}->{ $job->{JobUid} }->{timers}->{durationSeconds} = $duration;
$self->{jobs}->{ $job->{JobUid} }->{timers}->{durationHuman} = centreon::plugins::misc::change_seconds(value => $duration);
}
}
$self->{jobs}->{ $job->{JobUid} }->{failed}->{total}++;
if (defined($job->{Result}) && $job->{Result} =~ /Failed/i) {
$self->{jobs}->{ $job->{JobUid} }->{failed}->{failed}++;
}
}
foreach my $uid (keys %{$self->{jobs}}) {
$self->{jobs}->{$uid}->{failed}->{failedPrct} = $self->{jobs}->{$uid}->{failed}->{total} > 0 ? $self->{jobs}->{$uid}->{failed}->{failed} * 100 / $self->{jobs}->{$uid}->{failed}->{total} : 0;
@ -308,7 +353,7 @@ Filter jobs by type.
=item B<--timeframe>
Timeframe to get BackupJobSession (in seconds. Default: 86400).
Timeframe to get BackupJobSession and ReplicaJobSession (in seconds. Default: 86400).
=item B<--unit>

View File

@ -51,6 +51,8 @@ sub manage_selection {
my $results = {};
my $jobs_exec = $options{custom}->cache_backup_job_session(timeframe => $self->{option_results}->{timeframe});
my $jobs_replica = $options{custom}->get_replica_job_session(timeframe => $self->{option_results}->{timeframe});
foreach my $job (@{$jobs_exec->{Entities}->{BackupJobSessions}->{BackupJobSessions}}) {
next if (defined($results->{ $job->{JobUid} }));
@ -60,6 +62,15 @@ sub manage_selection {
}
}
foreach my $job (@{$jobs_replica->{Entities}->{ReplicaJobSessions}->{ReplicaJobSessions}}) {
next if (defined($results->{ $job->{JobUid} }));
$results->{ $job->{JobUid} } = {
jobName => $job->{JobName},
jobType => $job->{JobType}
}
}
return $results;
}
@ -67,7 +78,7 @@ sub run {
my ($self, %options) = @_;
my $results = $self->manage_selection(%options);
foreach my $uid (keys %$results) {
foreach my $uid (sort keys %$results) {
$self->{output}->output_add(
long_msg => sprintf(
'[uid: %s][jobName: %s][jobType: %s]',
@ -117,7 +128,7 @@ List jobs.
=item B<--timeframe>
Timeframe to get BackupJobSession (in seconds. Default: 86400).
Timeframe to get BackupJobSession and ReplicaJobSession (in seconds. Default: 86400).
=back

View File

@ -38,7 +38,7 @@ sub set_counters {
{ label => 'clients-' . $label,
nlabel => 'clients.' . $label . '.count',
set => {
key_values => [{ name => $label }],
key_values => [ { name => $label } ],
output_template => ucfirst($label) . ' clients: %d',
perfdatas => [
{ label => $label . '_clients', template => '%d',
@ -51,7 +51,7 @@ sub set_counters {
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
return $self;
@ -62,7 +62,7 @@ sub manage_selection {
my %results = $options{mqtt}->queries(
base_topic => '$SYS/broker/clients/',
topics => ['connected', 'maximum', 'active', 'inactive']
topics => [ 'connected', 'maximum', 'active', 'inactive' ]
);
for my $topic (keys %results) {
@ -80,10 +80,37 @@ Check clients statistics.
=over 8
=item B<--warning-*> B<--critical-*>
=item B<--warning-clients-connected>
Thresholds.
Can be: 'clients-connected', 'clients-maximum', 'clients-active', 'clients-inactive'.
Warning threshold for connected clients.
=item B<--critical-clients-connected>
Critical threshold for connected clients.
=item B<--warning-clients-maximum>
Warning threshold for maximum clients.
=item B<--critical-clients-maximum>
Critical threshold for maximum clients.
=item B<--warning-clients-active>
Warning threshold for active clients.
=item B<--critical-clients-active>
Critical threshold for active clients.
=item B<--warning-clients-inactive>
Warning threshold for inactive clients.
=item B<--critical-clients-inactive>
Critical threshold for inactive clients.
=back

View File

@ -38,7 +38,7 @@ sub set_counters {
{ label => 'messages-' . $label,
nlabel => 'messages.' . $label . '.count',
set => {
key_values => [{ name => $label }],
key_values => [ { name => $label } ],
output_template => ucfirst($label) . ' messages: %d',
perfdatas => [
{ label => $label . '_messages', template => '%d',
@ -51,7 +51,7 @@ sub set_counters {
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
return $self;
@ -62,7 +62,7 @@ sub manage_selection {
my %results = $options{mqtt}->queries(
base_topic => '$SYS/broker/messages/',
topics => ['stored', 'received', 'sent']
topics => [ 'stored', 'received', 'sent' ]
);
for my $topic (keys %results) {
$self->{global}->{$topic} = $results{$topic};
@ -79,10 +79,29 @@ Check messages statistics.
=over 8
=item B<--warning-*> B<--critical-*>
=item B<--warning-messages-stored>
Thresholds.
Can be: 'messages-stored', 'messages-received', 'messages-sent'.
Warning threshold for stored messages.
=item B<--critical-messages-stored>
Critical threshold for stored messages.
=item B<--warning-messages-received>
Warning threshold for received messages.
=item B<--critical-messages-received>
Critical threshold for received messages.
=item B<--warning-messages-sent>
Warning threshold for sent messages.
=item B<--critical-messages-sent>
Critical threshold for sent messages.
=back

View File

@ -30,13 +30,13 @@ use POSIX qw(floor);
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$options{options}->add_options(arguments => {
'topic:s' => { name => 'topic' },
'warning:s' => { name => 'warning' },
'critical:s' => { name => 'critical' },
'warning:s' => { name => 'warning', redirect => 'warning-generic' },
'critical:s' => { name => 'critical', redirect => 'critical-generic' },
'extracted-pattern:s' => { name => 'extracted_pattern' },
'format:s' => { name => 'format' },
'format-custom:s' => { name => 'format_custom' },
@ -57,25 +57,20 @@ sub custom_generic_output {
$format = $self->{instance_mode}{option_results}->{format};
}
my $value = $self->{result_values}->{numericvalue};
if (!centreon::plugins::misc::is_empty($self->{instance_mode}{option_results}->{format_custom})) {
$value = eval "$value $self->{instance_mode}{option_results}->{format_custom}";
}
return sprintf($format, $value);
return sprintf($format, $self->{result_values}->{numericvalue});
}
sub custom_generic_perfdata {
my ($self, %options) = @_;
$self->{output}->perfdata_add(
label => $options{option_results}->{perfdata_name},
unit => $options{option_results}->{perfdata_unit},
label => $self->{instance_mode}->{option_results}->{perfdata_name},
unit => $self->{instance_mode}->{option_results}->{perfdata_unit},
value => $self->{result_values}->{numericvalue},
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}),
min => $options{option_results}->{perfdata_min},
max => $options{option_results}->{perfdata_max}
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-generic'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-generic'),
min => $self->{instance_mode}->{option_results}->{perfdata_min},
max => $self->{instance_mode}->{option_results}->{perfdata_max}
);
}
@ -85,9 +80,9 @@ sub custom_generic_threshold {
return $self->{perfdata}->threshold_check(
value => $self->{result_values}->{numericvalue},
threshold => [
{ label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' },
{ label => 'warning-' . $self->{thlabel}, exit_litteral => 'warning' },
{ label => 'unknown-' . $self->{thlabel}, exit_litteral => 'unknown' }
{ label => 'critical-generic', exit_litteral => 'critical' },
{ label => 'warning-generic', exit_litteral => 'warning' },
{ label => 'unknown-generic', exit_litteral => 'unknown' }
]
);
}
@ -102,7 +97,7 @@ sub set_counters {
$self->{maps_counters}->{global} = [
{ label => 'generic',
set => {
key_values => [{ name => 'numericvalue' }],
key_values => [ { name => 'numericvalue' } ],
closure_custom_output => $self->can('custom_generic_output'),
closure_custom_perfdata => $self->can('custom_generic_perfdata'),
closure_custom_threshold_check => $self->can('custom_generic_threshold')
@ -146,6 +141,10 @@ sub manage_selection {
$self->{output}->option_exit();
}
if (!centreon::plugins::misc::is_empty($self->{option_results}->{format_custom})) {
$value = eval "$value $self->{option_results}->{format_custom}";
}
$self->{global} = { numericvalue => $value };
}
@ -177,7 +176,7 @@ Define a pattern to extract a number from the returned string.
=item B<--format>
Output format (default: 'current value is %s')
Output format (default: 'current value is %s').
=item B<--format-custom>
@ -186,19 +185,19 @@ Apply a custom change on the value
=item B<--perfdata-unit>
Perfdata unit in perfdata output (default: '')
Perfdata unit in perfdata output (default: '').
=item B<--perfdata-name>
Perfdata name in perfdata output (default: 'value')
Perfdata name in perfdata output (default: 'value').
=item B<--perfdata-min>
Minimum value to add in perfdata output (default: '')
Minimum value to add in perfdata output (default: '').
=item B<--perfdata-max>
Maximum value to add in perfdata output (default: '')
Maximum value to add in perfdata output (default: '').
=back

View File

@ -28,7 +28,7 @@ use centreon::plugins::misc;
use Time::HiRes qw(time);
use POSIX qw(floor);
my $unitdiv = { s => 1, w => 604800, d => 86400, h => 3600, m => 60 };
my $unitdiv = { s => 1, w => 604800, d => 86400, h => 3600, m => 60 };
my $unitdiv_long = { s => 'seconds', w => 'weeks', d => 'days', h => 'hours', m => 'minutes' };
sub custom_uptime_output {
@ -76,7 +76,7 @@ sub set_counters {
$self->{maps_counters}->{global} = [
{ label => 'uptime',
set => {
key_values => [{ name => 'uptime' }],
key_values => [ { name => 'uptime' } ],
closure_custom_output => $self->can('custom_uptime_output'),
closure_custom_perfdata => $self->can('custom_uptime_perfdata'),
closure_custom_threshold_check => $self->can('custom_uptime_threshold')
@ -87,7 +87,7 @@ sub set_counters {
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$options{options}->add_options(arguments => {
@ -109,7 +109,7 @@ sub check_options {
sub manage_selection {
my ($self, %options) = @_;
my $topic = '$SYS/broker/uptime';
my $topic = '$SYS/broker/uptime';
my $uptime = $options{mqtt}->query(
topic => $topic
);

View File

@ -0,0 +1,319 @@
#
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package apps::exense::step::restapi::custom::api;
use strict;
use warnings;
use centreon::plugins::http;
use centreon::plugins::statefile;
use JSON::XS;
use Digest::MD5 qw(md5_hex);
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
if (!defined($options{output})) {
print "Class Custom: Need to specify 'output' argument.\n";
exit 3;
}
if (!defined($options{options})) {
$options{output}->add_option_msg(short_msg => "Class Custom: Need to specify 'options' argument.");
$options{output}->option_exit();
}
if (!defined($options{noptions})) {
$options{options}->add_options(arguments => {
'hostname:s' => { name => 'hostname' },
'port:s' => { name => 'port' },
'proto:s' => { name => 'proto' },
'api-username:s' => { name => 'api_username' },
'api-password:s' => { name => 'api_password' },
'token:s' => { name => 'token' },
'timeout:s' => { name => 'timeout' }
});
}
$options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1);
$self->{output} = $options{output};
$self->{http} = centreon::plugins::http->new(%options, default_backend => 'curl');
$self->{cache_connect} = centreon::plugins::statefile->new(%options);
return $self;
}
sub set_options {
my ($self, %options) = @_;
$self->{option_results} = $options{option_results};
}
sub set_defaults {}
sub check_options {
my ($self, %options) = @_;
$self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : '';
$self->{proto} = (defined($self->{option_results}->{proto})) ? $self->{option_results}->{proto} : 'https';
$self->{port} = (defined($self->{option_results}->{port})) ? $self->{option_results}->{port} : 443;
$self->{api_username} = (defined($self->{option_results}->{api_username})) ? $self->{option_results}->{api_username} : '';
$self->{api_password} = (defined($self->{option_results}->{api_password})) ? $self->{option_results}->{api_password} : '';
$self->{token} = (defined($self->{option_results}->{token})) ? $self->{option_results}->{token} : '';
$self->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 30;
$self->{unknown_http_status} = (defined($self->{option_results}->{unknown_http_status})) ? $self->{option_results}->{unknown_http_status} : '%{http_code} < 200 or %{http_code} >= 300' ;
$self->{warning_http_status} = (defined($self->{option_results}->{warning_http_status})) ? $self->{option_results}->{warning_http_status} : '';
$self->{critical_http_status} = (defined($self->{option_results}->{critical_http_status})) ? $self->{option_results}->{critical_http_status} : '';
if ($self->{hostname} eq '') {
$self->{output}->add_option_msg(short_msg => 'Need to specify hostname option.');
$self->{output}->option_exit();
}
if ($self->{token} ne '') {
return 0;
}
if ($self->{api_username} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify --api-username or --token option.");
$self->{output}->option_exit();
}
if ($self->{api_password} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify --api-password option.");
$self->{output}->option_exit();
}
$self->{cache_connect}->check_options(option_results => $self->{option_results});
return 0;
}
sub get_connection_infos {
my ($self, %options) = @_;
return $self->{hostname} . '_' . $self->{http}->get_port();
}
sub get_hostname {
my ($self, %options) = @_;
return $self->{hostname};
}
sub get_port {
my ($self, %options) = @_;
return $self->{port};
}
sub build_options_for_httplib {
my ($self, %options) = @_;
$self->{option_results}->{hostname} = $self->{hostname};
$self->{option_results}->{port} = $self->{port};
$self->{option_results}->{proto} = $self->{proto};
}
sub settings {
my ($self, %options) = @_;
$self->build_options_for_httplib();
$self->{http}->add_header(key => 'Accept', value => 'application/json');
$self->{http}->add_header(key => 'Content-Type', value => 'application/json');
$self->{http}->set_options(%{$self->{option_results}});
}
sub clean_session_id {
my ($self, %options) = @_;
my $datas = { updated => time() };
$self->{cache_connect}->write(data => $datas);
}
sub get_session_id {
my ($self, %options) = @_;
my $has_cache_file = $self->{cache_connect}->read(statefile => 'exense_step_' . md5_hex($self->{option_results}->{hostname}) . '_' . md5_hex($self->{option_results}->{api_username}));
my $session_id = $self->{cache_connect}->get(name => 'session_id');
my $md5_secret_cache = $self->{cache_connect}->get(name => 'md5_secret');
my $md5_secret = md5_hex($self->{api_username} . $self->{api_password});
if ($has_cache_file == 0 ||
!defined($session_id) ||
(defined($md5_secret_cache) && $md5_secret_cache ne $md5_secret)
) {
my $json_request = { username => $self->{api_username}, password => $self->{api_password} };
my $encoded = centreon::plugins::misc::json_encode($json_request);
unless($encoded) {
$self->{output}->add_option_msg(short_msg => 'cannot encode json request');
$self->{output}->option_exit();
}
my ($content) = $self->{http}->request(
method => 'POST',
url_path => '/rest/access/login',
query_form_post => $encoded,
warning_status => '', unknown_status => '', critical_status => ''
);
if ($self->{http}->get_code() != 200) {
$self->{output}->add_option_msg(short_msg => "Authentication error [code: '" . $self->{http}->get_code() . "'] [message: '" . $self->{http}->get_message() . "']");
$self->{output}->option_exit();
}
my (@cookies) = $self->{http}->get_first_header(name => 'Set-Cookie');
foreach my $cookie (@cookies) {
$session_id = $1 if ($cookie =~ /sessionid=(.+?);/);
}
if (!defined($session_id)) {
$self->{output}->add_option_msg(short_msg => "Cannot get cookie");
$self->{output}->option_exit();
}
my $datas = {
updated => time(),
session_id => $session_id,
md5_secret => $md5_secret
};
$self->{cache_connect}->write(data => $datas);
}
return $session_id;
}
sub credentials {
my ($self, %options) = @_;
my $creds = {};
if ($self->{token} ne '') {
$creds = {
header => ['Authorization: Bearer ' . $self->{token}],
unknown_status => $self->{unknown_http_status},
warning_status => $self->{warning_http_status},
critical_status => $self->{critical_http_status}
};
} else {
my $session_id = $self->get_session_id();
$creds = {
header => ['Cookie: sessionid=' . $session_id],
warning_status => '',
unknown_status => '',
critical_status => ''
};
}
return $creds;
}
sub request {
my ($self, %options) = @_;
my $endpoint = $options{endpoint};
$self->settings();
my $creds = $self->credentials();
my $content = $self->{http}->request(
method => $options{method},
url_path => $endpoint,
get_param => $options{get_param},
query_form_post => $options{query_form_post},
%$creds
);
# Maybe there is an issue with the token. So we retry.
if ($self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300) {
$self->clean_session_id();
$creds = $self->credentials();
$creds->{unknown_status} = $self->{unknown_http_status};
$creds->{warning_status} = $self->{warning_status};
$creds->{critical_http_status} = $self->{critical_http_status};
$content = $self->{http}->request(
method => $options{method},
url_path => $endpoint,
get_param => $options{get_param},
query_form_post => $options{query_form_post},
%$creds
);
}
return if (defined($options{skip_decode}));
my $decoded = centreon::plugins::misc::json_decode($content);
if (!defined($decoded)) {
$self->{output}->add_option_msg(short_msg => 'Error while retrieving data (add --debug option for detailed message)');
$self->{output}->option_exit();
}
return $decoded;
}
1;
__END__
=head1 NAME
Exense Step API
=head1 SYNOPSIS
Exense Step API
=head1 REST API OPTIONS
=over 8
=item B<--hostname>
API hostname.
=item B<--port>
API port (default: 443)
=item B<--proto>
Specify https if needed (default: 'https')
=item B<--token>
Use token authentication.
=item B<--api-username>
Set API username
=item B<--api-password>
Set API password
=item B<--timeout>
Set HTTP timeout
=back
=head1 DESCRIPTION
B<custom>.
=cut

View File

@ -0,0 +1,147 @@
#
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package apps::exense::step::restapi::mode::listplans;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
use JSON::XS;
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$options{options}->add_options(arguments => {
'tenant-name:s' => { name => 'tenant_name' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (!defined($self->{option_results}->{tenant_name}) || $self->{option_results}->{tenant_name} eq '') {
$self->{option_results}->{tenant_name} = '[All]';
}
}
sub manage_selection {
my ($self, %options) = @_;
my $payload = $self->{option_results}->{tenant_name};
$options{custom}->request(method => 'POST', endpoint => '/rest/tenants/current', query_form_post => $payload, skip_decode => 1);
$payload = {
skip => 0,
limit => 4000000,
filters => [
{
collectionFilter => { type => 'True', field => 'visible' }
}
],
'sort' => {
'field' => 'attributes.name',
'direction' => 'ASCENDING'
}
};
$payload = centreon::plugins::misc::json_encode($payload);
unless($payload) {
$self->{output}->add_option_msg(short_msg => 'cannot encode json request');
$self->{output}->option_exit();
}
my $plans = $options{custom}->request(method => 'POST', endpoint => '/rest/table/plans', query_form_post => $payload);
my $results = [];
foreach my $plan (@{$plans->{data}}) {
# skip plans created by keyword single execution
next if ($plan->{visible} =~ /false|0/);
push @$results, {
id => $plan->{id},
name => $plan->{attributes}->{name}
};
}
return $results;
}
sub run {
my ($self, %options) = @_;
my $results = $self->manage_selection(%options);
foreach (@$results) {
$self->{output}->output_add(
long_msg => sprintf(
'[id: %s][name: %s]',
$_->{id},
$_->{name}
)
);
}
$self->{output}->output_add(
severity => 'OK',
short_msg => 'List plans:'
);
$self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
$self->{output}->exit();
}
sub disco_format {
my ($self, %options) = @_;
$self->{output}->add_disco_format(elements => ['id', 'name']);
}
sub disco_show {
my ($self, %options) = @_;
my $results = $self->manage_selection(%options);
foreach (@$results) {
$self->{output}->add_disco_entry(
id => $_->{id},
name => $_->{name}
);
}
}
1;
__END__
=head1 MODE
List plans.
=over 8
=item B<--tenant-name>
Check plan of a tenant (default: '[All]').
=back
=cut

View File

@ -0,0 +1,111 @@
#
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package apps::exense::step::restapi::mode::listtenants;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
use JSON::XS;
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$options{options}->add_options(arguments => {});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
}
sub manage_selection {
my ($self, %options) = @_;
my $tenants = $options{custom}->request(method => 'GET', endpoint => '/rest/tenants');
my $results = [];
foreach my $tenant (@$tenants) {
push @$results, {
name => $tenant->{name},
projectId => defined($tenant->{projectId}) ? $tenant->{projectId} : '',
global => $tenant->{global} =~ /true|1/i ? 1 : 0
};
}
return $results;
}
sub run {
my ($self, %options) = @_;
my $results = $self->manage_selection(%options);
foreach (@$results) {
$self->{output}->output_add(
long_msg => sprintf(
'[name: %s][projectId: %s][global: %s]',
$_->{name},
$_->{projectId},
$_->{global}
)
);
}
$self->{output}->output_add(
severity => 'OK',
short_msg => 'List tenants:'
);
$self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
$self->{output}->exit();
}
sub disco_format {
my ($self, %options) = @_;
$self->{output}->add_disco_format(elements => ['name', 'projectId', 'global']);
}
sub disco_show {
my ($self, %options) = @_;
my $results = $self->manage_selection(%options);
foreach (@$results) {
$self->{output}->add_disco_entry(%$_);
}
}
1;
__END__
=head1 MODE
List tenants.
=over 8
=back
=cut

View File

@ -0,0 +1,507 @@
#
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package apps::exense::step::restapi::mode::plans;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use DateTime;
use POSIX;
use JSON::XS;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
use centreon::plugins::misc;
my $unitdiv = { s => 1, w => 604800, d => 86400, h => 3600, m => 60 };
my $unitdiv_long = { s => 'seconds', w => 'weeks', d => 'days', h => 'hours', m => 'minutes' };
sub custom_last_exec_perfdata {
my ($self, %options) = @_;
$self->{output}->perfdata_add(
nlabel => $self->{nlabel} . '.' . $unitdiv_long->{ $self->{instance_mode}->{option_results}->{unit} },
instances => $self->{result_values}->{name},
unit => $self->{instance_mode}->{option_results}->{unit},
value => $self->{result_values}->{lastExecSeconds} >= 0 ? floor($self->{result_values}->{lastExecSeconds} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }) : $self->{result_values}->{lastExecSeconds},
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}),
min => 0
);
}
sub custom_last_exec_threshold {
my ($self, %options) = @_;
return $self->{perfdata}->threshold_check(
value => $self->{result_values}->{lastExecSeconds} >= 0 ? floor($self->{result_values}->{lastExecSeconds} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }) : $self->{result_values}->{lastExecSeconds},
threshold => [
{ label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' },
{ label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' },
{ label => 'unknown-'. $self->{thlabel}, exit_litteral => 'unknown' }
]
);
}
sub custom_duration_perfdata {
my ($self, %options) = @_;
$self->{output}->perfdata_add(
nlabel => $self->{nlabel} . '.' . $unitdiv_long->{ $self->{instance_mode}->{option_results}->{unit} },
instances => $self->{result_values}->{name},
unit => $self->{instance_mode}->{option_results}->{unit},
value => floor($self->{result_values}->{durationSeconds} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }),
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}),
min => 0
);
}
sub custom_duration_threshold {
my ($self, %options) = @_;
return $self->{perfdata}->threshold_check(
value => floor($self->{result_values}->{durationSeconds} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }),
threshold => [
{ label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' },
{ label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' },
{ label => 'unknown-'. $self->{thlabel}, exit_litteral => 'unknown' }
]
);
}
sub custom_execution_status_output {
my ($self, %options) = @_;
return sprintf(
"result: %s, status: %s",
$self->{result_values}->{result},
$self->{result_values}->{status}
);
}
sub plan_long_output {
my ($self, %options) = @_;
return sprintf(
"checking plan '%s'",
$options{instance_value}->{name}
);
}
sub prefix_plan_output {
my ($self, %options) = @_;
my $plan_name = defined $options{instance_value}->{name} ? $options{instance_value}->{name} : 'unknown';
return sprintf("plan '%s' ", $plan_name);
}
sub prefix_global_output {
my ($self, %options) = @_;
return 'Number of plans ';
}
sub prefix_execution_output {
my ($self, %options) = @_;
return sprintf(
"execution '%s' [env: %s] [started: %s] ",
$options{instance_value}->{executionId},
$options{instance_value}->{environment},
$options{instance_value}->{started}
);
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0, cb_prefix_output => 'prefix_global_output' },
{ name => 'plans', type => 3, cb_prefix_output => 'prefix_plan_output', cb_long_output => 'prefix_plan_output', indent_long_output => ' ', message_multiple => 'All plans are ok',
group => [
{ name => 'exec_detect', type => 0 },
{ name => 'failed', type => 0 },
{ name => 'timers', type => 0, skipped_code => { -10 => 1 } },
{ name => 'executions', type => 1, cb_prefix_output => 'prefix_execution_output', message_multiple => 'executions are ok', display_long => 1, sort_method => 'num', skipped_code => { -10 => 1 } }
]
}
];
$self->{maps_counters}->{global} = [
{ label => 'plans-detected', display_ok => 0, nlabel => 'plans.detected.count', set => {
key_values => [ { name => 'detected' } ],
output_template => 'detected: %s',
perfdatas => [
{ template => '%s', min => 0 }
]
}
}
];
$self->{maps_counters}->{exec_detect} = [
{ label => 'plan-executions-detected', nlabel => 'plan.executions.detected.count', set => {
key_values => [ { name => 'detected' }, { name => 'name' } ],
output_template => 'number of plan executions detected: %s',
perfdatas => [
{ template => '%s', min => 0, label_extra_instance => 1, instance_use => 'name' }
]
}
}
];
$self->{maps_counters}->{failed} = [
{ label => 'plan-executions-failed-prct', nlabel => 'plan.executions.failed.percentage', set => {
key_values => [ { name => 'failedPrct' }, { name => 'name' } ],
output_template => 'number of failed executions: %.2f %%',
perfdatas => [
{ template => '%.2f', unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'name' }
]
}
}
];
$self->{maps_counters}->{timers} = [
{ label => 'plan-execution-last', nlabel => 'plan.execution.last', set => {
key_values => [ { name => 'lastExecSeconds' }, { name => 'lastExecHuman' }, { name => 'name' } ],
output_template => 'last execution %s',
output_use => 'lastExecHuman',
closure_custom_perfdata => $self->can('custom_last_exec_perfdata'),
closure_custom_threshold_check => $self->can('custom_last_exec_threshold')
}
},
{ label => 'plan-running-duration', nlabel => 'plan.running.duration', set => {
key_values => [ { name => 'durationSeconds' }, { name => 'durationHuman' }, { name => 'name' } ],
output_template => 'running duration %s',
output_use => 'durationHuman',
closure_custom_perfdata => $self->can('custom_duration_perfdata'),
closure_custom_threshold_check => $self->can('custom_duration_threshold')
}
}
];
$self->{maps_counters}->{executions} = [
{
label => 'plan-execution-status',
type => 2,
set => {
key_values => [
{ name => 'status' }, {name => 'result' }, { name => 'planName' }
],
closure_custom_output => $self->can('custom_execution_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
}
];
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
'filter-plan-id:s' => { name => 'filter_plan_id' },
'filter-plan-name:s' => { name => 'filter_plan_name' },
'filter-environment:s' => { name => 'filter_environment' },
'since-timeperiod:s' => { name => 'since_timeperiod' },
'status-failed:s' => { name => 'status_failed' },
'only-last-execution' => { name => 'only_last_execution' },
'tenant-name:s' => { name => 'tenant_name' },
'timezone:s' => { name => 'timezone' },
'unit:s' => { name => 'unit', default => 's' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
if ($self->{option_results}->{unit} eq '' || !defined($unitdiv->{$self->{option_results}->{unit}})) {
$self->{option_results}->{unit} = 's';
}
if (!defined($self->{option_results}->{since_timeperiod}) || $self->{option_results}->{since_timeperiod} eq '') {
$self->{option_results}->{since_timeperiod} = 86400;
}
if (!defined($self->{option_results}->{tenant_name}) || $self->{option_results}->{tenant_name} eq '') {
$self->{option_results}->{tenant_name} = '[All]';
}
$self->{tz} = {};
if (defined($self->{option_results}->{timezone}) && $self->{option_results}->{timezone} ne '') {
$self->{tz} = centreon::plugins::misc::set_timezone(name => $self->{option_results}->{timezone});
}
$self->{option_results}->{timezone} = 'UTC' if (!defined($self->{option_results}->{timezone}) || $self->{option_results}->{timezone}
eq '');
if (!defined($self->{option_results}->{status_failed}) || $self->{option_results}->{status_failed} eq '') {
$self->{option_results}->{status_failed} = '%{result} =~ /technical_error|failed|interrupted/i';
}
$self->{option_results}->{status_failed} =~ s/%\{(.*?)\}/\$values->{$1}/g;
$self->{option_results}->{status_failed} =~ s/%\((.*?)\)/\$values->{$1}/g;
}
sub manage_selection {
my ($self, %options) = @_;
my $status_filter = {};
if (defined($self->{option_results}->{filter_status}) && $self->{option_results}->{filter_status}[0] ne '') {
$status_filter->{statusFilter} = $self->{option_results}->{filter_status};
}
my $payload = $self->{option_results}->{tenant_name};
$options{custom}->request(method => 'POST', endpoint => '/rest/tenants/current', query_form_post => $payload, skip_decode => 1);
$payload = {
skip => 0,
limit => 4000000,
filters => [
{
collectionFilter => { type => 'True', field => 'visible' }
}
],
'sort' => {
'field' => 'attributes.name',
'direction' => 'ASCENDING'
}
};
eval {
$payload = encode_json($payload);
};
if ($@) {
$self->{output}->add_option_msg(short_msg => 'cannot encode json request');
$self->{output}->option_exit();
}
my $plans = $options{custom}->request(method => 'POST', endpoint => '/rest/table/plans', query_form_post => $payload);
my $ctime = time();
my $filterTime = ($ctime - $self->{option_results}->{since_timeperiod}) * 1000;
$payload = {
skip => 0,
limit => 4000000,
filters => [
{
collectionFilter => { type => 'Gte', field => 'startTime', value => $filterTime }
}
],
'sort' => {
'field' => 'startTime',
'direction' => 'DESCENDING'
}
};
eval {
$payload = encode_json($payload);
};
if ($@) {
$self->{output}->add_option_msg(short_msg => 'cannot encode json request');
$self->{output}->option_exit();
}
my $executions = $options{custom}->request(method => 'POST', endpoint => '/rest/table/executions', query_form_post => $payload);
$self->{global} = { detected => 0 };
$self->{plans} = {};
foreach my $plan (@{$plans->{data}}) {
# skip plans created by keyword single execution
next if (defined $plan->{visible} && $plan->{visible} =~ /false|0/);
next if (defined($self->{option_results}->{filter_plan_id}) && $self->{option_results}->{filter_plan_id} ne '' &&
$plan->{id} !~ /$self->{option_results}->{filter_plan_id}/);
next if (defined($self->{option_results}->{filter_plan_name}) && $self->{option_results}->{filter_plan_name} ne '' &&
$plan->{attributes}->{name} !~ /$self->{option_results}->{filter_plan_name}/);
$self->{global}->{detected}++;
$self->{plans}->{ $plan->{id} } = {
name => $plan->{attributes}->{name},
exec_detect => { detected => 0, name => $plan->{attributes}->{name} },
failed => { failedPrct => 0, name => $plan->{attributes}->{name} },
timers => {},
executions => {}
};
my ($last_exec, $older_running_exec);
my ($failed, $total) = (0, 0);
my $i = 0;
foreach my $plan_exec (@{$executions->{data}}) {
next if (!defined($plan_exec->{planId}));
next if ($plan_exec->{planId} ne $plan->{id});
$plan_exec->{startTimeSec} = $plan_exec->{startTime} / 1000;
next if ($plan_exec->{startTimeSec} < ($ctime - $self->{option_results}->{since_timeperiod}));
next if (defined($self->{option_results}->{filter_environment}) && $self->{option_results}->{filter_environment} ne '' &&
$plan_exec->{executionParameters}->{customParameters}->{env} !~ /$self->{option_results}->{filter_environment}/);
# if the endTime is empty, we store this older running execution for later
if (!defined($plan_exec->{endTime}) || $plan_exec->{endTime} eq '') {
$older_running_exec = $plan_exec;
}
if (!defined($last_exec)) {
$last_exec = $plan_exec;
}
$self->{plans}->{ $plan->{id} }->{exec_detect}->{detected}++;
$failed++ if ($self->{output}->test_eval(test => $self->{option_results}->{status_failed}, values => { result => lc($plan_exec->{result}), status => lc($plan_exec->{status}) }));
$total++;
my $dt = DateTime->from_epoch(epoch => $plan_exec->{startTimeSec}, %{$self->{tz}});
my $timeraised = sprintf(
'%02d-%02d-%02dT%02d:%02d:%02d (%s)', $dt->year, $dt->month, $dt->day, $dt->hour, $dt->minute, $dt->second, $self->{option_results}->{timezone}
);
$self->{plans}->{ $plan->{id} }->{executions}->{$i} = {
executionId => $plan_exec->{id},
planName => $plan->{attributes}->{name},
environment => $plan_exec->{executionParameters}->{customParameters}->{env},
started => $timeraised,
status => lc($plan_exec->{status}),
result => lc($plan_exec->{result})
};
$i++;
last if (defined($self->{option_results}->{only_last_execution}));
}
$self->{plans}->{ $plan->{id} }->{failed}->{failedPrct} = $total > 0 ? $failed * 100 / $total : 0;
if (defined($last_exec)) {
$self->{plans}->{ $plan->{id} }->{timers} = {
name => $plan->{attributes}->{name},
lastExecSeconds => defined($last_exec->{startTime}) ? $ctime - $last_exec->{startTimeSec} : -1,
lastExecHuman => defined($last_exec->{startTime}) ? centreon::plugins::misc::change_seconds(value => $ctime - $last_exec->{startTimeSec}) : 'never'
};
}
if (defined($older_running_exec)) {
my $duration = $ctime - $older_running_exec->{startTime};
$self->{plans}->{ $plan->{name} }->{timers}->{durationSeconds} = $duration;
$self->{plans}->{ $plan->{name} }->{timers}->{durationHuman} = centreon::plugins::misc::change_seconds(value => $duration);
}
}
}
1;
__END__
=head1 MODE
Check plans.
=over 8
=item B<--tenant-name>
Check plan of a tenant (default: '[All]').
=item B<--filter-plan-id>
Filter plans by plan ID.
=item B<--filter-plan-name>
Filter plans by plan name.
=item B<--filter-environment>
Filter plan executions by environment name.
=item B<--since-timeperiod>
Time period to get plans executions information (in seconds. default: 86400).
=item B<--only-last-execution>
Check only last plan execution.
=item B<--timezone>
Define timezone for start/end plan execution time (default is 'UTC').
=item B<--status-failed>
Expression to define status failed (default: '%{result} =~ /technical_error|failed|interrupted/i').
=item B<--unknown-plan-execution-status>
Set unknown threshold for last plan execution status.
You can use the following variables: %{status}, %{planName}
=item B<--warning-plan-execution-status>
Set warning threshold for last plan execution status.
You can use the following variables: %{status}, %{planName}
=item B<--critical-plan-execution-status>
Set critical threshold for last plan execution status.
You can use the following variables: %{status}, %{planName}
=item B<--warning-plans-detected>
Thresholds.
=item B<--critical-plans-detected>
Thresholds.
=item B<--warning-plan-executions-detected>
Thresholds.
=item B<--critical-plan-executions-detected>
Thresholds.
=item B<--warning-plan-executions-failed-prct>
Thresholds.
=item B<--critical-plan-executions-failed-prct>
Thresholds.
=item B<--warning-plan-execution-last>
Thresholds.
=item B<--critical-plan-execution-last>
Thresholds.
=item B<--warning-plan-running-duration>
Thresholds.
=item B<--critical-plan-running-duration>
Thresholds.
=back
=cut

View File

@ -0,0 +1,51 @@
#
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package apps::exense::step::restapi::plugin;
use strict;
use warnings;
use base qw(centreon::plugins::script_custom);
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$self->{modes} = {
'list-plans' => 'apps::exense::step::restapi::mode::listplans',
'list-tenants' => 'apps::exense::step::restapi::mode::listtenants',
'plans' => 'apps::exense::step::restapi::mode::plans'
};
$self->{custom_modes}->{api} = 'apps::exense::step::restapi::custom::api';
return $self;
}
1;
__END__
=head1 PLUGIN DESCRIPTION
Check Exense Step using Rest API.
=cut

View File

@ -53,7 +53,16 @@ sub set_counters {
{ name => 'scenarios', type => 3, cb_prefix_output => 'prefix_scenario_output', cb_long_output => 'prefix_scenario_output', indent_long_output => ' ', message_multiple => 'All scenarios are ok',
group => [
{ name => 'global', type => 0, skipped_code => { -10 => 1 } },
{ name => 'steps', display_long => 1, cb_prefix_output => 'prefix_steps_output', message_multiple => 'All steps are ok', type => 1, skipped_code => { -10 => 1 } }
{
name => 'steps',
type => 1,
cb_prefix_output => 'prefix_steps_output',
display_long => 1,
message_multiple => 'All steps are ok',
skipped_code => { -10 => 1 },
sort_method => 'num',
sort_attribute => 'index'
}
]
}
];
@ -98,7 +107,7 @@ sub set_counters {
];
$self->{maps_counters}->{steps} = [
{ label => 'time-step', nlabel => 'scenario.step.time.milliseconds', set => {
key_values => [ { name => 'time_step' }, { name => 'display' }, { name => 'last_exec' } ],
key_values => [ { name => 'time_step' }, { name => 'display' }, { name => 'last_exec' }, { name => 'index' } ],
output_template => 'time step: %s ms',
perfdatas => [
{ template => '%s', unit => 'ms', min => 0, label_extra_instance => 1 }
@ -106,7 +115,7 @@ sub set_counters {
}
},
{ label => 'time-total', nlabel => 'scenario.steps.time.total.milliseconds', set => {
key_values => [ { name => 'time_total' }, { name => 'display' }, { name => 'last_exec' } ],
key_values => [ { name => 'time_total' }, { name => 'display' }, { name => 'last_exec' }, { name => 'index' } ],
output_template => 'time total: %s ms',
perfdatas => [
{ template => '%s', unit => 'ms', min => 0, label_extra_instance => 1 }
@ -209,6 +218,7 @@ sub manage_selection {
$self->{scenarios}->{ $scenario->{scenarioName} }->{steps}->{ $self->{scenarios}->{ $scenario->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} } }->{ $step_metrics->{metric} } = $step_metrics->{value};
$self->{scenarios}->{ $scenario->{scenarioName} }->{steps}->{ $self->{scenarios}->{ $scenario->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} } }->{last_exec} = POSIX::strftime('%d-%m-%Y %H:%M:%S %Z', localtime($exec_time));
$self->{scenarios}->{ $scenario->{scenarioName} }->{steps}->{ $self->{scenarios}->{ $scenario->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} } }->{display} = $self->{scenarios}->{ $scenario->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} };
$self->{scenarios}->{ $scenario->{scenarioName} }->{steps}->{ $self->{scenarios}->{ $scenario->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} } }->{index} = $step_metrics->{stepId};
}
}
@ -254,14 +264,53 @@ Syntax: C<--warning-scenario-status='%{status} =~ "xxx"'>
Critical threshold for scenario status (default: '%{status} =~ "Failure"').
Syntax: --critical-scenario-status='%{status} =~ "xxx"'
=item B<--warning-*> B<--critical-*>
=item B<--warning-availability>
Thresholds.
Common: 'availability' (%),
For WEB scenarios: 'time-total-allsteps' (ms), 'time-step' (ms),
For HTTPR scenarios: 'time-total' (ms),
FOR BPL scenarios: 'time-interaction' (ms), 'time-total' (ms).
Thresholds in %.
=item B<--critical-availability>
Thresholds in %.
=item B<--warning-time-total-allsteps>
Thresholds in ms for WEB scenarios.
=item B<--critical-time-total-allsteps>
Thresholds in ms for WEB scenarios.
=item B<--warning-time-step>
Thresholds in ms for WEB scenarios.
=item B<--critical-time-step>
Thresholds in ms for WEB scenarios.
=item B<--warning-time-total>
Thresholds in ms for HTTPR scenarios.
=item B<--critical-time-total>
Thresholds in ms for HTTPR scenarios.
=item B<--warning-time-interaction>
Thresholds in ms for BPL scenarios.
=item B<--critical-time-interaction>
Thresholds in ms for BPL scenarios.
=item B<--warning-time-total>
Thresholds in ms for BPL scenarios.
=item B<--critical-time-total>
Thresholds in ms for BPL scenarios.
=back

View File

@ -103,7 +103,7 @@ sub set_counters {
$self->{maps_counters}->{cpu_usage} = [
{
label => 'usage-percentage',
label => 'usage-prct',
type => 1,
nlabel => 'cpu.capacity.usage.percentage',
set => {
@ -135,6 +135,7 @@ sub set_counters {
{
value => 'cpu_usage_hertz',
template => '%s',
min => 0,
max => 'cpu_provisioned_hertz',
unit => 'Hz'
}
@ -145,7 +146,7 @@ sub set_counters {
$self->{maps_counters}->{cpu_contention} = [
{
label => 'contention-percentage',
label => 'contention-prct',
type => 1,
nlabel => 'cpu.capacity.contention.percentage',
set => {
@ -167,7 +168,7 @@ sub set_counters {
];
$self->{maps_counters}->{cpu_demand} = [
{
label => 'demand-percentage',
label => 'demand-prct',
type => 1,
nlabel => 'cpu.capacity.demand.percentage',
set => {
@ -175,7 +176,7 @@ sub set_counters {
key_values => [ { name => 'prct_demand' } ],
output_use => 'prct_demand',
threshold_use => 'prct_demand',
perfdatas => [ { value => 'prct_demand', template => '%s' } ]
perfdatas => [ { value => 'prct_demand', template => '%s', unit => '%', min => 0, max => 100 } ]
}
},
{
@ -195,6 +196,7 @@ sub set_counters {
{
value => 'cpu_demand_hertz',
template => '%s',
min => 0,
max => 'cpu_provisioned_hertz',
unit => 'Hz'
}
@ -205,7 +207,7 @@ sub set_counters {
$self->{maps_counters}->{cpu_corecount} = [
{
label => 'corecount-usage',
label => 'corecount-usage-count',
type => 1,
nlabel => 'cpu.corecount.usage.count',
set => {
@ -302,7 +304,7 @@ __END__
=head1 MODE
Monitor the status of VMware ESX hosts through vSphere 8 REST API.
Monitor the CPU stats of VMware ESX hosts through vSphere 8 REST API.
Meaning of the available counters in the VMware API:
- cpu.capacity.provisioned.HOST Capacity in kHz of the physical CPU cores.
@ -337,61 +339,53 @@ Add counter related to CPU core count:
C<cpu.corecount.usage.HOST>: The number of virtual processors running on the host.
=item B<--warning-usage-percentage>
=item B<--warning-contention-prct>
Threshold in %.
Threshold in percentage.
=item B<--critical-usage-percentage>
=item B<--critical-contention-prct>
Threshold in %.
Threshold in percentage.
=item B<--warning-usage-frequency>
=item B<--warning-corecount-usage-count>
Threshold in Hz.
Threshold.
=item B<--critical-usage-frequency>
=item B<--critical-corecount-usage-count>
Threshold in Hz.
=item B<--warning-contention-percentage>
Threshold in %.
=item B<--critical-contention-percentage>
Threshold in %.
=item B<--warning-contention-frequency>
Threshold in Hz.
=item B<--critical-contention-frequency>
Threshold in Hz.
=item B<--warning-demand-percentage>
Threshold in %.
=item B<--critical-demand-percentage>
Threshold in %.
Threshold.
=item B<--warning-demand-frequency>
Threshold in Hz.
Threshold in Hertz.
=item B<--critical-demand-frequency>
Threshold in Hz.
Threshold in Hertz.
=item B<--warning-corecount-usage>
=item B<--warning-demand-prct>
Threshold in number of cores.
Threshold in percentage.
=item B<--critical-corecount-usage>
=item B<--critical-demand-prct>
Threshold in number of cores.
Threshold in percentage.
=item B<--warning-usage-frequency>
Threshold in Hertz.
=item B<--critical-usage-frequency>
Threshold in Hertz.
=item B<--warning-usage-prct>
Threshold in percentage.
=item B<--critical-usage-prct>
Threshold in percentage.
=back

View File

@ -0,0 +1,117 @@
#
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package apps::vmware::vsphere8::esx::mode::diskio;
use strict;
use warnings;
use base qw(apps::vmware::vsphere8::esx::mode);
my @counters = (
"disk.throughput.usage.HOST",
"disk.throughput.contention.HOST"
);
sub custom_diskio_output {
my ($self, %options) = @_;
my $msg = sprintf("Disk throughput usage: %s %s/s",
$self->{perfdata}->change_bytes(value => $self->{result_values}->{throughput_bps})
);
return $msg;
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'diskio', type => 0 }
];
$self->{maps_counters}->{diskio} = [
{
label => 'usage-bps',
type => 1,
nlabel => 'disk.throughput.usage.bytespersecond',
set => {
key_values => [ { name => 'disk.throughput.usage.HOST' }, { name => 'throughput_bps' } ],
closure_custom_output => $self->can('custom_diskio_output'),
perfdatas => [ { value => 'throughput_bps', template => '%s', unit => 'Bps' } ]
}
},
{
label => 'contention-ms',
type => 1,
nlabel => 'disk.throughput.contention.milliseconds',
set => {
key_values => [ { name => 'disk.throughput.contention.HOST' }],
output_template => 'Disk throughput contention is %s ms',
output_use => 'disk.throughput.contention.HOST',
threshold_use => 'disk.throughput.contention.HOST',
perfdatas => [ { value => 'disk.throughput.contention.HOST', template => '%s', unit => 'ms' } ]
}
}
];
}
sub manage_selection {
my ($self, %options) = @_;
my %structure = map {
$_ => $self->get_esx_stats(%options, cid => $_, esx_id => $self->{esx_id}, esx_name => $self->{esx_name} )
} @counters;
$self->{diskio} = \%structure;
if ( defined($structure{'disk.throughput.usage.HOST'}) ) {
$self->{diskio}->{throughput_bps} = $structure{'disk.throughput.usage.HOST'} * 1024;
}
}
1;
=head1 MODE
Monitor the disk throughput and contention of VMware ESX hosts through vSphere 8 REST API.
Meaning of the available counters in the VMware API:
- disk.throughput.usage.HOST Aggregated disk I/O rate (in kB/s), including the rates for all virtual machines running on the host during the collection interval
- disk.throughput.contention.HOST Average amount of time (in milliseconds) for an I/O operation to complete successfully
=over 8
=item B<--warning-contention-ms>
Threshold in milliseconds.
=item B<--critical-contention-ms>
Threshold in milliseconds.
=item B<--warning-usage-bps>
Threshold in bytes per second.
=item B<--critical-usage-bps>
Threshold in bytes per second.
=back
=cut

View File

@ -52,7 +52,7 @@ sub set_counters {
$self->{maps_counters}->{memory} = [
{
label => 'vms-usage-percentage',
label => 'usage-prct',
type => 1,
nlabel => 'vms.memory.usage.percentage',
set => {
@ -72,7 +72,7 @@ sub set_counters {
}
},
{
label => 'vms-usage-bytes',
label => 'usage-bytes',
type => 1,
nlabel => 'vms.memory.usage.bytes',
set => {
@ -119,29 +119,32 @@ sub manage_selection {
Monitor the memory of VMware ESX hosts consumed by the virtual machines through vSphere 8 REST API.
Meaning of the available counters in the VMware API:
mem.reservedCapacityPct.HOST Percent of memory that has been reserved either through VMkernel use, by userworlds or due to virtual machine memory reservations.
mem.capacity.provisioned.HOST Total amount of memory available to the host.
mem.capacity.usable.HOST Amount of physical memory available for use by virtual machines on this host
mem.capacity.usage.HOST Amount of physical memory actively used
mem.capacity.contention.HOST Percentage of time VMs are waiting to access swapped, compressed or ballooned memory.
mem.consumed.vms.HOST Amount of physical memory consumed by VMs on this host.
mem.consumed.userworlds.HOST Amount of physical memory consumed by userworlds on this host
- mem.reservedCapacityPct.HOST Percent of memory that has been reserved either through VMkernel use, by userworlds or due to virtual machine memory reservations.
- mem.capacity.provisioned.HOST Total amount of memory available to the host.
- mem.capacity.usable.HOST Amount of physical memory available for use by virtual machines on this host
- mem.capacity.usage.HOST Amount of physical memory actively used
- mem.capacity.contention.HOST Percentage of time VMs are waiting to access swapped, compressed or ballooned memory.
- mem.consumed.vms.HOST Amount of physical memory consumed by VMs on this host.
- mem.consumed.userworlds.HOST Amount of physical memory consumed by userworlds on this host
=over 8
=item B<--warning-vms-usage-percentage>
=item B<--warning-usage-bytes>
Thresholds in percentage.
Threshold in bytes.
=item B<--critical-vms-usage-percentage>
=item B<--critical-usage-bytes>
Thresholds in percentage.
Threshold in bytes.
=item B<--warning-vms-usage-bytes>
=item B<--warning-usage-prct>
Thresholds in bytes.
Threshold in percentage.
=item B<--critical-vms-usage-bytes>
=item B<--critical-usage-prct>
Thresholds in bytes.
Threshold in percentage.
=back
=cut

View File

@ -0,0 +1,197 @@
#
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package apps::vmware::vsphere8::esx::mode::network;
use strict;
use warnings;
use base qw(apps::vmware::vsphere8::esx::mode);
my @counters = (
#'net.throughput.provisioned.HOST', # not used atm
'net.throughput.usable.HOST',
'net.throughput.usage.HOST',
#'net.throughput.contention.HOST' # pushed in manage_selection if necessary
);
sub custom_network_output {
my ($self, %options) = @_;
my $msg = sprintf("Network throughput usage: %s %s/s of %s %s/s usable",
$self->{perfdata}->change_bytes(value => $self->{result_values}->{usage_bps}),
$self->{perfdata}->change_bytes(value => $self->{result_values}->{max_bps})
);
return $msg;
}
# Skip contention processing if there is no available data
sub skip_contention {
my ($self, %options) = @_;
return 0 if (defined($self->{contention})
&& ref($self->{contention}) eq 'HASH'
&& scalar(keys %{$self->{contention}}) > 0);
return 1;
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'network', type => 0 },
{ name => 'contention', type => 0, cb_init => 'skip_contention' }
];
$self->{maps_counters}->{network} = [
{
label => 'usage-bps',
type => 1,
nlabel => 'network.throughput.usage.bytespersecond',
set => {
key_values => [ { name => 'usage_bps' }, { name => 'max_bps' }, { name => 'usage_prct' } ],
closure_custom_output => $self->can('custom_network_output'),
perfdatas => [ { value => 'usage_bps', template => '%s', unit => 'Bps', min => 0, max => 'max_bps' } ]
}
},
{
label => 'usage-prct',
type => 1,
nlabel => 'network.throughput.usage.percent',
set => {
key_values => [ { name => 'usage_prct' } ],
output_template => "%.2f%% of usable network throughput used",
output_use => "usage_prct",
perfdatas => [ { value => 'usage_prct', template => '%s', unit => '%', min => 0, max => '100' } ]
}
}
];
$self->{maps_counters}->{contention} = [
{
label => 'contention-count',
type => 1,
nlabel => 'network.throughput.contention.count',
set => {
key_values => [ { name => 'net.throughput.contention.HOST' } ],
output_template => "%d packet(s) dropped",
output_use => "net.throughput.contention.HOST",
perfdatas => [ { value => 'net.throughput.contention.HOST', template => '%s', unit => '' } ]
}
}
];
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
$options{options}->add_options(
arguments => {
'add-contention' => { name => 'add_contention' }
}
);
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
# If a threshold is given on rates, we enable the corresponding data collection
if (grep {$_ =~ /contention/ && defined($self->{option_results}->{$_}) && $self->{option_results}->{$_} ne ''} keys %{$self->{option_results}}) {
$self->{option_results}->{add_contention} = 1;
}
}
sub manage_selection {
my ($self, %options) = @_;
push @counters, 'net.throughput.contention.HOST' if ($self->{option_results}->{add_contention});
my %structure = map {
$_ => $self->get_esx_stats(%options, cid => $_, esx_id => $self->{esx_id}, esx_name => $self->{esx_name} )
} @counters;
$self->{network} = {};
$self->{contention} = {};
if ( defined($structure{'net.throughput.usage.HOST'}) && defined($structure{'net.throughput.usable.HOST'})) {
$self->{network}->{usage_bps} = $structure{'net.throughput.usage.HOST'} * 1024;
$self->{network}->{max_bps} = $structure{'net.throughput.usable.HOST'} * 1024;
if ($structure{'net.throughput.usable.HOST'} != 0) {
$self->{network}->{usage_prct} = 100 * $structure{'net.throughput.usage.HOST'} / $structure{'net.throughput.usable.HOST'};
} else {
$self->{network}->{usage_prct} = 0;
}
}
if ( defined($structure{'net.throughput.contention.HOST'}) ) {
$self->{contention}->{'net.throughput.contention.HOST'} = $structure{'net.throughput.contention.HOST'};
}
}
1;
=head1 MODE
Monitor the swap usage of VMware ESX hosts through vSphere 8 REST API.
- net.throughput.provisioned.HOST The maximum network bandwidth (in kB/s) for the host.
- net.throughput.usable.HOST The currently available network bandwidth (in kB/s) for the host.
- net.throughput.usage.HOST The current network bandwidth usage (in kB/s) for the host.
- net.throughput.contention.HOST The aggregate network droppped packets for the host.
=over 8
=item B<--add-contention>
Add counters related to network throughput contention.
This option is implicitly enabled if thresholds related to contention are set.
=item B<--warning-contention-count>
Threshold.
=item B<--critical-contention-count>
Threshold.
=item B<--warning-usage-bps>
Threshold in bytes per second.
=item B<--critical-usage-bps>
Threshold in bytes per second.
=item B<--warning-usage-prct>
Threshold in percentage.
=item B<--critical-usage-prct>
Threshold in percentage.
=back
=cut

View File

@ -37,7 +37,7 @@ sub set_counters {
$self->{maps_counters}->{power} = [
{
label => 'power-usage-watts',
label => 'usage-watts',
type => 1,
nlabel => 'power.capacity.usage.watts',
output_template => 'Power usage is %d Watts',
@ -46,7 +46,7 @@ sub set_counters {
key_values => [ { name => 'power.capacity.usage.HOST' } ],
output_use => 'power.capacity.usage.HOST',
threshold_use => 'power.capacity.usage.HOST',
perfdatas => [ { value => 'power.capacity.usage.HOST', template => '%sW' } ]
perfdatas => [ { value => 'power.capacity.usage.HOST', template => '%s', unit => 'W', min => 0 } ]
}
}
];
@ -79,11 +79,11 @@ Since our tests showed that only C<power.capacity.usage.HOST> was different from
=over 8
=item B<--warning-power-usage-watts>
=item B<--warning-usage-watts>
Threshold in Watts.
=item B<--critical-power-usage-watts>
=item B<--critical-usage-watts>
Threshold in Watts.

View File

@ -0,0 +1,236 @@
#
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package apps::vmware::vsphere8::esx::mode::swap;
use strict;
use warnings;
use base qw(apps::vmware::vsphere8::esx::mode);
my @counters = (
'mem.swap.current.HOST',
'mem.swap.target.HOST',
#'mem.swap.readrate.HOST', # pushed in manage_selection if necessary
#'mem.swap.writerate.HOST' # pushed in manage_selection if necessary
);
sub custom_swap_output {
my ($self, %options) = @_;
my $msg = sprintf("Swap usage: %s %s (max available is %s %s)",
$self->{perfdata}->change_bytes(value => $self->{result_values}->{used_bytes}),
$self->{perfdata}->change_bytes(value => $self->{result_values}->{max_bytes})
);
return $msg;
}
sub custom_swap_read_rate_output {
my ($self, %options) = @_;
my $msg = sprintf("Swap read rate is: %s %s/s",
$self->{perfdata}->change_bytes(value => $self->{result_values}->{read_rate_bps})
);
return $msg;
}
sub custom_swap_write_rate_output {
my ($self, %options) = @_;
my $msg = sprintf("Swap write rate is: %s %s/s",
$self->{perfdata}->change_bytes(value => $self->{result_values}->{write_rate_bps})
);
return $msg;
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
$options{options}->add_options(
arguments => {
'add-rates' => { name => 'add_rates' }
}
);
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
# If a threshold is given on rates, we enable the corresponding data collection
if (grep {$_ =~ /rate/ && defined($self->{option_results}->{$_}) && $self->{option_results}->{$_} ne ''} keys %{$self->{option_results}}) {
$self->{option_results}->{add_rates} = 1;
}
}
# Skip rates processing if there is no available data
sub skip_rates {
my ($self, %options) = @_;
return 0 if (defined($self->{swap_rates})
&& ref($self->{swap_rates}) eq 'HASH'
&& scalar(keys %{$self->{swap_rates}}) > 0);
return 1;
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'swap_usage', type => 0 },
{ name => 'swap_rates', type => 0, cb_init => 'skip_rates' }
];
$self->{maps_counters}->{swap_usage} = [
{
label => 'usage-bytes',
type => 1,
nlabel => 'swap.usage.bytes',
set => {
key_values => [ { name => 'used_bytes' }, { name => 'max_bytes' }, { name => 'used_prct' } ],
closure_custom_output => $self->can('custom_swap_output'),
perfdatas => [ { value => 'used_bytes', template => '%s', unit => 'B', max => 'max_bytes' } ]
},
},
{
label => 'usage-prct',
type => 1,
nlabel => 'swap.usage.percent',
set => {
key_values => [ { name => 'used_prct' } ],
output_template => "Percent used: %.2f%%",
output_use => 'used_prct',
perfdatas => [ { value => 'used_prct', template => '%s', unit => '%', min => 0, max => 100 } ]
}
}
];
$self->{maps_counters}->{swap_rates} = [
{
label => 'read-rate-bps',
type => 1,
nlabel => 'swap.read-rate.bytespersecond',
set => {
closure_custom_output => $self->can('custom_swap_read_rate_output'),
key_values => [ { name => 'read_rate_bps' } ],
perfdatas => [ { value => 'read_rate_bps', template => '%s', unit => 'Bps' } ]
}
},
{
label => 'write-rate-bps',
type => 1,
nlabel => 'swap.write-rate.bytespersecond',
set => {
closure_custom_output => $self->can('custom_swap_write_rate_output'),
key_values => [ { name => 'write_rate_bps' } ],
perfdatas => [ { value => 'write_rate_bps', template => '%s', unit => 'Bps' } ]
}
}
];
}
sub manage_selection {
my ($self, %options) = @_;
push @counters, 'mem.swap.readrate.HOST', 'mem.swap.writerate.HOST' if ($self->{option_results}->{add_rates});
my %structure = map {
$_ => $self->get_esx_stats(%options, cid => $_, esx_id => $self->{esx_id}, esx_name => $self->{esx_name})
} @counters;
$self->{swap_usage} = {};
if (defined($structure{'mem.swap.current.HOST'}) && defined($structure{'mem.swap.target.HOST'})) {
$self->{swap_usage}->{used_bytes} = $structure{'mem.swap.current.HOST'} * 1024;
$self->{swap_usage}->{max_bytes} = $structure{'mem.swap.target.HOST'} * 1024;
if ($structure{'mem.swap.target.HOST'} != 0) {
$self->{swap_usage}->{used_prct} = 100 * $structure{'mem.swap.current.HOST'} / $structure{'mem.swap.target.HOST'};
}
else {
$self->{swap_usage}->{used_prct} = 0;
}
}
if (defined($structure{'mem.swap.readrate.HOST'})) {
$self->{swap_rates}->{read_rate_bps} = $structure{'mem.swap.readrate.HOST'} * 1024;
}
if (defined($structure{'mem.swap.writerate.HOST'})) {
$self->{swap_rates}->{write_rate_bps} = $structure{'mem.swap.writerate.HOST'} * 1024;
}
}
1;
=head1 MODE
Monitor the swap usage of VMware ESX hosts through vSphere 8 REST API.
Meaning of the available counters in the VMware API:
- mem.swap.current.HOST Amount (in kB) of memory that is used by swap. Sum of memory swapped of all powered on VMs and vSphere services on the host.
- mem.swap.target.HOST Target size (in kB) for the virtual machine swap file. The VMkernel manages swapping by comparing swaptarget against swapped.
- mem.swap.readrate.HOST Rate (in kB/s) at which memory is swapped from disk into active memory during the interval. This counter applies to virtual machines and is generally more useful than the swapin counter to determine if the virtual machine is running slow due to swapping, especially when looking at real-time statistics.
- mem.swap.writerate.HOST Rate (in kB/s) at which memory is being swapped from active memory to disk during the current interval. This counter applies to virtual machines and is generally more useful than the swapout counter to determine if the virtual machine is running slow due to swapping, especially when looking at real-time statistics.
=over 8
=item B<--add-rates>
Add counters related to swap read and write rates.
This option is implicitly enabled if thresholds related to rates are set.
=item B<--warning-read-rate-bps>
Threshold in bytes per second.
=item B<--critical-read-rate-bps>
Threshold in bytes per second.
=item B<--warning-usage-bytes>
Threshold in B.
=item B<--critical-usage-bytes>
Threshold in B.
=item B<--warning-usage-prct>
Threshold in percentage.
=item B<--critical-usage-prct>
Threshold in percentage.
=item B<--warning-write-rate-bps>
Threshold in bytes per second.
=item B<--critical-write-rate-bps>
Threshold in bytes per second.
=back
=cut

View File

@ -33,9 +33,12 @@ sub new {
$self->{modes} = {
'cpu' => 'apps::vmware::vsphere8::esx::mode::cpu',
'discovery' => 'apps::vmware::vsphere8::esx::mode::discovery',
'disk-io' => 'apps::vmware::vsphere8::esx::mode::diskio',
'host-status' => 'apps::vmware::vsphere8::esx::mode::hoststatus',
'memory' => 'apps::vmware::vsphere8::esx::mode::memory',
'network' => 'apps::vmware::vsphere8::esx::mode::network',
'power' => 'apps::vmware::vsphere8::esx::mode::power',
'swap' => 'apps::vmware::vsphere8::esx::mode::swap',
};
$self->{custom_modes}->{api} = 'apps::vmware::vsphere8::custom::api';

View File

@ -24,98 +24,111 @@ use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold);
sub custom_status_perfdata {
my ($self, %options) = @_;
$self->{output}->perfdata_add(label => 'running_last_changed', unit => 's',
value => sprintf("%d", $self->{result_values}->{running_last_changed}),
min => 0);
$self->{output}->perfdata_add(label => 'running_last_saved', unit => 's',
value => sprintf("%d", $self->{result_values}->{running_last_saved}),
min => 0);
$self->{output}->perfdata_add(label => 'startup_last_changed', unit => 's',
value => sprintf("%d", $self->{result_values}->{startup_last_changed}),
min => 0);
}
use centreon::plugins::misc;
sub custom_status_output {
my ($self, %options) = @_;
my $msg = sprintf("Configuration Running Last Changed: %s, Running Last Saved: %s, Startup Last Changed: %s",
($self->{result_values}->{running_last_changed} > 0) ? centreon::plugins::misc::change_seconds(value => $self->{result_values}->{running_last_changed}) : "-",
($self->{result_values}->{running_last_saved} > 0) ? centreon::plugins::misc::change_seconds(value => $self->{result_values}->{running_last_saved}) : "-",
($self->{result_values}->{startup_last_changed} > 0) ? centreon::plugins::misc::change_seconds(value => $self->{result_values}->{startup_last_changed}) : "-");
return $msg;
}
sub custom_status_calc {
my ($self, %options) = @_;
$self->{result_values}->{running_last_changed} = $options{new_datas}->{$self->{instance} . '_running_last_changed'};
$self->{result_values}->{running_last_saved} = $options{new_datas}->{$self->{instance} . '_running_last_saved'};
$self->{result_values}->{startup_last_changed} = $options{new_datas}->{$self->{instance} . '_startup_last_changed'};
return 0;
return $self->{result_values}->{output_message};
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0 },
{ name => 'global', type => 0 }
];
$self->{maps_counters}->{global} = [
{ label => 'status', threshold => 0, set => {
key_values => [ { name => 'running_last_changed' }, { name => 'running_last_saved' }, { name => 'startup_last_changed' } ],
closure_custom_calc => $self->can('custom_status_calc'),
{
label => 'config-running-ahead', nlabel => 'configuration.running.ahead.since.seconds',
set => {
key_values => [ { name => 'running_ahead' }, { name => 'output_message' } ],
closure_custom_output => $self->can('custom_status_output'),
closure_custom_perfdata => $self->can('custom_status_perfdata'),
closure_custom_threshold_check => \&catalog_status_threshold,
perfdatas => [
{ template => '%s', min => 0, unit => 's' }
]
}
},
}
];
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments =>
{
"warning-status:s" => { name => 'warning_status', default => '' },
"critical-status:s" => { name => 'critical_status', default => '%{running_last_changed} > %{running_last_saved}' },
});
$options{options}->add_options(arguments => {});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
$self->change_macros(macros => ['warning_status', 'critical_status']);
}
my $oid_ccmHistoryRunningLastChanged = '.1.3.6.1.4.1.9.9.43.1.1.1.0';
my $oid_ccmHistoryRunningLastSaved = '.1.3.6.1.4.1.9.9.43.1.1.2.0';
my $oid_ccmHistoryStartupLastChanged = '.1.3.6.1.4.1.9.9.43.1.1.3.0';
sub manage_selection {
my ($self, %options) = @_;
$self->{snmp} = $options{snmp};
$self->{global} = {};
my $oid_ccmHistoryRunningLastChanged = '.1.3.6.1.4.1.9.9.43.1.1.1.0';
my $oid_ccmHistoryRunningLastSaved = '.1.3.6.1.4.1.9.9.43.1.1.2.0';
my $oid_ccmHistoryStartupLastChanged = '.1.3.6.1.4.1.9.9.43.1.1.3.0';
my $oid_sysUpTime = '.1.3.6.1.2.1.1.3.0';
my $oid_snmpEngineTime = '.1.3.6.1.6.3.10.2.1.3.0';
my $ctime = time();
my $runningChangedMarginAfterReload = 300;
my $results = $options{snmp}->get_leef(
oids => [
$oid_ccmHistoryRunningLastChanged,
$oid_ccmHistoryRunningLastSaved,
$oid_ccmHistoryStartupLastChanged,
$oid_sysUpTime,
$oid_snmpEngineTime
],
nothing_quit => 1
);
my $uptime = defined($results->{$oid_snmpEngineTime}) ? $results->{$oid_snmpEngineTime} : ($results->{$oid_sysUpTime} / 100);
my $start_time = $ctime - $uptime;
my $ccmHistoryRunningLastChanged = $start_time + ($results->{$oid_ccmHistoryRunningLastChanged} / 100);
my $ccmHistoryRunningLastSaved = $start_time + ($results->{$oid_ccmHistoryRunningLastSaved} / 100);
my $ccmHistoryStartupLastChanged = $start_time + ($results->{$oid_ccmHistoryStartupLastChanged} / 100);
$self->{output}->output_add(long_msg => sprintf(
"ccmHistoryRunningLastChanged: %s (%s)",
$ccmHistoryRunningLastChanged,
scalar(localtime($ccmHistoryRunningLastChanged)))
);
$self->{output}->output_add(long_msg => sprintf(
"ccmHistoryRunningLastSaved: %s (%s)",
$ccmHistoryRunningLastSaved,
scalar(localtime($ccmHistoryRunningLastSaved)))
);
$self->{output}->output_add(long_msg => sprintf(
"ccmHistoryStartupLastChanged: %s (%s)",
$ccmHistoryStartupLastChanged,
scalar(localtime($ccmHistoryStartupLastChanged)))
);
my $runningUnchangedDuration = $ctime - $ccmHistoryRunningLastChanged;
my $startupUnchangedDuration = $ctime - $ccmHistoryStartupLastChanged;
my $runningAhead = 0;
my $output = 'saved config is up to date';
if ($runningUnchangedDuration < $startupUnchangedDuration) {
if (($runningUnchangedDuration + $runningChangedMarginAfterReload) > $uptime) {
$output = sprintf("running config has not changed since reload (using a %d second margin)", $runningChangedMarginAfterReload);
} else {
$output = sprintf(
"running config is ahead of startup config since %s. changes will be lost in case of a reboot",
centreon::plugins::misc::change_seconds(value => $runningUnchangedDuration)
);
$runningAhead = $runningUnchangedDuration;
}
}
$self->{results} = $self->{snmp}->get_leef(oids => [ $oid_ccmHistoryRunningLastChanged, $oid_ccmHistoryRunningLastSaved,
$oid_ccmHistoryStartupLastChanged ], nothing_quit => 1);
$self->{global} = {
running_last_changed => $self->{results}->{$oid_ccmHistoryRunningLastChanged} / 100,
running_last_saved => $self->{results}->{$oid_ccmHistoryRunningLastSaved} / 100,
startup_last_changed => $self->{results}->{$oid_ccmHistoryStartupLastChanged} / 100,
output_message => $output,
running_ahead => $runningAhead
}
}
@ -129,15 +142,13 @@ Check Cisco changed and saved configurations (CISCO-CONFIG-MAN-MIB).
=over 8
=item B<--warning-status>
=item B<--warning-config-running-ahead>
Define the conditions to match for the status to be WARNING (default: '').
You can use the following variables: %{running_last_changed}, %{running_last_saved}, %{startup_last_changed}
Thresholds.
=item B<--critical-status>
=item B<--critical-config-running-ahead>
Define the conditions to match for the status to be CRITICAL (default: '%{running_last_changed} > %{running_last_saved}').
You can use the following variables: %{running_last_changed}, %{running_last_saved}, %{startup_last_changed}
Thresholds.
=back

View File

@ -0,0 +1,203 @@
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::common::fortinet::fortigate::snmp::mode::listswitches;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
my $mapping_status = { 0 => 'down', 1 => 'up' };
my $map_admin_connection_state = { 0 => 'discovered', 1 => 'disable', 2 => 'authorized' };
my $mapping = {
serial => { oid => '.1.3.6.1.4.1.12356.101.24.1.1.1.3' },# fgSwDeviceSerialNum
name => { oid => '.1.3.6.1.4.1.12356.101.24.1.1.1.4' },# fgSwDeviceName
version => { oid => '.1.3.6.1.4.1.12356.101.24.1.1.1.5' },# fgSwDeviceVersion
admin => { oid => '.1.3.6.1.4.1.12356.101.24.1.1.1.6', map => $map_admin_connection_state },# fgSwDeviceAuthorized
state => { oid => '.1.3.6.1.4.1.12356.101.24.1.1.1.7', map => $mapping_status },# fgSwDeviceStatus
ip => { oid => '.1.3.6.1.4.1.12356.101.24.1.1.1.9' },# fgSwDeviceIp
};
my $fgSwDeviceEntry = '.1.3.6.1.4.1.12356.101.24.1.1.1';
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$options{options}->add_options(
arguments => {
'filter-name:s' => { name => 'filter_name' },
'filter-status:s' => { name => 'filter_status' },
'filter-admin:s' => { name => 'filter_admin' },
'filter-ip:s' => { name => 'filter_ip' }
}
);
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
}
sub manage_selection {
my ($self, %options) = @_;
my $snmp_result = $options{snmp}->get_table(
oid => $fgSwDeviceEntry,
start => $mapping->{serial},
end => $mapping->{ip},
nothing_quit => 1
);
foreach my $oid ($options{snmp}->oid_lex_sort(sort keys %{$snmp_result})) {
next if ($oid !~ /^$mapping->{serial}->{oid}\.(.*)$/);
my $instance = $1;
my $result = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $instance);
if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
$result->{name} !~ /$self->{option_results}->{filter_name}/) {
$self->{output}->output_add(
long_msg => "skipping '" . $result->{name} . "': no matching filter.",
debug => 1
);
next;
}
if (defined($self->{option_results}->{filter_status}) && $self->{option_results}->{filter_status} ne '' &&
$result->{state} !~ /$self->{option_results}->{filter_status}/) {
$self->{output}->output_add(
long_msg => "skipping '" . $result->{state} . "': no matching filter.",
debug => 1
);
next;
}
if (defined($self->{option_results}->{filter_admin}) && $self->{option_results}->{filter_admin} ne '' &&
$result->{admin} !~ /$self->{option_results}->{filter_admin}/) {
$self->{output}->output_add(
long_msg => "skipping '" . $result->{admin} . "': no matching filter.",
debug => 1
);
next;
}
if (defined($self->{option_results}->{filter_ip}) && $self->{option_results}->{filter_ip} ne '' &&
$result->{ip} !~ /$self->{option_results}->{filter_ip}/) {
$self->{output}->output_add(
long_msg => "skipping '" . $result->{ip} . "': no matching filter.",
debug => 1
);
next;
}
push @{$self->{switch}}, $result;
}
}
sub run {
my ($self, %options) = @_;
$self->manage_selection(%options);
if (scalar(keys @{$self->{switch}}) <= 0) {
$self->{output}->add_option_msg(short_msg => "No switch found matching.");
$self->{output}->option_exit();
}
foreach (sort @{$self->{switch}}) {
$self->{output}->output_add(
long_msg =>
sprintf(
"[Name = %s] [Serial = %s] [IP = %s] [Version = %s] [State = %s] [Admin = %s]",
$_->{name},
$_->{serial},
$_->{ip},
$_->{version},
$_->{state},
$_->{admin},
)
);
}
$self->{output}->output_add(
severity => 'OK',
short_msg => 'List switches:'
);
$self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
$self->{output}->exit();
}
sub disco_format {
my ($self, %options) = @_;
$self->{output}->add_disco_format(elements => [ 'name', 'serial', 'ip', 'version', 'state', 'admin' ]);
}
sub disco_show {
my ($self, %options) = @_;
$self->manage_selection(%options);
foreach (@{$self->{switch}}) {
$self->{output}->add_disco_entry(
name => $_->{name},
serial => $_->{serial},
ip => $_->{ip},
version => $_->{version},
state => $_->{state},
admin => $_->{admin}
);
}
}
1;
__END__
=head1 MODE
List switches managed through Fortigate Switch Controller.
=over 8
=item B<--filter-name>
Filter switch by name (can be a regexp).
=item B<--filter-status>
Filter switch by status
=item B<--filter-admin>
Filter switch by admin connection state
=item B<--filter-ip>
Filter switch by IP (can be a regexp).
=back
=cut

View File

@ -0,0 +1,258 @@
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::common::fortinet::fortigate::snmp::mode::switchusage;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold);
use Digest::MD5 qw(md5_hex);
sub custom_status_output {
my ($self, %options) = @_;
return 'status: ' . $self->{result_values}->{status} . ' [admin: ' . $self->{result_values}->{admin} . ']';
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{
name => 'switch',
type => 1,
cb_prefix_output => 'prefix_ap_output',
message_multiple => 'All switches are ok',
skipped_code => { -10 => 1 }
}
];
$self->{maps_counters}->{switch} = [
{ label => 'status', threshold => 0, set => {
key_values => [ { name => 'status' }, { name => 'admin' }, { name => 'display' } ],
closure_custom_output => $self->can('custom_status_output'),
closure_custom_perfdata => sub {return 0;},
closure_custom_threshold_check => \&catalog_status_threshold
}
},
{ label => 'cpu', nlabel => 'switch.cpu.utilization.percentage', set => {
key_values => [ { name => 'cpu' }, { name => 'display' } ],
output_template => 'cpu usage: %.2f %%',
perfdatas => [
{ label => 'cpu', template => '%.2f',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' }
]
}
},
{ label => 'memory', nlabel => 'switch.memory.usage.bytes', set => {
key_values => [ { name => 'memory' }, { name => 'display' } ],
output_template => 'memory usage: %.2f %%',
perfdatas => [
{ label => 'memory', template => '%.2f',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' }
]
}
}
];
}
sub prefix_ap_output {
my ($self, %options) = @_;
return "Switch '" . $options{instance_value}->{display} . "' ";
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1);
bless $self, $class;
$options{options}->add_options(
arguments => {
'filter-name:s' => { name => 'filter_name' },
'filter-ip:s' => { name => 'filter_ip' },
'unknown-status:s' => { name => 'unknown_status', default => '' },
'warning-status:s' => { name => 'warning_status', default => '' },
'critical-status:s' => {
name => 'critical_status',
default => '%{admin} eq "authorized" and %{status} eq "down"'
}
}
);
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
$self->change_macros(macros => [ 'unknown_status', 'warning_status', 'critical_status' ]);
}
my %map_admin_connection_state = (
0 => 'discovered', 1 => 'disable', 2 => 'authorized',
);
my %map_switch_status = (
0 => 'down', 1 => 'up'
);
my $mapping = {
fgSwDeviceAuthorized => { oid => '.1.3.6.1.4.1.12356.101.24.1.1.1.6', map => \%map_admin_connection_state },
fgSwDeviceName => { oid => '.1.3.6.1.4.1.12356.101.24.1.1.1.4' },
fgSwDeviceIp => { oid => '.1.3.6.1.4.1.12356.101.24.1.1.1.9' }
};
my $mapping2 = {
fgSwDeviceStatus => { oid => '.1.3.6.1.4.1.12356.101.24.1.1.1.7', map => \%map_switch_status },
fgSwCpu => { oid => '.1.3.6.1.4.1.12356.101.24.1.1.1.11' },
fgSwMemory => { oid => '.1.3.6.1.4.1.12356.101.24.1.1.1.12' }
};
my $oid_fgSwDeviceTable = '.1.3.6.1.4.1.12356.101.24.1.1.1';
sub manage_selection {
my ($self, %options) = @_;
my $snmp_result = $options{snmp}->get_table(
oid => $oid_fgSwDeviceTable,
start => $mapping->{fgSwDeviceName}->{oid},
end => $mapping->{fgSwDeviceIp}->{oid},
nothing_quit => 1
);
$self->{switch} = {};
foreach my $oid (sort keys %{$snmp_result}) {
next if ($oid !~ /^$mapping->{fgSwDeviceName}->{oid}\.(.*)$/);
my $instance = $1;
my $result = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $instance);
if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
$result->{fgSwDeviceName} !~ /$self->{option_results}->{filter_name}/) {
$self->{output}->output_add(
long_msg => "skipping switch '" . $result->{fgSwDeviceName} . "'.",
debug => 1
);
next;
}
if (defined($self->{option_results}->{filter_ip}) && $self->{option_results}->{filter_ip} ne '' &&
$result->{fgSwDeviceIp} !~ /$self->{option_results}->{filter_ip}/) {
$self->{output}->output_add(
long_msg => "skipping switch '" . $result->{fgSwDeviceIp} . "'.",
debug => 1
);
next;
}
$self->{switch}->{$instance} = {
display => $result->{fgSwDeviceName},
admin => $result->{fgSwDeviceAuthorized},
status => 'n/a',
};
}
if (scalar(keys %{$self->{switch}}) <= 0) {
$self->{output}->add_option_msg(short_msg => "No switch found.");
$self->{output}->option_exit();
}
$options{snmp}->load(
oids => [
$mapping2->{fgSwDeviceStatus}->{oid},
$mapping2->{fgSwCpu}->{oid},
$mapping2->{fgSwMemory}->{oid},
],
instances => [ keys %{$self->{switch}} ],
instance_regexp => '^(.*)$'
);
$snmp_result = $options{snmp}->get_leef(nothing_quit => 1);
foreach (keys %{$self->{switch}}) {
my $result = $options{snmp}->map_instance(mapping => $mapping2, results => $snmp_result, instance => $_);
$self->{switch}->{$_}->{status} = $result->{fgSwDeviceStatus};
$self->{switch}->{$_}->{cpu} = $result->{fgSwCpu};
$self->{switch}->{$_}->{memory} = $result->{fgSwMemory};
}
$self->{cache_name} = 'fortigate_' . $self->{mode} . '_' . $options{snmp}->get_hostname() . '_' . $options{snmp}->get_port() . '_' .
(defined($self->{option_results}->{filter_counters}) ?
md5_hex($self->{option_results}->{filter_counters}) :
md5_hex('all')) . '_' .
(defined($self->{option_results}->{filter_name}) ?
md5_hex($self->{option_results}->{filter_name}) :
md5_hex('all'));
}
1;
__END__
=head1 MODE
Check switch usage through Fortigate Switch Controller.
=over 8
=item B<--warning-cpu>
Warning threshold (%).
=item B<--critical-cpu>
Critical threshold (%).
=item B<--warning-memory>
Warning threshold (%).
=item B<--critical-memory>
Critical threshold (%).
=item B<--filter-name>
Filter by switch name (can be a regexp).
=item B<--filter-ip>
Filter by switch IP (can be a regexp).
=item B<--unknown-status>
Define the conditions to match for the status to be UNKNOWN (default: '').
You can use the following variables: %{admin}, %{status}, %{display}
=item B<--warning-status>
Define the conditions to match for the status to be WARNING (default: '').
You can use the following variables: %{admin}, %{status}, %{display}
=item B<--critical-status>
Define the conditions to match for the status to be CRITICAL (default: '%{admin} eq "authorized" and %{status} eq "down"').
You can use the following variables: %{admin}, %{status}, %{display}
=back
=cut

View File

@ -353,7 +353,7 @@ sub check_relaunch {
}
if ($uid != $>) {
if ($> == 0) {
unshift @$args, '-s', '/bin/bash', '-l', $self->{runas}, '-c', join(' ', $cmd, $rebuild_args);
unshift @$args, '-s', '/bin/bash', '-l', $self->{runas}, '-c', join(' ', $cmd, @$rebuild_args);
$cmd = 'su';
} else {
unshift @$args, '-S', '-u', $self->{runas}, $cmd, @$rebuild_args;

View File

@ -365,10 +365,27 @@ sub run_instances {
my $message_separator = defined($options{config}->{message_separator}) ?
$options{config}->{message_separator}: ', ';
# The default sort method is cmp (string comparison)
my $sort_method = 'cmp';
# If configured otherwise, we take it from the counter (only other method is 'num' for '<=>')
$sort_method = $options{config}->{sort_method}
if (defined($options{config}->{sort_method}));
foreach my $id (sort { $sort_subs->{$sort_method}->() } keys %{$self->{$options{config}->{name}}}) {
# In the absence of sort_attribute the sort method is set now
my $sort_sub = $sort_subs->{$sort_method};
# If sort_attribute is set, then we'll redefine how things are sorted depending on the specified sort_method
if (defined($options{config}->{sort_attribute})) {
my $sort_attribute = $options{config}->{sort_attribute};
if ($sort_method eq 'cmp') {
$sort_sub = sub { $self->{$options{config}->{name}}->{$a}->{$sort_attribute} cmp $self->{$options{config}->{name}}->{$b}->{$sort_attribute}};
} else {
$sort_sub = sub { $self->{$options{config}->{name}}->{$a}->{$sort_attribute} <=> $self->{$options{config}->{name}}->{$b}->{$sort_attribute}};
}
}
# Now the loop begins with the desired sorting method
foreach my $id (sort { $sort_sub->() } keys %{$self->{$options{config}->{name}}}) {
my ($short_msg, $short_msg_append, $long_msg, $long_msg_append) = ('', '', '', '');
my @exits = ();
foreach (@{$self->{maps_counters}->{$options{config}->{name}}}) {
@ -547,10 +564,28 @@ sub run_multiple_instances {
my $message_separator = defined($options{config}->{message_separator}) ?
$options{config}->{message_separator} : ', ';
# The default sort method is cmp (string comparison)
my $sort_method = 'cmp';
# If configured otherwise, we take it from the counter (only other method is 'num' for '<=>')
$sort_method = $options{config}->{sort_method}
if (defined($options{config}->{sort_method}));
foreach my $id (sort { $sort_subs->{$sort_method}->() } keys %{$self->{$options{config}->{name}}}) {
# In the absence of sort_attribute the sort method is set now
my $sort_sub = $sort_subs->{$sort_method};
# If sort_attribute is set, then we'll redefine how things are sorted depending on the specified sort_method
if (defined($options{config}->{sort_attribute})) {
my $sort_attribute = $options{config}->{sort_attribute};
if ($sort_method eq 'cmp') {
$sort_sub = sub { $self->{$options{config}->{name}}->{$a}->{$sort_attribute} cmp $self->{$options{config}->{name}}->{$b}->{$sort_attribute}};
} else {
$sort_sub = sub { $self->{$options{config}->{name}}->{$a}->{$sort_attribute} <=> $self->{$options{config}->{name}}->{$b}->{$sort_attribute}};
}
}
# Now the loop begins with the desired sorting method
foreach my $id (sort { $sort_sub->() } keys %{$self->{$options{config}->{name}}}) {
my ($short_msg, $short_msg_append, $long_msg, $long_msg_append) = ('', '', '', '');
my @exits = ();
foreach (@{$self->{maps_counters}->{$options{config}->{name}}}) {

View File

@ -31,7 +31,8 @@ sub new {
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
# Adding options specific to this mode
$options{options}->add_options(arguments => {
"warning:s" => { name => 'warning' },
"critical:s" => { name => 'critical' },
"seconds" => { name => 'seconds' }
@ -44,10 +45,13 @@ sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
# Validating warning threshold
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
$self->{output}->option_exit();
}
# Validating critical threshold
if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
$self->{output}->option_exit();
@ -57,31 +61,45 @@ sub check_options {
sub run {
my ($self, %options) = @_;
$options{sql}->connect();
$options{sql}->connect();
# Checking if MySQL version is supported
if (!($options{sql}->is_version_minimum(version => '5'))) {
$self->{output}->add_option_msg(short_msg => "MySQL version '" . $self->{sql}->{version} . "' is not supported (need version >= '5.x').");
$self->{output}->option_exit();
}
# Querying MySQL for Uptime status
$options{sql}->query(query => q{SHOW /*!50000 global */ STATUS LIKE 'Uptime'});
my ($name, $value) = $options{sql}->fetchrow_array();
# Handling case where uptime value is not available
if (!defined($value)) {
$self->{output}->add_option_msg(short_msg => "Cannot get uptime.");
$self->{output}->option_exit();
}
# Checking the threshold and determining exit code
my $exit_code = $self->{perfdata}->threshold_check(value => $value, threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);
my $msg = sprintf("database is up since %d days", floor($value / 86400));
# Calculating uptime in days or seconds based on user preference
my $uptime_days = floor($value / 86400);
my $msg = sprintf("database is up since %d days", $uptime_days);
if (defined($self->{option_results}->{seconds})) {
$msg = sprintf("database is up since %d seconds", $value);
}
# Adding start time information to the message
$msg .= sprintf(" (Start time = %s)", strftime("%Y/%m/%d %H:%M:%S", localtime(time - $value)));
# Adding output message and performance data
$self->{output}->output_add(
severity => $exit_code,
short_msg => $msg
);
$self->{output}->perfdata_add(
label => 'uptime',
label => 'uptime',
nlabel => 'database.uptime.seconds',
unit => 's',
value => $value,
@ -90,7 +108,9 @@ sub run {
min => 0
);
# Displaying the output and exiting
$self->{output}->display();
$self->{output}->exit();
}

View File

@ -1,5 +1,5 @@
#
# Copyright 2024 Centreon (http://www.centreon.com/)
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
@ -48,6 +48,15 @@ sub custom_status_calc {
return 0;
}
sub custom_last_replace_output {
my ($self, %options) = @_;
return sprintf(
'replace last time: %s',
centreon::plugins::misc::change_seconds(value => $self->{result_values}->{last_replace_time})
);
}
sub custom_status_output {
my ($self, %options) = @_;
@ -80,108 +89,127 @@ sub set_counters {
$self->{maps_counters_type} = [
{ name => 'global', type => 0, skipped_code => { -10 => 1 } },
{ name => 'bpacks', type => 3, cb_prefix_output => 'prefix_bpack_output', cb_long_output => 'bpack_long_output', indent_long_output => ' ', message_multiple => 'All battery packs are ok',
group => [
{ name => 'bpack_global', type => 0, skipped_code => { -10 => 1 } },
{ name => 'cartridges', display_long => 1, cb_prefix_output => 'prefix_cartridge_output', message_multiple => 'cartridges are ok', type => 1, skipped_code => { -10 => 1 } }
]
{ name => 'bpacks', type => 3, cb_prefix_output => 'prefix_bpack_output', cb_long_output => 'bpack_long_output', indent_long_output => ' ', message_multiple => 'All battery packs are ok',
group => [
{ name => 'bpack_global', type => 0, skipped_code => { -10 => 1 } },
{ name => 'cartridges', display_long => 1, cb_prefix_output => 'prefix_cartridge_output', message_multiple => 'cartridges are ok', type => 1, skipped_code => { -10 => 1 } }
]
}
];
$self->{maps_counters}->{global} = [
{
label => 'status',
type => 2,
unknown_default => '%{status} =~ /unknown/i',
warning_default => '%{status} =~ /batteryLow/i',
critical_default => '%{replace} =~ /yes/i',
set => {
key_values => [
{
label => 'status',
type => 2,
unknown_default => '%{status} =~ /unknown/i',
warning_default => '%{status} =~ /batteryLow/i',
critical_default => '%{replace} =~ /yes/i',
set => {
key_values => [
{ name => 'upsBasicBatteryStatus' },
{ name => 'upsAdvBatteryReplaceIndicator' },
{ name => 'upsBasicBatteryLastReplaceDate' }
],
closure_custom_calc => $self->can('custom_status_calc'),
closure_custom_output => $self->can('custom_battery_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_calc => $self->can('custom_status_calc'),
closure_custom_output => $self->can('custom_battery_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
{ label => 'load', nlabel => 'battery.charge.remaining.percent', set => {
key_values => [ { name => 'upsAdvBatteryCapacity' } ],
output_template => 'remaining capacity: %s %%',
perfdatas => [
{ label => 'load', template => '%s', min => 0, max => 100, unit => '%' }
]
}
key_values => [ { name => 'upsAdvBatteryCapacity' } ],
output_template => 'remaining capacity: %s %%',
perfdatas => [
{ label => 'load',
template => '%s',
min => 0,
max => 100,
unit => '%' }
]
}
},
{ label => 'time', nlabel => 'battery.charge.remaining.minutes', set => {
key_values => [ { name => 'upsAdvBatteryRunTimeRemaining' } ],
output_template => 'remaining time: %.2f minutes',
perfdatas => [
{ label => 'load_time', template => '%.2f', min => 0, unit => 'm' }
]
}
key_values => [ { name => 'upsAdvBatteryRunTimeRemaining' } ],
output_template => 'remaining time: %.2f minutes',
perfdatas => [
{ label => 'load_time',
template => '%.2f',
min => 0,
unit => 'm' }
]
}
},
{ label => 'timeon', nlabel => 'battery.timeon.minutes', set => {
key_values => [ { name => 'upsBasicBatteryTimeOnBattery' } ],
output_template => 'time on battery: %.2f minutes',
perfdatas => [
{ label => 'timeon', template => '%.2f', min => 0, unit => 'm' }
]
}
key_values => [ { name => 'upsBasicBatteryTimeOnBattery' } ],
output_template => 'time on battery: %.2f minutes',
perfdatas => [
{ label => 'timeon',
template => '%.2f',
min => 0,
unit => 'm' }
]
}
},
{ label => 'current', nlabel => 'battery.current.ampere', set => {
key_values => [ { name => 'upsAdvBatteryCurrent' } ],
output_template => 'current: %s A',
perfdatas => [
{ label => 'current', template => '%s', min => 0, unit => 'A' }
]
}
key_values => [ { name => 'upsAdvBatteryCurrent' } ],
output_template => 'current: %s A',
perfdatas => [
{ label => 'current',
template => '%s',
min => 0,
unit => 'A' }
]
}
},
{ label => 'voltage', nlabel => 'battery.voltage.volt', set => {
key_values => [ { name => 'upsAdvBatteryActualVoltage' } ],
output_template => 'voltage: %s V',
perfdatas => [
{ label => 'voltage', template => '%s', unit => 'V' }
]
}
key_values => [ { name => 'upsAdvBatteryActualVoltage' } ],
output_template => 'voltage: %s V',
perfdatas => [
{ label => 'voltage',
template => '%s',
unit => 'V' }
]
}
},
{ label => 'temperature', nlabel => 'battery.temperature.celsius', set => {
key_values => [ { name => 'upsAdvBatteryTemperature' } ],
output_template => 'temperature: %s C',
perfdatas => [
{ label => 'temperature', template => '%s', unit => 'C'}
]
}
key_values => [ { name => 'upsAdvBatteryTemperature' } ],
output_template => 'temperature: %s C',
perfdatas => [
{ label => 'temperature',
template => '%s',
unit => 'C' }
]
}
},
{ label => 'replace-lasttime', nlabel => 'battery.replace.lasttime.seconds', display_ok => 0, set => {
key_values => [ { name => 'last_replace_time' } ],
output_template => 'replace last time: %s s',
perfdatas => [
{ label => 'replace_last_time', template => '%s', unit => 's'}
]
}
key_values => [ { name => 'last_replace_time' } ],
closure_custom_output => $self->can('custom_last_replace_output'),
perfdatas => [
{ label => 'replace_last_time',
template => '%s',
unit => 's' }
]
}
}
];
$self->{maps_counters}->{bpack_global} = [
{ label => 'battery-pack-status', type => 2, critical_default => '%{status} ne "OK"', set => {
key_values => [ { name => 'status' }, { name => 'display' } ],
closure_custom_output => $self->can('custom_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
key_values => [ { name => 'status' }, { name => 'display' } ],
closure_custom_output => $self->can('custom_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
}
];
$self->{maps_counters}->{cartridges} = [
{ label => 'cartridge-status', type => 2, critical_default => '%{status} ne "OK"', set => {
key_values => [ { name => 'status' }, { name => 'display' } ],
closure_custom_output => $self->can('custom_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
key_values => [ { name => 'status' }, { name => 'display' } ],
closure_custom_output => $self->can('custom_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
}
];
}
@ -221,17 +249,17 @@ my $map_replace_status = {
};
my $mapping = {
upsBasicBatteryStatus => { oid => '.1.3.6.1.4.1.318.1.1.1.2.1.1', map => $map_battery_status },
upsBasicBatteryTimeOnBattery => { oid => '.1.3.6.1.4.1.318.1.1.1.2.1.2' },
upsBasicBatteryLastReplaceDate => { oid => '.1.3.6.1.4.1.318.1.1.1.2.1.3' }
upsBasicBatteryStatus => { oid => '.1.3.6.1.4.1.318.1.1.1.2.1.1', map => $map_battery_status },
upsBasicBatteryTimeOnBattery => { oid => '.1.3.6.1.4.1.318.1.1.1.2.1.2' },
upsBasicBatteryLastReplaceDate => { oid => '.1.3.6.1.4.1.318.1.1.1.2.1.3' }
};
my $mapping2 = {
upsAdvBatteryCapacity => { oid => '.1.3.6.1.4.1.318.1.1.1.2.2.1' },
upsAdvBatteryTemperature => { oid => '.1.3.6.1.4.1.318.1.1.1.2.2.2' },
upsAdvBatteryRunTimeRemaining => { oid => '.1.3.6.1.4.1.318.1.1.1.2.2.3' },
upsAdvBatteryReplaceIndicator => { oid => '.1.3.6.1.4.1.318.1.1.1.2.2.4', map => $map_replace_status },
upsAdvBatteryActualVoltage => { oid => '.1.3.6.1.4.1.318.1.1.1.2.2.8' },
upsAdvBatteryCurrent => { oid => '.1.3.6.1.4.1.318.1.1.1.2.2.9' }
upsAdvBatteryCapacity => { oid => '.1.3.6.1.4.1.318.1.1.1.2.2.1' },
upsAdvBatteryTemperature => { oid => '.1.3.6.1.4.1.318.1.1.1.2.2.2' },
upsAdvBatteryRunTimeRemaining => { oid => '.1.3.6.1.4.1.318.1.1.1.2.2.3' },
upsAdvBatteryReplaceIndicator => { oid => '.1.3.6.1.4.1.318.1.1.1.2.2.4', map => $map_replace_status },
upsAdvBatteryActualVoltage => { oid => '.1.3.6.1.4.1.318.1.1.1.2.2.8' },
upsAdvBatteryCurrent => { oid => '.1.3.6.1.4.1.318.1.1.1.2.2.9' }
};
my $oid_upsBasicBattery = '.1.3.6.1.4.1.318.1.1.1.2.1';
my $oid_upsAdvBattery = '.1.3.6.1.4.1.318.1.1.1.2.2';
@ -239,11 +267,11 @@ my $oid_upsHighPrecBatteryPackOnlyStatus = '.1.3.6.1.4.1.318.1.1.1.2.3.10.4.1.5'
my $oid_upsHighPrecBatteryPackCartridgeStatus = '.1.3.6.1.4.1.318.1.1.1.2.3.10.2.1.10';
my $map_battery_pack_status = {
0 => 'disconnected', 1 => 'overvoltage',
2 => 'needsReplacement', 3 => 'overtemperatureCritical',
4 => 'charger', 5 => 'temperatureSensor',
6 => 'busSoftStart', 7 => 'overtemperatureWarning',
8 => 'generalError', 9 => 'communication',
0 => 'disconnected', 1 => 'overvoltage',
2 => 'needsReplacement', 3 => 'overtemperatureCritical',
4 => 'charger', 5 => 'temperatureSensor',
6 => 'busSoftStart', 7 => 'overtemperatureWarning',
8 => 'generalError', 9 => 'communication',
10 => 'disconnectedFrame', 11 => 'firmwareMismatch'
};
@ -257,12 +285,12 @@ sub add_battery_pack {
$oid =~ /^$oid_upsHighPrecBatteryPackOnlyStatus\.(\d+)/;
my $pack_index = $1;
$self->{bpacks}->{$pack_index} = {
display => $pack_index,
display => $pack_index,
bpack_global => {
display => $pack_index,
status => 'OK'
status => 'OK'
},
cartridges => {}
cartridges => {}
};
my $status = '';
@ -285,14 +313,14 @@ sub add_battery_pack {
my ($pack_index, $cartridge_index) = ($1, $2);
if (!defined($self->{bpacks}->{$pack_index})) {
$self->{bpacks}->{$pack_index} = {
display => $pack_index,
display => $pack_index,
cartridges => {}
};
}
$self->{bpacks}->{$pack_index}->{cartridges}->{$cartridge_index} = {
display => $cartridge_index,
status => 'OK'
status => 'OK'
};
my $status = '';
@ -313,7 +341,7 @@ sub manage_selection {
my ($self, %options) = @_;
my $snmp_result = $options{snmp}->get_multiple_table(
oids => [
oids => [
{ oid => $oid_upsBasicBattery },
{ oid => $oid_upsAdvBattery, end => $mapping2->{upsAdvBatteryCurrent}->{oid} },
{ oid => $oid_upsHighPrecBatteryPackCartridgeStatus },
@ -344,7 +372,7 @@ sub manage_selection {
}
$self->add_battery_pack(snmp_result => $snmp_result);
}
1;
@ -360,62 +388,112 @@ Check battery status and battery charge remaining.
=item B<--filter-counters>
Only display some counters (regexp can be used).
Example: --filter-counters='^status|load$'
Example: C<--filter-counters='^status|load$'>
=item B<--replace-lasttime-format>
Define the date format (default: '%m/%d/%Y').
Define the date format (default: C<%m/%d/%Y>).
=item B<--unknown-status>
Define the conditions to match for the status to be UNKNOWN (default: '%{status} =~ /unknown/i').
Define the conditions to match for the status to be UNKNOWN (default: C<%{status} =~ /unknown/i>).
You can use the following variables: %{status}, %{replace}
=item B<--warning-status>
Define the conditions to match for the status to be WARNING (default: '%{status} =~ /batteryLow/i').
You can use the following variables: %{status}, %{replace}
Define the conditions to match for the status to be WARNING (default: C<%{status} =~ /batteryLow/i>).
You can use the following variables: C<%{status}>, C<%{replace}>
=item B<--critical-status>
Define the conditions to match for the status to be CRITICAL (default: '%{replace} =~ /yes/i').
You can use the following variables: %{status}, %{replace}
Define the conditions to match for the status to be CRITICAL (default: C<%{replace} =~ /yes/i>).
You can use the following variables: C<%{status}>, C<%{replace}>
=item B<--unknown-battery-pack-status>
Define the conditions to match for the status to be UNKNOWN.
You can use the following variables: %{status}
You can use the following variables: C<%{status}>
=item B<--warning-battery-pack-status>
Define the conditions to match for the status to be WARNING.
You can use the following variables: %{status}
You can use the following variables: C<%{status}>
=item B<--critical-battery-pack-status>
Define the conditions to match for the status to be CRITICAL (default: '%{status} ne "OK"').
You can use the following variables: %{status}
Define the conditions to match for the status to be CRITICAL (default: C<%{status} ne "OK">).
You can use the following variables: C<%{status}>
=item B<--unknown-cartridge-status>
Define the conditions to match for the status to be UNKNOWN.
You can use the following variables: %{status}
You can use the following variables: C<%{status}>
=item B<--warning-cartridge-status>
Define the conditions to match for the status to be WARNING.
You can use the following variables: %{status}
You can use the following variables: C<%{status}>
=item B<--critical-cartridge-status>
Define the conditions to match for the status to be CRITICAL (default: '%{status} ne "OK"').
You can use the following variables: %{status}
Define the conditions to match for the status to be CRITICAL (default: C<%{status} ne "OK">).
You can use the following variables: C<%{status}>
=item B<--warning-*> B<--critical-*>
=item B<--warning-load>
Thresholds.
Can be: 'load', 'voltage', 'current',
'temperature', 'time', 'replace-lasttime', 'timeon'.
Warning threshold for battery load (in %).
=item B<--critical-load>
Critical threshold for battery load (in %).
=item B<--warning-voltage>
Warning threshold for battery voltage (in V).
=item B<--critical-voltage>
Critical threshold for battery voltage (in V).
=item B<--warning-current>
Warning threshold for battery current (in A).
=item B<--critical-current>
Critical threshold for battery current (in A).
=item B<--warning-temperature>
Warning threshold for battery temperature (in C).
=item B<--critical-temperature>
Critical threshold for battery temperature (in C).
=item B<--warning-time>
Warning threshold for battery remaining time (in minutes).
=item B<--critical-time>
Critical threshold for battery remaining time (in minutes).
=item B<--warning-replace-lasttime>
Warning threshold for battery last replace time (in seconds).
=item B<--critical-replace-lasttime>
Critical threshold for battery last replace time (in seconds).
=item B<--warning-timeon>
Warning threshold for time on battery (in minutes).
=item B<--critical-timeon>
Critical threshold for time on battery (in minutes).
=back

View File

@ -75,7 +75,7 @@ Check hardware.
=item B<--component>
Which component to check (default: '.*').
Can be: 'psu', 'temperature', 'fan', 'fantry'.
Can be: C<psu>, C<temperature>, C<fan>, C<fantray>.
=item B<--filter>

View File

@ -26,6 +26,24 @@ use strict;
use warnings;
use Digest::MD5 qw(md5_hex);
sub prefix_vs_output {
my ($self, %options) = @_;
return "Virtual server '" . $options{instance_value}->{display} . "' : ";
}
sub vs_long_output {
my ($self, %options) = @_;
return "checking virtual server '" . $options{instance_value}->{display} . "'";
}
sub prefix_ap_output {
my ($self, %options) = @_;
return "access profile '" . $options{instance_value}->{display} . "' ";
}
sub set_counters {
my ($self, %options) = @_;
@ -93,24 +111,6 @@ sub set_counters {
];
}
sub prefix_vs_output {
my ($self, %options) = @_;
return "Virtual server '" . $options{instance_value}->{display} . "' : ";
}
sub vs_long_output {
my ($self, %options) = @_;
return "checking virtual server '" . $options{instance_value}->{display} . "'";
}
sub prefix_ap_output {
my ($self, %options) = @_;
return "access profile '" . $options{instance_value}->{display} . "' ";
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1, statefile => 1);
@ -214,12 +214,54 @@ Filter virtual server name (can be a regexp).
Filter access profile name (can be a regexp).
=item B<--warning-*> B<--critical-*>
=item B<--warning-sessions-created>
Thresholds.
=item B<--critical-sessions-created>
Thresholds.
=item B<--warning-sessions-active>
Thresholds.
=item B<--critical-sessions-active>
Thresholds.
=item B<--warning-sessions-pending>
Thresholds.
=item B<--critical-sessions-pending>
Thresholds.
=item B<--warning-ap-sessions-created>
Thresholds.
=item B<--critical-ap-sessions-created>
Thresholds.
=item B<--warning-ap-sessions-active>
Thresholds.
=item B<--critical-ap-sessions-active>
Thresholds.
=item B<--warning-ap-sessions-pending>
Thresholds.
=item B<--critical-ap-sessions-pending>
Thresholds.
Can be: 'sessions-created', 'sessions-active', 'sessions-pending',
'ap-sessions-created', 'ap-sessions-active', 'ap-sessions-pending'.
=back
=cut
=cut

View File

@ -59,25 +59,37 @@ sub check {
$self->{components}->{fan}->{total}++;
$self->{output}->output_add(long_msg => sprintf("fan '%s' status is '%s' [instance: %s, speed: %s].",
$instance, $result->{sysChassisFanStatus}, $instance,
defined($result->{sysChassisFanSpeed}) ? $result->{sysChassisFanSpeed} : '-'));
$self->{output}->output_add(
long_msg => sprintf(
"fan '%s' status is '%s' [instance: %s, speed: %s].",
$instance, $result->{sysChassisFanStatus}, $instance,
defined($result->{sysChassisFanSpeed}) ? $result->{sysChassisFanSpeed} : '-'
)
);
my $exit = $self->get_severity(section => 'fan', value => $result->{sysChassisFanStatus});
if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(severity => $exit,
short_msg => sprintf("Fan '%s' status is '%s'",
$instance, $result->{sysChassisFanStatus}));
$self->{output}->output_add(
severity => $exit,
short_msg => sprintf(
"Fan '%s' status is '%s'",
$instance, $result->{sysChassisFanStatus}
)
);
}
if (defined($result->{sysChassisFanSpeed}) && $result->{sysChassisFanSpeed} =~ /[0-9]/) {
my ($exit2, $warn, $crit, $checked) = $self->get_severity_numeric(section => 'fan', instance => $instance, value => $result->{sysChassisFanSpeed});
if (!$self->{output}->is_status(value => $exit2, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(severity => $exit2,
short_msg => sprintf("fan speed '%s' is %s rpm", $instance, $result->{sysChassisFanSpeed}));
$self->{output}->output_add(
severity => $exit2,
short_msg => sprintf("fan speed '%s' is %s rpm", $instance, $result->{sysChassisFanSpeed})
);
}
$self->{output}->perfdata_add(
label => "fan", unit => 'rpm',
nlabel => 'hardware.fan.speed.rpm',
unit => 'rpm',
instances => $instance,
value => $result->{sysChassisFanSpeed},
warning => $warn,

View File

@ -57,14 +57,22 @@ sub check {
$self->{components}->{psu}->{total}++;
$self->{output}->output_add(long_msg => sprintf("power supply '%s' status is '%s' [instance: %s].",
$instance, $result->{sysChassisPowerSupplyStatus}, $instance
));
$self->{output}->output_add(
long_msg => sprintf(
"power supply '%s' status is '%s' [instance: %s].",
$instance, $result->{sysChassisPowerSupplyStatus}, $instance
)
);
my $exit = $self->get_severity(section => 'psu', value => $result->{sysChassisPowerSupplyStatus});
if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(severity => $exit,
short_msg => sprintf("Power supply '%s' status is '%s'",
$instance, $result->{sysChassisPowerSupplyStatus}));
$self->{output}->output_add(
severity => $exit,
short_msg => sprintf(
"Power supply '%s' status is '%s'",
$instance, $result->{sysChassisPowerSupplyStatus}
)
);
}
}
}

View File

@ -48,19 +48,25 @@ sub check {
next if ($self->check_filter(section => 'temperature', instance => $instance));
$self->{components}->{temperature}->{total}++;
$self->{output}->output_add(long_msg => sprintf("temperature '%s' is %.2f C [instance: %s].",
$instance, $result->{sysChassisTempTemperature}, $instance
));
$self->{output}->output_add(
long_msg => sprintf(
"temperature '%s' is %.2f C [instance: %s].",
$instance, $result->{sysChassisTempTemperature}, $instance
)
);
if (defined($result->{sysChassisTempTemperature}) && $result->{sysChassisTempTemperature} =~ /[0-9]/) {
my ($exit, $warn, $crit, $checked) = $self->get_severity_numeric(section => 'temperature', instance => $instance, value => $result->{sysChassisTempTemperature});
if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(severity => $exit,
short_msg => sprintf("Temperature '%s' is %.2f C", $instance, $result->{sysChassisTempTemperature}));
$self->{output}->output_add(
severity => $exit,
short_msg => sprintf("Temperature '%s' is %.2f C", $instance, $result->{sysChassisTempTemperature})
);
}
$self->{output}->perfdata_add(
label => "temp", unit => 'C',
nlabel => 'hardware.temperature.celsius',
unit => 'C',
instances => $instance,
value => sprintf("%.2f", $result->{sysChassisTempTemperature}),
warning => $warn,

View File

@ -42,67 +42,69 @@ sub set_counters {
$self->{maps_counters_type} = [
{ name => 'global', type => 0 },
];
$self->{maps_counters}->{global} = [
{ label => 'client', set => {
{ label => 'client', nlabel => 'connections.client.current.count', set => {
key_values => [ { name => 'client' } ],
output_template => 'Current client connections : %s',
perfdatas => [
{ label => 'Client', template => '%s', min => 0, unit => 'con' },
],
{ template => '%s', min => 0 }
]
}
},
{ label => 'client-ssl', set => {
{ label => 'client-ssl', nlabel => 'connections.client.ssl.current.count', set => {
key_values => [ { name => 'client_ssl' } ],
output_template => 'Current client SSL connections : %s',
perfdatas => [
{ label => 'ClientSSL', template => '%s', min => 0, unit => 'con' },
],
{ template => '%s', min => 0 }
]
}
},
{ label => 'client-ssl-tps', set => {
{ label => 'client-ssl-tps', nlabel => 'connections.client.ssl.persecond', set => {
key_values => [ { name => 'client_ssl_tot_native', diff => 1 }, { name => 'client_ssl_tot_compat', diff => 1 } ],
output_template => 'TPS client SSL connections : %.2f', threshold_use => 'client_ssl_tps', output_use => 'client_ssl_tps',
output_template => 'TPS client SSL connections : %.2f',
threshold_use => 'client_ssl_tps',
output_use => 'client_ssl_tps',
closure_custom_calc => $self->can('custom_client_tps_calc'),
perfdatas => [
{ label => 'ClientSSL_Tps', value => 'client_ssl_tps', template => '%.2f',
unit => 'tps', min => 0 },
],
{ value => 'client_ssl_tps', template => '%.2f', min => 0 }
]
}
},
{ label => 'server', set => {
{ label => 'server', nlabel => 'connections.server.current.count', set => {
key_values => [ { name => 'server' } ],
output_template => 'Current server connections: %s',
perfdatas => [
{ label => 'Server', template => '%s', min => 0, unit => 'con' },
],
{ template => '%s', min => 0 }
]
}
},
{ label => 'server-ssl', set => {
{ label => 'server-ssl', nlabel => 'connections.server.ssl.current.count', set => {
key_values => [ { name => 'server_ssl' } ],
output_template => 'Current server SSL connections : %s',
perfdatas => [
{ label => 'ServerSSL', template => '%s', min => 0, unit => 'con' },
],
{ template => '%s', min => 0 }
]
}
},
}
];
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1);
my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
});
return $self;
}
sub manage_selection {
my ($self, %options) = @_;
$self->{cache_name} = "f5_bipgip_" . $options{snmp}->get_hostname() . '_' . $options{snmp}->get_port() . '_' . $self->{mode} . '_' .
$self->{cache_name} = 'f5_bipgip_' . $options{snmp}->get_hostname() . '_' . $options{snmp}->get_port() . '_' . $self->{mode} . '_' .
(defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all'));
my $oid_sysStatClientCurConns = '.1.3.6.1.4.1.3375.2.1.1.2.1.8.0';
@ -111,12 +113,12 @@ sub manage_selection {
my $oid_sysServersslStatCurConns = '.1.3.6.1.4.1.3375.2.1.1.2.10.2.0';
my $oid_sysClientsslStatTotNativeConns = '.1.3.6.1.4.1.3375.2.1.1.2.9.6.0';
my $oid_sysClientsslStatTotCompatConns = '.1.3.6.1.4.1.3375.2.1.1.2.9.9.0';
if ($options{snmp}->is_snmpv1()) {
$self->{output}->add_option_msg(short_msg => "Need to use SNMP v2c or v3.");
$self->{output}->option_exit();
}
my $result = $options{snmp}->get_leef(
oids => [
$oid_sysStatClientCurConns, $oid_sysStatServerCurConns,
@ -125,13 +127,14 @@ sub manage_selection {
],
nothing_quit => 1
);
$self->{global} = {
client => $result->{$oid_sysStatClientCurConns},
client_ssl => $result->{$oid_sysClientsslStatCurConns},
client_ssl_tot_native => $result->{$oid_sysClientsslStatTotNativeConns},
client_ssl_tot_compat => $result->{$oid_sysClientsslStatTotCompatConns},
server => $result->{$oid_sysStatServerCurConns},
server_ssl => $result->{$oid_sysServersslStatCurConns},
server_ssl => $result->{$oid_sysServersslStatCurConns}
};
}
@ -150,15 +153,45 @@ Check current connections on F5 BIG IP device.
Only display some counters (regexp can be used).
Example to check SSL connections only : --filter-counters='^client-ssl|server-ssl$'
=item B<--warning-*>
=item B<--warning-client>
Warning threshold.
Can be: 'client', 'server', 'client-ssl', 'server-ssl', 'client-ssl-tps'.
Thresholds.
=item B<--critical-*>
=item B<--critical-client>
Critical threshold.
Can be: 'client', 'server', 'client-ssl', 'server-ssl', 'client-ssl-tps'.
Thresholds.
=item B<--warning-server>
Thresholds.
=item B<--critical-server>
Thresholds.
=item B<--warning-client-ssl>
Thresholds.
=item B<--critical-client-ssl>
Thresholds.
=item B<--warning-server-ssl>
Thresholds.
=item B<--critical-server-ssl>
Thresholds.
=item B<--warning-client-ssl-tps>
Thresholds.
=item B<--critical-client-ssl-tps>
Thresholds.
=back

View File

@ -0,0 +1,290 @@
#
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package network::f5::bigip::snmp::mode::cpuusage;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use Digest::MD5 qw(md5_hex);
sub custom_usage_perfdata {
my ($self, %options) = @_;
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'cpu', type => 1, cb_prefix_output => 'prefix_cpu_output', message_multiple => 'All CPU are ok', skipped_code => { -10 => 1 } },
];
$self->{maps_counters}->{cpu} = [
{ label => 'usage-5s', set => {
key_values => [ { name => 'sysMultiHostCpuUsageRatio5s' }, { name => 'display' } ],
output_template => 'CPU Usage 5sec : %s %%', output_error_template => "CPU Usage 5sec : %s",
perfdatas => [
{ label => 'usage_5s', value => 'sysMultiHostCpuUsageRatio5s', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'usage-1m', set => {
key_values => [ { name => 'sysMultiHostCpuUsageRatio1m' }, { name => 'display' } ],
output_template => 'CPU Usage 1min : %s %%', output_error_template => "CPU Usage 1min : %s",
perfdatas => [
{ label => 'usage_1m', value => 'sysMultiHostCpuUsageRatio1m', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'usage-5m', set => {
key_values => [ { name => 'sysMultiHostCpuUsageRatio5m' }, { name => 'display' } ],
output_template => 'CPU Usage 5min : %s %%', output_error_template => "CPU Usage 5min : %s",
perfdatas => [
{ label => 'usage_5m', value => 'sysMultiHostCpuUsageRatio5m', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'user-5s', set => {
key_values => [ { name => 'sysMultiHostCpuUser5s' }, { name => 'display' } ],
output_template => 'CPU User 5sec : %s %%', output_error_template => "CPU User 5sec : %s",
perfdatas => [
{ label => 'user_5s', value => 'sysMultiHostCpuUser5s', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'user-1m', set => {
key_values => [ { name => 'sysMultiHostCpuUser1m' }, { name => 'display' } ],
output_template => 'CPU User 1min : %s %%', output_error_template => "CPU User 1min : %s",
perfdatas => [
{ label => 'user_1m', value => 'sysMultiHostCpuUser1m', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'user-5m', set => {
key_values => [ { name => 'sysMultiHostCpuUser5m' }, { name => 'display' } ],
output_template => 'CPU User 5min : %s %%', output_error_template => "CPU User 5min : %s",
perfdatas => [
{ label => 'user_5m', value => 'sysMultiHostCpuUser5m', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'iowait-5s', set => {
key_values => [ { name => 'sysMultiHostCpuIowait5s' }, { name => 'display' } ],
output_template => 'CPU IO Wait 5sec : %s %%', output_error_template => "CPU IO Wait 5sec : %s",
perfdatas => [
{ label => 'iowait_5s', value => 'sysMultiHostCpuIowait5s', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'iowait-1m', set => {
key_values => [ { name => 'sysMultiHostCpuIowait1m' }, { name => 'display' } ],
output_template => 'CPU IO Wait 1min : %s %%', output_error_template => "CPU IO Wait 1min : %s",
perfdatas => [
{ label => 'iowait_1m', value => 'sysMultiHostCpuIowait1m', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'iowait-5m', set => {
key_values => [ { name => 'sysMultiHostCpuIowait5m' }, { name => 'display' } ],
output_template => 'CPU IO Wait 5min : %s %%', output_error_template => "CPU IO Wait 5min : %s",
perfdatas => [
{ label => 'iowait_5m', value => 'sysMultiHostCpuIowait5m', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'system-5s', set => {
key_values => [ { name => 'sysMultiHostCpuSystem5s' }, { name => 'display' } ],
output_template => 'CPU System 5sec : %s %%', output_error_template => "CPU System 5sec : %s",
perfdatas => [
{ label => 'system_5s', value => 'sysMultiHostCpuSystem5s', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'system-1m', set => {
key_values => [ { name => 'sysMultiHostCpuSystem1m' }, { name => 'display' } ],
output_template => 'CPU System 1min : %s %%', output_error_template => "CPU System 1min : %s",
perfdatas => [
{ label => 'system_1m', value => 'sysMultiHostCpuSystem1m', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'system-5m', set => {
key_values => [ { name => 'sysMultiHostCpuSystem5m' }, { name => 'display' } ],
output_template => 'CPU System 5min : %s %%', output_error_template => "CPU System 5min : %s",
perfdatas => [
{ label => 'system_5m', value => 'sysMultiHostCpuSystem5m', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'idle-5s', set => {
key_values => [ { name => 'sysMultiHostCpuIdle5s' }, { name => 'display' } ],
output_template => 'CPU Idle 5sec : %s %%', output_error_template => "CPU Idle 5sec : %s",
perfdatas => [
{ label => 'idle_5s', value => 'sysMultiHostCpuIdle5s', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'idle-1m', set => {
key_values => [ { name => 'sysMultiHostCpuIdle1m' }, { name => 'display' } ],
output_template => 'CPU Idle 1min : %s %%', output_error_template => "CPU Idle 1min : %s",
perfdatas => [
{ label => 'idle_1m', value => 'sysMultiHostCpuIdle1m', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'idle-5m', set => {
key_values => [ { name => 'sysMultiHostCpuIdle5m' }, { name => 'display' } ],
output_template => 'CPU Idle 5min : %s %%', output_error_template => "CPU Idle 5min : %s",
perfdatas => [
{ label => 'idle_5m', value => 'sysMultiHostCpuIdle5m', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
];
}
sub prefix_cpu_output {
my ($self, %options) = @_;
return "CPU '" . $options{instance_value}->{display} . "' ";
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
'filter-name:s' => { name => 'filter_name' },
});
return $self;
}
my $mapping = {
sysMultiHostCpuId => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.3' },
sysMultiHostCpuUsageRatio5s => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.19' },
sysMultiHostCpuUsageRatio1m => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.27' },
sysMultiHostCpuUsageRatio5m => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.35' },
sysMultiHostCpuUser5s => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.12' },
sysMultiHostCpuUser1m => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.20' },
sysMultiHostCpuUser5m => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.28' },
sysMultiHostCpuIowait5s => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.18' },
sysMultiHostCpuIowait1m => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.26' },
sysMultiHostCpuIowait5m => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.34' },
sysMultiHostCpuSystem5s => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.14' },
sysMultiHostCpuSystem1m => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.22' },
sysMultiHostCpuSystem5m => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.30' },
sysMultiHostCpuIdle5s => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.15' },
sysMultiHostCpuIdle1m => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.23' },
sysMultiHostCpuIdle5m => { oid => '.1.3.6.1.4.1.3375.2.1.7.5.2.1.31' },
};
my $oid_sysMultiHostCpuEntry = '.1.3.6.1.4.1.3375.2.1.7.5.2.1';
sub manage_selection {
my ($self, %options) = @_;
if ($options{snmp}->is_snmpv1()) {
$self->{output}->add_option_msg(short_msg => "Need to use SNMP v2c or v3.");
$self->{output}->option_exit();
}
my $results = $options{snmp}->get_table(
oid => $oid_sysMultiHostCpuEntry,
nothing_quit => 1
);
$self->{cpu} = {};
foreach my $oid (keys %$results) {
next if ($oid !~ /^$mapping->{sysMultiHostCpuId}->{oid}\.(.*)$/);
my $instance = $1;
my $result = $options{snmp}->map_instance(mapping => $mapping, results => $results, instance => $instance);
if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
$result->{sysMultiHostCpuId} !~ /$self->{option_results}->{filter_name}/) {
$self->{output}->output_add(long_msg => "skipping '" . $result->{sysMultiHostCpuId} . "': no matching filter name.", debug => 1);
next;
}
$self->{cpu}->{$result->{sysMultiHostCpuId}} = {
display => $result->{sysMultiHostCpuId},
%$result
};
}
if (scalar(keys %{$self->{cpu}}) <= 0) {
$self->{output}->add_option_msg(short_msg => "No CPU found.");
$self->{output}->option_exit();
}
$self->{cache_name} = "f5_bipgip_" . $options{snmp}->get_hostname() . '_' . $options{snmp}->get_port() . '_' . $self->{mode} . '_' .
(defined($self->{option_results}->{filter_name}) ? md5_hex($self->{option_results}->{filter_name}) : md5_hex('all')) . '_' .
(defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all'));
}
1;
__END__
=head1 MODE
Check CPU usages.
=over 8
=item B<--filter-counters>
Only display some counters (regexp can be used).
Example : --filter-counters='^usage$'
=item B<--filter-name>
Filter by CPU id (regexp can be used).
Example : --filter-name='2'
=item B<--warning-*>
Warning threshold.
Can be: 'usage-1m', 'usage-5m', 'iowait-5s'.
=item B<--critical-*>
Critical threshold.
Can be: 'usage-1m', 'usage-5m', 'iowait-5s'.
=back
=cut

View File

@ -24,7 +24,19 @@ use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold);
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
sub custom_syncstatus_output {
my ($self, %options) = @_;
return "Sync status is '" . $self->{result_values}->{syncstatus} . "'";
}
sub custom_failoverstatus_output {
my ($self, %options) = @_;
return "Failover status is '" . $self->{result_values}->{failoverstatus} . "'";
}
sub set_counters {
my ($self, %options) = @_;
@ -34,78 +46,36 @@ sub set_counters {
];
$self->{maps_counters}->{global} = [
{ label => 'sync-status', threshold => 0, set => {
{ label => 'sync-status', type => 2, critical_default => '%{syncstatus} =~ /unknown|syncFailed|syncDisconnected|incompatibleVersion/', set => {
key_values => [ { name => 'syncstatus' } ],
closure_custom_calc => \&custom_syncstatus_calc,
closure_custom_output => \&custom_syncstatus_output,
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold,
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
{ label => 'failover-status', threshold => 0, set => {
{ label => 'failover-status', type => 2, critical_default => '%{failoverstatus} =~ /unknown/', set => {
key_values => [ { name => 'failoverstatus' } ],
closure_custom_calc => \&custom_failoverstatus_calc,
closure_custom_output => \&custom_failoverstatus_output,
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold,
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
];
}
sub custom_syncstatus_output {
my ($self, %options) = @_;
my $msg = "Sync status is '" . $self->{result_values}->{syncstatus} . "'";
return $msg;
}
sub custom_syncstatus_calc {
my ($self, %options) = @_;
$self->{result_values}->{syncstatus} = $options{new_datas}->{$self->{instance} . '_syncstatus'};
return 0;
}
sub custom_failoverstatus_output {
my ($self, %options) = @_;
my $msg = "Failover status is '" . $self->{result_values}->{failoverstatus} . "'";
return $msg;
}
sub custom_failoverstatus_calc {
my ($self, %options) = @_;
$self->{result_values}->{failoverstatus} = $options{new_datas}->{$self->{instance} . '_failoverstatus'};
return 0;
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$options{options}->add_options(arguments => {
'warning-sync-status:s' => { name => 'warning_sync_status', default => '' },
'critical-sync-status:s' => { name => 'critical_sync_status', default => '%{syncstatus} =~ /unknown|syncFailed|syncDisconnected|incompatibleVersion/' },
'warning-failover-status:s' => { name => 'warning_failover_status', default => '' },
'critical-failover-status:s' => { name => 'critical_failover_status', default => '%{failoverstatus} =~ /unknown/' },
});
$options{options}->add_options(arguments => {});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
$self->change_macros(macros => ['warning_sync_status', 'critical_sync_status', 'warning_failover_status', 'critical_failover_status']);
}
my %map_boolean = (
0 => 'false',
1 => 'true',
1 => 'true'
);
my %map_sync_status = (
0 => 'unknown',
@ -117,21 +87,21 @@ my %map_sync_status = (
6 => 'standalone',
7 => 'awaitingInitialSync',
8 => 'incompatibleVersion',
9 => 'partialSync',
9 => 'partialSync'
);
my %map_failover_status = (
0 => 'unknown',
1 => 'offline',
2 => 'forcedOffline',
3 => 'standby',
4 => 'active',
4 => 'active'
);
my $mapping = {
sysAttrFailoverIsRedundant => { oid => '.1.3.6.1.4.1.3375.2.1.1.1.1.13', map => \%map_boolean },
sysAttrModeMaint => { oid => '.1.3.6.1.4.1.3375.2.1.1.1.1.21', map => \%map_boolean },
sysCmSyncStatusId => { oid => '.1.3.6.1.4.1.3375.2.1.14.1.1', map => \%map_sync_status },
sysCmFailoverStatusId => { oid => '.1.3.6.1.4.1.3375.2.1.14.3.1', map => \%map_failover_status },
sysCmFailoverStatusId => { oid => '.1.3.6.1.4.1.3375.2.1.14.3.1', map => \%map_failover_status }
};
sub manage_selection {
@ -164,7 +134,7 @@ sub manage_selection {
$self->{output}->display();
$self->{output}->exit();
}
$self->{global} = {
syncstatus => $result->{sysCmSyncStatusId},
failoverstatus => $result->{sysCmFailoverStatusId},
@ -187,22 +157,22 @@ Only display some counters (regexp can be used).
=item B<--warning-sync-status>
Set warning threshold for sync status
Define the conditions to match for the status to be WARNING.
You can use the following variables: %{syncstatus}
=item B<--critical-sync-status>
Set critical threshold for sync status (default: '%{syncstatus} =~ /unknown|syncFailed|syncDisconnected|incompatibleVersion/').
Define the conditions to match for the status to be CRITICAL (default: '%{syncstatus} =~ /unknown|syncFailed|syncDisconnected|incompatibleVersion/').
You can use the following variables: %{syncstatus}
=item B<--warning-failover-status>
Set warning threshold for failover status
Define the conditions to match for the status to be WARNING.
You can use the following variables: %{failoverstatus}
=item B<--critical-failover-status>
Set critical threshold for failover status (default: '%{failoverstatus} =~ /unknown/').
Define the conditions to match for the status to be CRITICAL (Default: '%{failoverstatus} =~ /unknown/').
You can use the following variables: %{failoverstatus}
=back

View File

@ -58,7 +58,7 @@ sub snmp_execute {
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {});

View File

@ -103,8 +103,10 @@ sub run {
);
}
$self->{output}->output_add(severity => 'OK',
short_msg => 'List nodes:');
$self->{output}->output_add(
severity => 'OK',
short_msg => 'List nodes:'
);
$self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
$self->{output}->exit();
}

View File

@ -106,8 +106,10 @@ sub run {
);
}
$self->{output}->output_add(severity => 'OK',
short_msg => 'List pools:');
$self->{output}->output_add(
severity => 'OK',
short_msg => 'List pools:'
);
$self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
$self->{output}->exit();
}

View File

@ -122,8 +122,10 @@ sub run {
);
}
$self->{output}->output_add(severity => 'OK',
short_msg => 'List virtual servers:');
$self->{output}->output_add(
severity => 'OK',
short_msg => 'List virtual servers:'
);
$self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
$self->{output}->exit();
}

View File

@ -64,12 +64,11 @@ sub set_counters {
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
{ label => 'current-server-connections', set => {
{ label => 'current-server-connections', nlabel => 'node.connections.server.current.count', set => {
key_values => [ { name => 'ltmNodeAddrStatServerCurConns' }, { name => 'display' } ],
output_template => 'current server connections : %s',
perfdatas => [
{ label => 'current_server_connections', template => '%s',
min => 0, label_extra_instance => 1, instance_use => 'display' }
{ template => '%s', min => 0, label_extra_instance => 1, instance_use => 'display' }
]
}
}
@ -78,7 +77,7 @@ sub set_counters {
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
@ -135,7 +134,7 @@ sub manage_selection {
foreach my $oid (keys %{$snmp_result->{$branch_name}}) {
$oid =~ /^$branch_name\.(.*?)\.(.*)$/;
my ($num, $index) = ($1, $2);
my $result = $options{snmp}->map_instance(mapping => $mapping->{$map}, results => $snmp_result->{$branch_name}, instance => $num . '.' . $index);
my $name = $self->{output}->decode(join('', map(chr($_), split(/\./, $index))));
@ -207,10 +206,13 @@ You can use the following variables: %{state}, %{status}, %{display}
Define the conditions to match for the status to be CRITICAL (default: '%{state} eq "enabled" and %{status} eq "red"').
You can use the following variables: %{state}, %{status}, %{display}
=item B<--warning-*> B<--critical-*>
=item B<--warning-current-server-connections>
Thresholds.
=item B<--critical-current-server-connections>
Thresholds.
Can be: 'current-server-connections'.
=back

View File

@ -91,7 +91,7 @@ sub set_counters {
];
$self->{maps_counters}->{pool_connections} = [
{ label => 'current-server-connections', nlabel => 'pool.connections.server.count', set => {
{ label => 'current-server-connections', nlabel => 'pool.connections.server.current.count', set => {
key_values => [ { name => 'ltmPoolStatServerCurConns' }, { name => 'display' } ],
output_template => 'current server connections: %s',
perfdatas => [
@ -360,10 +360,29 @@ You can use the following variables: %{state}, %{status}, %{poolName}, %{nodeNam
Define the conditions to match for the status to be CRITICAL.
You can use the following variables: %{state}, %{status}, %{poolName}, %{nodeName}
=item B<--warning-*> B<--critical-*>
=item B<--warning-current-server-connections>
Thresholds.
=item B<--critical-current-server-connections>
Thresholds.
=item B<--warning-current-active-members>
Thresholds.
=item B<--critical-current-active-members>
Thresholds.
=item B<--warning-current-total-members>
Thresholds.
=item B<--critical-current-total-members>
Thresholds.
Can be: 'current-server-connections', 'current-active-members', 'current-total-members'.
=back

View File

@ -30,8 +30,9 @@ sub custom_usage_perfdata {
my ($self, %options) = @_;
$self->{output}->perfdata_add(
label => 'memory_used', unit => 'B',
instances => $self->use_instances(extra_instance => $options{extra_instance}) ? $self->{result_values}->{display} : undef,
nlabel => $self->{nlabel},
unit => 'B',
instances => $self->{result_values}->{display},
value => $self->{result_values}->{used},
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}, total => $self->{result_values}->{total}, cast_int => 1),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}, total => $self->{result_values}->{total}, cast_int => 1),
@ -53,11 +54,12 @@ sub custom_usage_output {
my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used});
my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free});
my $msg = sprintf("Memory Total: %s Used: %s (%.2f%%) Free: %s (%.2f%%)",
$total_size_value . " " . $total_size_unit,
$total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used},
$total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free});
return $msg;
return sprintf(
"Memory Total: %s Used: %s (%.2f%%) Free: %s (%.2f%%)",
$total_size_value . " " . $total_size_unit,
$total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used},
$total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free}
);
}
sub custom_usage_calc {
@ -73,6 +75,12 @@ sub custom_usage_calc {
return 0;
}
sub prefix_tmm_output {
my ($self, %options) = @_;
return "TMM '" . $options{instance_value}->{display} . "' ";
}
sub set_counters {
my ($self, %options) = @_;
@ -81,84 +89,73 @@ sub set_counters {
];
$self->{maps_counters}->{tmm} = [
{ label => 'memory-usage', set => {
{ label => 'memory-usage', nlabel => 'tmm.memory.usage.bytes', set => {
key_values => [ { name => 'display' }, { name => 'sysTmmStatMemoryTotal' }, { name => 'sysTmmStatMemoryUsed' } ],
closure_custom_calc => $self->can('custom_usage_calc'),
closure_custom_output => $self->can('custom_usage_output'),
closure_custom_perfdata => $self->can('custom_usage_perfdata'),
closure_custom_threshold_check => $self->can('custom_usage_threshold'),
closure_custom_threshold_check => $self->can('custom_usage_threshold')
}
},
{ label => 'cpu-1m', set => {
{ label => 'cpu-1m', nlabel => 'tmm.cpu.utilization.1m.percentage', set => {
key_values => [ { name => 'sysTmmStatTmUsageRatio1m' }, { name => 'display' } ],
output_template => 'CPU Usage 1min : %s %%', output_error_template => "CPU Usage 1min : %s",
perfdatas => [
{ label => 'cpu_1m', value => 'sysTmmStatTmUsageRatio1m', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
{ template => '%s', unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' }
]
}
},
{ label => 'cpu-5m', set => {
{ label => 'cpu-5m', nlabel => 'tmm.cpu.utilization.5m.percentage', set => {
key_values => [ { name => 'sysTmmStatTmUsageRatio5m' }, { name => 'display' } ],
output_template => 'CPU Usage 5min : %s %%', output_error_template => "CPU Usage 5min : %s",
perfdatas => [
{ label => 'cpu_5m', value => 'sysTmmStatTmUsageRatio5m', template => '%s',
unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
{ template => '%s', unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' }
]
}
},
{ label => 'current-client-connections', set => {
{ label => 'current-client-connections', nlabel => 'tmm.connections.client.curent.count', set => {
key_values => [ { name => 'sysTmmStatClientCurConns' }, { name => 'display' } ],
output_template => 'Current Client Connections : %s', output_error_template => "Current Client Connections : %s",
perfdatas => [
{ label => 'current_client_connections', value => 'sysTmmStatClientCurConns', template => '%s',
min => 0, label_extra_instance => 1, instance_use => 'display' },
],
{ label => 'current_client_connections', template => '%s',
min => 0, label_extra_instance => 1, instance_use => 'display' }
]
}
},
{ label => 'total-client-connections', set => {
{ label => 'total-client-connections', nlabel => 'tmm.connections.client.total.count', set => {
key_values => [ { name => 'sysTmmStatClientTotConns', diff => 1 }, { name => 'display' } ],
output_template => 'Total Client Connections : %s', output_error_template => "Total Client Connections : %s",
perfdatas => [
{ label => 'total_client_connections', value => 'sysTmmStatClientTotConns', template => '%s',
min => 0, label_extra_instance => 1, instance_use => 'display' },
],
{ template => '%s', min => 0, label_extra_instance => 1, instance_use => 'display' }
]
}
},
{ label => 'current-server-connections', set => {
{ label => 'current-server-connections', nlabel => 'tmm.connections.server.current.count', set => {
key_values => [ { name => 'sysTmmStatServerCurConns' }, { name => 'display' } ],
output_template => 'Current Server Connections : %s', output_error_template => "Current Server Connections : %s",
perfdatas => [
{ label => 'current_server_connections', value => 'sysTmmStatServerCurConns', template => '%s',
min => 0, label_extra_instance => 1, instance_use => 'display' },
],
{ template => '%s', min => 0, label_extra_instance => 1, instance_use => 'display' }
]
}
},
{ label => 'total-server-connections', set => {
{ label => 'total-server-connections', nlabel => 'tmm.connections.server.total.count', set => {
key_values => [ { name => 'sysTmmStatServerTotConns', diff => 1 }, { name => 'display' } ],
output_template => 'Total Server Connections : %s', output_error_template => "Total Server Connections : %s",
perfdatas => [
{ label => 'total_server_connections', value => 'sysTmmStatServerTotConns', template => '%s',
min => 0, label_extra_instance => 1, instance_use => 'display' },
],
{ template => '%s', min => 0, label_extra_instance => 1, instance_use => 'display' }
]
}
},
}
];
}
sub prefix_tmm_output {
my ($self, %options) = @_;
return "TMM '" . $options{instance_value}->{display} . "' ";
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1);
my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
'filter-name:s' => { name => 'filter_name' },
'filter-name:s' => { name => 'filter_name' }
});
return $self;
@ -207,12 +204,12 @@ sub manage_selection {
%$result
};
}
if (scalar(keys %{$self->{tmm}}) <= 0) {
$self->{output}->add_option_msg(short_msg => "No TMM found.");
$self->{output}->option_exit();
}
$self->{cache_name} = "f5_bipgip_" . $options{snmp}->get_hostname() . '_' . $options{snmp}->get_port() . '_' . $self->{mode} . '_' .
(defined($self->{option_results}->{filter_name}) ? md5_hex($self->{option_results}->{filter_name}) : md5_hex('all')) . '_' .
(defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all'));
@ -224,7 +221,7 @@ __END__
=head1 MODE
Check TMM usages.
Check TMM (Traffic Management Microkernel) usages.
=over 8
@ -235,19 +232,63 @@ Example : --filter-counters='^memory-usage$'
=item B<--filter-name>
Filter by TMM name (regexp can be used).
Filter by TMM (Traffic Management Microkernel) name (regexp can be used).
=item B<--warning-*>
=item B<--warning-cpu-1m>
Warning threshold.
Can be: 'cpu-1m', 'cpu-5m', 'memory-usage' (%), 'total-client-connections', 'current-client-connections',
'total-server-connections', 'current-server-connections'.
Thresholds.
=item B<--critical-*>
=item B<--critical-cpu-1m>
Critical threshold.
Can be: 'cpu-1m', 'cpu-5m', 'memory-usage' (%), 'total-client-connections', 'current-client-connections',
'total-server-connections', 'current-server-connections'.
Thresholds.
=item B<--warning-cpu-5m>
Thresholds.
=item B<--critical-cpu-5m>
Thresholds.
=item B<--warning-memory-usage>
Thresholds in %.
=item B<--critical-memory-usage>
Thresholds in %.
=item B<--warning-total-client-connections>
Thresholds.
=item B<--critical-total-client-connections>
Thresholds.
=item B<--warning-current-client-connections>
Thresholds.
=item B<--critical-current-client-connections>
Thresholds.
=item B<--warning-total-server-connections>
Thresholds.
=item B<--critical-total-server-connections>
Thresholds.
=item B<--warning-current-server-connections>
Thresholds.
=item B<--critical-current-server-connections>
Thresholds.
=back

View File

@ -42,8 +42,9 @@ sub custom_traffic_perfdata {
my $speed = $self->{result_values}->{speed} > 0 ? $self->{result_values}->{speed} : undef;
$self->{output}->perfdata_add(
label => 'traffic_' . $self->{result_values}->{label}, unit => 'b/s',
instances => $self->use_instances(extra_instance => $options{extra_instance}) ? $self->{result_values}->{display} : undef,
nlabel => $self->{nlabel},
unit => 'b/s',
instances => $self->{result_values}->{display},
value => sprintf("%.2f", $self->{result_values}->{traffic_per_seconds}),
warning => $warning,
critical => $critical,
@ -101,8 +102,9 @@ sub custom_errors_perfdata {
my $critical = $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel});
$self->{output}->perfdata_add(
label => 'packets_error_' . $self->{result_values}->{label}, unit => '%',
instances => $self->use_instances(extra_instance => $options{extra_instance}) ? $self->{result_values}->{display} : undef,
nlabel => $self->{nlabel},
unit => '%',
instances => $self->{result_values}->{display},
value => sprintf("%.2f", $self->{result_values}->{errors_prct}),
warning => $warning,
critical => $critical,
@ -150,8 +152,9 @@ sub custom_drops_perfdata {
my $critical = $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel});
$self->{output}->perfdata_add(
label => 'packets_drop_' . $self->{result_values}->{label}, unit => '%',
instances => $self->use_instances(extra_instance => $options{extra_instance}) ? $self->{result_values}->{display} : undef,
nlabel => $self->{nlabel},
unit => '%',
instances => $self->{result_values}->{display},
value => sprintf("%.2f", $self->{result_values}->{drops_prct}),
warning => $warning,
critical => $critical,
@ -233,7 +236,7 @@ sub set_counters {
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
{ label => 'traffic-in', set => {
{ label => 'traffic-in', nlabel => 'trunk.traffic.in.bitspersecond', set => {
key_values => [ { name => 'sysTrunkStatBytesIn', diff => 1 }, { name => 'sysTrunkOperBw', diff => 1 }, { name => 'display' } ],
closure_custom_calc => $self->can('custom_traffic_calc'),
closure_custom_calc_extra_options => { label_ref => 'sysTrunkStatBytesIn', speed => 'sysTrunkOperBw', label => 'in' },
@ -242,7 +245,7 @@ sub set_counters {
closure_custom_threshold_check => $self->can('custom_traffic_threshold')
}
},
{ label => 'traffic-out', set => {
{ label => 'traffic-out', nlabel => 'trunk.traffic.out.bitspersecond', set => {
key_values => [ { name => 'sysTrunkStatBytesOut', diff => 1 }, { name => 'sysTrunkOperBw', diff => 1 }, { name => 'display' } ],
closure_custom_calc => $self->can('custom_traffic_calc'),
closure_custom_calc_extra_options => { label_ref => 'sysTrunkStatBytesOut', speed => 'sysTrunkOperBw', label => 'out' },
@ -251,7 +254,7 @@ sub set_counters {
closure_custom_threshold_check => $self->can('custom_traffic_threshold')
}
},
{ label => 'packets-error-in', set => {
{ label => 'packets-error-in', nlabel => 'trunk.packets.in.error.percentage', set => {
key_values => [ { name => 'sysTrunkStatErrorsIn', diff => 1 }, { name => 'sysTrunkStatPktsIn', diff => 1 }, { name => 'display' } ],
closure_custom_calc => $self->can('custom_errors_calc'),
closure_custom_calc_extra_options => { errors => 'sysTrunkStatErrorsIn', packets => 'sysTrunkStatPktsIn', label => 'in' },
@ -260,7 +263,7 @@ sub set_counters {
closure_custom_threshold_check => $self->can('custom_errors_threshold')
}
},
{ label => 'packets-error-out', set => {
{ label => 'packets-error-out', nlabel => 'trunk.packets.out.error.percentage', set => {
key_values => [ { name => 'sysTrunkStatErrorsOut', diff => 1 }, { name => 'sysTrunkStatPktsOut', diff => 1 }, { name => 'display' } ],
closure_custom_calc => $self->can('custom_errors_calc'),
closure_custom_calc_extra_options => { errors => 'sysTrunkStatErrorsOut', packets => 'sysTrunkStatPktsOut', label => 'out' },
@ -269,7 +272,7 @@ sub set_counters {
closure_custom_threshold_check => $self->can('custom_errors_threshold')
}
},
{ label => 'packets-drop-in', set => {
{ label => 'packets-drop-in', nlabel => 'trunk.packets.in.dropped.percentage', set => {
key_values => [ { name => 'sysTrunkStatDropsIn', diff => 1 }, { name => 'sysTrunkStatPktsIn', diff => 1 }, { name => 'display' } ],
closure_custom_calc => $self->can('custom_drops_calc'),
closure_custom_calc_extra_options => { drops => 'sysTrunkStatDropsIn', packets => 'sysTrunkStatPktsIn', label => 'in' },
@ -278,7 +281,7 @@ sub set_counters {
closure_custom_threshold_check => $self->can('custom_drops_threshold')
}
},
{ label => 'packets-drop-out', set => {
{ label => 'packets-drop-out', nlabel => 'trunk.packets.out.dropped.percentage', set => {
key_values => [ { name => 'sysTrunkStatDropsOut', diff => 1 }, { name => 'sysTrunkStatPktsOut', diff => 1 }, { name => 'display' } ],
closure_custom_calc => $self->can('custom_drops_calc'),
closure_custom_calc_extra_options => { drops => 'sysTrunkStatDropsOut', packets => 'sysTrunkStatPktsOut', label => 'out' },
@ -291,7 +294,7 @@ sub set_counters {
key_values => [ { name => 'total_interfaces' }, { name => 'display' } ],
output_template => 'total interfaces: %s',
perfdatas => [
{ label => 'total_interfaces', template => '%d', min => 0, label_extra_instance => 1 }
{ template => '%d', min => 0, label_extra_instance => 1 }
]
}
}
@ -310,7 +313,7 @@ sub set_counters {
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1);
my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
@ -465,8 +468,8 @@ Units of thresholds for the traffic (default: '%') ('%', 'b/s').
=item B<--speed>
Set trunk speed in Mbps (default: sysTrunkOperBw).
If not set and sysTrunkOperBw OID value is 0,
Set trunk speed in Mbps (default: C<sysTrunkOperBw>).
If not set and C<sysTrunkOperBw> OID value is 0,
percentage thresholds will not be applied on traffic metrics.
=item B<--add-interfaces>
@ -503,12 +506,61 @@ You can use the following variables: %{status}, %{display}
Define the conditions to match for the status to be CRITICAL.
You can use the following variables: %{status}, %{display}
=item B<--warning-*> B<--critical-*>
=item B<--warning-traffic-in>
Thresholds.
=item B<--critical-traffic-in>
Thresholds.
=item B<--warning-traffic-out>
Thresholds.
=item B<--critical-traffic-out>
Thresholds.
=item B<--warning-packets-error-in>
Thresholds in %.
=item B<--critical-packets-error-in>
Thresholds in %.
=item B<--warning-packets-error-out>
Thresholds in %.
=item B<--critical-packets-error-out>
Thresholds in %.
=item B<--warning-packets-drop-in>
Thresholds in %.
=item B<--critical-packets-drop-in>
Thresholds in %.
=item B<--warning-packets-drop-out>
Thresholds in %.
=item B<--critical-packets-drop-out>
Thresholds in %.
=item B<--warning-total-interfaces>
Thresholds.
=item B<--critical-total-interfaces>
Thresholds.
Can be: 'traffic-in', 'traffic-out', 'packets-error-in' (%),
'packets-error-out' (%), 'packets-drop-in' (%), 'packets-drop-out' (%),
'total-interfaces'.
=back

View File

@ -197,10 +197,13 @@ You can use the following variables: %{state}, %{status}, %{display}
Define the conditions to match for the status to be CRITICAL (default: '%{state} eq "enabled" and %{status} eq "red"').
You can use the following variables: %{state}, %{status}, %{display}
=item B<--warning-*> B<--critical-*>
=item B<--warning-current-client-connections>
Thresholds.
=item B<--critical-current-client-connections>
Thresholds.
Can be: 'current-client-connections'.
=back

View File

@ -1,5 +1,5 @@
#
# Copyright 2024 Centreon (http://www.centreon.com/)
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
@ -29,7 +29,6 @@ sub new {
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$self->{version} = '1.0';
$self->{modes} = {
'apm' => 'network::f5::bigip::snmp::mode::apm',
'connections' => 'network::f5::bigip::snmp::mode::connections',
@ -42,6 +41,7 @@ sub new {
'node-status' => 'network::f5::bigip::snmp::mode::nodestatus',
'pool-status' => 'network::f5::bigip::snmp::mode::poolstatus',
'tmm-usage' => 'network::f5::bigip::snmp::mode::tmmusage',
'cpu-usage' => 'network::f5::bigip::snmp::mode::cpuusage',
'trunks' => 'network::f5::bigip::snmp::mode::trunks',
'virtualserver-status' => 'network::f5::bigip::snmp::mode::virtualserverstatus'
};

View File

@ -0,0 +1,199 @@
#
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package network::fortinet::fortigate::restapi::mode::certificates;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
use centreon::plugins::misc;
use POSIX;
my $unitdiv = { s => 1, w => 604800, d => 86400, h => 3600, m => 60 };
my $unitdiv_long = { s => 'seconds', w => 'weeks', d => 'days', h => 'hours', m => 'minutes' };
sub custom_expires_perfdata {
my ($self, %options) = @_;
$self->{output}->perfdata_add(
nlabel => $self->{nlabel} . '.' . $unitdiv_long->{ $self->{instance_mode}->{option_results}->{unit} },
unit => $self->{instance_mode}->{option_results}->{unit},
instances => $self->{result_values}->{name},
value => floor($self->{result_values}->{expires_seconds} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }),
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}),
min => 0
);
}
sub custom_expires_threshold {
my ($self, %options) = @_;
return $self->{perfdata}->threshold_check(
value => floor($self->{result_values}->{expires_seconds} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }),
threshold => [
{ label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' },
{ label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' },
{ label => 'unknown-'. $self->{thlabel}, exit_litteral => 'unknown' }
]
);
}
sub custom_status_output {
my ($self, %options) = @_;
return 'status: ' . $self->{result_values}->{status};
}
sub prefix_certificate_output {
my ($self, %options) = @_;
return sprintf(
"Certificate '%s' ",
$options{instance_value}->{name}
);
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'certificates', type => 1, cb_prefix_output => 'prefix_certificate_output', message_multiple => 'All certificates are ok', skipped_code => { -10 => 1 } }
];
$self->{maps_counters}->{certificates} = [
{ label => 'status', type => 2, critical_default => '%{status} =~ /expired/i', set => {
key_values => [ { name => 'name' }, { name => 'status' } ],
closure_custom_output => $self->can('custom_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
{ label => 'expires', nlabel => 'certificate.expires', set => {
key_values => [ { name => 'expires_seconds' }, { name => 'expires_human' }, { name => 'name' } ],
output_template => 'expires in %s',
output_use => 'expires_human',
closure_custom_perfdata => $self->can('custom_expires_perfdata'),
closure_custom_threshold_check => $self->can('custom_expires_threshold')
}
}
];
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
'filter-name:s' => { name => 'filter_name' },
'unit:s' => { name => 'unit', default => 's' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
if ($self->{option_results}->{unit} eq '' || !defined($unitdiv->{$self->{option_results}->{unit}})) {
$self->{option_results}->{unit} = 's';
}
}
sub add_certificate {
my ($self, %options) = @_;
return if (!defined($options{entry}->{status}));
return if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
$options{name} !~ /$self->{option_results}->{filter_name}/);
$self->{certificates}->{ $options{name} } = {
name => $options{name},
status => $options{entry}->{status}
};
if (defined($options{entry}->{valid_to})) {
$self->{certificates}->{ $options{name} }->{expires_seconds} = $options{entry}->{valid_to} - time();
$self->{certificates}->{ $options{name} }->{expires_seconds} = 0 if ($self->{certificates}->{ $options{name} }->{expires_seconds} < 0);
$self->{certificates}->{ $options{name} }->{expires_human} = centreon::plugins::misc::change_seconds(
value => $self->{certificates}->{ $options{name} }->{expires_seconds}
);
}
}
sub manage_selection {
my ($self, %options) = @_;
my $certificates = $options{custom}->request_api(
endpoint => '/api/v2/monitor/system/available-certificates'
);
$self->{certificates} = {};
foreach my $certificate (@{ $certificates->{results} }) {
if (defined($certificate->{name}) and defined($certificate->{valid_to}) and defined($certificate->{status})) {
$self->add_certificate(name => $certificate->{name}, entry => $certificate);
}
}
}
1;
__END__
=head1 MODE
Check certificates.
=over 8
=item B<--filter-name>
Filter certificates by name (can be a regexp).
=item B<--warning-status>
Define the conditions to match for the status to be WARNING.
You can use the following variables: %{name}, %{status}.
=item B<--critical-status>
Define the conditions to match for the status to be CRITICAL (Default: '%{status} =~ /expired/i').
You can use the following variables: %{name}, %{status}.
=item B<--unit>
Select the unit for expires threshold. May be 's' for seconds, 'm' for minutes,
'h' for hours, 'd' for days, 'w' for weeks. Default is seconds.
=item B<--warning-expires>
Thresholds.
=item B<--critical-expires>
Thresholds.
=back
=cut

View File

@ -1,5 +1,5 @@
#
# Copyright 2024 Centreon (http://www.centreon.com/)
# Copyright 2025 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
@ -33,6 +33,7 @@ sub new {
'ha' => 'network::fortinet::fortigate::restapi::mode::ha',
'health' => 'network::fortinet::fortigate::restapi::mode::health',
'licenses' => 'network::fortinet::fortigate::restapi::mode::licenses',
'certificates' => 'network::fortinet::fortigate::restapi::mode::certificates',
'system' => 'network::fortinet::fortigate::restapi::mode::system'
};

View File

@ -36,17 +36,19 @@ sub new {
'cpu' => 'centreon::common::fortinet::fortigate::snmp::mode::cpu',
'disk' => 'centreon::common::fortinet::fortigate::snmp::mode::disk',
'hardware' => 'centreon::common::fortinet::fortigate::snmp::mode::hardware',
'interfaces' => 'centreon::common::fortinet::fortigate::snmp::mode::interfaces',
'interfaces' => 'centreon::common::fortinet::fortigate::snmp::mode::interfaces',
'ips-stats' => 'centreon::common::fortinet::fortigate::snmp::mode::ipsstats',
'link-monitor' => 'centreon::common::fortinet::fortigate::snmp::mode::linkmonitor',
'list-interfaces' => 'snmp_standard::mode::listinterfaces',
'list-link-monitors' => 'centreon::common::fortinet::fortigate::snmp::mode::listlinkmonitors',
'list-virtualdomains' => 'centreon::common::fortinet::fortigate::snmp::mode::listvirtualdomains',
'list-switches' => 'centreon::common::fortinet::fortigate::snmp::mode::listswitches',
'memory' => 'centreon::common::fortinet::fortigate::snmp::mode::memory',
'sessions' => 'centreon::common::fortinet::fortigate::snmp::mode::sessions',
'signatures' => 'centreon::common::fortinet::fortigate::snmp::mode::signatures',
'uptime' => 'snmp_standard::mode::uptime',
'sdwan' => 'centreon::common::fortinet::fortigate::snmp::mode::sdwan',
'switch-usage' => 'centreon::common::fortinet::fortigate::snmp::mode::switchusage',
'vdom-usage' => 'centreon::common::fortinet::fortigate::snmp::mode::vdomusage',
'virus' => 'centreon::common::fortinet::fortigate::snmp::mode::virus',
'vpn' => 'centreon::common::fortinet::fortigate::snmp::mode::vpn'

View File

@ -106,7 +106,7 @@ sub manage_selection {
foreach my $oid (keys %$snmp_result) {
next if($oid !~ /^$mapping->{uxDSPServiceStatus}->{oid}\.(.*)$/);
my $instance = $1;
my $result = $self->{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $instance);
my $result = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $instance);
next if ($result->{uxDSPIsPresent} eq '0');

View File

@ -150,7 +150,7 @@ __END__
Jasmin SMS HTTP-API
=head1 HTTP API OPTIONS
=head1 REST API OPTIONS
Jasmin SMS HTTP API

View File

@ -79,8 +79,8 @@ Check hardware (batteries, fan modules, fibre channels, flashcards, power suppli
=item B<--component>
Which component to check (default: 'all').
Can be: 'battery', 'fan', 'fibrechannel', 'flashcard', 'psu'.
Which component to check (default: '.*').
Can be: C<battery>, C<fan>, C<fibrechannel>, C<flashcard>, C<psu>.
=item B<--filter>
@ -89,7 +89,7 @@ You can also exclude items from specific instances: --filter=fan,1
=item B<--absent-problem>
Return an error if an entity is not 'notAvailable' (default is skipping) (comma separated list)
Return an error if an entity is not C<notAvailable> (default is skipping) (comma separated list).
Can be specific or global: --absent-problem=fan,2
=item B<--no-component>

View File

@ -0,0 +1,51 @@
*** Settings ***
Documentation Check Veeam Backup Enterprise Manager using Rest API,Check jobs.
Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}..${/}resources/import.resource
Suite Setup Start Mockoon ${MOCKOON_JSON}
Suite Teardown Stop Mockoon
Test Timeout 120s
*** Variables ***
${MOCKOON_JSON} ${CURDIR}${/}restapi.json
${cmd} ${CENTREON_PLUGINS}
... --plugin=apps::backup::veeam::vbem::restapi::plugin
... --hostname=${HOSTNAME}
... --api-username='username'
... --api-password='password'
... --proto='http'
... --port=${APIPORT}
*** Test Cases ***
Create cache from API
[Tags] apps backup veeam vbem restapi jobs cache
${output} Run
... ${CMD} --mode=cache --proto=http --port=${APIPORT} --hostname=${HOSTNAME}
Log ${output}
Should Contain ${output} OK: Cache files created successfully
jobs ${tc}
[Tags] apps backup veeam vbem restapi jobs
${command} Catenate
... ${cmd}
... --mode=jobs
... --cache-use
... ${extraoptions}
Ctn Verify Command Output ${command} ${expected_result}
Examples: tc extraoptions expected_result --
... 1 ${EMPTY} OK: All jobs are ok | 'jobs.executions.detected.count'=2;;;0; 'Backup client 2 - Tous les jours#job.executions.failed.percentage'=5.26%;;;0;100 'client 6 - Backup - VM Test et Lab#job.execution.last.seconds'
... 2 --critical-execution-status='\\\%{status} eq "Success"' CRITICAL: job 'Backup client 2 - Tous les jours' [type: Backup] execution started: 2025-02-19T11:30:08.103Z status: Success - job 'PROD Job 1' [type: Backup] execution started: 2025-02-19T12:00:11.94Z status: Success | 'jobs.executions.detected.count'=2;;;0;
... 3 --warning-execution-status='\\\%{status} eq "Success"' WARNING: job 'Backup client 2 - Tous les jours' [type: Backup] execution started: 2025-02-19T11:30:08.103Z status: Success - job 'PROD Job 1' [type: Backup] execution started: 2025-02-19T12:00:11.94Z status: Success | 'jobs.executions.detected.count'=2;;;0;
... 4 --filter-uid='urn:veeam:Job' OK: All jobs are ok | 'jobs.executions.detected.count'=2;;;0; 'Backup client 2 - Tous les jours#job.executions.failed.percentage'=5.26%;;;0;100 'client 6 - Backup - VM Test et Lab#job.execution.last.seconds'
... 5 --filter-name='PROD Job 1' CRITICAL: job 'PROD Job 1' [type: Backup] execution started: 2025-02-19T03:51:03.037Z status: Failed | 'jobs.executions.detected.count'=2;;;0; 'PROD Job 1#job.executions.failed.percentage'=50.00%;;;0;100
... 6 --filter-type='toto' OK: | 'jobs.executions.detected.count'=0;;;0;
... 7 --timeframe='0' OK: All jobs are ok | 'jobs.executions.detected.count'=2;;;0; 'Backup client 2 - Tous les jours#job.executions.failed.percentage'=5.26%;;;0;100 'client 6 - Backup - VM Test et Lab#job.execution.last.seconds'
... 8 --unknown-execution-status='\\\%{status} eq "Success"' --filter-name='client 6' --filter-type='Backup' UNKNOWN: job 'client 6 - Backup - Infrastructure g0t0-oob' [type: Backup] execution started: 2025-02-19T11:30:08.103Z status: Success - job 'client 6 - Backup - Infrastructure g0t0-bck' [type: Backup] execution started: 2025-02-19T12:00:11.94Z status: Success | 'jobs.executions.detected.count'=2;;;0;
... 9 --warning-job-executions-failed-prct=0 --critical-job-executions-failed-prct=10 CRITICAL: job 'PROD Job 1' [type: Backup] number of failed executions: 60.00 % WARNING: job 'Backup client 2 - Tous les jours' [type: Backup]

Some files were not shown because too many files have changed in this diff Show More