Merge branch 'develop' into MON-155920

This commit is contained in:
itoussies 2025-07-24 10:47:47 +02:00 committed by GitHub
commit 18def5a66c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
237 changed files with 76025 additions and 762 deletions

View File

@ -0,0 +1,308 @@
name: Workflow incident tracking
description: Create Jira ticket on incident
inputs:
jira_base_url:
required: true
description: jira base url
jira_user_email:
required: true
description: jira user email
jira_api_token:
required: true
description: jira api token
module_name:
required: true
description: module name
ticket_labels:
required: true
description: ticket labels, usually Pipeline + Nightly/Veracode + x
default: 'Pipeline'
ticket_squad:
required: true
description: id of the squad to assign the ticket to
default: 'DevSecOps'
runs:
using: "composite"
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Get ticket elements from context
id: get_context
run: |
# Safely set/unset IFS in order to properly parse the table of labels
[ -n "${IFS+set}" ] && saved_IFS=$IFS
IFS=', ' read -a ticket_labels <<< $(echo "${{ inputs.ticket_labels }}" | tr -d "[],'")
unset IFS
[ -n "${saved_IFS+set}" ] && { IFS=$saved_IFS; unset saved_IFS; }
# Change the context elements (summary, parent epic, etc.) that are checked depending on these ticket labels
if [[ "${ticket_labels[@]}" =~ "Nightly" ]]; then
parent_epic_id=206242
parent_epic_key="MON-151547"
ticket_summary="$(date '+%Y-%m-%d') ${{ inputs.module_name }}-${{ github.ref_name }} nightly build failure"
JSON_TEMPLATE_FILE="./.github/actions/create-jira-ticket/nightly-ticket-template.json"
sed -i \
-e 's|@MODULE_NAME@|${{ inputs.module_name }}|g' \
-e "s|@DATE@|$(date '+%Y-%m-%d')|g" $JSON_TEMPLATE_FILE
else
echo "::error::Cannot find a valid labelling option for the ticket."
exit 1
fi
case "${{ inputs.ticket_squad }}" in
"DevSecOps")
ticket_squad_id=10524
ticket_board_id=184
squad_name="DEVSECOPS"
project_name="MON"
;;
"Connectors")
ticket_squad_id=10504
ticket_board_id=222
squad_name="CONNECTORS"
project_name="CTOR"
*)
echo "::error::Cannot find a valid squad for value ${{ inputs.ticket_squad }}."
exit 1
;;
esac
echo "Ticket will be assigned to the $squad_name team."
current_sprint=$(curl --request GET \
--url ${{ inputs.jira_base_url }}/rest/agile/1.0/board/$ticket_board_id/sprint?state=active \
--user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \
--header "Accept: application/json" | jq --arg squad_name "$squad_name" '.values[] | select(.name | test($squad_name; "i")) | .id')
echo "[DEBUG] current_sprint: $current_sprint"
# General updates on all template files
sed -i \
-e 's|@GITHUB_BRANCH@|${{ github.base_ref || github.ref_name }}|g' \
-e 's|@GITHUB_SERVER_URL@|${{ github.server_url }}|g' \
-e 's|@GITHUB_REPOSITORY@|${{ github.repository }}|g' \
-e 's|@GITHUB_RUN_ID@|${{ github.run_id }}|g' \
-e 's|@GITHUB_RUN_ATTEMPT@|${{ github.run_attempt }}|g' $JSON_TEMPLATE_FILE
echo "parent_epic_id=$parent_epic_id" >> $GITHUB_OUTPUT
echo "parent_epic_key=$parent_epic_key" >> $GITHUB_OUTPUT
echo "ticket_summary=$ticket_summary" >> $GITHUB_OUTPUT
echo "ticket_board_id=$ticket_board_id" >> $GITHUB_OUTPUT
echo "ticket_squad_id=$ticket_squad_id" >> $GITHUB_OUTPUT
echo "project_name=$project_name" >> $GITHUB_OUTPUT
echo "current_sprint=$current_sprint" >> $GITHUB_OUTPUT
echo "json_template_file=$JSON_TEMPLATE_FILE" >> $GITHUB_OUTPUT
cat $JSON_TEMPLATE_FILE
cat $GITHUB_OUTPUT
shell: bash
env:
GH_TOKEN: ${{ github.token }}
- name: Check if the ticket already exists
id: check_ticket
run: |
# Checking if an incident ticket already exists
response=$(curl \
--write-out "%{http_code}" \
--request POST \
--url "${{ inputs.jira_base_url }}/rest/api/3/search" \
--user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \
--header "Accept:application/json" \
--header "Content-Type:application/json" \
--data '{
"fields": ["summary"],
"jql": "project = ${{ steps.get_context.outputs.project_name }} AND parentEpic = ${{ steps.get_context.outputs.parent_epic_key }} AND issueType = Technical AND summary ~ \"${{ steps.get_context.outputs.ticket_summary }}\" AND component = \"${{ inputs.module_name }}\" AND resolution = unresolved ORDER BY key ASC",
"maxResults": 1
}'
)
echo "[DEBUG] $response"
if [[ $(echo "$response" | tr -d '\n' | tail -c 3) -ne 200 ]]; then
echo "::error:: Jira API request was not completed properly."
fi
ticket_key=$(echo "$response" | head -c -4 | jq .issues[0].key | xargs)
if [[ "$ticket_key" != "null" ]]; then
echo "abort_ticket_creation=true" >> $GITHUB_ENV
echo "ticket_key=$ticket_key" >> $GITHUB_ENV
echo "::notice::ticket found as $ticket_key aborting ticket creation"
fi
shell: bash
- name: Update existing nightly Jira ticket
if: |
env.abort_ticket_creation == 'true' &&
contains(steps.get_context.outputs.parent_epic_key, 'MON-151547')
run: |
# Adding failed job labels for already existing ticket
[ -n "${IFS+set}" ] && saved_IFS=$IFS
IFS=', ' read -a ticket_labels <<< $(echo "${{ inputs.ticket_labels }}" | tr -d "[],'")
unset IFS
[ -n "${saved_IFS+set}" ] && { IFS=$saved_IFS; unset saved_IFS; }
for label in ${ticket_labels[@]}; do
response=$(curl \
--request PUT \
--url "${{ inputs.jira_base_url }}/rest/api/3/issue/${{ env.ticket_key }}" \
--user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data "{ \"update\": { \"labels\": [ { \"add\": \"$label\" } ] } }"
)
done
ticket_description=$(curl --request GET \
--url "${{ inputs.jira_base_url }}/rest/api/3/issue/${{ env.ticket_key }}" \
--user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \
--header "Accept: application/json" | jq '.fields.description')
mapfile -t jobs_failed < <(gh run view ${{ github.run_id }} --json jobs -q '.jobs[] | select(.conclusion == "failure") | .name')
echo "[DEBUG] - jobs failed for component ${FAILED_COMPONENTS[index]}: $jobs_failed"
new_list_of_failed_jobs=$(for job in "${jobs_failed[@]}"; do
cat <<EOF
{
"type": "listItem",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "$job"
}
]
}
]
}
EOF
done | jq -s '.'
)
updated_ticket_description=$(echo "$ticket_description" | jq --argjson new_list_of_failed_jobs "$new_list_of_failed_jobs" '
(.content[] | select(.type == "bulletList") | .content) = $new_list_of_failed_jobs
')
echo "[DEBUG] - updated_ticket_description = $updated_ticket_description"
curl --request PUT \
--url "${{ inputs.jira_base_url }}/rest/api/3/issue/${{ env.ticket_key }}" \
--user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--data "{
\"fields\": {
\"description\": $updated_ticket_description
}
}"
shell: bash
env:
GH_TOKEN: ${{ github.token }}
- name: Create Jira Issue
if: ${{ env.abort_ticket_creation != 'true' }}
run: |
# Get the name of the current job and list it
failed_job_name=$(gh run view ${{ github.run_id }} --json jobs | jq -r --arg job_name "${{ github.job }}" '.jobs[] | select(.name == $job_name) | .name')
CONTENT_TO_ADD_TO_TEMPLATE_FILE=$(jq -n --arg job "$failed_job_name" '{
"type": "bulletList",
"content": [
{
"type": "listItem",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": $job
}
]
}
]
}
]
}')
echo "[DEBUG] - CONTENT_TO_ADD_TO_TEMPLATE_FILE: $CONTENT_TO_ADD_TO_TEMPLATE_FILE"
TEMPLATE_FILE=$(cat ${{ steps.get_context.outputs.json_template_file }})
UPDATED_TEMPLATE_FILE=$(jq --argjson NEW_CONTENT "$CONTENT_TO_ADD_TO_TEMPLATE_FILE" '.content += [$NEW_CONTENT]' <<< "$TEMPLATE_FILE")
# Creating a new incident ticket on Jira
DATA=$( cat <<-EOF
{
"fields": {
"summary": "${{ steps.get_context.outputs.ticket_summary }}",
"project": {"key": "${{ steps.get_context.outputs.project_name }}"},
"issuetype": {"id": "10209"},
"parent": {"id": "${{ steps.get_context.outputs.parent_epic_id }}", "key": "${{ steps.get_context.outputs.parent_epic_key }}"},
"labels": ${{ inputs.ticket_labels }},
"components":[{"name": "${{ inputs.module_name }}"}],
"customfield_10902": {"id": "${{ steps.get_context.outputs.ticket_squad_id }}", "value": "${{ inputs.ticket_squad }}"},
"description": $UPDATED_TEMPLATE_FILE
}
}
EOF
)
if [[ ${{ steps.get_context.outputs.current_sprint }} != "null" ]]; then
DATA=$(echo "$DATA" | jq '.fields.customfield_10007 = ${{ steps.get_context.outputs.current_sprint }}')
fi
echo "[DEBUG] - DATA: $DATA"
response=$(curl \
--request POST \
--url "${{ inputs.jira_base_url }}/rest/api/3/issue" \
--user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data "$DATA")
if [ $? -ne 0 ]; then
echo "::error::Failed to create ticket: $response"
exit 1
fi
echo $response
ticket_key=$(echo "$response" | jq -r .key)
echo "::notice::Created ticket: $ticket_key"
# Update priority on newly created ticket since you cannot create a ticket with another priority than medium
response=$(curl \
--request PUT \
--url "${{ inputs.jira_base_url }}/rest/api/3/issue/$ticket_key" \
--user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{ "fields": { "priority": { "id": "1" } } }'
)
echo $response
# Update ticket status so that squad members can see it in their respective sprints
for transition_id in 11 21; do
response=$(curl \
--request POST \
--url "${{ inputs.jira_base_url }}/rest/api/latest/issue/$ticket_key/transitions" \
--user "${{ inputs.jira_user_email }}:${{ inputs.jira_api_token }}" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data "{\"transition\": {\"id\": \"$transition_id\"} }"
)
echo $response
done
shell: bash
env:
GH_TOKEN: ${{ github.token }}

View File

@ -0,0 +1,55 @@
{
"version": 1,
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "This ticket was automatically created by the nightly workflow run. Feel free to update it as you need to. If you have any feedback about it, please contact the Delivery team.",
"marks": [
{
"type": "em"
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "This incident ticket relates to the @MODULE_NAME@ nightly on the @GITHUB_BRANCH@ branch which failed on @DATE@."
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Link to the failed nightly",
"marks": [
{
"type": "link",
"attrs": {
"href": "@GITHUB_SERVER_URL@/@GITHUB_REPOSITORY@/actions/runs/@GITHUB_RUN_ID@/attempts/@GITHUB_RUN_ATTEMPT@"
}
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "List of jobs that failed on the @MODULE_NAME@ nightly:"
}
]
}
]
}

View File

@ -11,10 +11,10 @@ dnf install -y \
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
wget https://dlcdn.apache.org/maven/maven-3/3.8.9/binaries/apache-maven-3.8.9-bin.tar.gz
tar zxf apache-maven-3.8.9-bin.tar.gz
ln -s /usr/local/src/apache-maven-3.8.9/bin/mvn /usr/bin/mvn
rm -f apache-maven-3.8.9-bin.tar.gz
echo '[goreleaser]
name=GoReleaser

View File

@ -11,10 +11,10 @@ dnf install -y \
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
wget https://dlcdn.apache.org/maven/maven-3/3.8.9/binaries/apache-maven-3.8.9-bin.tar.gz
tar zxf apache-maven-3.8.9-bin.tar.gz
ln -s /usr/local/src/apache-maven-3.8.9/bin/mvn /usr/bin/mvn
rm -f apache-maven-3.8.9-bin.tar.gz
echo '[goreleaser]
name=GoReleaser

View File

@ -13,10 +13,10 @@ apt-get install -y \
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
wget https://dlcdn.apache.org/maven/maven-3/3.8.9/binaries/apache-maven-3.8.9-bin.tar.gz
tar zxf apache-maven-3.8.9-bin.tar.gz
ln -s /usr/local/src/apache-maven-3.8.9/bin/mvn /usr/bin/mvn
rm -f apache-maven-3.8.9-bin.tar.gz
echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | tee /etc/apt/sources.list.d/goreleaser.list

View File

@ -13,10 +13,10 @@ apt-get install -y \
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
wget https://dlcdn.apache.org/maven/maven-3/3.8.9/binaries/apache-maven-3.8.9-bin.tar.gz
tar zxf apache-maven-3.8.9-bin.tar.gz
ln -s /usr/local/src/apache-maven-3.8.9/bin/mvn /usr/bin/mvn
rm -f apache-maven-3.8.9-bin.tar.gz
echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | tee /etc/apt/sources.list.d/goreleaser.list

View File

@ -46,10 +46,11 @@ gpgcheck=1\n\
gpgkey=https://yum-gpg.centreon.com/RPM-GPG-KEY-CES\n'\
>> /etc/yum.repos.d/centreon-plugins.repo
dnf install -y gcc make cpan perl-Test2-Suite.noarch perl-Test2-Plugin-NoWarnings.noarch 'perl(Authen::Radius)' 'perl(Convert::Binary::C)' 'perl(Crypt::OpenSSL::RSA)' 'perl(Data::Dumper)' 'perl(Date::Parse)' 'perl(DateTime)' 'perl(DateTime::Duration)' 'perl(DateTime-Format-Duration-ISO8601)' 'perl(DateTime::Format::Strptime)' 'perl(DBD::mysql)' 'perl(DBD::ODBC)' 'perl(DBD::Pg)' 'perl(DBD::Sybase)' 'perl(DBI)' 'perl(Device::Modbus::RTU::Client)' 'perl(Device::Modbus::TCP::Client)' 'perl(Digest::CRC)' 'perl(Digest::MD5)' 'perl(Digest::SHA)' 'perl(Email::MIME)' 'perl(Email::Sender)' 'perl(Email::Send::SMTP::Gmail)' 'perl(Email::Simple)' 'perl(File::Basename)' 'perl(Filesys::SmbClient)' 'perl(Hash::Ordered)' 'perl(HTML::Template)' 'perl(IO::Select)' 'perl(IO::Socket)' 'perl(IO::Socket::INET)' 'perl(IO::Socket::INET6)' 'perl(IO::Socket::SSL)' 'perl(JMX::Jmx4Perl)' 'perl(JSON)' 'perl(JSON::Path)' 'perl(JSON::WebToken)' 'perl(JSON::XS)' 'perl(Libssh::Session)' 'perl-Mail-IMAPClient' 'perl(MIME::Base64)' 'perl(MongoDB)' 'perl(NetAddr::IP)' 'perl(Net::DHCP::Constants)' 'perl(Net::DHCP::Packet)' 'perl(Net::DNS)' 'perl(Net::FTP)' 'perl(Net::FTPSSL)' 'perl(Net::LDAP)' 'perl(Net::MQTT::Simple)' 'perl(Net::NTP)' 'perl(Net::SSLeay)' 'perl(Net::Subnet)' 'perl(Net::Telnet)' 'perl(Net::TFTP)' 'perl(openwsman)' 'perl(Pod::Parser)' 'perl(POSIX)' 'perl(Redis)' 'perl(RRDs)' 'perl(SNMP)' 'perl(Socket)' 'perl(Text::CSV)' 'perl(Time::HiRes)' 'perl(URI::Encode)' 'perl(URI::Escape)' 'perl(UUID)' 'perl(WWW::Selenium)' 'perl(XML::LibXML)' 'perl(XML::LibXML::Simple)' 'perl(XML::Simple)' 'perl(XML::XPath)' 'perl(ZMQ::LibZMQ4)' 'perl(Data::UUID)'
dnf install -y gcc make cpan perl-Test2-Suite.noarch perl-Test2-Plugin-NoWarnings.noarch 'perl(Authen::Radius)' 'perl(Convert::Binary::C)' 'perl(Crypt::OpenSSL::RSA)' 'perl(Data::Dumper)' 'perl(Date::Parse)' 'perl(DateTime)' 'perl(DateTime::Duration)' 'perl(DateTime-Format-Duration-ISO8601)' 'perl(DateTime::Format::Strptime)' 'perl(DBD::mysql)' 'perl(DBD::ODBC)' 'perl(DBD::Pg)' 'perl(DBD::Sybase)' 'perl(DBI)' 'perl(Device::Modbus::RTU::Client)' 'perl(Device::Modbus::TCP::Client)' 'perl(Digest::CRC)' 'perl(Digest::MD5)' 'perl(Digest::SHA)' 'perl(Email::MIME)' 'perl(Email::Sender)' 'perl(Email::Send::SMTP::Gmail)' 'perl(Email::Simple)' 'perl(File::Basename)' 'perl(Filesys::SmbClient)' 'perl(Hash::Ordered)' 'perl(HTML::Template)' 'perl(IO::Select)' 'perl(IO::Socket)' 'perl(IO::Socket::INET)' 'perl(IO::Socket::INET6)' 'perl(IO::Socket::SSL)' 'perl(JMX::Jmx4Perl)' 'perl(JSON)' 'perl(JSON::Path)' 'perl(JSON::WebToken)' 'perl(JSON::XS)' 'perl(Libssh::Session)' 'perl-Mail-IMAPClient' 'perl(MIME::Base64)' 'perl(MongoDB)' 'perl(NetAddr::IP)' 'perl(Net::DHCP::Constants)' 'perl(Net::DHCP::Packet)' 'perl(Net::DNS)' 'perl(Net::FTP)' 'perl(Net::FTPSSL)' 'perl(Net::LDAP)' 'perl(Net::MQTT::Simple)' 'perl(Net::NTP)' 'perl(Net::SSLeay)' 'perl(Net::Subnet)' 'perl(Net::Telnet)' 'perl(Net::TFTP)' 'perl(openwsman)' 'perl(Pod::Parser)' 'perl(POSIX)' 'perl(Redis)' 'perl(RRDs)' 'perl(SNMP)' 'perl(Socket)' 'perl(Text::CSV)' 'perl(Time::HiRes)' 'perl(URI::Encode)' 'perl(URI::Escape)' 'perl(UUID)' 'perl(WWW::Selenium)' 'perl(XML::LibXML)' 'perl(XML::LibXML::Simple)' 'perl(XML::Simple)' 'perl(XML::XPath)' 'perl(ZMQ::LibZMQ4)' 'perl(Data::UUID)' 'perl(String::ShellQuote)'
# this image is used by centreon-perl-libs unit test and centreon-gorgone unit tests.
dnf -y install 'perl(Crypt::OpenSSL::AES)' 'perl-Net-Curl' 'perl(YAML::XS)' 'perl(Hash::Merge)' 'perl(Clone)' 'perl(CryptX)' 'perl(JSON::XS)' 'perl(JSON::PP)'
dnf -y install 'perl(Crypt::OpenSSL::AES)' 'perl-Net-Curl' 'perl(YAML::XS)' 'perl(Hash::Merge)' 'perl(Clone)' 'perl(CryptX)' 'perl(JSON::XS)' 'perl(JSON::PP)' 'perl(Digest::MD5::File)'
dnf -y install 'perl(Hash::Merge)' 'perl(YAML::XS)' 'perl(ZMQ::FFI)' 'perl-CryptX' 'perl(EV)' 'perl(RRDs)' 'perl(DBI)' 'perl(DBD::SQLite)' 'perl(HTTP::Daemon)' 'perl(HTTP::Daemon::SSL)' 'perl(NetAddr::IP)' 'perl(Mojolicious)' 'perl(Mojo::IOLoop::Signal)' 'perl(Net::Curl)' 'perl(EV)'
dnf -y install git mariadb
dnf clean all
mkdir -p /var/lib/centreon/centplugins/
chmod 777 /var/lib/centreon/centplugins/

View File

@ -45,10 +45,12 @@ enabled=1\n\
gpgcheck=1\n\
gpgkey=https://yum-gpg.centreon.com/RPM-GPG-KEY-CES\n'\
>> /etc/yum.repos.d/centreon-plugins.repo
dnf install -y make cpan perl-Test2-Suite.noarch 'perl(Authen::Radius)' 'perl(Convert::Binary::C)' 'perl(Crypt::OpenSSL::RSA)' 'perl(Data::Dumper)' 'perl(Date::Parse)' 'perl(DateTime)' 'perl(DateTime::Duration)' 'perl(DateTime-Format-Duration-ISO8601)' 'perl(DateTime::Format::Strptime)' 'perl(DBD::mysql)' 'perl(DBD::ODBC)' 'perl(DBD::Pg)' 'perl(DBD::Sybase)' 'perl(DBI)' 'perl(Device::Modbus::RTU::Client)' 'perl(Device::Modbus::TCP::Client)' 'perl(Digest::CRC)' 'perl(Digest::MD5)' 'perl(Digest::SHA)' 'perl(Email::MIME)' 'perl(Email::Sender)' 'perl(Email::Send::SMTP::Gmail)' 'perl(Email::Simple)' 'perl(File::Basename)' 'perl(Filesys::SmbClient)' 'perl(Hash::Ordered)' 'perl(HTML::Template)' 'perl(IO::Select)' 'perl(IO::Socket)' 'perl(IO::Socket::INET)' 'perl(IO::Socket::INET6)' 'perl(IO::Socket::SSL)' 'perl(JMX::Jmx4Perl)' 'perl(JSON)' 'perl(JSON::Path)' 'perl(JSON::WebToken)' 'perl(JSON::XS)' 'perl(Libssh::Session)' 'perl-Mail-IMAPClient' 'perl(MIME::Base64)' 'perl(MongoDB)' 'perl(NetAddr::IP)' 'perl(Net::DHCP::Constants)' 'perl(Net::DHCP::Packet)' 'perl(Net::DNS)' 'perl(Net::FTP)' 'perl(Net::FTPSSL)' 'perl(Net::LDAP)' 'perl(Net::MQTT::Simple)' 'perl(Net::NTP)' 'perl(Net::SSLeay)' 'perl(Net::Subnet)' 'perl(Net::Telnet)' 'perl(Net::TFTP)' 'perl(openwsman)' 'perl(Pod::Parser)' 'perl(POSIX)' 'perl(Redis)' 'perl(RRDs)' 'perl(SNMP)' 'perl(Socket)' 'perl(Text::CSV)' 'perl(Time::HiRes)' 'perl(URI::Encode)' 'perl(URI::Escape)' 'perl(UUID)' 'perl(WWW::Selenium)' 'perl(XML::LibXML)' 'perl(XML::LibXML::Simple)' 'perl(XML::Simple)' 'perl(XML::XPath)' 'perl(ZMQ::LibZMQ4)' 'perl(Data::UUID)'
dnf install -y make cpan perl-Test2-Suite.noarch 'perl(Authen::Radius)' 'perl(Convert::Binary::C)' 'perl(Crypt::OpenSSL::RSA)' 'perl(Data::Dumper)' 'perl(Date::Parse)' 'perl(DateTime)' 'perl(DateTime::Duration)' 'perl(DateTime-Format-Duration-ISO8601)' 'perl(DateTime::Format::Strptime)' 'perl(DBD::mysql)' 'perl(DBD::ODBC)' 'perl(DBD::Pg)' 'perl(DBD::Sybase)' 'perl(DBI)' 'perl(Device::Modbus::RTU::Client)' 'perl(Device::Modbus::TCP::Client)' 'perl(Digest::CRC)' 'perl(Digest::MD5)' 'perl(Digest::SHA)' 'perl(Email::MIME)' 'perl(Email::Sender)' 'perl(Email::Send::SMTP::Gmail)' 'perl(Email::Simple)' 'perl(File::Basename)' 'perl(Filesys::SmbClient)' 'perl(Hash::Ordered)' 'perl(HTML::Template)' 'perl(IO::Select)' 'perl(IO::Socket)' 'perl(IO::Socket::INET)' 'perl(IO::Socket::INET6)' 'perl(IO::Socket::SSL)' 'perl(JMX::Jmx4Perl)' 'perl(JSON)' 'perl(JSON::Path)' 'perl(JSON::WebToken)' 'perl(JSON::XS)' 'perl(Libssh::Session)' 'perl-Mail-IMAPClient' 'perl(MIME::Base64)' 'perl(MongoDB)' 'perl(NetAddr::IP)' 'perl(Net::DHCP::Constants)' 'perl(Net::DHCP::Packet)' 'perl(Net::DNS)' 'perl(Net::FTP)' 'perl(Net::FTPSSL)' 'perl(Net::LDAP)' 'perl(Net::MQTT::Simple)' 'perl(Net::NTP)' 'perl(Net::SSLeay)' 'perl(Net::Subnet)' 'perl(Net::Telnet)' 'perl(Net::TFTP)' 'perl(openwsman)' 'perl(Pod::Parser)' 'perl(POSIX)' 'perl(Redis)' 'perl(RRDs)' 'perl(SNMP)' 'perl(Socket)' 'perl(Text::CSV)' 'perl(Time::HiRes)' 'perl(URI::Encode)' 'perl(URI::Escape)' 'perl(UUID)' 'perl(WWW::Selenium)' 'perl(XML::LibXML)' 'perl(XML::LibXML::Simple)' 'perl(XML::Simple)' 'perl(XML::XPath)' 'perl(ZMQ::LibZMQ4)' 'perl(Data::UUID)' 'perl(String::ShellQuote)'
# this image is used by centreon-perl-libs unit test and centreon-gorgone unit tests.
dnf -y install 'perl(Crypt::OpenSSL::AES)' 'perl-Net-Curl' 'perl(YAML::XS)' 'perl(Hash::Merge)' 'perl(Clone)' 'perl(CryptX)' 'perl(JSON::XS)' 'perl(JSON::PP)'
dnf -y install 'perl(Crypt::OpenSSL::AES)' 'perl-Net-Curl' 'perl(YAML::XS)' 'perl(Hash::Merge)' 'perl(Clone)' 'perl(CryptX)' 'perl(JSON::XS)' 'perl(JSON::PP)' 'perl(Digest::MD5::File)'
dnf -y install 'perl(ZMQ::FFI)' 'perl(EV)' 'perl(RRDs)' 'perl(DBI)' 'perl(DBD::SQLite)' 'perl(HTTP::Daemon)' 'perl(HTTP::Daemon::SSL)' 'perl(NetAddr::IP)' 'perl(Mojolicious)' 'perl(Mojo::IOLoop::Signal)' 'perl(Net::Curl)'
dnf -y install git mariadb
dnf clean all
dnf clean all

View File

@ -23,10 +23,11 @@ echo "deb https://packages.centreon.com/apt-plugins-testing/ bookworm main" | te
echo "deb https://packages.centreon.com/apt-plugins-unstable/ bookworm 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 libtest2-harness-perl 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 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 perl perl-modules
apt-get -y install libtest2-harness-perl 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 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 perl perl-modules libstring-shellquote-perl
# 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 librrds-perl libdbi-perl libdbd-sqlite3-perl libhttp-daemon-perl libhttp-daemon-ssl-perl libnetaddr-ip-perl libmojolicious-perl libmojo-ioloop-signal-perl libnet-curl-perl libev-perl
apt-get -y install libhash-merge-perl libyaml-libyaml-perl libzmq-ffi-perl libcryptx-perl libev-perl
apt-get -y install libhash-merge-perl libyaml-libyaml-perl libzmq-ffi-perl libcryptx-perl libev-perl libdigest-md5-file-perl
apt-get -y install git mariadb-client
apt-get clean
EOF

View File

@ -23,9 +23,10 @@ echo "deb https://packages.centreon.com/apt-plugins-testing/ bullseye main" | te
echo "deb https://packages.centreon.com/apt-plugins-unstable/ bullseye 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 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 perl perl-modules libdata-uuid-perl
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 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 perl perl-modules libdata-uuid-perl libdigest-md5-file-perl libstring-shellquote-perl
# this image is used by centreon-perl-libs unit test and centreon-gorgone unit tests.
apt-get -y install libzmq-ffi-perl libcpanel-json-xs-perl libcrypt-openssl-aes-perl libnet-curl-perl libyaml-libyaml-perl libhash-merge-perl libclone-choose-perl libcryptx-perl libjson-xs-perl libjson-pp-perl librrds-perl libdbi-perl libdbd-sqlite3-perl libhttp-daemon-perl libhttp-daemon-ssl-perl libnetaddr-ip-perl libmojolicious-perl libmojo-ioloop-signal-perl libnet-curl-perl libev-perl
apt-get -y install libzmq-ffi-perl libcpanel-json-xs-perl libcrypt-openssl-aes-perl libnet-curl-perl libyaml-libyaml-perl libhash-merge-perl libclone-choose-perl libcryptx-perl libjson-xs-perl libjson-pp-perl librrds-perl libdbi-perl libdbd-sqlite3-perl libhttp-daemon-perl libhttp-daemon-ssl-perl libnetaddr-ip-perl libmojolicious-perl libmojo-ioloop-signal-perl libnet-curl-perl libev-perl libdigest-md5-file-perl
apt-get -y install git mariadb-client
apt-get clean
NONINTERACTIVE_TESTING=1 PERL_MM_USE_DEFAULT=1 cpan Test2::Harness UUID
EOF

View File

@ -25,9 +25,10 @@ echo "deb https://packages.centreon.com/ubuntu-plugins-unstable/ jammy main" | t
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
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 libstring-shellquote-perl
# 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 librrds-perl libdbi-perl libdbd-sqlite3-perl libhttp-daemon-perl libhttp-daemon-ssl-perl libnetaddr-ip-perl libmojolicious-perl libmojo-ioloop-signal-perl libnet-curl-perl libev-perl
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 librrds-perl libdbi-perl libdbd-sqlite3-perl libhttp-daemon-perl libhttp-daemon-ssl-perl libnetaddr-ip-perl libmojolicious-perl libmojo-ioloop-signal-perl libnet-curl-perl libev-perl libdigest-md5-file-perl
apt-get -y install git mariadb-client
apt-get clean
NONINTERACTIVE_TESTING=1 PERL_MM_USE_DEFAULT=1 cpan Test2::Harness UUID

View File

@ -25,9 +25,10 @@ echo "deb https://packages.centreon.com/ubuntu-plugins-unstable/ noble main" | t
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
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 libstring-shellquote-perl
# 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 librrds-perl libdbi-perl libdbd-sqlite3-perl libhttp-daemon-perl libhttp-daemon-ssl-perl libnetaddr-ip-perl libmojolicious-perl libmojo-ioloop-signal-perl libnet-curl-perl libev-perl
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 librrds-perl libdbi-perl libdbd-sqlite3-perl libhttp-daemon-perl libhttp-daemon-ssl-perl libnetaddr-ip-perl libmojolicious-perl libmojo-ioloop-signal-perl libnet-curl-perl libev-perl libdigest-md5-file-perl
apt-get -y install git mariadb-client
apt-get clean
NONINTERACTIVE_TESTING=1 PERL_MM_USE_DEFAULT=1 cpan Test2::Harness UUID

View File

@ -53,6 +53,7 @@ overrides:
perl(FindBin),
perl(lib),
perl(sort),
perl(String::ShellQuote),
@RPM_DEPENDENCIES@
]
conflicts:
@ -80,6 +81,7 @@ overrides:
libkeepass-reader-perl,
libdatetime-perl,
libxml-libxml-perl,
libstring-shellquote-perl,
@DEB_DEPENDENCIES@
]
conflicts:

View File

@ -66,6 +66,7 @@ foreach my $plugin (@plugins) {
File::Path::remove_tree('lib');
File::Path::make_path('lib');
my @common_files = (
'centreon/plugins/curllogger.pm',
'centreon/plugins/http.pm',
'centreon/plugins/misc.pm',
'centreon/plugins/mode.pm',

View File

@ -10,8 +10,8 @@ def get_tests_folders(plugin_name):
folder_list = []
pkg_file = open("./packaging/" + plugin_name + "/pkg.json")
packaging = json.load(pkg_file)
for file in packaging["files"]: # loop on "files" array in pkg.json file.
if os.path.isdir("tests/" + file): # check if the path is a directory in the "tests" folder
for file in packaging["files"]: # loop on "files" array in pkg.json file.
if os.path.isdir("tests/" + file): # check if the path is a directory in the "tests" folder
folder_list.append("tests/" + file)
return folder_list
@ -27,8 +27,10 @@ def test_plugin(plugin_name):
print(f"{plugin_name} folders_list : {folders_list}")
if len(folders_list) == 0:
return 0 # no tests present at the moment, but we still have tested the plugin can be installed.
robot_results = subprocess.run("robot --exclude notauto -v ''CENTREON_PLUGINS:" + get_plugin_full_path(plugin_name) + " " + " ".join(folders_list),
shell=True, check=False)
robot_results = subprocess.run(
"robot --exclude notauto -v ''CENTREON_PLUGINS:" + get_plugin_full_path(plugin_name) + " " + " ".join(
folders_list),
shell=True, check=False)
return robot_results.returncode
@ -52,12 +54,13 @@ def launch_snmp_sim():
snmpsim_cmd = "snmpsim-command-responder --logging-method=null --agent-udpv4-endpoint=127.0.0.1:2024 --process-user=snmp --process-group=snmp --data-dir='./tests' &"
try_command(cmd=snmpsim_cmd, error="can't launch snmp sim daemon.")
def refresh_packet_manager(archi):
with open('/var/log/robot-plugins-installation-tests.log', "a") as outfile:
if archi == "deb":
outfile.write("apt-get update\n")
output_status = (subprocess.run(
"apt-get update",
"apt-get update",
shell=True, check=False, stderr=subprocess.STDOUT, stdout=outfile)).returncode
elif archi == "rpm":
return 0
@ -66,17 +69,20 @@ def refresh_packet_manager(archi):
exit(1)
return output_status
def install_plugin(plugin, archi):
with open('/var/log/robot-plugins-installation-tests.log', "a") as outfile:
if archi == "deb":
outfile.write("apt-get install -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' -y ./" + plugin.lower() + "*.deb\n")
outfile.write(
"apt-get install -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' -y ./" + plugin.lower() + "*.deb\n")
output_status = (subprocess.run(
"apt-get install -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' -y ./" + plugin.lower() + "*.deb",
"apt-get install -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' -y ./" + plugin.lower() + "*.deb",
shell=True, check=False, stderr=subprocess.STDOUT, stdout=outfile)).returncode
elif archi == "rpm":
outfile.write("dnf install --setopt=keepcache=True -y ./" + plugin + "*.rpm\n")
output_status = (subprocess.run("dnf install --setopt=keepcache=True -y ./" + plugin + "*.rpm", shell=True, check=False,
stderr=subprocess.STDOUT, stdout=outfile)).returncode
output_status = (
subprocess.run("dnf install --setopt=keepcache=True -y ./" + plugin + "*.rpm", shell=True, check=False,
stderr=subprocess.STDOUT, stdout=outfile)).returncode
else:
print(f"Unknown architecture, expected deb or rpm, got {archi}. Exiting.")
exit(1)
@ -86,17 +92,19 @@ def install_plugin(plugin, archi):
def remove_plugin(plugin, archi):
with open('/var/log/robot-plugins-installation-tests.log', "a") as outfile:
if archi == "deb":
outfile.write("apt-get -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' autoremove -y " + plugin.lower() + "\n")
outfile.write(
"export SUDO_FORCE_REMOVE=yes; apt-get -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' autoremove -y "
+ plugin.lower() + "\n")
output_status = (subprocess.run(
"apt-get -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' autoremove -y " + plugin.lower(),
"export SUDO_FORCE_REMOVE=yes; apt-get -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' autoremove -y " + plugin.lower(),
shell=True, check=False, stderr=subprocess.STDOUT, stdout=outfile)).returncode
# -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' is an option to force apt to keep the package in
# /var/cache/apt/archives, so it do not re download them for every installation.
# 'autoremove', contrary to 'remove' all dependancy while removing the original package.
elif archi == "rpm":
outfile.write("dnf remove --setopt=keepcache=True -y " + plugin + "\n")
output_status = (subprocess.run("dnf remove --setopt=keepcache=True -y " + plugin, shell=True, check=False,
outfile.write("dnf remove --setopt=protected_packages= --setopt=keepcache=True -y " + plugin + "\n")
output_status = (subprocess.run("dnf remove --setopt=protected_packages= --setopt=keepcache=True -y " + plugin, shell=True, check=False,
stderr=subprocess.STDOUT, stdout=outfile)).returncode
else:
print(f"Unknown architecture, expected deb or rpm, got {archi}. Exiting.")
@ -135,7 +143,7 @@ if __name__ == '__main__':
print("plugin : ", plugin)
folders_list = get_tests_folders(plugin)
if len(folders_list) == 0:
print(f"we don't test {plugin} as it don't have any robots tests.")
print(f"we don't test {plugin} as it doesn't have any robot tests.")
continue
nb_plugins += 1
@ -153,7 +161,7 @@ if __name__ == '__main__':
error_purge += tmp
print(f"{nb_plugins} plugins tested.\n there was {error_install} installation error, {error_tests} test "
f"errors, and {error_purge} removal error list of error : {list_plugin_error}",)
f"errors, and {error_purge} removal error list of error : {list_plugin_error}", )
if error_install != 0 or error_tests != 0 or error_purge != 0:
exit(1)

View File

@ -0,0 +1,128 @@
name: centreon-plugins-sudoers
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
on:
workflow_dispatch:
pull_request:
paths:
- "dependencies/centreon-plugins-sudoers/**"
push:
branches:
- develop
- master
paths:
- "dependencies/centreon-plugins-sudoers/**"
env:
module_name: centreon-plugins-sudoers
jobs:
get-environment:
uses: ./.github/workflows/get-environment.yml
package:
needs: [get-environment]
if: |
needs.get-environment.outputs.skip_workflow == 'false' &&
needs.get-environment.outputs.stability != 'stable'
strategy:
fail-fast: false
matrix:
include:
- image: packaging-plugins-alma8
distrib: el8
package_extension: rpm
- image: packaging-plugins-alma9
distrib: el9
package_extension: rpm
- image: packaging-plugins-bullseye
distrib: bullseye
package_extension: deb
- image: packaging-plugins-bookworm
distrib: bookworm
package_extension: deb
- image: packaging-plugins-jammy
distrib: jammy
package_extension: deb
- image: packaging-plugins-noble
distrib: noble
package_extension: deb
runs-on: ubuntu-24.04
container:
image: ${{ vars.DOCKER_INTERNAL_REGISTRY_URL }}/${{ matrix.image }}:latest
credentials:
username: ${{ secrets.HARBOR_CENTREON_PULL_USERNAME }}
password: ${{ secrets.HARBOR_CENTREON_PULL_TOKEN }}
name: package ${{ matrix.distrib }}
steps:
- name: Checkout sources
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Package
uses: ./.github/actions/package-nfpm
with:
nfpm_file_pattern: "dependencies/centreon-plugins-sudoers/centreon-plugins-sudoers.yaml"
distrib: ${{ matrix.distrib }}
package_extension: ${{ matrix.package_extension }}
version: ${{ needs.get-environment.outputs.version }}
release: ${{ needs.get-environment.outputs.release }}
arch: all
commit_hash: ${{ github.sha }}
cache_key: cache-${{ github.run_id }}-${{ matrix.package_extension }}-${{ env.module_name }}-${{ matrix.distrib }}
rpm_gpg_key: ${{ secrets.RPM_GPG_SIGNING_KEY }}
rpm_gpg_signing_key_id: ${{ secrets.RPM_GPG_SIGNING_KEY_ID }}
rpm_gpg_signing_passphrase: ${{ secrets.RPM_GPG_SIGNING_PASSPHRASE }}
stability: ${{ needs.get-environment.outputs.stability }}
deliver-packages:
needs: [get-environment, package]
if: |
(contains(fromJson('["testing", "unstable"]'), needs.get-environment.outputs.stability) || ( needs.get-environment.outputs.stability == 'stable' && github.event_name != 'workflow_dispatch'))
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
include:
- distrib: el8
package_extension: rpm
- distrib: el9
package_extension: rpm
- distrib: bullseye
package_extension: deb
- distrib: bookworm
package_extension: deb
- distrib: jammy
package_extension: deb
- distrib: noble
package_extension: deb
name: deliver ${{ matrix.distrib }}
steps:
- name: Checkout sources
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Delivery
uses: ./.github/actions/package-delivery
with:
module_name: ${{ env.module_name }}
distrib: ${{ matrix.distrib }}
cache_key: cache-${{ github.run_id }}-${{ matrix.package_extension }}-${{ env.module_name }}-${{ matrix.distrib }}
stability: ${{ needs.get-environment.outputs.stability }}
release_type: ${{ needs.get-environment.outputs.release_type }}
artifactory_token: ${{ secrets.ARTIFACTORY_ACCESS_TOKEN }}
set-skip-label:
needs: [get-environment, deliver-packages]
if: |
needs.get-environment.outputs.skip_workflow == 'false' &&
! cancelled() &&
! contains(needs.*.result, 'failure') &&
! contains(needs.*.result, 'cancelled')
uses: ./.github/workflows/set-pull-request-skip-label.yml

View File

@ -21,43 +21,43 @@ on:
required: true
jobs:
build:
name: Binary preparation
pipeline-scan:
name: Run a pipeline scan
runs-on: ubuntu-24.04
outputs:
enable_analysis: ${{ steps.routing.outputs.enable_analysis }}
steps:
- name: routing
id: routing
run: |
# Quality gate settings
ENABLE_QG="true"
if [[ "${{ vars.CHECKMARX_QUALITY_GATE }}" != "true" ]]; then
if [[ "${{ vars.CHECKMARX_QUALITY_GATE }}" != "yes" ]]; then
# disabling all QG in case of incident with the service
ENABLE_QG="false"
echo "Skipping analysis. Caused by QG override"
elif [[ "${{ github.event_name }}" == "pull_request" && "${{ github.event.pull_request.user.id }}" == "49699333" ]]; then
# disabling the QG in case of pull request opened by dependabot bot
# As dependabot will ne be able to access GH secrets
# As dependabot will not be able to access GH secrets
ENABLE_QG="false"
echo "Skipping analysis. Caused by dependabot PR"
fi
echo "enable_analysis=$ENABLE_QG" >> $GITHUB_OUTPUT
cat $GITHUB_OUTPUT
pipeline-scan:
needs: [build]
name: Run a pipeline scan
runs-on: ubuntu-24.04
if: needs.build.outputs.enable_analysis == 'true'
# Check forced full scan
SCAN_MODE="--sast-incremental"
if [[ "${{ github.event_name }}" == "schedule" || "${{ vars.CHECKMARX_FORCE_FULL_SCAN }}" == "yes" ]]; then
SCAN_MODE="--sast-incremental=false"
fi
echo "scan_mode=$SCAN_MODE" >> $GITHUB_ENV
echo "enable_analysis=$ENABLE_QG" >> $GITHUB_ENV
cat $GITHUB_ENV
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
if: env.enable_analysis == 'true'
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Checkmarx One CLI Action
uses: checkmarx/ast-github-action@ef93013c95adc60160bc22060875e90800d3ecfc # v.2.3.19
if: env.enable_analysis == 'true'
uses: checkmarx/ast-github-action@88c60148b7b9689d67eb05bf66a65bbb871f2f2c # v2.3.20
with:
project_name: ${{ inputs.module_name }}
base_uri: ${{ secrets.base_uri }}
@ -65,4 +65,4 @@ jobs:
cx_client_id: ${{ secrets.cx_client_id }}
cx_client_secret: ${{ secrets.cx_client_secret }}
source_dir: "./"
additional_params: --file-filter "!**/.githooks/**" --scan-types "sast,sca,api-security"
additional_params: --scan-types "sast,sca,api-security" ${{ env.scan_mode }}

View File

@ -90,9 +90,9 @@ jobs:
username: ${{ secrets.HARBOR_CENTREON_PUSH_USERNAME }}
password: ${{ secrets.HARBOR_CENTREON_PUSH_TOKEN }}
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
- uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
file: .github/docker/packaging/Dockerfile.${{ matrix.dockerfile }}
context: .

View File

@ -73,9 +73,9 @@ jobs:
username: ${{ secrets.HARBOR_CENTREON_PUSH_USERNAME }}
password: ${{ secrets.HARBOR_CENTREON_PUSH_TOKEN }}
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
- uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
file: .github/docker/testing/Dockerfile.testing-plugins-${{ matrix.dockerfile }}
context: .

View File

@ -72,9 +72,9 @@ jobs:
username: ${{ secrets.HARBOR_CENTREON_PUSH_USERNAME }}
password: ${{ secrets.HARBOR_CENTREON_PUSH_TOKEN }}
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
- uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
file: .github/docker/unit-tests/Dockerfile.unit-tests-${{ matrix.dockerfile }}
context: .

23
.github/workflows/generic-plugins.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: Generic Plugins
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
on:
workflow_dispatch:
pull_request:
paths:
- 'experimental/**'
push:
branches:
- develop
- master
paths:
- 'experimental/**'
jobs:
get-environment:
uses: ./.github/workflows/get-environment.yml
with:
version_file: experimental/Cargo.toml

View File

@ -4,6 +4,9 @@ on:
version_file:
required: false
type: string
nightly_manual_trigger:
required: false
type: boolean
outputs:
version:
description: "version"
@ -17,6 +20,9 @@ on:
target_stability:
description: "Final target branch stability (stable, testing, unstable, canary or not defined if not a pull request)"
value: ${{ jobs.get-environment.outputs.target_stability }}
is_nightly:
description: "if the current workflow run is considered a nightly"
value: ${{ jobs.get-environment.outputs.is_nightly }}
release_type:
description: "type of release (hotfix, release or not defined if not a release)"
value: ${{ jobs.get-environment.outputs.release_type }}
@ -40,6 +46,7 @@ jobs:
target_stability: ${{ steps.get_stability.outputs.target_stability }}
release_type: ${{ steps.get_release_type.outputs.release_type }}
is_targeting_feature_branch: ${{ steps.get_stability.outputs.is_targeting_feature_branch }}
is_nightly: ${{ steps.get_nightly_status.outputs.is_nightly }}
skip_workflow: ${{ steps.skip_workflow.outputs.result }}
labels: ${{ steps.has_skip_label.outputs.labels }}
@ -244,6 +251,26 @@ jobs:
core.setOutput('is_targeting_feature_branch', isTargetingFeatureBranch);
- name: Detect nightly status
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
id: get_nightly_status
env:
NIGHTLY_MANUAL_TRIGGER: ${{ inputs.nightly_manual_trigger }}
with:
script: |
const getNightlyInput = () => {
const nightly_manual_trigger = process.env.NIGHTLY_MANUAL_TRIGGER;
console.log(nightly_manual_trigger);
if (typeof nightly_manual_trigger === 'undefined' || nightly_manual_trigger === '' || '${{ github.repository }}'.match(/^workflow-.*$/)) {
return 'false';
} else if (context.eventName === 'schedule' || context.eventName === 'workflow_dispatch' && nightly_manual_trigger === 'true' ) {
return 'true';
}
return 'false';
};
core.setOutput('is_nightly', getNightlyInput());
- name: Get version
id: get_version
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
@ -342,6 +369,7 @@ jobs:
['release_type', '${{ steps.get_release_type.outputs.release_type || '<em>not defined because this is not a release</em>' }}'],
['is_targeting_feature_branch', '${{ steps.get_stability.outputs.is_targeting_feature_branch }}'],
['target_stability', '${{ steps.get_stability.outputs.target_stability || '<em>not defined because current run is not triggered by pull request event</em>' }}'],
['is_nightly', '${{ steps.get_nightly_status.outputs.is_nightly }}'],
['skip_workflow', '${{ steps.skip_workflow.outputs.result }}'],
['labels', '${{ steps.has_skip_label.outputs.labels }}'],
];

View File

@ -1,4 +1,9 @@
name: gitleaks
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
on:
pull_request:
workflow_dispatch:
@ -15,7 +20,7 @@ jobs:
- uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2.3.9
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_LICENSE: "Centreon"
GITLEAKS_LICENSE: Centreon
GITLEAKS_ENABLE_COMMENTS: false
GITLEAKS_ENABLE_UPLOAD_ARTIFACT: false
GITLEAKS_ENABLE_SUMMARY: false

View File

@ -417,7 +417,7 @@ jobs:
arch: arm64
runner_name: centreon-collect-arm64
- name: "Crypt::Argon2"
build_names: "bullseye-amd64,jammy,noble,bullseye-arm64"
build_names: "bullseye-amd64,jammy,bullseye-arm64"
preinstall_cpanlibs: "Dist::Build"
use_dh_make_perl: "false"
no-auto-depends: "true"
@ -430,11 +430,13 @@ jobs:
no-auto-depends: "true"
build_names: "bullseye-amd64,bookworm,jammy,noble,bullseye-arm64"
- name: "Device::Modbus::RTU::Client"
build_names: "bookworm"
build_names: "bookworm,noble"
- name: "Device::Modbus::TCP::Client"
build_names: "bookworm"
build_names: "bookworm,noble"
- name: "Digest::SHA1"
build_names: "jammy,noble"
- name: "Hash::Ordered"
build_names: "bullseye-amd64,bookworm,jammy"
- name: "Libssh::Session"
use_dh_make_perl: "false"
build_names: "bullseye-amd64,bookworm,jammy,noble,bullseye-arm64"
@ -443,7 +445,7 @@ jobs:
deb_provides: "libssh-session-perl-dbgsym libssh-session-sftp"
revision: "2"
- name: "Net::Amazon::Signature::V4"
build_names: ["bullseye-amd64", "jammy", "noble"]
build_names: "bullseye-amd64,jammy"
- name: "Net::Curl"
use_dh_make_perl: "false"
build_names: "bullseye-amd64,bookworm,jammy,noble,bullseye-arm64"
@ -453,6 +455,7 @@ jobs:
revision: "2"
- name: "Net::MQTT::Simple"
version: "1.29"
build_names: "bullseye-amd64,bookworm,jammy"
- name: "Paws"
use_dh_make_perl: "false"
deb_dependencies: "libmoose-perl libmoosex-classattribute-perl libjson-maybexs-perl liburl-encode-perl libargv-struct-perl libmoo-perl libtype-tiny-perl libdatastruct-flat-perl libmodule-find-perl libthrowable-perl liburi-template-perl libnet-amazon-signature-v4-perl"
@ -692,6 +695,7 @@ jobs:
runner_name: ubuntu-24.04
- package_extension: deb
image: ubuntu:noble
distrib: noble
arch: amd64
runner_name: ubuntu-24.04
- package_extension: deb

View File

@ -1,4 +1,5 @@
name: plugins
run-name: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.nightly_manual_trigger == 'true')) && format('plugins nightly {0}', github.ref_name) || '' }}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@ -6,6 +7,14 @@ concurrency:
on:
workflow_dispatch:
inputs:
nightly_manual_trigger:
description: 'Set to true to trigger a nightly run'
required: true
default: false
type: boolean
schedule:
- cron: "30 1 * * 1"
pull_request:
paths:
- '.github/workflows/plugins.yml'
@ -27,9 +36,42 @@ on:
jobs:
get-environment:
uses: ./.github/workflows/get-environment.yml
with:
nightly_manual_trigger: ${{ inputs.nightly_manual_trigger || false }}
changes:
needs: [get-environment]
runs-on: ubuntu-24.04
outputs:
changes_common: ${{ steps.filter.outputs.common || 'true' }}
changes_packages: ${{ steps.filter.outputs.packages || 'false' }}
changes_plugins: ${{ steps.filter.outputs.plugins || 'false' }}
packages_files: ${{ steps.filter.outputs.packages_files }}
plugins_files: ${{ steps.filter.outputs.plugins_files }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
if: |
github.event_name == 'pull_request' &&
contains(fromJson('["testing", "unstable", "canary"]'), needs.get-environment.outputs.stability)
with:
base: ${{ github.head_ref || github.ref_name }}
list-files: shell
filters: |
common:
- added|deleted|modified: src/centreon/**
- modified: .github/packaging/centreon-plugin.yaml.template
packages:
- added|modified: packaging/**
plugins:
- added|modified: src/**
get-plugins:
runs-on: ubuntu-24.04
needs: [get-environment, changes]
outputs:
plugins: ${{ steps.get_plugins.outputs.plugins }}
steps:
@ -41,24 +83,10 @@ jobs:
with:
python-version: '3.9'
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
base: ${{ github.ref }}
list-files: shell
filters: |
common:
- added|deleted|modified: src/centreon/**
- modified: .github/packaging/centreon-plugin.yaml.template
packages:
- added|modified: packaging/**
plugins:
- added|modified: src/**
- name: transform to directories
run: |
folders=()
for f in ${{ steps.filter.outputs.packages_files }}; do
for f in ${{ needs.changes.outputs.packages_files }}; do
echo "Adding $(dirname $f) to folders"
folders+=($(dirname $f))
done
@ -66,7 +94,7 @@ jobs:
jq --compact-output --null-input '$ARGS.positional' --args -- ${unique_folders[@]} > package_directories.txt
files=()
for f in ${{ steps.filter.outputs.plugins_files }}; do
for f in ${{ needs.changes.outputs.plugins_files }}; do
echo "Adding $f to files"
files+=($f)
done
@ -76,9 +104,9 @@ jobs:
- name: Get plugins for build
id: get_plugins
if: ${{ steps.filter.outputs.common == 'true' || steps.filter.outputs.packages == 'true' || steps.filter.outputs.plugins == 'true' }}
if: ${{ needs.changes.outputs.changes_common == 'true' || needs.changes.outputs.changes_packages == 'true' || needs.changes.outputs.changes_plugins == 'true' }}
run: |
PLUGINS="$(python3 .github/scripts/process-plugins.py '${{ steps.filter.outputs.common == 'true' }}')"
PLUGINS="$(python3 .github/scripts/process-plugins.py '${{ needs.changes.outputs.changes_common == 'true' }}')"
echo "plugins=$(echo $PLUGINS)" >> $GITHUB_OUTPUT
if [ "$PLUGINS" == '' ]; then
@ -87,6 +115,20 @@ jobs:
shell: bash
- name: Create Jira ticket on nightly build failure
if: |
needs.get-environment.outputs.is_nightly == 'true' && github.run_attempt == 1 &&
failure() &&
startsWith(github.ref_name, 'dev')
uses: ./.github/actions/create-jira-ticket
with:
jira_base_url: ${{ secrets.JIRA_BASE_URL }}
jira_user_email: ${{ secrets.XRAY_JIRA_USER_EMAIL }}
jira_api_token: ${{ secrets.XRAY_JIRA_TOKEN }}
module_name: "monitoring-plugins"
ticket_labels: '["Nightly", "Pipeline", "nightly-${{ github.ref_name }}", "${{ github.job }}"]'
ticket_squad: "DevSecOps"
unit-tests:
needs: [get-environment, get-plugins]
if: |
@ -146,6 +188,20 @@ jobs:
path: ./lastlog.jsonl
retention-days: 1
- name: Create Jira ticket on nightly build failure
if: |
needs.get-environment.outputs.is_nightly == 'true' && github.run_attempt == 1 &&
failure() &&
startsWith(github.ref_name, 'dev')
uses: ./.github/actions/create-jira-ticket
with:
jira_base_url: ${{ secrets.JIRA_BASE_URL }}
jira_user_email: ${{ secrets.XRAY_JIRA_USER_EMAIL }}
jira_api_token: ${{ secrets.XRAY_JIRA_TOKEN }}
module_name: "monitoring-plugins"
ticket_labels: '["Nightly", "Pipeline", "nightly-${{ github.ref_name }}", "${{ github.job }}"]'
ticket_squad: "Connectors"
fatpacker:
needs: [get-environment, get-plugins, unit-tests]
if: |
@ -305,6 +361,20 @@ jobs:
rpm_gpg_signing_passphrase: ${{ secrets.RPM_GPG_SIGNING_PASSPHRASE }}
stability: ${{ needs.get-environment.outputs.stability }}
- name: Create Jira ticket on nightly build failure
if: |
needs.get-environment.outputs.is_nightly == 'true' && github.run_attempt == 1 &&
failure() &&
startsWith(github.ref_name, 'dev')
uses: ./.github/actions/create-jira-ticket
with:
jira_base_url: ${{ secrets.JIRA_BASE_URL }}
jira_user_email: ${{ secrets.XRAY_JIRA_USER_EMAIL }}
jira_api_token: ${{ secrets.XRAY_JIRA_TOKEN }}
module_name: "monitoring-plugins"
ticket_labels: '["Nightly", "Pipeline", "nightly-${{ github.ref_name }}", "${{ github.job }}"]'
ticket_squad: "DevSecOps"
test-plugins:
needs: [get-environment, get-plugins, package]
if: |
@ -368,6 +438,20 @@ jobs:
path: /var/log/robot-plugins-installation-tests.log
retention-days: 1
- name: Create Jira ticket on nightly build failure
if: |
needs.get-environment.outputs.is_nightly == 'true' && github.run_attempt == 1 &&
failure() &&
startsWith(github.ref_name, 'dev')
uses: ./.github/actions/create-jira-ticket
with:
jira_base_url: ${{ secrets.JIRA_BASE_URL }}
jira_user_email: ${{ secrets.XRAY_JIRA_USER_EMAIL }}
jira_api_token: ${{ secrets.XRAY_JIRA_TOKEN }}
module_name: "monitoring-plugins"
ticket_labels: '["Nightly", "Pipeline", "nightly-${{ github.ref_name }}", "${{ github.job }}"]'
ticket_squad: "Connectors"
deliver-packages:
needs: [get-environment, get-plugins, test-plugins]
if: |
@ -412,6 +496,20 @@ jobs:
release_type: ${{ needs.get-environment.outputs.release_type }}
artifactory_token: ${{ secrets.ARTIFACTORY_ACCESS_TOKEN }}
- name: Create Jira ticket on nightly build failure
if: |
needs.get-environment.outputs.is_nightly == 'true' && github.run_attempt == 1 &&
failure() &&
startsWith(github.ref_name, 'dev')
uses: ./.github/actions/create-jira-ticket
with:
jira_base_url: ${{ secrets.JIRA_BASE_URL }}
jira_user_email: ${{ secrets.XRAY_JIRA_USER_EMAIL }}
jira_api_token: ${{ secrets.XRAY_JIRA_TOKEN }}
module_name: "monitoring-plugins"
ticket_labels: '["Nightly", "Pipeline", "nightly-${{ github.ref_name }}", "${{ github.job }}"]'
ticket_squad: "DevSecOps"
deliver-sources:
needs: [get-environment, fatpacker]
if: |

View File

@ -1 +1 @@
20250303
20250800

View File

@ -0,0 +1,40 @@
name: "centreon-plugins-sudoers"
arch: "all"
platform: "linux"
version_schema: "none"
version: "${VERSION}"
release: "${RELEASE}${DIST}"
section: "default"
priority: "optional"
maintainer: "Centreon <contact@centreon.com>"
description: |
Sudoers configuration for centreon plugins
Commit: @COMMIT_HASH@
vendor: "Centreon"
homepage: "https://www.centreon.com"
license: "Apache-2.0"
contents:
- src: ./sudoersCentreonPlugins
dst: /etc/sudoers.d/centreon-plugins
file_info:
mode: 0600
overrides:
rpm:
provides:
- centreon-cwrapper-perl
replaces:
- centreon-cwrapper-perl
depends:
- sudo
deb:
depends:
- sudo
rpm:
summary: Sudoers configuration for centreon plugins
compression: zstd
signature:
key_file: ${RPM_SIGNING_KEY_FILE}
key_id: ${RPM_SIGNING_KEY_ID}

View File

@ -0,0 +1,6 @@
User_Alias CENTREON_COLLECT_USERS=centreon-engine,centreon-gorgone
Defaults:CENTREON_COLLECT_USERS !requiretty
CENTREON_COLLECT_USERS ALL = NOPASSWD: /usr/lib/centreon/plugins/centreon_protocol_dhcp.pl *
CENTREON_COLLECT_USERS ALL = NOPASSWD: /usr/lib/centreon/plugins/centreon_protocol_udp.pl *
CENTREON_COLLECT_USERS ALL = NOPASSWD: /usr/lib/centreon/plugins/centreon_nmap_cli.pl *

View File

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

View File

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

View File

@ -0,0 +1,9 @@
{
"pkg_name": "centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi",
"pkg_summary": "Centreon Plugin",
"plugin_name": "centreon_hpe_alletra_restapi.pl",
"files": [
"centreon/plugins/script_custom.pm",
"storage/hp/alletra/restapi/"
]
}

View File

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

View File

@ -8,10 +8,10 @@
"snmp_standard/mode/cpudetailed.pm",
"snmp_standard/mode/interfaces.pm",
"snmp_standard/mode/listinterfaces.pm",
"snmp_standard/mode/resources/",
"snmp_standard/mode/resources/",
"snmp_standard/mode/memory.pm",
"snmp_standard/mode/swap.pm",
"network/juniper/common/ive",
"network/juniper/sa"
"network/juniper/common/ive/snmp",
"network/juniper/sa/snmp"
]
}

View File

@ -7,10 +7,10 @@
"centreon/plugins/snmp.pm",
"snmp_standard/mode/interfaces.pm",
"snmp_standard/mode/listinterfaces.pm",
"snmp_standard/mode/resources/",
"snmp_standard/mode/resources/",
"snmp_standard/mode/liststorages.pm",
"snmp_standard/mode/storage.pm",
"network/juniper/common/junos",
"network/juniper/srx"
"network/juniper/common/junos/snmp",
"network/juniper/srx/snmp"
]
}

View File

@ -7,8 +7,8 @@
"centreon/plugins/snmp.pm",
"snmp_standard/mode/interfaces.pm",
"snmp_standard/mode/listinterfaces.pm",
"snmp_standard/mode/resources/",
"network/juniper/common/screenos",
"network/juniper/ssg"
"snmp_standard/mode/resources/",
"network/juniper/common/screenos/snmp",
"network/juniper/ssg/snmp"
]
}

View File

@ -5,6 +5,6 @@
"files": [
"centreon/plugins/script_snmp.pm",
"centreon/plugins/snmp.pm",
"network/juniper/ggsn/"
"network/juniper/ggsn/snmp/"
]
}

View File

@ -7,7 +7,7 @@
"centreon/plugins/snmp.pm",
"snmp_standard/mode/interfaces.pm",
"snmp_standard/mode/listinterfaces.pm",
"snmp_standard/mode/resources/",
"snmp_standard/mode/resources/",
"network/juniper/common/screenos/snmp/",
"network/juniper/isg/snmp/"
]

View File

@ -7,10 +7,10 @@
"centreon/plugins/snmp.pm",
"snmp_standard/mode/interfaces.pm",
"snmp_standard/mode/listinterfaces.pm",
"snmp_standard/mode/resources/",
"snmp_standard/mode/resources/",
"snmp_standard/mode/memory.pm",
"snmp_standard/mode/swap.pm",
"network/juniper/common/ive",
"network/juniper/mag"
"network/juniper/common/ive/snmp",
"network/juniper/mag/snmp"
]
}

View File

@ -0,0 +1,7 @@
{
"dependencies": [
"plink",
"libssh-session-perl",
"libxml-libxml-simple-perl"
]
}

View File

@ -0,0 +1,13 @@
{
"pkg_name": "centreon-plugin-Network-Routers-Juniper-Mseries-Netconf",
"pkg_summary": "Centreon Plugin",
"plugin_name": "centreon_juniper_mseries_netconf.pl",
"files": [
"centreon/plugins/script_custom.pm",
"centreon/plugins/backend/ssh/",
"centreon/plugins/ssh.pm",
"network/juniper/common/junos/netconf/custom/",
"network/juniper/common/junos/netconf/mode/",
"network/juniper/mseries/netconf/"
]
}

View File

@ -0,0 +1,7 @@
{
"dependencies": [
"plink",
"perl(Libssh::Session)",
"perl(XML::LibXML::Simple)"
]
}

View File

@ -1,16 +1,16 @@
{
"pkg_name": "centreon-plugin-Network-Routers-Juniper-Mseries-Snmp",
"pkg_summary": "Centreon Plugin",
"plugin_name": "centreon_juniper_mseries.pl",
"plugin_name": "centreon_juniper_mseries_snmp.pl",
"files": [
"centreon/plugins/script_snmp.pm",
"centreon/plugins/snmp.pm",
"snmp_standard/mode/interfaces.pm",
"snmp_standard/mode/listinterfaces.pm",
"snmp_standard/mode/resources/",
"snmp_standard/mode/resources/",
"snmp_standard/mode/liststorages.pm",
"snmp_standard/mode/storage.pm",
"network/juniper/common/junos/",
"network/juniper/mseries/"
"network/juniper/common/junos/snmp/",
"network/juniper/mseries/snmp/"
]
}

View File

@ -7,10 +7,10 @@
"centreon/plugins/snmp.pm",
"snmp_standard/mode/interfaces.pm",
"snmp_standard/mode/listinterfaces.pm",
"snmp_standard/mode/resources/",
"snmp_standard/mode/resources/",
"snmp_standard/mode/liststorages.pm",
"snmp_standard/mode/storage.pm",
"network/juniper/common/junos/",
"network/juniper/ex/"
"network/juniper/common/junos/snmp/",
"network/juniper/ex/snmp/"
]
}

View File

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

View File

@ -0,0 +1,10 @@
{
"pkg_name": "centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi",
"pkg_summary": "Centreon Plugin to monitor VMware vCenter using vSphere 8 REST API",
"plugin_name": "centreon_vmware8_vcenter_restapi.pl",
"files": [
"centreon/plugins/script_custom.pm",
"apps/vmware/vsphere8/vcenter/",
"apps/vmware/vsphere8/custom/"
]
}

View File

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

View File

@ -20,7 +20,7 @@
package apps::apache::serverstatus::mode::requests;
use base qw(centreon::plugins::mode);
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
@ -30,7 +30,7 @@ use centreon::plugins::misc;
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
@ -43,22 +43,23 @@ sub new {
'username:s' => { name => 'username' },
'password:s' => { name => 'password' },
'header:s@' => { name => 'header' },
'warning:s' => { name => 'warning' },
'critical:s' => { name => 'critical' },
'warning-bytes:s' => { name => 'warning_bytes' },
'critical-bytes:s' => { name => 'critical_bytes' },
'warning-access:s' => { name => 'warning_access' },
'critical-access:s' => { name => 'critical_access' },
'warning:s' => { name => 'warning', redirect => 'warning-apache-request-average-persecond' },
'critical:s' => { name => 'critical', redirect => 'critical-apache-request-average-persecond' },
'warning-bytes:s' => { name => 'warning_bytes', redirect => 'warning-apache-bytes-persecond' },
'critical-bytes:s' => { name => 'critical_bytes', redirect => 'critical-apache-bytes-persecond' },
'warning-access:s' => { name => 'warning_access', redirect => 'warning-apache-access-persecond' },
'critical-access:s' => { name => 'critical_access', redirect => 'critical-apache-access-persecond' },
'timeout:s' => { name => 'timeout' },
});
$self->{http} = centreon::plugins::http->new(%options);
$self->{statefile_value} = centreon::plugins::statefile->new(%options);
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
$self->SUPER::check_options(%options);
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} . "'.");
@ -86,13 +87,97 @@ sub check_options {
}
$self->{http}->set_options(%{$self->{option_results}});
$self->{statefile_value}->check_options(%options);
}
sub run {
sub custom_bytes_persecond_calc {
my ($self, %options) = @_;
my $webcontent = $self->{http}->request();
unless (defined $options{old_datas}->{global_total_bytes}) {
$self->{error_msg} = "Buffer creation";
return -1;
}
my $delta_time = $options{delta_time} || 1;
my $old_total_bytes = $options{old_datas}->{global_total_bytes} || 0;
$old_total_bytes = 0 if $old_total_bytes > $options{new_datas}->{global_total_bytes};
$self->{result_values}->{bPerSec} = ($options{new_datas}->{global_total_bytes} - $old_total_bytes) / $delta_time;
return 0;
}
sub custom_access_persecond_calc {
my ($self, %options) = @_;
unless (defined $options{old_datas}->{global_total_access}) {
$self->{error_msg} = "Buffer creation";
return -1;
}
my $delta_time = $options{delta_time} || 1;
my $old_total_access = $options{old_datas}->{global_total_access} || 0;
$old_total_access = 0 if $old_total_access > $options{new_datas}->{global_total_access};
$self->{result_values}->{aPerSec} = ($options{new_datas}->{global_total_access} - $old_total_access) / $delta_time;
return 0;
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0, cb_prefix_output => 'prefix_global_output', askipped_code => { -2 => 1 } }
];
$self->{maps_counters}->{global} = [
{ label => 'bytesPerSec', nlabel => 'apache.bytes.persecond',
set => {
key_values => [ { name => 'bPerSec' }, { name => 'total_bytes' } ],
output_template => 'BytesPerSec: %.2f %s',
output_change_bytes => 1,
closure_custom_calc => $self->can('custom_bytes_persecond_calc'),
perfdatas => [ { template => '%.2f', min => 0, unit => 'B' } ]
}
},
{ label => 'accessPerSec', nlabel => 'apache.access.persecond',
set => {
key_values => [ { name => 'aPerSec' }, { name => 'total_access' } ],
closure_custom_calc => $self->can('custom_access_persecond_calc'),
output_template => 'AccessPerSec: %.2f',
perfdatas => [ { template => '%.2f', min => 0, } ]
}
},
{ label => 'avg_RequestPerSec', nlabel => 'apache.request.average.persecond',
set => {
key_values => [ { name => 'rPerSec' } ],
output_template => 'RequestPerSec: %.2f',
perfdatas => [ { template => '%.2f', min => 0, } ]
}
},
{ label => 'avg_bytesPerRequest', nlabel => 'apache.bytes.average.perrequest',
set => {
key_values => [ { name => 'bPerReq' } ],
output_change_bytes => 1,
output_template => 'BytesPerRequest: %.2f %s',
perfdatas => [ { template => '%.2f', min => 0, unit => 'B', } ]
}
},
{ label => 'avg_bytesPerSec', nlabel => 'apache.bytes.average.persecond',
display_ok => 0,
set => {
key_values => [ { name => 'avg_bPerSec' } ],
perfdatas => [ { min => 0, unit => 'B' } ]
}
},
];
}
sub manage_selection {
my ($self, %options) = @_;
my ($webcontent) = $self->{http}->request();
#Total accesses: 7323 - Total Traffic: 243.7 MB - Total Duration: 7175675
#CPU Usage: u1489.98 s1118.39 cu0 cs0 - .568% CPU load
@ -105,7 +190,6 @@ sub run {
if ($webcontent =~ /Total\s+Traffic:\s+(\S+)\s+(.|)B\s+/mi) {
$total_bytes = centreon::plugins::misc::convert_bytes(value => $1, unit => $2 . 'B');
}
$rPerSec = $1 if ($webcontent =~ /^ReqPerSec:\s+([^\s]+)/mi);
if ($webcontent =~ /^(\S+)\s+requests\/sec/mi) {
$rPerSec = $1;
@ -127,79 +211,23 @@ sub run {
$self->{output}->add_option_msg(short_msg => "Apache 'ExtendedStatus' option is off.");
$self->{output}->option_exit();
}
$rPerSec = '0' . $rPerSec if ($rPerSec =~ /^\./);
$avg_bPerSec = '0' . $avg_bPerSec if ($avg_bPerSec =~ /^\./);
$bPerReq = '0' . $bPerReq if ($bPerReq =~ /^\./);
$self->{statefile_value}->read(statefile => 'apache_' . $self->{option_results}->{hostname} . '_' . $self->{http}->get_port() . '_' . $self->{mode});
my $old_timestamp = $self->{statefile_value}->get(name => 'last_timestamp');
my $old_total_access = $self->{statefile_value}->get(name => 'total_access');
my $old_total_bytes = $self->{statefile_value}->get(name => 'total_bytes');
my $new_datas = {};
$new_datas->{last_timestamp} = time();
$new_datas->{total_bytes} = $total_bytes;
$new_datas->{total_access} = $total_access;
$self->{statefile_value}->write(data => $new_datas);
if (!defined($old_timestamp) || !defined($old_total_access)) {
$self->{output}->output_add(severity => 'OK',
short_msg => "Buffer creation...");
$self->{output}->display();
$self->{output}->exit();
}
$old_total_access = 0 if ($old_total_access > $new_datas->{total_access});
$old_total_bytes = 0 if ($old_total_bytes > $new_datas->{total_bytes});
my $delta_time = $new_datas->{last_timestamp} - $old_timestamp;
$delta_time = 1 if ($delta_time == 0); # One seconds ;)
my $bPerSec = ($new_datas->{total_bytes} - $old_total_bytes) / $delta_time;
my $aPerSec = ($new_datas->{total_access} - $old_total_access) / $delta_time;
my $exit1 = $self->{perfdata}->threshold_check(value => $rPerSec, threshold => [ { label => 'critical', 'exit_litteral' => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);
my $exit2 = $self->{perfdata}->threshold_check(value => $bPerSec, threshold => [ { label => 'critical-bytes', 'exit_litteral' => 'critical' }, { label => 'warning-bytes', exit_litteral => 'warning' } ]);
my $exit3 = $self->{perfdata}->threshold_check(value => $aPerSec, threshold => [ { label => 'critical-access', 'exit_litteral' => 'critical' }, { label => 'warning-access', exit_litteral => 'warning' } ]);
my $exit = $self->{output}->get_most_critical(status => [ $exit1, $exit2, $exit3 ]);
my ($bPerSec_value, $bPerSec_unit) = $self->{perfdata}->change_bytes(value => $bPerSec);
my ($bPerReq_value, $bPerReq_unit) = $self->{perfdata}->change_bytes(value => $bPerReq);
$self->{output}->output_add(severity => $exit,
short_msg => sprintf("BytesPerSec: %s AccessPerSec: %.2f RequestPerSec: %.2f BytesPerRequest: %s ",
$bPerSec_value . ' ' . $bPerSec_unit,
$aPerSec,
$rPerSec,
$bPerReq_value . ' ' . $bPerReq_unit
));
$self->{output}->perfdata_add(label => "avg_RequestPerSec",
value => sprintf("%.2f", $rPerSec),
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'),
min => 0
);
$self->{output}->perfdata_add(label => "bytesPerSec", unit => 'B',
value => sprintf("%.2f", $bPerSec),
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-bytes'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-bytes'),
min => 0);
$self->{output}->perfdata_add(label => "avg_bytesPerRequest", unit => 'B',
value => $bPerReq,
min => 0
);
$self->{output}->perfdata_add(label => "avg_bytesPerSec", unit => 'B',
value => $avg_bPerSec,
min => 0
);
$self->{output}->perfdata_add(label => "accessPerSec",
value => sprintf("%.2f", $aPerSec),
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-access'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-access'),
min => 0);
$self->{global} = {
rPerSec => $rPerSec,
bPerReq => $bPerReq,
avg_bPerSec => $avg_bPerSec,
total_bytes => $total_bytes,
total_access => $total_access,
bPerSec => 0, # Will be calculated in custom_bytes_persecond_calc
aPerSec => 0, # Will be calculated in custom_access_persecond_calc
$self->{output}->display();
$self->{output}->exit();
};
$self->{cache_name} = 'apache_' . $self->{option_results}->{hostname} . '_' . $self->{http}->get_port() . '_' . $self->{mode};
}
1;
@ -256,6 +284,12 @@ Threshold for HTTP timeout
Set HTTP headers (multiple option)
=item B<--filter-counters>
Only display some counters (regexp can be used).
Can be : C<bytesPerSec>, C<accessPerSec>, C<avg_RequestPerSec>, C<avg_bytesPerRequest>, C<avg_bytesPerSec>
Example : --filter-counters='^accessPerSec$'
=item B<--warning>
Warning Threshold for Request per seconds

View File

@ -176,9 +176,9 @@ sub set_counters {
closure_custom_perfdata => $self->can('custom_value_perfdata'),
}
},
{ label => 'gracefuly-finished', nlabel => 'apache.slot.gracefulyfinished.count', set => {
key_values => [ { name => 'gracefuly_finished' }, { name => 'total' } ],
closure_custom_calc => $self->can('custom_value_calc'), closure_custom_calc_extra_options => { label_ref => 'gracefuly_finished' },
{ label => 'gracefully-finishing', nlabel => 'apache.slot.gracefullyfinishing.count', set => {
key_values => [ { name => 'gracefully_finishing' }, { name => 'total' } ],
closure_custom_calc => $self->can('custom_value_calc'), closure_custom_calc_extra_options => { label_ref => 'gracefully_finishing' },
closure_custom_output => $self->can('custom_value_output'),
closure_custom_threshold_check => $self->can('custom_value_threshold'),
closure_custom_perfdata => $self->can('custom_value_perfdata'),
@ -197,7 +197,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 => {
@ -211,7 +211,12 @@ sub new {
'password:s' => { name => 'password' },
'header:s@' => { name => 'header' },
'timeout:s' => { name => 'timeout' },
'units:s' => { name => 'units', default => '%' }
'units:s' => { name => 'units', default => '%' },
# To keep compatibility with old thresholds
'warning-gracefuly-finished:s' => { name => 'warning_gracefully_finishing', redirect => 'warning-apache-slot-gracefullyfinishing-count' },
'warning-apache-slot-gracefulyfinished-count:s' => { name => 'warning_gracefully_finishing', redirect => 'warning-apache-slot-gracefullyfinishing-count' },
'critical-gracefuly-finished:s' => { name => 'critical_gracefuly_finished', redirect => 'critical-apache-slot-gracefullyfinishing-count' },
'critical-apache-slot-gracefulyfinished-count:s' => { name => 'critical_apache_smpt_gracefulyfinished_count', redirect => 'critical-apache-slot-gracefullyfinishing-count' },
});
$self->{http} = centreon::plugins::http->new(%options);
@ -248,7 +253,7 @@ sub manage_selection {
reading => ($ScoreBoard =~ tr/R//), sending => ($ScoreBoard =~ tr/W//),
keepalive => ($ScoreBoard =~ tr/K//), dns_lookup => ($ScoreBoard =~ tr/D//),
closing => ($ScoreBoard =~ tr/C//), logging => ($ScoreBoard =~ tr/L//),
gracefuly_finished => ($ScoreBoard =~ tr/G//), idle_cleanup_worker => ($ScoreBoard =~ tr/I//)
gracefully_finishing => ($ScoreBoard =~ tr/G//), idle_cleanup_worker => ($ScoreBoard =~ tr/I//)
};
}
@ -258,7 +263,7 @@ __END__
=head1 MODE
Check Apache WebServer Slots informations
Check Apache WebServer Slots information
=over 8
@ -310,21 +315,101 @@ Set HTTP headers (multiple option)
Threshold unit (default: '%'. Can be: '%' or 'absolute')
=item B<--warning-*>
=item B<--warning-busy>
Warning threshold.
Can be: 'busy', 'free', 'waiting', 'starting', 'reading',
'sending', 'keepalive', 'dns-lookup', 'closing',
'logging', 'gracefuly-finished', 'idle-cleanup-worker'.
Threshold.
=item B<--critical-*>
=item B<--critical-busy>
Critical threshold.
Can be: 'busy', 'free', 'waiting', 'starting', 'reading',
'sending', 'keepalive', 'dns-lookup', 'closing',
'logging', 'gracefuly-finished', 'idle-cleanup-worker'.
Threshold.
=over 8)
=item B<--warning-closing>
Threshold.
=item B<--critical-closing>
Threshold.
=item B<--warning-dns-lookup>
Threshold.
=item B<--critical-dns-lookup>
Threshold.
=item B<--warning-free>
Threshold.
=item B<--critical-free>
Threshold.
=item B<--warning-gracefully-finishing>
Threshold.
=item B<--critical-gracefully-finishing>
Threshold.
=item B<--warning-idle-cleanup-worker>
Threshold.
=item B<--critical-idle-cleanup-worker>
Threshold.
=item B<--warning-keepalive>
Threshold.
=item B<--critical-keepalive>
Threshold.
=item B<--warning-logging>
Threshold.
=item B<--critical-logging>
Threshold.
=item B<--warning-reading>
Threshold.
=item B<--critical-reading>
Threshold.
=item B<--warning-sending>
Threshold.
=item B<--critical-sending>
Threshold.
=item B<--warning-starting>
Threshold.
=item B<--critical-starting>
Threshold.
=item B<--warning-waiting>
Threshold.
=item B<--critical-waiting>
Threshold.
=back

View File

@ -317,6 +317,11 @@ sub request_api_paginate {
$self->{output}->option_exit();
}
if ($self->{http}->get_code() == 401) {
$self->{output}->add_option_msg(short_msg => "Request needs authentication!");
return
}
my $decoded;
eval {
$decoded = JSON::XS->new->allow_nonref(1)->utf8->decode($content);
@ -375,6 +380,11 @@ sub request_api {
);
}
if ($self->{http}->get_code() == 401) {
$self->{output}->add_option_msg(short_msg => "Authentication failed");
$self->{output}->option_exit();
}
return $result;
}

View File

@ -27,6 +27,7 @@ use warnings;
use centreon::plugins::misc;
use Time::HiRes qw(time);
use POSIX qw(floor);
use Encode;
sub new {
my ($class, %options) = @_;
@ -119,8 +120,12 @@ sub check_options {
sub manage_selection {
my ($self, %options) = @_;
my $topic = $self->{option_results}->{topic};
eval {
$topic = decode("utf8", $topic);
};
my $value = $options{mqtt}->query(
topic => $self->{option_results}->{topic}
topic => $topic
);
if (!centreon::plugins::misc::is_empty($self->{option_results}->{extracted_pattern})) {

View File

@ -27,6 +27,7 @@ use warnings;
use centreon::plugins::misc;
use Time::HiRes qw(time);
use POSIX qw(floor);
use Encode;
sub new {
my ($class, %options) = @_;
@ -116,8 +117,12 @@ sub check_options {
sub manage_selection {
my ($self, %options) = @_;
my $topic = $self->{option_results}->{topic};
eval {
$topic = decode("utf8", $topic);
};
my $value = $options{mqtt}->query(
topic => $self->{option_results}->{topic}
topic => $topic
);
if (!centreon::plugins::misc::is_empty($self->{option_results}->{format_custom})) {

View File

@ -47,6 +47,13 @@ sub set_counters {
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
{ label => 'standby-status', type => 2, set => {
key_values => [ { name => 'standby' } ],
output_template => "standby status : %s",
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
}
];
}
@ -57,12 +64,32 @@ sub custom_prefix_output {
return 'Server ' . $options{instance_value}->{cluster_name} . ' ';
}
# List of parameteres added during the API call
our @code_parameters = (
{ 'code' => 'perfstandbyok', 'type' => 'bool' },
{ 'code' => 'activecode', 'type' => 'status' },
{ 'code' => 'drsecondarycode', 'type' => 'status' },
{ 'code' => 'haunhealthycode', 'type' => 'status' },
{ 'code' => 'performancestandbycode', 'type' => 'status' },
{ 'code' => 'removedcode', 'type' => 'status' },
# By default API will return error codes if sealed, uninit or standby
{ 'code' => 'standbyok', 'type' => 'bool', default => 'true' },
{ 'code' => 'sealedcode', 'type' => 'status', default => '200' },
{ 'code' => 'uninitcode', 'type' => 'status', default => '200' },
{ 'code' => 'standbycode', 'type' => 'status', default => '200' },
);
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 => {});
my %arguments;
$arguments{$_->{'code'}.':s'} = { name => $_->{'code'}, default => $_->{'default'} // '', }
foreach (@code_parameters);
$options{options}->add_options(arguments => \%arguments );
return $self;
}
@ -71,17 +98,49 @@ sub set_options {
my ($self, %options) = @_;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
foreach my $param (@code_parameters) {
my $value = lc $self->{option_results}->{$param->{'code'}};
next if $value eq '';
my $valid = 0;
if ($param->{'type'} eq 'status') {
$valid = $value =~ /^\d{1,3}$/;
} elsif ($param->{'type'} eq 'bool') {
$valid = $value eq 'true' || $value eq 'false';
$self->{option_results}->{$param->{code}} = lc $value;
}
unless ($valid) {
$self->{output}->add_option_msg(short_msg => "Invalid value for ".$param->{'code'}.".");
$self->{output}->option_exit();
}
}
}
sub manage_selection {
my ($self, %options) = @_;
my $code_param = '?sealedcode=200&uninitcode=200'; # By default API will return error codes if sealed or uninit
my $result = $options{custom}->request_api(url_path => 'health' . $code_param);
my @code_arr;
foreach my $param (@code_parameters) {
next if $self->{option_results}->{$param->{'code'}} eq '';
push @code_arr, $param->{'code'} .'='. $self->{option_results}->{$param->{'code'}};
}
my $result = $options{custom}->request_api(url_path => 'health' . (@code_arr ? '?'.join('&', @code_arr) : ''));
my $cluster_name = defined($result->{cluster_name}) ? $result->{cluster_name} : $self->{option_results}->{hostname};
$self->{vault_cluster}->{$cluster_name} = {
cluster_name => $cluster_name,
sealed => $result->{sealed} ? 'sealed' : 'unsealed',
init => $result->{initialized} ? 'initialized' : 'not initialized',
standby => $result->{standby} ? 'true' : 'false',
};
}
@ -91,16 +150,22 @@ __END__
=head1 MODE
Check Hashicorp Vault Health status.
Check HashiCorp Vault Health status.
Example:
perl centreon_plugins.pl --plugin=apps::hashicorp::vault::restapi::plugin --mode=health
--hostname=10.0.0.1 --vault-token='s.aBCD123DEF456GHI789JKL012' --verbose
More information on'https://www.vaultproject.io/api-docs/system/health'.
More information on'https://developer.hashicorp.com/vault/api-docs/system/health'.
=over 8
=item B<--standbyok --perfstandbyok --activecode --standbycode --drsecondarycode --haunhealthycode --performancestandbycode --removedcode --sealedcode --uninitcode>
Arguments to pass to the health API call, default are --sealedcode=200 and --uninitcode=200.
More information on'https://developer.hashicorp.com/vault/api-docs/system/health#parameters.'
=item B<--warning-seal-status>
Set warning threshold for seal status (default: none).
@ -117,6 +182,14 @@ Set warning threshold for initialization status (default: none).
Set critical threshold for initialization status (default: '%{init} ne "initialized"').
=item B<--warning-standby-status>
Set warning threshold for standby status (default: none).
=item B<--critical-standby-status>
Set critical threshold for standby status (default: none).
=back
=cut

View File

@ -246,7 +246,7 @@ sub get_container_infos {
my ($self, %options) = @_;
my $stats = $self->request(
endpoint => 'containers/stats?stream=false&amp;containers=' . $options{container_name},
endpoint => 'containers/stats?stream=false&containers=' . $options{container_name},
method => 'GET'
);

View File

@ -203,7 +203,7 @@ sub try_request_api {
}
my $decoded = ($method eq 'GET') ? centreon::plugins::misc::json_decode($content) : {};
my $decoded = ($method eq 'GET') ? centreon::plugins::misc::json_decode($content, booleans_as_strings => 1) : {};
return $decoded;
}

View File

@ -20,7 +20,7 @@
package apps::vmware::vsphere8::esx::mode;
use strict;
use warnings FATAL => 'all';
use warnings;
use base qw(centreon::plugins::templates::counter);

View File

@ -0,0 +1,170 @@
#
# 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::vcenter::mode;
use strict;
use warnings;
use base qw(centreon::plugins::templates::counter);
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
$options{options}->add_options(
arguments => {}
);
$options{options}->add_help(package => __PACKAGE__, sections => 'VMWARE 8 VCENTER OPTIONS', once => 1);
return $self;
}
sub get_vms {
my ($self, %options) = @_;
# Retrieve the data
return $options{custom}->request_api('endpoint' => '/vcenter/vm', 'method' => 'GET');
}
sub get_datastore {
my ($self, %options) = @_;
# if a datastore_id option is given, prepare to append it to the endpoint path
my $datastore_param = defined($options{datastore_id}) ? '/' . $options{datastore_id} : '';
# Retrieve the data
return $options{custom}->request_api('endpoint' => '/vcenter/datastore' . $datastore_param, 'method' => 'GET');
}
sub get_cluster {
my ($self, %options) = @_;
# if a cluster_id option is given, prepare to append it to the endpoint path
my $cluster_param = defined($options{cluster_id}) ? '/' . $options{cluster_id} : '';
# Retrieve the data
return $options{custom}->request_api('endpoint' => '/vcenter/cluster' . $cluster_param, 'method' => 'GET');
}
sub request_api {
my ($self, %options) = @_;
return $options{custom}->request_api(%options);
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
}
1;
__END__
=head1 VMWARE 8 VCENTER OPTIONS
=over 4
No specific options for vCenter modes.
=back
=cut
=head1 NAME
apps::vmware::vsphere8::vcenter::mode - Template for modes monitoring VMware vCenter
=head1 SYNOPSIS
use base apps::vmware::vsphere8::vcenter::mode;
sub set_counters {...}
sub manage_selection {
my ($self, %options) = @_;
$self->set_options(option_results => $option_results);
$self->check_options();
my $vm_data = $self->get_vms(%options);
my $datastore_data = $self->get_datastore(%options);
}
=head1 DESCRIPTION
This module provides methods to interact with the VMware vSphere 8 REST API. It handles generic API requests and VMs GET requests.
=head1 METHODS
=head2 get_datastore
my $all_datastores = $self->get_datastore(%options);
my $one_datastore = $self->get_datastore(%options, datastore_id => 'datastore-35');
Retrieves the vCenter's datastores or only one datastore's specifics in case the `datastore_id` option is provided.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<custom> - The custom_mode object, defined in C<api.pm> and declared in C<plugin.pm> (mandatory).
=item * C<datastore_id> - The C<datastore_id> of a datastore (optional).
=back
=back
=head2 get_vms
my $all_vms = $self->get_vms(%options);
Retrieves the vCenter's virtual machines list.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<custom> - The custom_mode object, defined in C<api.pm> and declared in C<plugin.pm> (mandatory).
=back
=back
Returns the list of all the virtual machines with the following attributes for each VM:
=over 4
=item * C<vm>: ID of the virtual machine.
=item * C<name>: name of the virtual machine.
=item * C<cpu_count>: number of vCPU.
=item * C<power_state>: state of the VM. Can be POWERED_ON, POWERED_OFF, SUSPENDED.
=item * C<memory_size_MiB>: amount of memory allocated to the virtual machine.
=back
=cut

View File

@ -0,0 +1,167 @@
#
# 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::vcenter::mode::clusterstatus;
use base qw(apps::vmware::vsphere8::vcenter::mode);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
sub custom_ha_status_output {
my ($self, %options) = @_;
my $ha = ($self->{result_values}->{ha_enabled} eq 'true') ? 'enabled' : 'disabled';
return "'" . $self->{result_values}->{name} . "' has HA " . $ha;
}
sub custom_drs_status_output {
my ($self, %options) = @_;
my $drs = ($self->{result_values}->{drs_enabled} eq 'true') ? 'enabled' : 'disabled';
return "'" . $self->{result_values}->{name} . "' has DRS " . $drs;
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'cluster', type => 1, cb_prefix_output => 'prefix_cluster_output', message_multiple => 'All clusters are ok', skipped_code => { -10 => 1 } }
];
$self->{maps_counters}->{cluster} = [
{
label => 'ha-status',
type => 2,
warning_default => '%{ha_enabled} ne "true"',
set => {
key_values => [ { name => 'name' }, { name => 'cluster' }, { name => 'ha_enabled' } ],
closure_custom_output => $self->can('custom_ha_status_output'),
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
{
label => 'drs-status',
type => 2,
warning_default => '%{drs_enabled} ne "true"',
set => {
key_values => [ { name => 'name' }, { name => 'drs_enabled' }, { name => 'cluster' } ],
closure_custom_output => $self->can('custom_drs_status_output'),
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);
$options{options}->add_options(
arguments => {
'include-name:s' => { name => 'include_name', default => '' },
'exclude-name:s' => { name => 'exclude_name', default => '' }
}
);
$options{options}->add_help(package => __PACKAGE__, sections => 'MODE', once => 1);
return $self;
}
sub manage_selection {
my ($self, %options) = @_;
# get the list of clusters response from /api/vcenter/cluster endpoint
my $response = $self->get_cluster(%options);
for my $cluster (@{$response}) {
# exclude cluster if not whitelisted
if (centreon::plugins::misc::is_excluded($cluster->{name}, $self->{option_results}->{include_name}, $self->{option_results}->{exclude_name})) {
$self->{output}->output_add(long_msg => "skipping excluded cluster '" . $cluster->{name} . "'", debug => 1);
next;
}
# and now we store the information
$self->{cluster}->{$cluster->{cluster}} = {
name => $cluster->{name},
cluster => $cluster->{cluster},
drs_enabled => $cluster->{drs_enabled},
ha_enabled => $cluster->{ha_enabled}
};
}
if (!defined($self->{cluster}) || keys(%{$self->{cluster}}) == 0) {
$self->{output}->output_add(
severity => 'UNKNOWN',
short_msg => 'No clusters found.'
);
}
}
1;
__END__
=head1 MODE
Monitor the status of a vSphere cluster through vSphere 8 REST API.
=over 8
=item B<--include-name>
Filter by including only the clusters whose name matches the regular expression provided after this parameter.
Example : C<--include-name='^Prod.*'>
=item B<--exclude-name>
Filter by excluding the clusters whose name matches the regular expression provided after this parameter.
Example : C<--exclude-name='^Sandbox.*'>
=item B<--warning-ha-status>
Define the conditions to match for the status to be WARNING. You can use the following variables: C<%{name}>, C<%{ha_enabled}>,
C<%{cluster}>.
Default: C<%{ha_enabled} ne "true">
=item B<--critical-ha-status>
Define the conditions to match for the status to be CRITICAL. You can use the following variables: C<%{name}>, C<%{ha_enabled}>,
C<%{cluster}>.
=item B<--warning-drs-status>
Define the conditions to match for the status to be WARNING. You can use the following variables: C<%{name}>, C<%{drs_enabled}>,
C<%{cluster}>.
Default: C<%{drs_enabled} ne "true">
=item B<--critical-drs-status>
Define the conditions to match for the status to be CRITICAL. You can use the following variables: C<%{name}>, C<%{drs_enabled}>,
C<%{cluster}>.
=back
=cut

View File

@ -0,0 +1,229 @@
#
# 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::vcenter::mode::datastoreusage;
use base qw(apps::vmware::vsphere8::vcenter::mode);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
sub custom_status_output {
my ($self, %options) = @_;
return "'" . $self->{result_values}->{display} . "' accessible" if ($self->{result_values}->{accessible} eq 'true');
return "'" . $self->{result_values}->{display} . "' NOT accessible" if ($self->{result_values}->{accessible} ne 'true');
}
sub custom_usage_output {
my ($self, %options) = @_;
my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total_space});
my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used_space});
my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free_space});
my $msg = sprintf(
'Used: %s (%.2f%%) - Free: %s (%.2f%%) - Total: %s',
$total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used_space},
$total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free_space},
$total_size_value . " " . $total_size_unit
);
return $msg;
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'datastore', type => 1, cb_prefix_output => 'prefix_datastore_output', message_multiple => 'All datastores are ok', skipped_code => { -10 => 1 } }
];
$self->{maps_counters}->{datastore} = [
{
label => 'status',
type => 2,
critical_default => '%{accessible} ne "true"',
set => {
key_values => [ { name => 'accessible' }, { name => 'display' },
{ name => 'thin_provisioning_supported' }, { name => 'multiple_host_access' } ],
closure_custom_output => $self->can('custom_status_output'),
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
{
label => 'usage',
nlabel => 'datastore.space.usage.bytes',
type => 1,
set => {
key_values => [
{ name => 'used_space' }, { name => 'free_space' }, { name => 'prct_used_space' },
{ name => 'prct_free_space' }, { name => 'total_space' }, { name => 'display' }
],
closure_custom_output => $self->can('custom_usage_output'),
perfdatas => [
{ label => 'used', template => '%d', min => 0, max => 'total_space',
unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'display' }
]
}
},
{
label => 'usage-free',
nlabel => 'datastore.space.free.bytes',
display_ok => 0,
type => 1,
set => {
key_values => [
{ name => 'free_space' }, { name => 'used_space' }, { name => 'prct_used_space' },
{ name => 'prct_free_space' }, { name => 'total_space' }, { name => 'display' } ],
closure_custom_output => $self->can('custom_usage_output'),
perfdatas => [
{ label => 'free', template => '%d', min => 0, max => 'total_space',
unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'display' }
]
}
},
{
label => 'usage-prct',
nlabel => 'datastore.space.usage.percentage',
display_ok => 0,
type => 1,
set => {
key_values => [
{ name => 'prct_used_space' }, { name => 'free_space' }, { name => 'used_space' },
{ name => 'prct_free_space' }, { name => 'total_space' }, { name => 'display' }
],
closure_custom_output => $self->can('custom_usage_output'),
perfdatas => [
{ label => 'used_prct', template => '%.2f', min => 0, max => 100,
unit => '%', label_extra_instance => 1, instance_use => 'display' }
]
}
}
];
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
$options{options}->add_options(
arguments => {
'include-name:s' => { name => 'include_name', default => '' },
'exclude-name:s' => { name => 'exclude_name', default => '' }
}
);
$options{options}->add_help(package => __PACKAGE__, sections => 'MODE', once => 1);
return $self;
}
sub manage_selection {
my ($self, %options) = @_;
# get the list of datastores response from /api/vcenter/datastore endpoint
my $response = $self->get_datastore(%options);
for my $ds (@{$response}) {
# exclude datastores not whitelisted
if ( centreon::plugins::misc::is_excluded($ds->{name}, $self->{option_results}->{include_name}, $self->{option_results}->{exclude_name}) ) {
$self->{output}->output_add(long_msg => "skipping excluded datastore '" . $ds->{name} . "'", debug => 1);
next;
}
# at this point the current datastore must be monitored
# let's get the missing data for the current datastore with a new API request
my $detail = $self->get_datastore(%options, datastore_id => $ds->{datastore});
# and now we store the information
$self->{datastore}->{$ds->{datastore}} = {
display => $ds->{name},
type => $ds->{type},
free_space => $ds->{free_space},
total_space => $ds->{capacity},
used_space => $ds->{capacity} - $ds->{free_space},
prct_used_space => 100 * ($ds->{capacity} - $ds->{free_space}) / $ds->{capacity},
prct_free_space => 100 * $ds->{free_space} / $ds->{capacity},
thin_provisioning_supported => $detail->{thin_provisioning_supported},
accessible => $detail->{accessible},
multiple_host_access => $detail->{multiple_host_access}
};
}
}
1;
__END__
=head1 MODE
Monitor the usage of a vCenter's datastores through vSphere 8 REST API.
=over 8
=item B<--include-name>
Filter by including only the VMs whose name matches the regular expression provided after this parameter.
Example : C<--include-name='^prod.*'>
=item B<--exclude-name>
Filter by excluding the VMs whose name matches the regular expression provided after this parameter.
Example : C<--exclude-name='^sandbox.*'>
=item B<--warning-status>
Define the conditions to match for the status to be WARNING. You can use the following variables: C<%{accessible}>,
C<%{display}>, C<%{thin_provisioning_supported}>, C<%{multiple_host_access}>.
=item B<--critical-status>
Define the conditions to match for the status to be CRITICAL. You can use the following variables: C<%{accessible}>,
C<%{display}>, C<%{thin_provisioning_supported}>, C<%{multiple_host_access}>.
Default: C<%{accessible} ne "true">
=item B<--warning-usage>
Threshold in bytes.
=item B<--critical-usage>
Threshold in bytes.
=item B<--warning-usage-free>
Threshold in bytes.
=item B<--critical-usage-free>
Threshold in bytes.
=item B<--warning-usage-prct>
Threshold in percentage.
=item B<--critical-usage-prct>
Threshold in percentage.
=back
=cut

View File

@ -0,0 +1,95 @@
#
# 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::vcenter::mode::listclusters;
use base qw(apps::vmware::vsphere8::vcenter::mode);
use strict;
use warnings;
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 run {
my ($self, %options) = @_;
my $response = $self->get_cluster(%options);
for my $cluster (@{$response}) {
$self->{output}->output_add(
long_msg => sprintf(" %s [id=%s] [drs_enabled=%s] [ha_enabled=%s]",
$cluster->{name},
$cluster->{cluster},
$cluster->{drs_enabled},
$cluster->{ha_enabled})
);
}
$self->{output}->output_add(severity => 'OK',
short_msg => 'List clusters:');
$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', 'cluster', 'drs_enabled', 'ha_enabled']);
}
sub disco_show {
my ($self, %options) = @_;
my $response = $self->get_cluster(%options);
for my $ds (@{$response}) {
$self->{output}->add_disco_entry(
drs_enabled => $ds->{drs_enabled},
cluster => $ds->{cluster},
name => $ds->{name},
ha_enabled => $ds->{ha_enabled}
);
}
}
1;
__END__
=head1 MODE
List clusters for service discovery.
=over 8
=back
=cut

View File

@ -0,0 +1,96 @@
#
# 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::vcenter::mode::listdatastores;
use base qw(apps::vmware::vsphere8::vcenter::mode);
use strict;
use warnings;
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 run {
my ($self, %options) = @_;
my $response = $self->get_datastore(%options);
for my $ds (@{$response}) {
$self->{output}->output_add(long_msg => sprintf(" %s [%s] [%s] [%s free over %s]",
$ds->{name},
$ds->{type},
$ds->{datastore},
$ds->{free_space},
$ds->{capacity},
));
}
$self->{output}->output_add(severity => 'OK',
short_msg => 'List datastore(s):');
$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', 'datastore', 'free_space', 'capacity', 'type']);
}
sub disco_show {
my ($self, %options) = @_;
my $response = $self->get_datastore(%options);
for my $ds (@{$response}) {
$self->{output}->add_disco_entry(
name => $ds->{name},
datastore => $ds->{datastore},
free_space => $ds->{free_space},
capacity => $ds->{capacity},
type => $ds->{type}
);
}
}
1;
__END__
=head1 MODE
List datastores for service discovery.
=over 8
=back
=cut

View File

@ -0,0 +1,218 @@
#
# 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::vcenter::mode::vmcount;
use base qw(apps::vmware::vsphere8::vcenter::mode);
use strict;
use warnings;
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0 }
];
$self->{maps_counters}->{global} = [
{
label => 'on-count',
nlabel => 'vm.poweredon.count',
type => 1,
set => {
key_values => [ { name => 'POWERED_ON' }, { name => 'total' } ],
output_template => '%s VM(s) powered on',
perfdatas => [
{ label => 'POWERED_ON', template => '%s', min => 0, max => 'total' }
]
}
},
{
label => 'off-count',
nlabel => 'vm.poweredoff.count',
type => 1,
set => {
key_values => [ { name => 'POWERED_OFF' }, { name => 'total' } ],
output_template => '%s VM(s) powered off',
perfdatas => [
{ label => 'POWERED_OFF', template => '%s', min => 0, max => 'total' }
]
}
},
{
label => 'suspended-count',
nlabel => 'vm.suspended.count',
type => 1,
set => {
key_values => [ { name => 'SUSPENDED' }, { name => 'total' } ],
output_template => '%s VM(s) suspended',
perfdatas => [
{ label => 'SUSPENDED', template => '%s', min => 0, max => 'total' }
]
}
},
{
label => 'total-count',
nlabel => 'vm.total.count',
type => 1,
warning_default => '1:',
set => {
key_values => [ { name => 'total' } ],
output_template => '%s VM(s) in total',
perfdatas => [
{ label => 'total', template => '%s', min => 0 }
]
}
}
];
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
$options{options}->add_options(
arguments => {
'include-name:s' => { name => 'include_name', default => '' },
'exclude-name:s' => { name => 'exclude_name', default => '' },
'include-state:s' => { name => 'include_state', default => '' },
'exclude-state:s' => { name => 'exclude_state', default => '' },
}
);
$options{options}->add_help(package => __PACKAGE__, sections => 'VMWARE 8 VCENTER OPTIONS', once => 1);
return $self;
}
sub manage_selection {
my ($self, %options) = @_;
# get the response from /api/vcenter/vm endpoint
my $response = $self->get_vms(%options);
$self->{global} = {
'POWERED_ON' => 0,
'POWERED_OFF' => 0,
'SUSPENDED' => 0,
'total' => 0,
'UNKNOWN' => 0
};
for my $vm (@{$response}) {
# avoid undef values
my $entry = {
vm => $vm->{vm},
name => $vm->{name},
cpu_count => $vm->{cpu_count} // 0,
power_state => $vm->{power_state} // 'UNKNOWN',
memory_size_MiB => $vm->{memory_size_MiB} // 0
};
my $entry_desc = sprintf(
"VM '%s' (%s) which is %s, has %d CPUs and %d MiB of RAM",
$entry->{name},
$entry->{vm},
$entry->{power_state},
$entry->{cpu_count},
$entry->{memory_size_MiB}
);
if ( centreon::plugins::misc::is_excluded($entry->{name}, $self->{option_results}->{include_name}, $self->{option_results}->{exclude_name})
|| centreon::plugins::misc::is_excluded($entry->{power_state}, $self->{option_results}->{include_state}, $self->{option_results}->{exclude_state}) ) {
$self->{output}->output_add(long_msg => "skipping VM " . $entry_desc . " (excluded)", debug => 1);
next;
}
$self->{output}->output_add(long_msg => $entry_desc);
$self->{global}->{ $entry->{power_state} }++;
$self->{global}->{total}++;
}
}
1;
__END__
=head1 MODE
Monitor the number of VMware VMs through vSphere 8 REST API.
=over 8
=item B<--include-name>
Filter by including only the VMs whose name matches the regular expression provided after this parameter.
Example : C<--include-name='^prod.*'>
=item B<--exclude-name>
Filter by excluding the VMs whose name matches the regular expression provided after this parameter.
Example : C<--exclude-name='^sandbox.*'>
=item B<--include-state>
Filter by including only the VMs whose power state matches the regular expression provided after this parameter.
Example : C<--include-name='^POWERED_ON$'>
=item B<--exclude-state>
Filter by excluding the VMs whose state matches the regular expression provided after this parameter.
Example : C<--exclude-name='^POWERED_OFF|SUSPENDED$'>
=item B<--warning-on-count>
Threshold.
=item B<--critical-on-count>
Threshold.
=item B<--warning-off-count>
Threshold.
=item B<--critical-off-count>
Threshold.
=item B<--warning-suspended-count>
Threshold.
=item B<--critical-suspended-count>
Threshold.
=item B<--warning-total-count>
Threshold.
=item B<--critical-total-count>
Threshold.
=back
=cut

View File

@ -0,0 +1,53 @@
#
# 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::vcenter::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->{version} = '0.1';
$self->{modes} = {
'cluster-status' => 'apps::vmware::vsphere8::vcenter::mode::clusterstatus',
'datastore-usage' => 'apps::vmware::vsphere8::vcenter::mode::datastoreusage',
'list-clusters' => 'apps::vmware::vsphere8::vcenter::mode::listclusters',
'list-datastores' => 'apps::vmware::vsphere8::vcenter::mode::listdatastores',
'vm-count' => 'apps::vmware::vsphere8::vcenter::mode::vmcount',
};
$self->{custom_modes}->{api} = 'apps::vmware::vsphere8::custom::api';
return $self;
}
1;
__END__
=head1 PLUGIN DESCRIPTION
Monitor VMware physical hosts through vSphere 8 REST API.
=cut

View File

@ -36,6 +36,7 @@ sub check {
my $instance = $chassis->{Id};
next if ($self->check_filter(section => 'chassis', instance => $instance));
next if ($chassis->{ChassisType} eq 'Enclosure');
$self->{components}->{chassis}->{total}++;
$self->{output}->output_add(

View File

@ -39,6 +39,8 @@ sub new {
$self->{output} = $options{output};
$self->{curl_log} = $options{curl_logger};
return $self;
}
@ -158,23 +160,39 @@ sub set_method {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => undef);
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPGET'), parameter => 1);
my $skip_log_post = 0;
# POST inferred by CURLOPT_POSTFIELDS
if ($options{content_type_forced} == 1) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => $options{request}->{query_form_post})
if (defined($options{request}->{query_form_post}));
if (defined($options{request}->{query_form_post})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => $options{request}->{query_form_post});
$self->{curl_log}->log("--data-raw", $options{request}->{query_form_post});
$skip_log_post = 1;
}
} elsif (defined($options{request}->{post_params})) {
my $uri_post = URI->new();
$uri_post->query_form($options{request}->{post_params});
push @{$options{headers}}, 'Content-Type: application/x-www-form-urlencoded';
my $urlencodedheader = 'Content-Type: application/x-www-form-urlencoded';
push @{$options{headers}}, $urlencodedheader;
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => $uri_post->query);
$self->{curl_log}->log("-H", $urlencodedheader);
$self->{curl_log}->log("--data-raw", $uri_post->query);
$skip_log_post = 1;
}
if ($options{request}->{method} eq 'GET') {
return ;
# no curl_log call because GET is the default value
return;
}
if ($options{request}->{method} eq 'POST') {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POST'), parameter => 1);
$self->{curl_log}->log('-X', $options{request}->{method}) unless $skip_log_post;
return;
}
$self->{curl_log}->log('-X', $options{request}->{method});
if ($options{request}->{method} eq 'PUT') {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => $options{request}->{method});
}
@ -192,25 +210,38 @@ sub set_auth {
if (defined($options{request}->{credentials})) {
if (defined($options{request}->{basic})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_BASIC'));
$self->{curl_log}->log('--basic');
} elsif (defined($options{request}->{ntlmv2})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_NTLM'));
$self->{curl_log}->log('--ntlm');
} elsif (defined($options{request}->{digest})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_DIGEST'));
$self->{curl_log}->log('--digest');
}else {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_ANY'));
$self->{curl_log}->log('--anyauth');
}
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_USERPWD'), parameter => $options{request}->{username} . ':' . $options{request}->{password});
my $userpassword = $options{request}->{username} . ':' . $options{request}->{password};
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_USERPWD'), parameter => $userpassword);
$self->{curl_log}->log('--user', $userpassword);
}
if (defined($options{request}->{cert_file}) && $options{request}->{cert_file} ne '') {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERT'), parameter => $options{request}->{cert_file});
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLKEY'), parameter => $options{request}->{key_file});
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_KEYPASSWD'), parameter => $options{request}->{cert_pwd});
$self->{curl_log}->log('--cert', $options{request}->{cert_file});
$self->{curl_log}->log('--key', $options{request}->{key_file});
$self->{curl_log}->log('--pass', $options{request}->{cert_pwd}) if defined $options{request}->{cert_pwd} && $options{request}->{cert_pwd} ne '';
}
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERTTYPE'), parameter => "PEM");
if (defined($options{request}->{cert_pkcs12})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERTTYPE'), parameter => "P12");
$self->{curl_log}->log('--cert-type', 'p12');
} else {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERTTYPE'), parameter => "PEM");
# no curl_log call because PEM is the default value
}
}
@ -234,6 +265,9 @@ sub set_form {
$args{ $self->{constant_cb}->(name => 'CURLFORM_COPYCONTENTS()') } = $_->{copycontents}
if (defined($_->{copycontents}));
$form->add(%args);
$self->{curl_log}->log("--form-string", $_->{copyname}."=".$_->{copycontents})
if defined($_->{copyname}) && defined($_->{copycontents});
}
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPPOST()'), parameter => $form);
@ -244,6 +278,7 @@ sub set_proxy {
if (defined($options{request}->{proxyurl}) && $options{request}->{proxyurl} ne '') {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_PROXY'), parameter => $options{request}->{proxyurl});
$self->{curl_log}->log("--proxy", $options{request}->{proxyurl});
}
if (defined($options{request}->{proxypac}) && $options{request}->{proxypac} ne '') {
@ -261,14 +296,35 @@ sub set_extra_curl_opt {
$key = centreon::plugins::misc::trim($key);
if (!defined($entries->{$key})) {
$entries->{$key} = { val => [], force_array => 0 };
$entries->{$key} = { val => [], constants => [], force_array => 0 };
}
$value = centreon::plugins::misc::trim($value);
# Here we want to convert a string containing curl options into a single value or into
# an array of values depending on whether it begins with '[' and ends with ']'.
# We also remove the quotes.
# for example:
#
# $opt = ["CURLOPT_SSL_VERIFYPEER =>[opt1,'opt2','opt3']"];
# is converted to a Perl array like this:
# $VAR1 = [
# 'opt1',
# 'opt2',
# 'opt3'
# ];
#
# $opt = [ "CURLOPT_SSL_VERIFYPEER => 'opt1'" ];
# is converted to:
# $VAR1 = 'opt1';
if ($value =~ /^\[(.*)\]$/) {
$entries->{$key}->{force_array} = 1;
$value = centreon::plugins::misc::trim($1);
push @{$entries->{$key}->{constants}}, map { $_ = centreon::plugins::misc::trim($_); s/^'(.*)'$/$1/; $_ } split ',', $value;
} else {
push @{$entries->{$key}->{constants}}, $value =~ /^'(.*)'$/ ? $1 : $value;
}
if ($value =~ /^CURLOPT|CURL/) {
$value = $self->{constant_cb}->(name => $value);
}
@ -278,15 +334,26 @@ sub set_extra_curl_opt {
foreach (keys %$entries) {
my $key = $_;
if ($self->{curl_log}->is_enabled()) {
$self->{curl_log}->convert_curlopt_to_cups_parameter(
key => $key,
parameter => $entries->{$key}->{constants},
);
}
if (/^CURLOPT|CURL/) {
$key = $self->{constant_cb}->(name => $_);
}
my $parameter;
if ($entries->{$_}->{force_array} == 1 || scalar(@{$entries->{$_}->{val}}) > 1) {
$self->curl_setopt(option => $key, parameter => $entries->{$_}->{val});
$parameter = $entries->{$_}->{val};
} else {
$self->curl_setopt(option => $key, parameter => pop @{$entries->{$_}->{val}});
$parameter = pop @{$entries->{$_}->{val}};
}
$self->curl_setopt(option => $key, parameter => $parameter);
}
}
@ -315,29 +382,13 @@ sub cb_get_header {
sub request {
my ($self, %options) = @_;
# Enable curl logger when debug mode is on
$self->{curl_log}->init( enabled => $self->{output}->is_debug() );
if (!defined($self->{curl_easy})) {
$self->{curl_easy} = Net::Curl::Easy->new();
}
if ($self->{output}->is_debug()) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_DEBUGFUNCTION'), parameter => \&cb_debug);
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_DEBUGDATA'), parameter => $self);
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_VERBOSE'), parameter => 1);
}
if (defined($options{request}->{timeout}) && $options{request}->{timeout} =~ /\d/) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_TIMEOUT'), parameter => $options{request}->{timeout});
}
if (defined($options{request}->{cookies_file})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_COOKIEFILE'), parameter => $options{request}->{cookies_file});
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_COOKIEJAR'), parameter => $options{request}->{cookies_file});
}
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FOLLOWLOCATION'), parameter => 1);
if (defined($options{request}->{no_follow})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FOLLOWLOCATION'), parameter => 0);
}
my $url;
if (defined($options{request}->{full_url})) {
$url = $options{request}->{full_url};
@ -347,14 +398,6 @@ sub request {
$url = $options{request}->{proto}. "://" . $options{request}->{hostname} . $options{request}->{url_path};
}
if (defined($options{request}->{http_peer_addr}) && $options{request}->{http_peer_addr} ne '') {
$url =~ /^(?:http|https):\/\/(.*?)(\/|\:|$)/;
$self->{curl_easy}->setopt(
$self->{constant_cb}->(name => 'CURLOPT_RESOLVE'),
[$1 . ':' . $options{request}->{port_force} . ':' . $options{request}->{http_peer_addr}]
);
}
my $uri = URI->new($url);
if (defined($options{request}->{get_params})) {
$uri->query_form($options{request}->{get_params});
@ -362,13 +405,54 @@ sub request {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_URL'), parameter => $uri);
$self->{curl_log}->log($uri);
if ($self->{output}->is_debug()) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_DEBUGFUNCTION'), parameter => \&cb_debug);
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_DEBUGDATA'), parameter => $self);
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_VERBOSE'), parameter => 1);
$self->{curl_log}->log('--verbose');
}
if (defined($options{request}->{timeout}) && $options{request}->{timeout} =~ /\d/) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_TIMEOUT'), parameter => $options{request}->{timeout});
$self->{curl_log}->log("--max-time", $options{request}->{timeout});
}
if (defined($options{request}->{cookies_file})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_COOKIEFILE'), parameter => $options{request}->{cookies_file});
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_COOKIEJAR'), parameter => $options{request}->{cookies_file});
$self->{curl_log}->log('--cookie', $options{request}->{cookies_file});
$self->{curl_log}->log('--cookie-jar', $options{request}->{cookies_file});
}
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FOLLOWLOCATION'), parameter => 1);
if (defined($options{request}->{no_follow})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FOLLOWLOCATION'), parameter => 0);
} else {
$self->{curl_log}->log('-L');
}
if (defined($options{request}->{http_peer_addr}) && $options{request}->{http_peer_addr} ne '') {
$url =~ /^(?:http|https):\/\/(.*?)(\/|\:|$)/;
my $resolve = $1 . ':' . $options{request}->{port_force} . ':' . $options{request}->{http_peer_addr};
$self->{curl_easy}->setopt(
$self->{constant_cb}->(name => 'CURLOPT_RESOLVE'),
[$resolve]
);
$self->{curl_log}->log('--resolve', $resolve);
}
my $headers = [];
my $content_type_forced = 0;
foreach my $key (keys %{$options{request}->{headers}}) {
push @$headers, $key . ':' . (defined($options{request}->{headers}->{$key}) ? $options{request}->{headers}->{$key} : '');
my $header = $key . ':' . (defined($options{request}->{headers}->{$key}) ? $options{request}->{headers}->{$key} : '');
push @$headers, $header;
if ($key =~ /content-type/i) {
$content_type_forced = 1;
}
$self->{curl_log}->log("-H", $header);
}
$self->set_method(%options, content_type_forced => $content_type_forced, headers => $headers);
@ -383,16 +467,17 @@ sub request {
if (defined($options{request}->{cacert_file}) && $options{request}->{cacert_file} ne '') {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CAINFO'), parameter => $options{request}->{cacert_file});
$self->{curl_log}->log('--cacert', $options{request}->{cacert_file});
}
if (defined($options{request}->{insecure})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSL_VERIFYPEER'), parameter => 0);
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSL_VERIFYHOST'), parameter => 0);
$self->{curl_log}->log('--insecure');
}
$self->set_auth(%options);
$self->set_proxy(%options);
$self->set_extra_curl_opt(%options);
$self->{response_body} = '';
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FILE'), parameter => \$self->{response_body});
$self->{nheaders} = 0;
@ -402,9 +487,15 @@ sub request {
if (defined($options{request}->{certinfo}) && $options{request}->{certinfo} == 1) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CERTINFO'), parameter => 1);
# no curl_log call because there is no equivalent in command line
}
$self->{response_code} = undef;
if ($self->{curl_log}->is_enabled()) {
$self->{output}->output_add(long_msg => 'curl request [curl backend]: ' . $self->{curl_log}->get_log());
}
eval {
$self->{curl_easy}->perform();
};

View File

@ -26,7 +26,7 @@ use centreon::plugins::misc;
sub new {
my ($class, %options) = @_;
my $self = {};
my $self = {};
bless $self, $class;
if (!defined($options{noptions}) || $options{noptions} != 1) {
@ -45,8 +45,8 @@ sub new {
sub check_options {
my ($self, %options) = @_;
$self->{ssh_command} = defined($options{option_results}->{sshcli_command}) && $options{option_results}->{sshcli_command} ne '' ?
$options{option_results}->{sshcli_command} : 'ssh';
$self->{ssh_command} = defined($options{option_results}->{sshcli_command}) && $options{option_results}->{sshcli_command} ne '' ?
$options{option_results}->{sshcli_command} : 'ssh';
$self->{ssh_path} = $options{option_results}->{sshcli_path};
$self->{ssh_option} = defined($options{option_results}->{sshcli_option}) ? $options{option_results}->{sshcli_option} : [];
$self->{ssh_port} = defined($options{option_results}->{ssh_port}) && $options{option_results}->{ssh_port} =~ /(\d+)/ ? $1 : 22;
@ -58,10 +58,10 @@ sub check_options {
}
centreon::plugins::misc::check_security_command(
output => $self->{output},
command => $options{option_results}->{sshcli_command},
output => $self->{output},
command => $options{option_results}->{sshcli_command},
command_options => join('', @{$self->{ssh_option}}),
command_path => $self->{ssh_path}
command_path => $self->{ssh_path}
);
push @{$self->{ssh_option}}, '-o=BatchMode=yes';
@ -77,21 +77,23 @@ sub execute {
$options{command} .= $options{cmd_exit} if (defined($options{cmd_exit}) && $options{cmd_exit} ne '');
my ($content, $exit_code) = centreon::plugins::misc::execute(
output => $self->{output},
sudo => $options{sudo},
command => $options{command},
command_path => $options{command_path},
output => $self->{output},
sudo => $options{sudo},
command => $options{command},
command_path => $options{command_path},
command_options => $options{command_options},
ssh_pipe => $options{ssh_pipe},
options => {
remote => 1,
ssh_address => $options{hostname},
ssh_command => $self->{ssh_command},
ssh_path => $self->{ssh_path},
ssh_option => $self->{ssh_option},
timeout => $options{timeout}
ssh_pipe => $options{ssh_pipe},
options => {
remote => 1,
ssh_address => $options{hostname},
ssh_command => $self->{ssh_command},
ssh_path => $self->{ssh_path},
ssh_option => $self->{ssh_option},
timeout => $options{timeout},
ssh_option_eol => $options{default_sshcli_option_eol}
},
no_quit => $options{no_quit}
no_quit => $options{no_quit}
);
if (defined($options{ssh_pipe}) && $options{ssh_pipe} == 1) {
@ -109,32 +111,32 @@ __END__
=head1 NAME
ssh cli backend.
SSH CLI backend.
=head1 SYNOPSIS
ssh cli backend.
SSH CLI backend.
=head1 BACKEND SSHCLI OPTIONS
=head1 BACKEND SSH CLI OPTIONS
=over 8
=item B<--sshcli-command>
ssh command (default: 'ssh').
ssh command (default: C<ssh>).
=item B<--sshcli-path>
ssh command path (default: none)
ssh command path (default: C<none>)
=item B<--sshcli-option>
Specify ssh cli options (example: --sshcli-option='-o=StrictHostKeyChecking=no').
Specify SSH CLI options (example: C<--sshcli-option='-o=StrictHostKeyChecking=no'>).
=back
=head1 DESCRIPTION
B<sshcli>.
B<SSH CLI>.
=cut

View File

@ -0,0 +1,126 @@
#
# 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 centreon::plugins::curllogger;
use strict;
use warnings;
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
$self->{log_as_curl} = [];
$self->{is_log_as_curl} = 0;
# As this is only used for debugging purposes, we disable it if the ShellQuote
# module is missing
eval "use String::ShellQuote";
if ($@) {
$self->{is_log_as_curl} = -1;
}
$self;
}
sub init {
my ($self, %options) = @_;
$self->{log_as_curl} = [];
return if $self->{is_log_as_curl} == -1;
$self->{is_log_as_curl} = $options{enabled} || 0;
}
sub is_enabled {
my ($self) = @_;
return $self->{is_log_as_curl} == 1;
}
sub log {
my ($self, @params) = @_;
return unless $self->{is_log_as_curl} == 1 && @params;
push @{$self->{log_as_curl}}, shell_quote(@params);
}
sub get_log {
my ($self) = @_;
return "curl ".join ' ', @{$self->{log_as_curl}};
}
# Conversion of some parameters manually passed to the set_extra_curl_opt function
# into their command-line equivalents. Only the parameters used in the plugin code
# are handled. If new parameters are added, this hash must be updated.
# The hash contains curl parameters CURLOPT_*. Its keys map to either a hash when
# multiple values are handled, or directly to an array when only one response is
# supported. The placeholder <value> is replaced by the provided value.
# Eg: "CURLOPT_POSTREDIR => CURL_REDIR_POST_ALL" will produce: --post301 --post302 --post303
# "CURLOPT_SSL_VERIFYPEER => 0" will produce: --insecure
# "CURLOPT_AWS_SIGV4 => 'osc'" will produce: --aws-sigv4 osc
our %curlopt_to_parameter = (
'CURLOPT_POSTREDIR' => { 'CURL_REDIR_POST_ALL' => [ '--post301', '--post302', '--post303', ],
'CURL_REDIR_POST_301' => [ '--post301' ],
'CURL_REDIR_POST_302' => [ '--post302' ],
'CURL_REDIR_POST_303' => [ '--post303' ],
},
'CURLOPT_SSLVERSION' => { 'CURL_SSLVERSION_TLSv1_0' => [ '--tlsv1.0' ],
'CURL_SSLVERSION_TLSv1_1' => [ '--tlsv1.1' ],
'CURL_SSLVERSION_TLSv1_2' => [ '--tlsv1.2' ],
'CURL_SSLVERSION_TLSv1_3' => [ '--tlsv1.3' ],
},
'CURLOPT_SSL_VERIFYPEER' => { '0' => [ '--insecure' ] },
'CURLOPT_SSL_VERIFYHOST' => { '0' => [ '--insecure' ] },
'CURLOPT_AWS_SIGV4' => [ '--aws-sigv4', '<value>' ],
);
sub convert_curlopt_to_cups_parameter {
my ($self, %options) = @_;
my $key = $options{key};
return unless exists $curlopt_to_parameter{$key};
# we want an array of parameters
my $parameters = ref $options{parameter} eq 'ARRAY' ? $options{parameter} : [ $options{parameter} ] ;
my @cups_parameters ;
if (ref $curlopt_to_parameter{$key} eq 'ARRAY') {
@cups_parameters = map { s/<value>/$parameters->[0]/; $_ } @{$curlopt_to_parameter{$key}};
} else {
foreach my $parameter (@$parameters) {
if (exists $curlopt_to_parameter{$key}->{$parameter}) {
push @cups_parameters, @{$curlopt_to_parameter{$key}->{$parameter}};
} elsif ($parameter =~ /^-/) {
push @cups_parameters, $parameter;
}
}
}
$self->log($_) for @cups_parameters;
}
1;
__END__

View File

@ -23,6 +23,8 @@ package centreon::plugins::http;
use strict;
use warnings;
use centreon::plugins::curllogger;
sub new {
my ($class, %options) = @_;
my $self = {};
@ -39,6 +41,8 @@ sub new {
$options{options}->add_help(package => __PACKAGE__, sections => 'HTTP GLOBAL OPTIONS');
}
my $curllogger = centreon::plugins::curllogger->new();
centreon::plugins::misc::mymodule_load(
output => $options{output},
module => 'centreon::plugins::backend::http::lwp',
@ -51,7 +55,7 @@ sub new {
module => 'centreon::plugins::backend::http::curl',
error_msg => "Cannot load module 'centreon::plugins::backend::http::curl'."
);
$self->{backend_curl} = centreon::plugins::backend::http::curl->new(%options);
$self->{backend_curl} = centreon::plugins::backend::http::curl->new(%options, curl_logger => $curllogger);
$self->{default_backend} = defined($options{default_backend}) && $options{default_backend} ne '' ?
$options{default_backend} : 'lwp';
@ -260,7 +264,7 @@ Proxy URL. Example: http://my.proxy:3128
=item B<--proxypac>
Proxy pac file (can be a URL or a local file).
Proxy PAC file (can be a URL or a local file).
=item B<--insecure>

View File

@ -152,6 +152,15 @@ sub unix_execute {
push @$args, $options{options}->{hostname};
}
if (defined($options{options}->{ssh_option_eol})) {
foreach (@{$options{options}->{ssh_option_eol}}) {
if (/^(.*?)(?:=(.*))?$/) {
push @$args, $1 if (defined($1));
push @$args, $2 if (defined($2));
}
}
}
$sub_cmd = 'sudo ' if (defined($options{sudo}));
$sub_cmd .= $options{command_path} . '/' if (defined($options{command_path}));
$sub_cmd .= $options{command} . ' ' if (defined($options{command}));
@ -758,18 +767,30 @@ sub check_security_whitelist {
}
sub json_decode {
my ($content) = @_;
my ($content, %options) = @_;
$content =~ s/\r//mg;
my $object;
my $decoder = JSON::XS->new->utf8;
# this option
if ($options{booleans_as_strings}) {
# boolean_values() is not available on old versions of JSON::XS (Alma 8 still provides v3.04)
if (JSON::XS->can('boolean_values')) {
$decoder = $decoder->boolean_values("false", "true");
} else {
# if boolean_values is not available, perform a dirty substitution of booleans
$content =~ s/"(\w+)"\s*:\s*(true|false)(\s*,?)/"$1": "$2"$3/gm;
}
}
eval {
$object = JSON::XS->new->utf8->decode($content);
$object = $decoder->decode($content);
};
if ($@) {
print STDERR "Cannot decode JSON string: $@" . "\n";
return undef;
}
return $object;
}
@ -789,6 +810,15 @@ sub json_encode {
return $encoded;
}
# function to assess if a string has to be excluded given an include regexp and an exclude regexp
sub is_excluded {
my ($string, $include_regexp, $exclude_regexp) = @_;
return 1 unless defined($string);
return 1 if (defined($exclude_regexp) && $exclude_regexp ne '' && $string =~ /$exclude_regexp/);
return 0 if (!defined($include_regexp) || $include_regexp eq '' || $string =~ /$include_regexp/);
return 1;
}
1;
@ -1278,7 +1308,7 @@ Checks if a command is in the security whitelist.
=head2 json_decode
my $decoded = centreon::plugins::misc::json_decode($content);
my $decoded = centreon::plugins::misc::json_decode($content, %options);
Decodes a JSON string.
@ -1286,6 +1316,15 @@ Decodes a JSON string.
=item * C<$content> - The JSON string to decode and transform into an object.
=item * C<%options> - Options passed to the function.
=over 4
=item * C<booleans_as_strings> - Defines whether booleans must be converted to C<true>/C<false> strings instead of
JSON:::PP::Boolean values. C<1> => strings, C<0> => booleans.
=back
=back
=head2 json_encode
@ -1300,6 +1339,28 @@ Encodes an object to a JSON string.
=back
=head2 is_excluded
my $excluded = is_excluded($string, $include_regexp, $exclude_regexp);
Determines whether a string should be excluded based on include and exclude regular expressions.
=over 4
=item * C<$string> - The string to evaluate. If undefined, the function returns 1 (excluded).
=item * C<$include_regexp> - A regular expression to include the string.
=item * C<$exclude_regexp> - A regular expression to exclude the string. If defined and matches the string, the function returns 1 (excluded).
=back
Returns 1 if the string is excluded, 0 if it is included.
The string is excluded if $exclude_regexp is defined and matches the string, or if $include_regexp is defined and does
not match the string. The string will also be excluded if it is undefined.
=cut
=head1 AUTHOR
Centreon

View File

@ -28,7 +28,6 @@ sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
$self->{perfdata} = centreon::plugins::perfdata->new(output => $options{output});
%{$self->{option_results}} = ();
@ -45,7 +44,6 @@ sub new {
sub init {
my ($self, %options) = @_;
# options{default} = { mode_xxx => { option_name => option_value }, }
%{$self->{option_results}} = %{$options{option_results}};
# Manage default value
return if (!defined($options{default}));

View File

@ -122,7 +122,7 @@ sub query {
$self->{mqtt}->unsubscribe($options{topic});
};
if (%mqtt_received) {
return %mqtt_received{$options{topic}};
return $mqtt_received{$options{topic}};
} else {
$self->{output}->add_option_msg(short_msg => 'No message in topic: ' . $options{topic});
$self->{output}->option_exit();

View File

@ -25,7 +25,7 @@ use warnings;
sub new {
my ($class, %options) = @_;
my $self = {};
my $self = {};
bless $self, $class;
if (!defined($options{noptions}) || $options{noptions} != 1) {
@ -40,22 +40,22 @@ sub new {
}
centreon::plugins::misc::mymodule_load(
output => $options{output},
module => 'centreon::plugins::backend::ssh::sshcli',
output => $options{output},
module => 'centreon::plugins::backend::ssh::sshcli',
error_msg => "Cannot load module 'centreon::plugins::backend::ssh::sshcli'."
);
$self->{backend_sshcli} = centreon::plugins::backend::ssh::sshcli->new(%options);
centreon::plugins::misc::mymodule_load(
output => $options{output},
module => 'centreon::plugins::backend::ssh::plink',
output => $options{output},
module => 'centreon::plugins::backend::ssh::plink',
error_msg => "Cannot load module 'centreon::plugins::backend::ssh::plink'."
);
$self->{backend_plink} = centreon::plugins::backend::ssh::plink->new(%options);
centreon::plugins::misc::mymodule_load(
output => $options{output},
module => 'centreon::plugins::backend::ssh::libssh',
output => $options{output},
module => 'centreon::plugins::backend::ssh::libssh',
error_msg => "Cannot load module 'centreon::plugins::backend::ssh::libssh'."
);
$self->{backend_libssh} = centreon::plugins::backend::ssh::libssh->new(%options);
@ -68,7 +68,11 @@ sub check_options {
my ($self, %options) = @_;
$self->{ssh_backend} = $options{option_results}->{ssh_backend};
$self->{ssh_port} = defined($options{option_results}->{ssh_port}) && $options{option_results}->{ssh_port} =~ /(\d+)/ ? $1 : 22;
my $default_port = 22;
if (defined($options{default_ssh_port}) && $options{default_ssh_port} =~ /\d+/) {
$default_port = $options{default_ssh_port};
}
$self->{ssh_port} = defined($options{option_results}->{ssh_port}) && $options{option_results}->{ssh_port} =~ /(\d+)/ ? $1 : $default_port;
$self->{ssh_backend} = 'sshcli'
if (!defined($options{option_results}->{ssh_backend}) || $options{option_results}->{ssh_backend} eq '');
if (!defined($self->{'backend_' . $self->{ssh_backend}})) {
@ -84,6 +88,12 @@ sub get_port {
return $self->{ssh_port};
}
sub get_ssh_backend {
my ($self, %options) = @_;
return $self->{ssh_backend};
}
sub execute {
my ($self, %options) = @_;
@ -100,7 +110,7 @@ SSH abstraction layer.
=head1 SYNOPSIS
SSH abstraction layer for sscli, plink and libssh backends
SSH abstraction layer for SSH CLI, Plink and libSSH backends
=head1 SSH GLOBAL OPTIONS
@ -109,7 +119,7 @@ SSH abstraction layer for sscli, plink and libssh backends
=item B<--ssh-backend>
Define the backend you want to use.
It can be: sshcli (default), plink and libssh.
It can be: C<sshcli> (default), C<plink> and C<libssh>.
=item B<--ssh-username>
@ -118,8 +128,8 @@ Define the user name to log in to the host.
=item B<--ssh-password>
Define the password associated with the user name.
Cannot be used with the sshcli backend.
Warning: using a password is not recommended. Use --ssh-priv-key instead.
Cannot be used with the C<sshcli> backend.
Warning: using a password is not recommended. Use C<--ssh-priv-key> instead.
=item B<--ssh-port>

View File

@ -351,6 +351,38 @@ sub run {
$self->{output}->exit();
}
sub disco_format {
my ($self, %options) = @_;
$self->{output}->add_disco_format(elements => ['component', 'instance', 'description']);
}
sub disco_show {
my ($self, %options) = @_;
$self->{loaded} = 0;
$self->call_object_callback(method_name => $self->{cb_hook1}, %options);
$self->load_components(%options);
if ($self->{loaded} == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong option. Cannot find component '" . $self->{option_results}->{component} . "'.");
$self->{output}->option_exit();
}
$self->call_object_callback(method_name => $self->{cb_hook2}, %options);
foreach (@{$self->{components_module}}) {
if (/$self->{option_results}->{component}/) {
my $mod_name = $self->{components_path} . "::$_";
if (my $func = $mod_name->can('disco_show')) {
$func->($self);
}
}
}
$self->call_object_callback(method_name => $self->{cb_hook3}, %options);
}
sub check_filter {
my ($self, %options) = @_;

View File

@ -172,18 +172,24 @@ sub manage_selection {
$self->{metrics} = {};
foreach my $label (keys %{$metric_results}) {
foreach my $stat (('minimum', 'maximum', 'average', 'sum')) {
next if (!defined($metric_results->{$label}->{$stat}));
foreach my $statistic (@{$self->{aws_statistics}}) {
next if (!defined($metric_results->{$label}->{lc($statistic)}) &&
!defined($self->{option_results}->{zeroed}));
my $name = $label . '_' . $stat;
my $name = $label . '_' . $statistic;
$name = $self->{dimension_name} . '_' . $name if ($self->{dimension_name} ne '');
$self->{metrics}->{$name} = {
display => $name,
value => $metric_results->{$label}->{$stat},
perf_label => $label . '_' . $stat,
value => $metric_results->{$label}->{lc($statistic)} // 0,
perf_label => $label . '_' . $statistic,
};
}
}
if (scalar(keys %{$self->{metrics}}) <= 0) {
$self->{output}->add_option_msg(short_msg => 'No metrics. Check your options or use --zeroed option to set 0 on undefined values');
$self->{output}->option_exit();
}
}
1;
@ -192,7 +198,7 @@ __END__
=head1 MODE
Check cloudwatch metrics (same dimension and namespace).
Check CloudWatch metrics (same dimension and namespace).
Example:
perl centreon_plugins.pl --plugin=cloud::aws::plugin --custommode=paws --mode=cloudwatch-get-metrics --region=eu-west-1
@ -203,15 +209,15 @@ perl centreon_plugins.pl --plugin=cloud::aws::plugin --custommode=paws --mode=cl
=item B<--namespace>
Set cloudwatch namespace (required).
Set CloudWatch namespace (required).
=item B<--dimension>
Set cloudwatch dimensions.
Set CloudWatch dimensions.
=item B<--metric>
Set cloudwatch metrics (required).
Set CloudWatch metrics (required).
=item B<--warning-metric>

View File

@ -930,7 +930,7 @@ sub azure_get_usagedetails_set_url {
my $url = $self->{management_endpoint} . "/subscriptions/" . $self->{subscription};
$url .= "/resourceGroups/" . $options{resource_group} if (defined($options{resource_group}) && $options{resource_group} ne '');
$url .= "/providers/Microsoft.Consumption/usageDetails?\$filter=properties%2FusageStart ge %27" . $options{usage_start} . "%27 and properties%2FusageEnd le %27" . $options{usage_end} . "%27";
$url .= "&metric=actualcost";
$url .= "&metric=". ($options{cost_metric} || 'actualcost');
$url .= "&api-version=" . $self->{api_version};
return $url;
}
@ -1261,7 +1261,7 @@ The application needs the 'Monitoring Reader' role (see https://docs.microsoft.c
This custom mode is using the 'OAuth 2.0 Client Credentials Grant Flow'
For further informations, visit https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-oauth2-client-creds-grant-flow
For further information, visit https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-oauth2-client-creds-grant-flow
=over 8
@ -1295,7 +1295,7 @@ Set timeframe in seconds (i.e. 3600 to check last hour).
=item B<--interval>
Set interval of the metric query (can be : PT1M, PT5M, PT15M, PT30M, PT1H, PT6H, PT12H, PT24H).
Set interval of the metric query (can be : C<PT1M>, C<PT5M>, C<PT15M>, C<PT30M>, C<PT1H>, C<PT6H>, C<PT12H>, C<PT24H>).
=item B<--aggregation>

View File

@ -113,6 +113,7 @@ sub new {
"lookup-days:s" => { name => 'lookup_days', default => 30 },
"units:s" => { name => 'units', default => '%' },
"timeout:s" => { name => 'timeout', default => '60' },
"cost-metric:s" => { name => "cost_metric", default => 'actualcost' }
});
return $self;
@ -127,6 +128,12 @@ sub check_options {
$self->{output}->option_exit();
}
$self->{option_results}->{cost_metric} = lc($self->{option_results}->{cost_metric});
if ($self->{option_results}->{cost_metric} !~ /^(actualcost|amortized)$/) {
$self->{output}->add_option_msg(short_msg => "Invalid cost metric '" . $self->{option_results}->{cost_metric} . "'. Valid values are 'actualcost' or 'amortized'.");
$self->{output}->option_exit();
}
$self->{lookup_days} = $self->{option_results}->{lookup_days};
}
@ -143,7 +150,8 @@ sub manage_selection {
my $costs = $options{custom}->azure_get_usagedetails(
resource_group => $self->{option_results}->{resource_group},
usage_start => $usage_start,
usage_end => $usage_end
usage_end => $usage_end,
cost_metric => $self->{option_results}->{cost_metric},
);
my $cost = 0;
@ -183,7 +191,7 @@ perl centreon_plugins.pl --plugin=cloud::azure::management::costs::plugin --cust
You should NOT execute the plugin for a given subscription/resource group more than once a day otherwise,
you might reach the Azure API calls limit if you have many.
For subscription with large ressource with usagedetail consumption that might requite many API calls,
For subscription with large resources and detailed usage consumption that might require many API calls,
you may have to increase timeout.
=over 8
@ -209,8 +217,13 @@ Set warning threshold for cost).
Define the conditions to match for the status to be CRITICAL.
=item B<--units>
Unit of thresholds (default: '%') ('%', 'count').
=item B<--cost-metric>
Choose the cost metric to use (default: C<actualcost>) (C<actualcost>, C<amortized>).
=back
=cut

View File

@ -333,9 +333,14 @@ sub gcp_get_metrics {
}
my $metric_calc = { points => 0 };
my $value;
foreach my $point (@{$timeserie->{points}}) {
if (defined($point->{value})) {
my $value = $point->{value}->{ lc($timeserie->{valueType}) . 'Value' };
if (lc($timeserie->{valueType}) eq 'distribution') {
$value = $point->{value}->{ lc($timeserie->{valueType}) . 'Value' }->{count};
} else {
$value = $point->{value}->{ lc($timeserie->{valueType}) . 'Value' };
}
if (defined($aggregations{average})) {
$metric_calc->{average} = 0 if (!defined($metric_calc->{average}));
$metric_calc->{average} += $value;

View File

@ -62,11 +62,11 @@ sub manage_selection {
$self->{pods}->{$pod->{metadata}->{uid}} = {
uid => $pod->{metadata}->{uid},
name => $pod->{metadata}->{name},
namespace => $pod->{metadata}->{namespace},
node => $pod->{spec}->{nodeName},
status => $pod->{status}->{phase},
ip => $pod->{status}->{podIP},
name => $pod->{metadata}->{name} // '',
namespace => $pod->{metadata}->{namespace} // '',
node => $pod->{spec}->{nodeName} // '',
status => $pod->{status}->{phase} // '',
ip => $pod->{status}->{podIP} // '',
}
}
}
@ -106,9 +106,9 @@ sub disco_show {
uid => $self->{pods}->{$pod}->{uid},
name => $self->{pods}->{$pod}->{name},
namespace => $self->{pods}->{$pod}->{namespace},
namespace => $self->{pods}->{$pod}->{node},
namespace => $self->{pods}->{$pod}->{status},
namespace => $self->{pods}->{$pod}->{ip},
node => $self->{pods}->{$pod}->{node},
status => $self->{pods}->{$pod}->{status},
ip => $self->{pods}->{$pod}->{ip},
);
}
}

View File

@ -0,0 +1,51 @@
{
"constants": {
"warning": "",
"critical": ""
},
"api": {
"commands": [
{
"name": "messages",
"command": "<rpc><get-log><filename>messages</filename></get-log></rpc>",
"rtype": "txt",
"parse": [
{
"name": "content",
"re": "^(.*?)\\n",
"modifier": "ms",
"multiple": 1,
"entries": [
{ "id": "line", "offset": "1" }
]
}
]
}
]
},
"selection": [
{
"name": "logCount",
"functions": [
{ "type": "count", "src": "%(api.tables.messagesContent)", "filter": "%(src.line) =~ /SECONDARY_TIMEOUT|PRIMARY_TIMEOUT/ and %(src.line) !~ /CMDLINE_READ_LINE/", "save": "%(logCount)" }
],
"warning": "defined(%(constants.warning)) and %(constants.warning) ne '' and %(logCount) > %(constants.warning)",
"critical": "defined(%(constants.critical)) and %(constants.critical) ne '' and %(logCount) > %(constants.critical)",
"perfdatas": [
{ "nlabel": "logs.timeout.count", "value": "%(logCount)", "warning": "%(constants.warning)", "critical": "%(constants.critical)", "min": 0 }
],
"formatting": {
"printf_msg":"number of timeout logs: %s",
"printf_var":[
"%(logCount)"
],
"display_ok": true
}
}
],
"formatting":{
"custom_message_global": "All selections are OK",
"separator": "-"
}
}

View File

@ -36,9 +36,8 @@ sub set_version {
$self->{is_mariadb} = 0;
$self->{version} = $self->{instance}->get_info(18); # SQL_DBMS_VER
# MariaDB: 5.5.5-10.1.36-MariaDB or 10.1.36-MariaDB
if ($self->{version} =~ /([0-9\.]*?)-MariaDB/i) {
$self->{version} = $1;
# MariaDB: 5.5.5-10.1.36-MariaDB, 10.1.36-MariaDB or 11.4.4-2-MariaDB-enterprise-log
if ($self->{version} =~ /(?:\d\.\d\.\d-)?(\d+\.\d+\.\d+).*MariaDB/i) {
$self->{is_mariadb} = 1;
}
}

View File

@ -53,7 +53,7 @@ sub new {
'sql-errors-exit:s' => { name => 'sql_errors_exit', default => 'unknown' }
});
}
$options{options}->add_help(package => __PACKAGE__, sections => 'MYSQLCMD OPTIONS', once => 1);
$options{options}->add_help(package => __PACKAGE__, sections => 'MYSQL COMMAND OPTIONS', once => 1);
$self->{output} = $options{output};
$self->{sqlmode_name} = $options{sqlmode_name};
@ -209,8 +209,8 @@ sub set_version {
$self->{is_mariadb} = 0;
$self->{version} = $options{version};
# MariaDB: 5.5.5-10.1.36-MariaDB or 10.1.36-MariaDB
if ($self->{version} =~ /([0-9\.]*?)-MariaDB/i) {
# MariaDB: 5.5.5-10.1.36-MariaDB, 10.1.36-MariaDB or 11.4.4-2-MariaDB-enterprise-log
if ($self->{version} =~ /(?:\d\.\d\.\d-)?(\d+\.\d+\.\d+).*MariaDB/i) {
$self->{version} = $1;
$self->{is_mariadb} = 1;
}
@ -307,13 +307,13 @@ __END__
=head1 NAME
mysqlcmd global
MySQL command global
=head1 SYNOPSIS
mysqlcmd class
MySQL command class
=head1 MYSQLCMD OPTIONS
=head1 MYSQL COMMAND OPTIONS
=over 8

View File

@ -54,7 +54,7 @@ sub load {
sub check {
my ($self) = @_;
foreach my $entry (keys $mapping) {
foreach my $entry (keys %{$mapping}) {
$mapping->{$entry}->{oid} =~ s/#/$self->{blade_id}/;
}

View File

@ -53,7 +53,7 @@ sub load {
sub check {
my ($self) = @_;
foreach my $entry (keys $mapping) {
foreach my $entry (keys %{$mapping}) {
$mapping->{$entry}->{oid} =~ s/#/$self->{blade_id}/;
}

View File

@ -53,7 +53,7 @@ sub load {
sub check {
my ($self) = @_;
foreach my $entry (keys $mapping) {
foreach my $entry (keys %{$mapping}) {
$mapping->{$entry}->{oid} =~ s/#/$self->{blade_id}/;
}

View File

@ -53,7 +53,7 @@ sub load {
sub check {
my ($self) = @_;
foreach my $entry (keys $mapping) {
foreach my $entry (keys %{$mapping}) {
$mapping->{$entry}->{oid} =~ s/#/$self->{blade_id}/;
}

View File

@ -52,7 +52,7 @@ sub load {
sub check {
my ($self) = @_;
foreach my $entry (keys $mapping) {
foreach my $entry (keys %{$mapping}) {
$mapping->{$entry}->{oid} =~ s/#/$self->{blade_id}/;
}

View File

@ -39,7 +39,7 @@ sub load {
sub check {
my ($self) = @_;
foreach my $entry (keys $mapping) {
foreach my $entry (keys %{$mapping}) {
$mapping->{$entry}->{oid} =~ s/#/$self->{blade_id}/;
}

View File

@ -1,215 +0,0 @@
#
# 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 network::juniper::common::ive::mode::users;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
sub custom_node_output {
my ($self, %options) = @_;
if ($self->{result_values}->{node_total} ne '') {
return sprintf(
'concurrent users licenses usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)',
$self->{result_values}->{node_total},
$self->{result_values}->{node_used},
$self->{result_values}->{node_used} * 100 / $self->{result_values}->{node_total},
$self->{result_values}->{node_total} - $self->{result_values}->{node_used},
($self->{result_values}->{node_total} - $self->{result_values}->{node_used}) * 100 / $self->{result_values}->{node_total}
);
} else {
return sprintf(
'concurrent users licenses used: %s',
$self->{result_values}->{node_used}
);
}
}
sub custom_cluster_output {
my ($self, %options) = @_;
if ($self->{result_values}->{cluster_total} ne '') {
return sprintf(
'concurrent cluster users licenses usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)',
$self->{result_values}->{cluster_total},
$self->{result_values}->{cluster_used},
$self->{result_values}->{cluster_used} * 100 / $self->{result_values}->{cluster_total},
$self->{result_values}->{cluster_total} - $self->{result_values}->{cluster_used},
($self->{result_values}->{cluster_total} - $self->{result_values}->{cluster_used}) * 100 / $self->{result_values}->{cluster_total}
);
} else {
return sprintf(
'concurrent cluster users licenses used: %s',
$self->{result_values}->{cluster_used}
);
}
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0, message_separator => ' - ', skipped_code => { -10 => 1 } },
];
$self->{maps_counters}->{global} = [
{ label => 'node-users-usage', nlabel => 'node.users.usage.count', set => {
key_values => [ { name => 'node_used' }, { name => 'node_total' } ],
closure_custom_output => $self->can('custom_node_output'),
perfdatas => [
{ template => '%d', min => 0, max => 'node_total' }
]
}
},
{ label => 'node-users-free', nlabel => 'node.users.free.count', display_ok => 0, set => {
key_values => [ { name => 'node_free' }, { name => 'node_used' }, { name => 'node_total' } ],
closure_custom_output => $self->can('custom_node_output'),
perfdatas => [
{ template => '%d', min => 0, max => 'node_total' }
]
}
},
{ label => 'node-users-usage-prct', nlabel => 'node.users.usage.percentage', display_ok => 0, set => {
key_values => [ { name => 'node_prct_used' }, { name => 'node_free' }, { name => 'node_used' }, { name => 'node_total' } ],
closure_custom_output => $self->can('custom_node_output'),
perfdatas => [
{ template => '%.2f', min => 0, max => 100, unit => '%' }
]
}
},
{ label => 'cluster-users-usage', nlabel => 'cluster.users.usage.count', set => {
key_values => [ { name => 'cluster_used' }, { name => 'cluster_total' } ],
closure_custom_output => $self->can('custom_cluster_output'),
perfdatas => [
{ template => '%d', min => 0, max => 'cluster_total' }
]
}
},
{ label => 'cluster-users-free', nlabel => 'cluster.users.free.count', display_ok => 0, set => {
key_values => [ { name => 'cluster_free' }, { name => 'cluster_used' }, { name => 'cluster_total' } ],
closure_custom_output => $self->can('custom_cluster_output'),
perfdatas => [
{ template => '%d', min => 0, max => 'cluster_total' }
]
}
},
{ label => 'cluster-users-usage-prct', nlabel => 'cluster.users.usage.percentage', display_ok => 0, set => {
key_values => [ { name => 'cluster_prct_used' }, { name => 'cluster_free' }, { name => 'cluster_used' }, { name => 'cluster_total' } ],
closure_custom_output => $self->can('custom_cluster_output'),
perfdatas => [
{ template => '%.2f', min => 0, max => 100, unit => '%' }
]
}
},
{ label => 'web-users-signedin-usage', nlabel => 'web.users.signedin.usage.count', set => {
key_values => [ { name => 'web' } ],
output_template => 'current concurrent signed-in web users connections: %s',
perfdatas => [
{ value => 'web', template => '%s', min => 0 }
]
}
},
{ label => 'meeting-users-usage', nlabel => 'meeting.users.usage.count', set => {
key_values => [ { name => 'meeting' } ],
output_template => 'current concurrent meeting users connections: %s',
perfdatas => [
{ value => 'meeting', template => '%s', min => 0 }
]
}
}
];
}
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 => {
});
return $self;
}
sub manage_selection {
my ($self, %options) = @_;
my $oid_signedInWebUsers = '.1.3.6.1.4.1.12532.2.0';
my $oid_meetingUserCount = '.1.3.6.1.4.1.12532.9.0';
my $oid_iveConcurrentUsers = '.1.3.6.1.4.1.12532.12.0';
my $oid_clusterConcurrentUsers = '.1.3.6.1.4.1.12532.13.0';
my $oid_iveMaxConcurrentUsersLicenseCapacity = '.1.3.6.1.4.1.12532.55.0';
my $result = $options{snmp}->get_leef(
oids => [
$oid_signedInWebUsers, $oid_meetingUserCount,
$oid_iveConcurrentUsers, $oid_clusterConcurrentUsers,
$oid_iveMaxConcurrentUsersLicenseCapacity
],
nothing_quit => 1
);
$self->{global} = {
web => $result->{$oid_signedInWebUsers},
meeting => $result->{$oid_meetingUserCount},
cluster_used => $result->{$oid_clusterConcurrentUsers},
cluster_free => defined($result->{$oid_iveMaxConcurrentUsersLicenseCapacity}) && $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} > 0 ?
$result->{$oid_iveMaxConcurrentUsersLicenseCapacity} - $result->{$oid_clusterConcurrentUsers} : undef,
cluster_total => defined($result->{$oid_iveMaxConcurrentUsersLicenseCapacity}) && $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} > 0 ? $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} : '',
cluster_prct_used =>
defined($result->{$oid_iveMaxConcurrentUsersLicenseCapacity}) && $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} > 0 ?
$result->{$oid_clusterConcurrentUsers} * 100 / $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} : undef,
node_used => $result->{$oid_iveConcurrentUsers},
node_free => defined($result->{$oid_iveMaxConcurrentUsersLicenseCapacity}) && $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} > 0 ?
$result->{$oid_iveMaxConcurrentUsersLicenseCapacity} - $result->{$oid_iveConcurrentUsers} : undef,
node_total => defined($result->{$oid_iveMaxConcurrentUsersLicenseCapacity}) && $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} > 0 ? $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} : '',
node_prct_used =>
defined($result->{$oid_iveMaxConcurrentUsersLicenseCapacity}) && $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} > 0 ?
$result->{$oid_iveConcurrentUsers} * 100 / $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} : undef
};
}
1;
__END__
=head1 MODE
Check users connections (web users, cluster users, node users, meeting users) (JUNIPER-IVE-MIB).
=over 8
=item B<--filter-counters>
Only display some counters (regexp can be used).
Example: --filter-counters='web|meeting'
=item B<--warning-*> B<--critical-*>
Thresholds.
Can be: 'node-users-usage', 'node-users-free', 'node-users-usage-prct',
'cluster-users-usage', 'cluster-users-free', 'cluster-users-usage-prct',
'web-users-signedin-usage', 'meeting-users-usage'.
=back
=cut

View File

@ -18,7 +18,7 @@
# limitations under the License.
#
package network::juniper::common::ive::mode::cpu;
package network::juniper::common::ive::snmp::mode::cpu;
use base qw(centreon::plugins::mode);

View File

@ -18,7 +18,7 @@
# limitations under the License.
#
package network::juniper::common::ive::mode::disk;
package network::juniper::common::ive::snmp::mode::disk;
use base qw(centreon::plugins::mode);

View File

@ -18,7 +18,7 @@
# limitations under the License.
#
package network::juniper::common::ive::mode::logfile;
package network::juniper::common::ive::snmp::mode::logfile;
use base qw(centreon::plugins::mode);

View File

@ -0,0 +1,280 @@
#
# 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 network::juniper::common::ive::snmp::mode::users;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
sub custom_node_output {
my ($self, %options) = @_;
if ($self->{result_values}->{node_total} ne '') {
return sprintf(
'concurrent users licenses usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)',
$self->{result_values}->{node_total},
$self->{result_values}->{node_used},
$self->{result_values}->{node_used} * 100 / $self->{result_values}->{node_total},
$self->{result_values}->{node_total} - $self->{result_values}->{node_used},
($self->{result_values}->{node_total} - $self->{result_values}->{node_used}) * 100 / $self->{result_values}->{node_total}
);
} else {
return sprintf(
'concurrent users licenses used: %s',
$self->{result_values}->{node_used}
);
}
}
sub custom_cluster_output {
my ($self, %options) = @_;
if ($self->{result_values}->{cluster_total} ne '') {
return sprintf(
'concurrent cluster users licenses usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)',
$self->{result_values}->{cluster_total},
$self->{result_values}->{cluster_used},
$self->{result_values}->{cluster_used} * 100 / $self->{result_values}->{cluster_total},
$self->{result_values}->{cluster_total} - $self->{result_values}->{cluster_used},
($self->{result_values}->{cluster_total} - $self->{result_values}->{cluster_used}) * 100 / $self->{result_values}->{cluster_total}
);
} else {
return sprintf(
'concurrent cluster users licenses used: %s',
$self->{result_values}->{cluster_used}
);
}
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0, message_separator => ' - ', skipped_code => { -10 => 1 } },
];
$self->{maps_counters}->{global} = [
{ label => 'node-users-usage', nlabel => 'node.users.usage.count', set => {
key_values => [ { name => 'node_used' }, { name => 'node_total' } ],
closure_custom_output => $self->can('custom_node_output'),
perfdatas => [
{ template => '%d', min => 0, max => 'node_total' }
]
}
},
{ label => 'node-users-free', nlabel => 'node.users.free.count', display_ok => 0, set => {
key_values => [ { name => 'node_free' }, { name => 'node_used' }, { name => 'node_total' } ],
closure_custom_output => $self->can('custom_node_output'),
perfdatas => [
{ template => '%d', min => 0, max => 'node_total' }
]
}
},
{ label => 'node-users-usage-prct', nlabel => 'node.users.usage.percentage', display_ok => 0, set => {
key_values => [ { name => 'node_prct_used' }, { name => 'node_free' }, { name => 'node_used' }, { name => 'node_total' } ],
closure_custom_output => $self->can('custom_node_output'),
perfdatas => [
{ template => '%.2f', min => 0, max => 100, unit => '%' }
]
}
},
{ label => 'cluster-users-usage', nlabel => 'cluster.users.usage.count', set => {
key_values => [ { name => 'cluster_used' }, { name => 'cluster_total' } ],
closure_custom_output => $self->can('custom_cluster_output'),
perfdatas => [
{ template => '%d', min => 0, max => 'cluster_total' }
]
}
},
{ label => 'cluster-users-free', nlabel => 'cluster.users.free.count', display_ok => 0, set => {
key_values => [ { name => 'cluster_free' }, { name => 'cluster_used' }, { name => 'cluster_total' } ],
closure_custom_output => $self->can('custom_cluster_output'),
perfdatas => [
{ template => '%d', min => 0, max => 'cluster_total' }
]
}
},
{ label => 'cluster-users-usage-prct', nlabel => 'cluster.users.usage.percentage', display_ok => 0, set => {
key_values => [ { name => 'cluster_prct_used' }, { name => 'cluster_free' }, { name => 'cluster_used' }, { name => 'cluster_total' } ],
closure_custom_output => $self->can('custom_cluster_output'),
perfdatas => [
{ template => '%.2f', min => 0, max => 100, unit => '%' }
]
}
},
{ label => 'web-users-signedin-usage', nlabel => 'web.users.signedin.usage.count', set => {
key_values => [ { name => 'web' } ],
output_template => 'current concurrent signed-in web users connections: %s',
perfdatas => [
{ value => 'web', template => '%s', min => 0 }
]
}
},
{ label => 'meeting-users-usage', nlabel => 'meeting.users.usage.count', set => {
key_values => [ { name => 'meeting' } ],
output_template => 'current concurrent meeting users connections: %s',
perfdatas => [
{ value => 'meeting', template => '%s', min => 0 }
]
}
}
];
}
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 => {
});
return $self;
}
sub manage_selection {
my ($self, %options) = @_;
my $oid_signedInWebUsers = '.1.3.6.1.4.1.12532.2.0';
my $oid_meetingUserCount = '.1.3.6.1.4.1.12532.9.0';
my $oid_iveConcurrentUsers = '.1.3.6.1.4.1.12532.12.0';
my $oid_clusterConcurrentUsers = '.1.3.6.1.4.1.12532.13.0';
my $oid_iveMaxConcurrentUsersLicenseCapacity = '.1.3.6.1.4.1.12532.55.0';
my $result = $options{snmp}->get_leef(
oids => [
$oid_signedInWebUsers, $oid_meetingUserCount,
$oid_iveConcurrentUsers, $oid_clusterConcurrentUsers,
$oid_iveMaxConcurrentUsersLicenseCapacity
],
nothing_quit => 1
);
$self->{global} = {
web =>
$result->{$oid_signedInWebUsers},
meeting =>
$result->{$oid_meetingUserCount},
cluster_used =>
$result->{$oid_clusterConcurrentUsers},
cluster_free =>
defined($result->{$oid_iveMaxConcurrentUsersLicenseCapacity}) && $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} > 0 ?
$result->{$oid_iveMaxConcurrentUsersLicenseCapacity} - $result->{$oid_clusterConcurrentUsers} : undef,
cluster_total =>
defined($result->{$oid_iveMaxConcurrentUsersLicenseCapacity}) && $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} > 0 ? $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} : '',
cluster_prct_used =>
defined($result->{$oid_iveMaxConcurrentUsersLicenseCapacity}) && $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} > 0 ?
$result->{$oid_clusterConcurrentUsers} * 100 / $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} : undef,
node_used =>
$result->{$oid_iveConcurrentUsers},
node_free =>
defined($result->{$oid_iveMaxConcurrentUsersLicenseCapacity}) && $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} > 0 ?
$result->{$oid_iveMaxConcurrentUsersLicenseCapacity} - $result->{$oid_iveConcurrentUsers} : undef,
node_total =>
defined($result->{$oid_iveMaxConcurrentUsersLicenseCapacity}) && $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} > 0 ? $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} : '',
node_prct_used =>
defined($result->{$oid_iveMaxConcurrentUsersLicenseCapacity}) && $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} > 0 ?
$result->{$oid_iveConcurrentUsers} * 100 / $result->{$oid_iveMaxConcurrentUsersLicenseCapacity} : undef
};
}
1;
__END__
=head1 MODE
Check users connections (web users, cluster users, node users, meeting users) (JUNIPER-IVE-MIB).
=over 8
=item B<--filter-counters>
Only display some counters (regexp can be used).
Example: C<--filter-counters='web|meeting'>
=item B<--warning-node-users-usage>
Warning threshold for node users usage (count).
=item B<--critical-node-users-usage>
Critical threshold for node users usage (count).
=item B<--warning-node-users-free>
Warning threshold for node users free (count).
=item B<--critical-node-users-free>
Critical threshold for node users free (count).
=item B<--warning-node-users-usage-prct>
Warning threshold for node users usage (percentage).
=item B<--critical-node-users-usage-prct>
Critical threshold for node users usage (percentage).
=item B<--warning-cluster-users-usage>
Warning threshold for cluster users usage (count).
=item B<--critical-cluster-users-usage>
Critical threshold for cluster users usage (count).
=item B<--warning-cluster-users-free>
Warning threshold for cluster users free (count).
=item B<--critical-cluster-users-free>
Critical threshold for cluster users free (count).
=item B<--warning-cluster-users-usage-prct>
Warning threshold for cluster users usage (percentage).
=item B<--critical-cluster-users-usage-prct>
Critical threshold for cluster users usage (percentage).
=item B<--warning-web-users-signedin-usage>
Warning threshold for web users signed-in usage (count).
=item B<--critical-web-users-signedin-usage>
Critical threshold for web users signed-in usage (count).
=item B<--warning-meeting-users-usage>
Warning threshold for meeting users usage (count).
=item B<--critical-meeting-users-usage>
Critical threshold for meeting users usage (count).
=back
=cut

View File

@ -0,0 +1,985 @@
#
# 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::juniper::common::junos::netconf::custom::netconf;
use strict;
use warnings;
use centreon::plugins::ssh;
use centreon::plugins::misc;
use XML::LibXML::Simple;
use centreon::plugins::statefile;
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' },
'timeout:s' => { name => 'timeout', default => 45 },
'command:s' => { name => 'command' },
'command-path:s' => { name => 'command_path' },
'command-options:s' => { name => 'command_options' },
'cache-use' => { name => 'cache_use' }
});
}
$options{options}->add_help(package => __PACKAGE__, sections => 'SSH OPTIONS', once => 1);
$self->{output} = $options{output};
$self->{ssh} = centreon::plugins::ssh->new(%options);
$self->{cache} = 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) = @_;
if (defined($self->{option_results}->{hostname}) && $self->{option_results}->{hostname} ne '') {
$self->{ssh}->check_options(
option_results => $self->{option_results},
default_ssh_port => 830
);
if ($self->{ssh}->get_ssh_backend() !~ /^sshcli$/) {
$self->{output}->add_option_msg(short_msg => 'unsupported ssh backend (sshcli only)');
$self->{output}->option_exit();
}
}
centreon::plugins::misc::check_security_command(
output => $self->{output},
command => $self->{option_results}->{command},
command_options => $self->{option_results}->{command_options},
command_path => $self->{option_results}->{command_path}
);
$self->{cache}->check_options(option_results => $self->{option_results}, default_format => 'json');
return 0;
}
sub get_identifier {
my ($self, %options) = @_;
my $id = defined($self->{option_results}->{hostname}) ? $self->{option_results}->{hostname} : 'me';
if (defined($self->{option_results}->{hostname}) && $self->{option_results}->{hostname} ne '') {
$id .= ':' . $self->{ssh}->get_port();
}
return $id;
}
sub load_xml {
my ($self, %options) = @_;
my $content;
if (!defined($options{middle_tag})) {
if ($options{data} =~ /($options{start_tag}.*?$options{end_tag})/ms) {
$content = $1;
}
} else {
while ($options{data} =~ /($options{start_tag}.*?$options{end_tag})/msg) {
my $matched = $1;
if ($1 =~ /$options{middle_tag}/ms) {
$content = $matched;
last;
}
}
}
if (!defined($content)) {
if (defined($options{error_continue}) && $options{error_continue} == 1) {
return {};
}
$self->{output}->add_option_msg(short_msg => "Cannot find information");
$self->{output}->option_exit();
}
$content =~ s/junos://msg;
my $xml_result;
eval {
$SIG{__WARN__} = sub {};
$xml_result = XMLin($content, ForceArray => $options{force_array}, KeyAttr => []);
};
if ($@) {
$self->{output}->add_option_msg(short_msg => "Cannot decode xml response: $@");
$self->{output}->option_exit();
}
return $xml_result;
}
sub execute_command {
my ($self, %options) = @_;
$self->{ssh_commands} = '';
my $append = '';
foreach (@{$options{commands}}) {
$self->{ssh_commands} .= $append . " $_";
$append = "\n]]>]]>\n\n";
}
my $content;
if (defined($self->{option_results}->{hostname}) && $self->{option_results}->{hostname} ne '') {
($content) = $self->{ssh}->execute(
ssh_pipe => 1,
hostname => $self->{option_results}->{hostname},
command => $self->{ssh_commands},
timeout => $self->{option_results}->{timeout},
default_sshcli_option_eol => [ '-s=netconf' ]
);
} else {
if (!defined($self->{option_results}->{command}) || $self->{option_results}->{command} eq '') {
$self->{output}->add_option_msg(short_msg => 'please set --hostname option for ssh connection (or --command for local)');
$self->{output}->option_exit();
}
($content) = centreon::plugins::misc::execute(
ssh_pipe => 1,
output => $self->{output},
options => { timeout => $self->{option_results}->{timeout} },
command => $self->{option_results}->{command},
command_path => $self->{option_results}->{command_path},
command_options => defined($self->{option_results}->{command_options}) && $self->{option_results}->{command_options} ne '' ? $self->{option_results}->{command_options} : undef
);
}
return $content;
}
my $commands = {
'show chassis routing-engine' => '<rpc><get-route-engine-information></get-route-engine-information></rpc>',
'show chassis fpc' => '<rpc><get-fpc-information></get-fpc-information></rpc>',
'show system storage detail' => '<rpc><get-system-storage><detail/></get-system-storage></rpc>',
'show chassis environment' => '<rpc><get-environment-information></get-environment-information></rpc>',
'show chassis power' => '<rpc><get-power-usage-information></get-power-usage-information></rpc>',
'show chassis fan' => '<rpc><get-fan-information></get-fan-information></rpc>',
'show chassis fpc pic-status' => '<rpc><get-pic-information></get-pic-information></rpc>',
'show chassis afeb' => '<rpc><get-afeb-information></get-afeb-information></rpc>',
'show chassis hardware' => '<rpc><get-chassis-inventory></get-chassis-inventory></rpc>',
'show interfaces extensive' => '<rpc><get-interface-information><extensive/></get-interface-information></rpc>',
'show bgp neighbor' => '<rpc><get-bgp-neighbor-information></get-bgp-neighbor-information></rpc>',
'show ldp session extensive' => '<rpc><get-ldp-session-information><extensive/></get-ldp-session-information></rpc>',
'show mpls lsp' => '<rpc><get-mpls-lsp-information><statistics/></get-mpls-lsp-information></rpc>',
'show rsvp session statistics' => '<rpc><get-rsvp-session-information><statistics/></get-rsvp-session-information></rpc>',
'show services rpm probe-results' => '<rpc><get-probe-results></get-probe-results></rpc>',
'show ospf neighbor detail' => '<rpc><get-ospf-neighbor-information><detail/></get-ospf-neighbor-information></rpc>',
'show interfaces diagnostics optics' => '<rpc><get-interface-optics-diagnostics-information /></rpc>'
};
sub get_rpc_commands {
my ($self, %options) = @_;
my $rpc_commands = {};
foreach my $command (@{$options{commands}}) {
next if ($command eq '' || $command !~ /([a-z_]+)/);
my $label = $1;
if ($label eq 'cpu') {
$rpc_commands->{'show chassis routing-engine'} = $commands->{'show chassis routing-engine'};
$rpc_commands->{'show chassis fpc'} = $commands->{'show chassis fpc'};
} elsif ($label eq 'disk') {
$rpc_commands->{'show system storage detail'} = $commands->{'show system storage detail'};
} elsif ($label eq 'hardware') {
$rpc_commands->{'show chassis environment'} = $commands->{'show chassis environment'};
$rpc_commands->{'show chassis power'} = $commands->{'show chassis power'};
$rpc_commands->{'show chassis fan'} = $commands->{'show chassis fan'};
$rpc_commands->{'show chassis fpc'} = $commands->{'show chassis fpc'};
$rpc_commands->{'show chassis fpc pic-status'} = $commands->{'show chassis fpc pic-status'};
$rpc_commands->{'show chassis afeb'} = $commands->{'show chassis afeb'};
$rpc_commands->{'show chassis hardware'} = $commands->{'show chassis hardware'};
} elsif ($label eq 'interface') {
$rpc_commands->{'show interfaces extensive'} = $commands->{'show interfaces extensive'};
} elsif ($label eq 'interface_optical') {
$rpc_commands->{'show interfaces diagnostics optics'} = $commands->{'show interfaces diagnostics optics'};
} elsif ($label eq 'memory') {
$rpc_commands->{'show chassis routing-engine'} = $commands->{'show chassis routing-engine'};
$rpc_commands->{'show chassis fpc'} = $commands->{'show chassis fpc'};
} elsif ($label eq 'bgp') {
$rpc_commands->{'show bgp neighbor'} = $commands->{'show bgp neighbor'};
} elsif ($label eq 'ldp') {
$rpc_commands->{'show ldp session extensive'} = $commands->{'show ldp session extensive'};
} elsif ($label eq 'lsp') {
$rpc_commands->{'show mpls lsp'} = $commands->{'show mpls lsp'};
} elsif ($label eq 'rsvp') {
$rpc_commands->{'show rsvp session statistics'} = $commands->{'show rsvp session statistics'};
} elsif ($label eq 'service_rpm') {
$rpc_commands->{'show services rpm probe-results'} = $commands->{'show services rpm probe-results'};
} elsif ($label eq 'ospf') {
$rpc_commands->{'show ospf neighbor detail'} = $commands->{'show ospf neighbor detail'};
} else {
$self->{output}->add_option_msg(short_msg => "unsupported command: $command");
$self->{output}->option_exit();
}
}
return [ values(%$rpc_commands) ];
}
sub get_cache_file_response_command {
my ($self, %options) = @_;
$self->{cache}->read(statefile => 'cache_juniper_api_' . $self->get_identifier());
my $response = $self->{cache}->get(name => 'response');
if (!defined($response)) {
$self->{output}->add_option_msg(short_msg => 'Cache file missing');
$self->{output}->option_exit();
}
if (!defined($response->{ $options{command} })) {
$self->{output}->add_option_msg(short_msg => "Command '$options{command} missing in cache file");
$self->{output}->option_exit();
}
return $response->{ $options{command }};
}
sub cache_commands {
my ($self, %options) = @_;
my $content = $self->execute_command(commands => $self->get_rpc_commands(commands => $options{commands}));
my $response = {};
foreach my $command (@{$options{commands}}) {
next if ($command eq '' || $command !~ /([a-z]+)/);
my $label = $1;
my $method = $self->can('get_' . $label . '_infos');
if ($method) {
my $result = $self->$method(content => $content);
$response->{$label} = $result;
}
}
$self->{cache}->read(statefile => 'cache_juniper_api_' . $self->get_identifier());
$self->{cache}->write(data => {
update_time => time(),
response => $response
});
}
sub get_command_raw_result {
my ($self, %options) = @_;
my $content = $self->execute_command(commands => [ $options{command} ]);
$content =~ /(<nc:rpc-reply.*<\/nc:rpc-reply>)/msg;
$content = $1;
$content =~ s/junos://msg;
return $content;
}
sub get_cpu_infos {
my ($self, %options) = @_;
if (defined($self->{option_results}->{cache_use})) {
return $self->get_cache_file_response_command(command => 'cpu');
}
my $content = $options{content};
if (!defined($content)) {
$content = $self->execute_command(commands => $self->get_rpc_commands(commands => [ 'cpu' ]));
}
my $results = [];
my $result = $self->load_xml(data => $content, start_tag => '<route-engine-information.*?>', end_tag => '</route-engine-information>', force_array => [ 'route-engine' ]);
foreach (@{$result->{'route-engine'}}) {
push @$results, {
name => 'route engine slot ' . $_->{slot},
cpu_1min_avg => 100 - $_->{'cpu-idle1'},
cpu_5min_avg => 100 - $_->{'cpu-idle2'},
cpu_15min_avg => 100 - $_->{'cpu-idle3'}
};
}
$result = $self->load_xml(data => $content, start_tag => '<fpc-information.*?>', end_tag => '</fpc-information>', force_array => [ 'fpc' ]);
foreach (@{$result->{fpc}}) {
next if (!defined($_->{'cpu-1min-avg'}));
push @$results, {
name => 'fpc slot ' . $_->{slot},
cpu_1min_avg => $_->{'cpu-1min-avg'},
cpu_5min_avg => $_->{'cpu-5min-avg'},
cpu_15min_avg => $_->{'cpu-15min-avg'}
};
}
return $results;
}
sub get_disk_infos {
my ($self, %options) = @_;
if (defined($self->{option_results}->{cache_use})) {
return $self->get_cache_file_response_command(command => 'disk');
}
my $content = $options{content};
if (!defined($content)) {
$content = $self->execute_command(commands => $self->get_rpc_commands(commands => [ 'disk' ]));
}
my $results = [];
my $result = $self->load_xml(data => $content, start_tag => '<system-storage-information.*?>', end_tag => '</system-storage-information>', force_array => [ 'filesystem' ]);
foreach (@{$result->{filesystem}}) {
push @$results, {
mount => centreon::plugins::misc::trim($_->{'mounted-on'}),
space_used => $_->{'used-blocks'}->{format} * 1024,
space_total => $_->{'total-blocks'}->{format} * 1024,
space_free => $_->{'available-blocks'}->{format} * 1024,
space_used_prct => centreon::plugins::misc::trim($_->{'used-percent'}),
space_free_prct => 100 - centreon::plugins::misc::trim($_->{'used-percent'})
};
}
return $results;
}
sub get_hardware_infos {
my ($self, %options) = @_;
if (defined($self->{option_results}->{cache_use})) {
return $self->get_cache_file_response_command(command => 'hardware');
}
my $content = $options{content};
if (!defined($content)) {
$content = $self->execute_command(commands => $self->get_rpc_commands(commands => [ 'hardware' ]));
}
my $results = { 'fan' => [], 'psu' => [], 'env' => [], 'fpc' => [], 'pic' => {}, mic => {}, 'afeb' => [] };
my $result = $self->load_xml(data => $content, start_tag => '<fan-information.*?>', end_tag => '</fan-information>', force_array => [ 'fan-information-rpm-item' ]);
foreach (@{$result->{'fan-information-rpm-item'}}) {
push @{$results->{fan}}, {
name => $_->{name},
status => $_->{status},
rpm => $_->{rpm}
};
}
$result = $self->load_xml(data => $content, start_tag => '<power-usage-information.*?>', end_tag => '</power-usage-information>', force_array => [ 'power-usage-item' ], error_continue => 1);
if (defined($result->{'power-usage-item'})) {
foreach (@{$result->{'power-usage-item'}}) {
push @{$results->{psu}}, {
name => $_->{name},
status => $_->{state},
dc_output_load => $_->{'dc-output-detail'}->{'dc-load'}
};
}
}
$result = $self->load_xml(data => $content, start_tag => '<environment-information.*?>', end_tag => '</environment-information>', force_array => [ 'environment-item' ]);
foreach (@{$result->{'environment-item'}}) {
my $temperature = '';
if ($_->{class} eq 'Temp') {
$temperature = $_->{temperature}->{celsius};
}
push @{$results->{env}}, {
name => $_->{name},
status => $_->{status},
class => $_->{class},
temperature => $temperature
};
}
$result = $self->load_xml(
data => $content,
start_tag => '<fpc-information',
end_tag => '</fpc-information>',
middle_tag => 'fpc.*?(cpu-15min-avg|memory-dram-size)',
force_array => [ 'fpc' ]
);
foreach (@{$result->{fpc}}) {
push @{$results->{fpc}}, {
name => 'fpc slot ' . $_->{slot},
status => $_->{state}
};
}
$result = $self->load_xml(data => $content, start_tag => '<scb-information.*?>', end_tag => '</scb-information>', force_array => [ 'scb' ], error_continue => 1);
foreach (@{$result->{scb}}) {
push @{$results->{afeb}}, {
name => 'afeb slot ' . $_->{slot},
status => $_->{state}
};
}
$result = $self->load_xml(
data => $content,
start_tag => '<fpc-information',
end_tag => '</fpc-information>',
middle_tag => 'pic-state',
force_array => [ 'fpc', 'pic' ],
error_continue => 1
);
foreach my $fpc (@{$result->{fpc}}) {
foreach (@{$fpc->{pic}}) {
$results->{pic}->{'fpc' . $fpc->{slot} . '-pic' . $_->{'pic-slot'}} = {
fpc_slot => $fpc->{slot},
pic_slot => $_->{'pic-slot'},
description => $_->{'pic-type'},
instance => $fpc->{slot} . '/' . $_->{'pic-slot'},
status => $_->{'pic-state'}
};
}
}
$result = $self->load_xml(
data => $content,
start_tag => '<chassis-inventory.*?>',
end_tag => '</chassis-inventory>',
force_array => [ 'chassis-sub-module', 'chassis-sub-sub-module' ]
);
foreach my $module (@{$result->{chassis}->{'chassis-module'}}) {
next if ($module->{name} !~ /FPC\s+(\d+)/ || !defined($module->{'chassis-sub-module'}));
my $fpc_slot = $1;
foreach my $submodule (@{$module->{'chassis-sub-module'}}) {
next if ($submodule->{name} !~ /MIC\s+(\d+)/ || !defined($submodule->{'chassis-sub-sub-module'}));
my $mic_slot = $1;
$results->{mic}->{'fpc' . $fpc_slot . '-mic' . $mic_slot} = {
pics => [],
fpc_slot => $fpc_slot,
mic_slot => $mic_slot,
instance => $fpc_slot . '/' . $mic_slot,
description => $submodule->{description}
};
foreach my $subsubmodule (@{$submodule->{'chassis-sub-sub-module'}}) {
next if ($subsubmodule->{name} !~ /PIC\s+(\d+)/);
my $pic_slot = $1;
push @{$results->{mic}->{'fpc' . $fpc_slot . '-mic' . $mic_slot}->{pics}}, 'fpc' . $fpc_slot . '-pic' . $pic_slot;
$results->{pic}->{'fpc' . $fpc_slot . '-pic' . $pic_slot}->{mic_slot} = $mic_slot;
$results->{pic}->{'fpc' . $fpc_slot . '-pic' . $pic_slot}->{instance} = $fpc_slot . '/' . $mic_slot . '/' . $pic_slot;
}
}
}
return $results;
}
sub get_interface_infos {
my ($self, %options) = @_;
if (defined($self->{option_results}->{cache_use})) {
return $self->get_cache_file_response_command(command => 'interface');
}
my $content = $options{content};
if (!defined($content)) {
$content = $self->execute_command(commands => $self->get_rpc_commands(commands => [ 'interface' ]));
}
my $results = [];
my $result = $self->load_xml(
data => $content,
start_tag => '<interface-information',
end_tag => '</interface-information>',
middle_tag => 'admin-status',
force_array => [ 'physical-interface', 'logical-interface' ]
);
foreach (@{$result->{'physical-interface'}}) {
my $speed = centreon::plugins::misc::trim($_->{'speed'});
my ($speed_unit, $speed_value);
if ($speed =~ /^\s*([0-9]+)\s*([A-Za-z])/) {
($speed_value, $speed_unit) = ($1, $2);
}
$speed = centreon::plugins::misc::scale_bytesbit(
value => $speed_value,
src_quantity => $speed_unit,
dst_quantity => '',
src_unit => 'b',
dst_unit => 'b'
);
my $descr = centreon::plugins::misc::trim($_->{'description'});
my $name = centreon::plugins::misc::trim($_->{'name'});
my $item = {
descr => defined($descr) && $descr ne '' ? $descr : $name,
name => $name,
opstatus => centreon::plugins::misc::trim($_->{'oper-status'}),
admstatus => centreon::plugins::misc::trim($_->{'admin-status'}->{content}),
in => centreon::plugins::misc::trim($_->{'traffic-statistics'}->{'input-bytes'}) * 8,
out => centreon::plugins::misc::trim($_->{'traffic-statistics'}->{'output-bytes'}) * 8,
inPkts => centreon::plugins::misc::trim($_->{'traffic-statistics'}->{'input-packets'}),
outPkts => centreon::plugins::misc::trim($_->{'traffic-statistics'}->{'output-packets'}),
speed => $speed
};
if (defined($_->{'input-error-list'})) {
foreach my $label (keys %{$_->{'input-error-list'}}) {
$item->{'counter-in-' . $label} = centreon::plugins::misc::trim($_->{'input-error-list'}->{$label});
}
}
if (defined($_->{'output-error-list'})) {
foreach my $label (keys %{$_->{'output-error-list'}}) {
$item->{'counter-out-' . $label} = centreon::plugins::misc::trim($_->{'output-error-list'}->{$label});
}
}
push @$results, $item;
foreach my $logint (@{$_->{'logical-interface'}}) {
push @$results, {
descr => centreon::plugins::misc::trim($logint->{'name'}),
name => centreon::plugins::misc::trim($logint->{'name'}),
opstatus => centreon::plugins::misc::trim($_->{'oper-status'}),
admstatus => centreon::plugins::misc::trim($_->{'admin-status'}->{content}),
in => centreon::plugins::misc::trim($logint->{'traffic-statistics'}->{'input-bytes'}) * 8,
out => centreon::plugins::misc::trim($logint->{'traffic-statistics'}->{'output-bytes'}) * 8,
speed => $speed
};
}
}
return $results;
}
sub get_interface_optical_infos {
my ($self, %options) = @_;
if (defined($self->{option_results}->{cache_use})) {
return $self->get_cache_file_response_command(command => 'interface_optical');
}
my $content = $options{content};
if (!defined($content)) {
$content = $self->execute_command(commands => $self->get_rpc_commands(commands => [ 'interface_optical' ]));
}
my $results = [];
my $result = $self->load_xml(
data => $content,
start_tag => '<interface-information',
end_tag => '</interface-information>',
middle_tag => 'optics-diagnostics',
force_array => [ 'physical-interface' ]
);
foreach (@{$result->{'physical-interface'}}) {
my $entry = { name => centreon::plugins::misc::trim($_->{'name'}) };
if (defined($_->{'optics-diagnostics'}->{'laser-output-power-dbm'})) {
$entry->{outputPowerDbm} = centreon::plugins::misc::trim($_->{'optics-diagnostics'}->{'laser-output-power-dbm'});
$entry->{outputPowerDbmLowAlarmCrit} = centreon::plugins::misc::trim($_->{'optics-diagnostics'}->{'laser-tx-power-low-alarm-threshold-dbm'});
$entry->{outputPowerDbmHighAlarmCrit} = centreon::plugins::misc::trim($_->{'optics-diagnostics'}->{'laser-tx-power-high-alarm-threshold-dbm'});
$entry->{outputPowerDbmLowAlarmWarn} = centreon::plugins::misc::trim($_->{'optics-diagnostics'}->{'laser-tx-power-low-warn-threshold-dbm'});
$entry->{outputPowerDbmHighAlarmWarn} = centreon::plugins::misc::trim($_->{'optics-diagnostics'}->{'laser-tx-power-high-warn-threshold-dbm'});
}
if (defined($_->{'optics-diagnostics'}->{'laser-input-power-dbm'})) {
$entry->{inputPowerDbm} = centreon::plugins::misc::trim($_->{'optics-diagnostics'}->{'laser-input-power-dbm'});
$entry->{inputPowerDbmLowAlarmCrit} = centreon::plugins::misc::trim($_->{'optics-diagnostics'}->{'laser-rx-power-low-alarm-threshold-dbm'});
$entry->{inputPowerDbmHighAlarmCrit} = centreon::plugins::misc::trim($_->{'optics-diagnostics'}->{'laser-rx-power-high-alarm-threshold-dbm'});
$entry->{inputPowerDbmLowAlarmWarn} = centreon::plugins::misc::trim($_->{'optics-diagnostics'}->{'laser-rx-power-low-warn-threshold-dbm'});
$entry->{inputPowerDbmHighAlarmWarn} = centreon::plugins::misc::trim($_->{'optics-diagnostics'}->{'laser-rx-power-high-warn-threshold-dbm'});
}
$entry->{biasCurrent} = centreon::plugins::misc::trim($_->{'optics-diagnostics'}->{'laser-bias-current'});
$entry->{moduleTemperature} = centreon::plugins::misc::trim($_->{'optics-diagnostics'}->{'module-temperature'}->{celsius});
push @$results, $entry;
}
return $results;
}
sub get_memory_infos {
my ($self, %options) = @_;
if (defined($self->{option_results}->{cache_use})) {
return $self->get_cache_file_response_command(command => 'memory');
}
my $content = $options{content};
if (!defined($content)) {
$content = $self->execute_command(commands => $self->get_rpc_commands(commands => [ 'memory' ]));
}
my $results = [];
my $result = $self->load_xml(data => $content, start_tag => '<route-engine-information.*?>', end_tag => '</route-engine-information>', force_array => [ 'route-engine' ]);
foreach (@{$result->{'route-engine'}}) {
push @$results, {
name => 'route engine slot ' . $_->{slot},
mem_used => $_->{'memory-buffer-utilization'}
};
}
$result = $self->load_xml(data => $content, start_tag => '<fpc-information.*?>', end_tag => '</fpc-information>', force_array => [ 'fpc' ]);
foreach (@{$result->{fpc}}) {
next if (!defined($_->{'memory-heap-utilization'}));
push @$results, {
name => 'fpc slot ' . $_->{slot} . ' heap',
mem_used => $_->{'memory-heap-utilization'}
}, {
name => 'fpc slot ' . $_->{slot} . ' buffer',
mem_used => $_->{'memory-buffer-utilization'}
};
}
return $results;
}
sub get_ospf_infos {
my ($self, %options) = @_;
if (defined($self->{option_results}->{cache_use})) {
return $self->get_cache_file_response_command(command => 'ospf');
}
my $content = $options{content};
if (!defined($content)) {
$content = $self->execute_command(commands => $self->get_rpc_commands(commands => [ 'ospf' ]));
}
my $results = [];
my $result = $self->load_xml(data => $content, start_tag => '<ospf-neighbor-informatio.*?>', end_tag => '</ospf-neighbor-information>', force_array => [ 'ospf-neighbor' ]);
foreach (@{$result->{'ospf-neighbor'}}) {
push @$results, {
neighborId => $_->{'neighbor-id'},
neighborAddress => $_->{'neighbor-address'},
interfaceName => $_->{'interface-name'},
state => $_->{'ospf-neighbor-state'}
};
}
return $results;
}
sub get_bgp_infos {
my ($self, %options) = @_;
if (defined($self->{option_results}->{cache_use})) {
return $self->get_cache_file_response_command(command => 'bgp');
}
my $content = $options{content};
if (!defined($content)) {
$content = $self->execute_command(commands => $self->get_rpc_commands(commands => [ 'bgp' ]));
}
my $results = [];
my $result = $self->load_xml(data => $content, start_tag => '<bgp-information.*?>', end_tag => '</bgp-information>', force_array => [ 'bgp-peer', 'bgp-rib' ]);
foreach my $item (@{$result->{'bgp-peer'}}) {
my $ribs = [];
foreach (@{$item->{'bgp-rib'}}) {
push @$ribs, {
ribName => $_->{name},
sendState => $_->{'send-state'},
activePrefix => $_->{'active-prefix-count'}
};
}
$item->{'local-address'} =~ s/\+/:/g;
$item->{'peer-address'} =~ s/\+/:/g;
push @$results, {
snmpIndex => $item->{'snmp-index'},
localAddr => $item->{'local-address'},
localAs => $item->{'local-as'},
peerAddr => $item->{'peer-address'},
peerAs => $item->{'peer-as'},
peerState => $item->{'peer-state'},
inBytes => $item->{'input-octets'},
outBytes => $item->{'output-octets'},
ribs => $ribs
};
}
return $results;
}
sub get_ldp_infos {
my ($self, %options) = @_;
if (defined($self->{option_results}->{cache_use})) {
return $self->get_cache_file_response_command(command => 'ldp');
}
my $content = $options{content};
if (!defined($content)) {
$content = $self->execute_command(commands => $self->get_rpc_commands(commands => [ 'ldp' ]));
}
my $results = [];
my $result = $self->load_xml(data => $content, start_tag => '<ldp-session-information.*?>', end_tag => '</ldp-session-information>', force_array => [ 'ldp-session', 'ldp-session-statistics' ]);
foreach my $item (@{$result->{'ldp-session'}}) {
my $stats = [];
foreach (@{$item->{'ldp-session-statistics'}}) {
push @$stats, {
messageType => lc($_->{'ldp-message-type'}),
sent => $_->{'ldp-messages-sent'},
received => $_->{'ldp-messages-received'}
};
}
push @$results, {
id => $item->{'ldp-session-id'},
remoteAddress => $item->{'ldp-remote-address'},
sessionState => $item->{'ldp-session-state'},
connectionState => $item->{'ldp-connection-state'},
stats => $stats
};
}
return $results;
}
sub get_lsp_infos {
my ($self, %options) = @_;
if (defined($self->{option_results}->{cache_use})) {
return $self->get_cache_file_response_command(command => 'lsp');
}
my $content = $options{content};
if (!defined($content)) {
$content = $self->execute_command(commands => $self->get_rpc_commands(commands => [ 'lsp' ]));
}
my $results = [];
my $result = $self->load_xml(data => $content, start_tag => '<mpls-lsp-information.*?>', end_tag => '</mpls-lsp-information>', force_array => [ 'rsvp-session-data', 'rsvp-session' ]);
foreach my $item (@{$result->{'rsvp-session-data'}}) {
foreach (@{$item->{'rsvp-session'}}) {
my $lsp = $_;
if (defined($_->{'mpls-lsp'})) {
$lsp = $_->{'mpls-lsp'};
}
push @$results, {
type => $item->{'session-type'},
name => $lsp->{name},
srcAddress => $lsp->{'source-address'},
dstAddress => $lsp->{'destination-address'},
lspState => $lsp->{'lsp-state'},
lspBytes => $lsp->{'lsp-bytes'}
};
}
}
return $results;
}
sub get_rsvp_infos {
my ($self, %options) = @_;
if (defined($self->{option_results}->{cache_use})) {
return $self->get_cache_file_response_command(command => 'rsvp');
}
my $content = $options{content};
if (!defined($content)) {
$content = $self->execute_command(commands => $self->get_rpc_commands(commands => [ 'rsvp' ]));
}
my $result = $self->load_xml(data => $content, start_tag => '<rsvp-session-information.*?>', end_tag => '</rsvp-session-information>', force_array => [ 'rsvp-session-data', 'rsvp-session' ]);
my $results = [];
foreach my $item (@{$result->{'rsvp-session-data'}}) {
foreach (@{$item->{'rsvp-session'}}) {
my $bytes = 0;
if ($_->{'lsp-bytes'} =~ /([0-9]+)/) {
$bytes = $1;
}
push @$results, {
type => $item->{'session-type'},
name => $_->{name},
srcAddress => $_->{'source-address'},
dstAddress => $_->{'destination-address'},
lspState => $_->{'lsp-state'},
lspBytes => $bytes
};
}
}
return $results;
}
sub get_service_rpm_infos {
my ($self, %options) = @_;
if (defined($self->{option_results}->{cache_use})) {
return $self->get_cache_file_response_command(command => 'service_rpm');
}
my $content = $options{content};
if (!defined($content)) {
$content = $self->execute_command(commands => $self->get_rpc_commands(commands => [ 'service_rpm' ]));
}
my $results = [];
my $result = $self->load_xml(data => $content, start_tag => '<probe-results.*?>', end_tag => '</probe-results>', force_array => [ 'probe-test-results' ]);
foreach (@{$result->{'probe-test-results'}}) {
push @$results, {
testName =>
$_->{'test-name'},
targetAddress =>
$_->{'target-address'},
sourceAddress =>
$_->{'source-address'},
probeType =>
$_->{'probe-type'},
probeStatus =>
$_->{'probe-single-results'}->{'probe-status'},
lastLossPercentage =>
$_->{'probe-last-test-results'}->{'probe-test-generic-results'}->{'loss-percentage'},
lastRTTAvgDelay =>
centreon::plugins::misc::trim($_->{'probe-last-test-results'}->{'probe-test-generic-results'}->{'probe-test-rtt'}->{'probe-summary-results'}->{'avg-delay'}->{content}),
lastRTTJitterDelay =>
centreon::plugins::misc::trim($_->{'probe-last-test-results'}->{'probe-test-generic-results'}->{'probe-test-rtt'}->{'probe-summary-results'}->{'jitter-delay'}->{content}),
lastRTTStdevDelay =>
centreon::plugins::misc::trim($_->{'probe-last-test-results'}->{'probe-test-generic-results'}->{'probe-test-rtt'}->{'probe-summary-results'}->{'stddev-delay'}->{content}),
lastPRTJAvgDelay =>
centreon::plugins::misc::trim($_
->{'probe-last-test-results'}
->{'probe-test-generic-results'}
->{'probe-test-positive-round-trip-jitter'}
->{'probe-summary-results'}
->{'avg-delay'}
->{content}),
lastPRTJJitterDelay =>
centreon::plugins::misc::trim($_
->{'probe-last-test-results'}
->{'probe-test-generic-results'}
->{'probe-test-positive-round-trip-jitter'}
->{'probe-summary-results'}
->{'jitter-delay'}
->{content}),
lastPRTJStdevDelay =>
centreon::plugins::misc::trim($_
->{'probe-last-test-results'}
->{'probe-test-generic-results'}
->{'probe-test-positive-round-trip-jitter'}
->{'probe-summary-results'}
->{'stddev-delay'}
->{content}),
lastNRTJAvgDelay =>
centreon::plugins::misc::trim($_
->{'probe-last-test-results'}
->{'probe-test-generic-results'}
->{'probe-test-negative-round-trip-jitter'}
->{'probe-summary-results'}
->{'avg-delay'}
->{content}),
lastNRTJJitterDelay =>
centreon::plugins::misc::trim($_
->{'probe-last-test-results'}
->{'probe-test-generic-results'}
->{'probe-test-negative-round-trip-jitter'}
->{'probe-summary-results'}
->{'jitter-delay'}
->{content}),
lastNRTJStdevDelay =>
centreon::plugins::misc::trim($_
->{'probe-last-test-results'}
->{'probe-test-generic-results'}
->{'probe-test-negative-round-trip-jitter'}
->{'probe-summary-results'}
->{'stddev-delay'}
->{content}),
};
}
return $results;
}
1;
__END__
=head1 NAME
ssh
=head1 SYNOPSIS
my ssh
=head1 SSH OPTIONS
=over 8
=item B<--hostname>
Hostname to query.
=item B<--timeout>
Timeout in seconds for the command (default: 45).
=item B<--command>
Command to get information. Used it you have output in a file.
=item B<--command-path>
Command path.
=item B<--command-options>
Command options.
=item B<--cache-use>
Use the cache file (created with cache mode).
=back
=head1 DESCRIPTION
B<custom>.
=cut

View File

@ -0,0 +1,408 @@
#
# 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::juniper::common::junos::netconf::mode::bgp;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
use Digest::MD5 qw(md5_hex);
sub custom_bgp_rib_perfdata {
my ($self) = @_;
my $instances = [];
foreach (@{$self->{instance_mode}->{custom_perfdata_instances_bgp_rib}}) {
push @$instances, $self->{result_values}->{$_};
}
$self->{output}->perfdata_add(
nlabel => $self->{nlabel},
instances => $instances,
value => sprintf('%d', $self->{result_values}->{ $self->{key_values}->[0]->{name} }),
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_bgp_peer_perfdata {
my ($self) = @_;
my $instances = [];
foreach (@{$self->{instance_mode}->{custom_perfdata_instances_bgp_peer}}) {
push @$instances, $self->{result_values}->{$_};
}
$self->{output}->perfdata_add(
nlabel => $self->{nlabel},
instances => $instances,
value => sprintf('%d', $self->{result_values}->{ $self->{key_values}->[0]->{name} }),
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_rib_status_output {
my ($self, %options) = @_;
return sprintf(
'send state: %s',
$self->{result_values}->{sendState}
);
}
sub custom_status_output {
my ($self, %options) = @_;
return sprintf(
'state: %s',
$self->{result_values}->{peerState}
);
}
sub bgp_long_output {
my ($self, %options) = @_;
return sprintf(
"checking BGP peer [local address '%s', AS '%s'][peer address '%s', AS '%s']",
$options{instance_value}->{localAddr},
$options{instance_value}->{localAs},
$options{instance_value}->{peerAddr},
$options{instance_value}->{peerAs}
);
}
sub prefix_bgp_output {
my ($self, %options) = @_;
return sprintf(
"BGP peer [local address '%s', AS '%s'][peer address '%s', AS '%s'] ",
$options{instance_value}->{localAddr},
$options{instance_value}->{localAs},
$options{instance_value}->{peerAddr},
$options{instance_value}->{peerAs}
);
}
sub prefix_rib_output {
my ($self, %options) = @_;
return sprintf(
"RIB '%s' ",
$options{instance_value}->{ribName}
);
}
sub prefix_traffic_output {
my ($self, %options) = @_;
return 'traffic ';
}
sub prefix_global_output {
my ($self, %options) = @_;
return 'Number of BGP peers ';
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0, cb_prefix_output => 'prefix_global_output' },
{ name => 'bgp', type => 3, cb_prefix_output => 'prefix_bgp_output', cb_long_output => 'bgp_long_output',
indent_long_output => ' ', message_multiple => 'All BGP peers are ok',
group => [
{ name => 'status', type => 0, skipped_code => { -10 => 1 } },
{ name => 'traffic', type => 0, cb_prefix_output => 'prefix_traffic_output', skipped_code => { -10 => 1 } },
{ name => 'ribs', display_long => 1, cb_prefix_output => 'prefix_rib_output',
message_multiple => 'All BGP ribs are ok', type => 1 }
]
}
];
$self->{maps_counters}->{global} = [
{ label => 'bgp-peer-detected', display_ok => 0, nlabel => 'bgp.peers.detected.count', set => {
key_values => [ { name => 'detected' } ],
output_template => 'detected: %s',
perfdatas => [
{ template => '%s', min => 0 }
]
}
}
];
$self->{maps_counters}->{status} = [
{
label => 'status',
type => 2,
critical_default => '%{peerState} !~ /established/i',
set => {
key_values => [
{ name => 'localAddr' }, { name => 'localAs' }, { name => 'peerAddr' }, { name => 'peerAs' },
{ name => 'peerState' }
],
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}->{traffic} = [
{ label => 'bgp-peer-traffic-in', nlabel => 'bgp.peer.traffic.in.bytes', set => {
key_values => [ { name => 'inBytes', diff => 1 }, { name => 'localAddr' }, { name => 'localAs' }, { name => 'peerAddr' }, { name => 'peerAs' } ],
output_template => 'in: %s %s',
output_change_bytes => 1,
closure_custom_perfdata => $self->can('custom_bgp_peer_perfdata')
}
},
{ label => 'bgp-peer-traffic-out', nlabel => 'bgp.peer.traffic.out.bytes', set => {
key_values => [ { name => 'outBytes', diff => 1 }, { name => 'localAddr' }, { name => 'localAs' }, { name => 'peerAddr' }, { name => 'peerAs' } ],
output_template => 'out: %s %s',
output_change_bytes => 1,
closure_custom_perfdata => $self->can('custom_bgp_peer_perfdata')
}
}
];
$self->{maps_counters}->{ribs} = [
{
label => 'rib-status',
type => 2,
set => {
key_values => [
{ name => 'localAddr' }, { name => 'localAs' }, { name => 'peerAddr' }, { name => 'peerAs' },
{ name => 'ribName' }, { name => 'sendState' }
],
closure_custom_output => $self->can('custom_rib_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
{ label => 'bgp-peer-rib-prefixes-active', nlabel => 'bgp.peer.rib.prefixes.active.count', set => {
key_values => [
{ name => 'activePrefix' }, { name => 'localAddr' }, { name => 'localAs' }, { name => 'peerAddr' }, { name => 'peerAs' },
{ name => 'ribName' }
],
output_template => 'prefixes active: %d',
closure_custom_perfdata => $self->can('custom_bgp_rib_perfdata')
}
}
];
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
'filter-snmp-index:s' => { name => 'filter_snmp_index' },
'filter-local-address:s' => { name => 'filter_local_address' },
'filter-peer-address:s' => { name => 'filter_peer_address' },
'filter-rib-name:s' => { name => 'filter_rib_name' },
'custom-perfdata-instances-bgp-peer:s' => { name => 'custom_perfdata_instances_bgp_peer' },
'custom-perfdata-instances-bgp-rib:s' => { name => 'custom_perfdata_instances_bgp_rib' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
if (!defined($self->{option_results}->{custom_perfdata_instances_bgp_peer}) || $self->{option_results}->{custom_perfdata_instances_bgp_peer} eq '') {
$self->{option_results}->{custom_perfdata_instances_bgp_peer} = '%(localAddr) %(peerAddr)';
}
$self->{custom_perfdata_instances_bgp_peer} = $self->custom_perfdata_instances(
option_name => '--custom-perfdata-instances-bgp-peer',
instances => $self->{option_results}->{custom_perfdata_instances_bgp_peer},
labels => { localAddr => 1, localAs => 1, peerAddr => 1, peerAs => 1 }
);
if (!defined($self->{option_results}->{custom_perfdata_instances_bgp_rib}) || $self->{option_results}->{custom_perfdata_instances_bgp_rib} eq '') {
$self->{option_results}->{custom_perfdata_instances_bgp_rib} = '%(localAddr) %(peerAddr) %(ribName)';
}
$self->{custom_perfdata_instances_bgp_rib} = $self->custom_perfdata_instances(
option_name => '--custom-perfdata-instances-bgp-rib',
instances => $self->{option_results}->{custom_perfdata_instances_bgp_rib},
labels => { localAddr => 1, localAs => 1, peerAddr => 1, peerAs => 1, ribName => 1 }
);
}
sub manage_selection {
my ($self, %options) = @_;
my $result = $options{custom}->get_bgp_infos();
$self->{global} = { detected => 0 };
$self->{bgp} = {};
foreach my $item (@$result) {
next if (defined($self->{option_results}->{filter_snmp_index}) && $self->{option_results}->{filter_snmp_index} ne '' &&
$item->{snmpIndex} !~ /$self->{option_results}->{filter_snmp_index}/);
next if (defined($self->{option_results}->{filter_local_address}) && $self->{option_results}->{filter_local_address} ne '' &&
$item->{localAddr} !~ /$self->{option_results}->{filter_local_address}/);
next if (defined($self->{option_results}->{filter_peer_address}) && $self->{option_results}->{filter_peer_address} ne '' &&
$item->{peerAddr} !~ /$self->{option_results}->{filter_peer_address}/);
$self->{bgp}->{ $item->{snmpIndex} } = {
localAddr => $item->{localAddr},
localAs => $item->{localAs},
peerAddr => $item->{peerAddr},
peerAs => $item->{peerAs},
status => {
localAddr => $item->{localAddr},
localAs => $item->{localAs},
peerAddr => $item->{peerAddr},
peerAs => $item->{peerAs},
peerState => $item->{peerState}
},
traffic => {
localAddr => $item->{localAddr},
localAs => $item->{localAs},
peerAddr => $item->{peerAddr},
peerAs => $item->{peerAs},
inBytes => $item->{inBytes},
outBytes => $item->{outBytes}
},
ribs => {}
};
foreach (@{$item->{ribs}}) {
next if (defined($self->{option_results}->{filter_rib_name}) && $self->{option_results}->{filter_rib_name} ne '' &&
$_->{ribName} !~ /$self->{option_results}->{filter_rib_name}/);
$self->{bgp}->{ $item->{snmpIndex} }->{ribs}->{ $_->{ribName} } = {
localAddr => $item->{localAddr},
localAs => $item->{localAs},
peerAddr => $item->{peerAddr},
peerAs => $item->{peerAs},
%$_
};
}
$self->{global}->{detected}++;
}
$self->{cache_name} = 'juniper_api_' . $options{custom}->get_identifier() . '_' . $self->{mode} . '_' .
md5_hex(
(defined($self->{option_results}->{filter_counters}) ? $self->{option_results}->{filter_counters} : '') . '_' .
(defined($self->{option_results}->{filter_snmp_index}) ? $self->{option_results}->{filter_snmp_index} : '') . '_' .
(defined($self->{option_results}->{filter_local_address}) ? $self->{option_results}->{filter_local_address} : '') . '_' .
(defined($self->{option_results}->{filter_peer_address}) ? $self->{option_results}->{filter_peer_address} : '') . '_' .
(defined($self->{option_results}->{filter_rib_name}) ? $self->{option_results}->{filter_rib_name} : '')
);
}
1;
__END__
=head1 MODE
Check BGP peers.
=over 8
=item B<--filter-snmp-index>
Filter BGP peer by SNMP index.
=item B<--filter-local-address>
Filter BGP peer by local address.
=item B<--filter-peer-address>
Filter BGP peer by peer address.
=item B<--filter-rib-name>
Filter BGP RIB by RIB name.
=item B<--custom-perfdata-instances-bgp-peer>
Define performance data instances (default: '%(localAddr) %(peerAddr)')
=item B<--unknown-status>
Define the conditions to match for the status to be UNKNOWN.
You can use the following variables: %{localAddr}, %{localAs}, %{peerAddr}, %{peerAs}, %{peerState}
=item B<--warning-status>
Define the conditions to match for the status to be WARNING.
You can use the following variables: %{localAddr}, %{localAs}, %{peerAddr}, %{peerAs}, %{peerState}
=item B<--critical-status>
Define the conditions to match for the status to be CRITICAL (default: '%{peerState} !~ /established/i').
You can use the following variables: %{localAddr}, %{localAs}, %{peerAddr}, %{peerAs}, %{peerState}
=item B<--unknown-rib-status>
Define the conditions to match for the status to be UNKNOWN.
You can use the following variables: %{localAddr}, %{localAs}, %{peerAddr}, %{peerAs}, %{peerState}, %{ribName}, %{sendState}
=item B<--warning-rib-status>
Define the conditions to match for the status to be WARNING.
You can use the following variables: %{localAddr}, %{localAs}, %{peerAddr}, %{peerAs}, %{peerState}, %{ribName}, %{sendState}
=item B<--critical-rib-status>
Define the conditions to match for the status to be CRITICAL.
You can use the following variables: %{localAddr}, %{localAs}, %{peerAddr}, %{peerAs}, %{peerState}, %{ribName}, %{sendState}
=item B<--warning-bgp-peer-detected>
Warning threshold for number of BGP peers detected.
=item B<--critical-bgp-peer-detected>
Critical threshold for number of BGP peers detected.
=item B<--warning-bgp-peer-traffic-in>
Warning threshold for BGP peer traffic in.
=item B<--critical-bgp-peer-traffic-in>
Critical threshold for BGP peer traffic in.
=item B<--warning-bgp-peer-traffic-out>
Warning threshold for BGP peer traffic out.
=item B<--critical-bgp-peer-traffic-out>
Critical threshold for BGP peer traffic out.
=back
=cut

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,72 @@
#
# 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::juniper::common::junos::netconf::mode::components::afeb;
use strict;
use warnings;
sub load {}
sub disco_show {
my ($self) = @_;
foreach my $item (@{$self->{results}->{afeb}}) {
$self->{output}->add_disco_entry(
component => 'afeb',
instance => $item->{name}
);
}
}
sub check {
my ($self) = @_;
$self->{output}->output_add(long_msg => "checking afeb");
$self->{components}->{afeb} = { name => 'afeb', total => 0, skip => 0 };
return if ($self->check_filter(section => 'afeb'));
foreach my $item (@{$self->{results}->{afeb}}) {
next if ($self->check_filter(section => 'afeb', instance => $item->{name}));
$self->{components}->{afeb}->{total}++;
$self->{output}->output_add(
long_msg => sprintf(
"%s status is %s%s.",
$item->{name},
$item->{status},
defined($self->{option_results}->{display_instances}) ? ' [instance: ' . $item->{name} . ']' : ''
)
);
my $exit = $self->get_severity(section => 'afeb', value => $item->{status});
if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(
severity => $exit,
short_msg => sprintf(
"%s status is %s",
$item->{name}, $item->{status}
)
);
}
}
}
1;

View File

@ -0,0 +1,94 @@
#
# 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::juniper::common::junos::netconf::mode::components::fan;
use strict;
use warnings;
sub load {}
sub disco_show {
my ($self) = @_;
foreach my $item (@{$self->{results}->{fan}}) {
$self->{output}->add_disco_entry(
component => 'fan',
instance => $item->{name}
);
}
}
sub check {
my ($self) = @_;
$self->{output}->output_add(long_msg => "checking fans");
$self->{components}->{fan} = { name => 'fans', total => 0, skip => 0 };
return if ($self->check_filter(section => 'fan'));
foreach my $item (@{$self->{results}->{fan}}) {
next if ($self->check_filter(section => 'fan', instance => $item->{name}));
$self->{components}->{fan}->{total}++;
$self->{output}->output_add(
long_msg => sprintf(
"fan '%s' status is %s [rpm: %s%s].",
$item->{name},
$item->{status},
defined($item->{rpm}) ? $item->{rpm} : '-',
defined($self->{option_results}->{display_instances}) ? ', instance: ' . $item->{name} : ''
)
);
my $exit = $self->get_severity(section => 'fan', value => $item->{status});
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",
$item->{name}, $item->{status}
)
);
}
if (defined($item->{rpm})) {
my ($exit2, $warn, $crit, $checked) = $self->get_severity_numeric(section => 'fan', instance => $item->{name}, value => $item->{rpm});
if (!$self->{output}->is_status(value => $exit2, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(
severity => $exit2,
short_msg => sprintf("Fan '%s' speed is %s rpm", $item->{name}, $item->{rpm})
);
}
$self->{output}->perfdata_add(
nlabel => 'hardware.fan.speed.rpm',
unit => 'rpm',
instances => $item->{name},
value => $item->{rpm},
warning => $warn,
critical => $crit,
min => 0
);
}
}
}
1;

View File

@ -0,0 +1,72 @@
#
# 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::juniper::common::junos::netconf::mode::components::fpc;
use strict;
use warnings;
sub load {}
sub disco_show {
my ($self) = @_;
foreach my $item (@{$self->{results}->{fpc}}) {
$self->{output}->add_disco_entry(
component => 'fpc',
instance => $item->{name}
);
}
}
sub check {
my ($self) = @_;
$self->{output}->output_add(long_msg => "checking fpc");
$self->{components}->{fpc} = { name => 'fpc', total => 0, skip => 0 };
return if ($self->check_filter(section => 'fpc'));
foreach my $item (@{$self->{results}->{fpc}}) {
next if ($self->check_filter(section => 'fpc', instance => $item->{name}));
$self->{components}->{fpc}->{total}++;
$self->{output}->output_add(
long_msg => sprintf(
"%s status is %s%s.",
$item->{name},
$item->{status},
defined($self->{option_results}->{display_instances}) ? ' [instance: ' . $item->{name} . ']' : ''
)
);
my $exit = $self->get_severity(section => 'fpc', value => $item->{status});
if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(
severity => $exit,
short_msg => sprintf(
"%s status is %s",
$item->{name}, $item->{status}
)
);
}
}
}
1;

View File

@ -0,0 +1,92 @@
#
# 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::juniper::common::junos::netconf::mode::components::mic;
use strict;
use warnings;
sub load {}
sub disco_show {
my ($self) = @_;
foreach my $item (sort { $a->{instance} cmp $b->{instance} } values %{$self->{results}->{mic}}) {
next if (scalar(@{$item->{pics}}) <= 0);
$self->{output}->add_disco_entry(
component => 'mic',
instance => $item->{instance},
description => $item->{description},
fpc_slot => $item->{fpc_slot},
mic_slot => $item->{mic_slot}
);
}
}
sub check {
my ($self) = @_;
$self->{output}->output_add(long_msg => "checking mic");
$self->{components}->{mic} = { name => 'mic', total => 0, skip => 0 };
return if ($self->check_filter(section => 'mic'));
foreach my $item (sort { $a->{instance} cmp $b->{instance} } values %{$self->{results}->{mic}}) {
next if (scalar(@{$item->{pics}}) <= 0);
next if ($self->check_filter(section => 'mic', instance => $item->{instance}));
$self->{components}->{mic}->{total}++;
my @attrs = ('fpc slot: ' . $item->{fpc_slot});
push @attrs, 'mic slot: ' . $item->{mic_slot};
my $status = 'Online';
foreach my $pic_instance (@{$item->{pics}}) {
my $exit = $self->get_severity(section => 'pic', value => $self->{results}->{pic}->{$pic_instance}->{status});
$status = 'Error' if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1));
}
$self->{output}->output_add(
long_msg => sprintf(
"mic '%s @ %s' status is %s [%s]%s.",
$item->{description},
$item->{instance},
$status,
join(', ', @attrs),
defined($self->{option_results}->{display_instances}) ? ' [instance: ' . $item->{instance} . ']' : ''
)
);
my $exit = $self->get_severity(section => 'mic', value => $status);
if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(
severity => $exit,
short_msg => sprintf(
"mic '%s @ %s' status is %s [%s]",
$item->{description},
$item->{instance},
$status,
join(', ', @attrs)
)
);
}
}
}
1;

View File

@ -0,0 +1,85 @@
#
# 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::juniper::common::junos::netconf::mode::components::pic;
use strict;
use warnings;
sub load {}
sub disco_show {
my ($self) = @_;
foreach my $item (sort { $a->{instance} cmp $b->{instance} } values %{$self->{results}->{pic}}) {
my %attrs = (fpc_slot => $item->{fpc_slot}, pic_slot => $item->{pic_slot});
$attrs{mic_slot} = $item->{mic_slot} if (defined($item->{mic_slot}));
$self->{output}->add_disco_entry(
component => 'pic',
instance => $item->{instance},
description => $item->{description},
%attrs
);
}
}
sub check {
my ($self) = @_;
$self->{output}->output_add(long_msg => "checking pic");
$self->{components}->{pic} = { name => 'pic', total => 0, skip => 0 };
return if ($self->check_filter(section => 'pic'));
foreach my $item (sort { $a->{instance} cmp $b->{instance} } values %{$self->{results}->{pic}}) {
next if ($self->check_filter(section => 'pic', instance => $item->{instance}));
$self->{components}->{pic}->{total}++;
my @attrs = ('fpc slot: ' . $item->{fpc_slot});
push @attrs, 'mic slot: ' . $item->{mic_slot} if (defined($item->{mic_slot}));
push @attrs, 'pic slot: ' . $item->{pic_slot};
$self->{output}->output_add(
long_msg => sprintf(
"pic '%s @ %s' status is %s [%s]%s.",
$item->{description},
$item->{instance},
$item->{status},
join(', ', @attrs),
defined($self->{option_results}->{display_instances}) ? ' [instance: ' . $item->{instance} . ']' : ''
)
);
my $exit = $self->get_severity(section => 'pic', value => $item->{status});
if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(
severity => $exit,
short_msg => sprintf(
"pic '%s @ %s' status is %s [%s]",
$item->{description},
$item->{instance},
$item->{status},
join(', ', @attrs)
)
);
}
}
}
1;

View File

@ -0,0 +1,95 @@
#
# Copyright 2022 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::juniper::common::junos::netconf::mode::components::psu;
use strict;
use warnings;
sub load {}
sub disco_show {
my ($self) = @_;
foreach my $item (@{$self->{results}->{psu}}) {
$self->{output}->add_disco_entry(
component => 'psu',
instance => $item->{name}
);
}
}
sub check {
my ($self) = @_;
$self->{output}->output_add(long_msg => "checking power supplies");
$self->{components}->{psu} = { name => 'psus', total => 0, skip => 0 };
return if ($self->check_filter(section => 'psu'));
foreach my $item (@{$self->{results}->{psu}}) {
next if ($self->check_filter(section => 'psu', instance => $item->{name}));
$self->{components}->{psu}->{total}++;
$self->{output}->output_add(
long_msg => sprintf(
"power supply '%s' status is %s [dc output load: %s%s].",
$item->{name},
$item->{status},
defined($item->{dc_output_load}) ? $item->{dc_output_load} . '%' : '-',
defined($self->{option_results}->{display_instances}) ? ', instance: ' . $item->{name} : ''
)
);
my $exit = $self->get_severity(section => 'psu', value => $item->{status});
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",
$item->{name}, $item->{status}
)
);
}
if (defined($item->{dc_output_load})) {
my ($exit2, $warn, $crit, $checked) = $self->get_severity_numeric(section => 'psu', instance => $item->{name}, value => $item->{dc_output_load});
if (!$self->{output}->is_status(value => $exit2, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(
severity => $exit2,
short_msg => sprintf("Power supply '%s' DC output load is %s%%", $item->{name}, $item->{dc_output_load})
);
}
$self->{output}->perfdata_add(
nlabel => 'hardware.psu.dc.output.load.percentage',
unit => '%',
instances => $item->{name},
value => $item->{dc_output_load},
warning => $warn,
critical => $crit,
min => 0,
max => 100
);
}
}
}
1;

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