Enclume : editeur CLAPI, catalogue de 26 packs, empaquetage Docker
Construction et publication / verifier (push) Successful in 41s
Construction et publication / verifier (pull_request) Successful in 11s
Construction et publication / publier (pull_request) Skipped
Construction et publication / publier (push) Failing after 6m3s

This commit is contained in:
hcornet committed 2026-09-10 11:32:03 +02:00
1 parent aa71251de0
commit de7a691861
59 files changed
+4466 -205

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
.git
.gitea
.venv
__pycache__
**/__pycache__
*.pyc
docs
docker-compose*.yaml
compose*.yaml
.env
.env.*
!.env.example
README.md
sortie
-1
View File
@@ -1 +0,0 @@
# Template
+20
View File
@@ -0,0 +1,20 @@
# Copier en .env et renseigner. Ce fichier ne doit pas partir dans Git.
# Image et version deployee. C'est la seule ligne a changer pour un retour arriere.
ENCLUME_REGISTRY=harbor.tips-of-mine.com
ENCLUME_TAG=1.0.0
# Publication
ENCLUME_DOMAINE=clapi.tips-of-mine.com
TRAEFIK_RESOLVER=cloudflare
TZ=Europe/Paris
GUNICORN_WORKERS=2
# Catalogue synchronise depuis Git (laisser vide pour n'utiliser que celui de l'image)
CATALOG_REPO=https://gitea.tips-of-mine.com/hubert/enclume-catalogue.git
CATALOG_BRANCH=main
CATALOG_SUBDIR=catalog
SYNC_INTERVAL=300
# Base de donnees (chantier 2)
POSTGRES_PASSWORD=a-remplacer
+4
View File
@@ -0,0 +1,4 @@
* text=auto eol=lf
*.sh text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
+100
View File
@@ -0,0 +1,100 @@
# Construction, verification et publication des images sur Harbor.
#
# Une image par evolution : le tag reprend la version du depot et le commit.
# Rien n'est jamais publie sous "latest" pour la production : le compose
# pointe une version explicite, ce qui rend le retour arriere immediat.
name: Construction et publication
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
env:
REGISTRY: harbor.tips-of-mine.com
PROJET: enclume
jobs:
verifier:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Dependances
run: pip install -r requirements.txt pytest
- name: Tests
run: python -m pytest -q
- name: Coherence du catalogue
run: python -m enclume.cli --list > /dev/null
publier:
needs: verifier
if: gitea.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Calcul des tags
id: version
run: |
COURT=$(echo "${{ gitea.sha }}" | cut -c1-8)
if [ "${{ gitea.ref_type }}" = "tag" ]; then
VERSION="${{ gitea.ref_name }}"
VERSION="${VERSION#v}"
else
VERSION="0.0.0-main.${COURT}"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "commit=$COURT" >> "$GITHUB_OUTPUT"
echo "date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.HARBOR_USER }}
password: ${{ secrets.HARBOR_TOKEN }}
- name: Image de l'application
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile
push: true
build-args: |
ENCLUME_VERSION=${{ steps.version.outputs.version }}
ENCLUME_COMMIT=${{ steps.version.outputs.commit }}
BUILD_DATE=${{ steps.version.outputs.date }}
tags: |
${{ env.REGISTRY }}/${{ env.PROJET }}/web:${{ steps.version.outputs.version }}
${{ env.REGISTRY }}/${{ env.PROJET }}/web:sha-${{ steps.version.outputs.commit }}
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.PROJET }}/web:cache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.PROJET }}/web:cache,mode=max
- name: Image du synchroniseur de catalogue
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile.catalog-sync
push: true
tags: |
${{ env.REGISTRY }}/${{ env.PROJET }}/catalog-sync:${{ steps.version.outputs.version }}
${{ env.REGISTRY }}/${{ env.PROJET }}/catalog-sync:sha-${{ steps.version.outputs.commit }}
- name: Analyse de vulnerabilites
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.PROJET }}/web:${{ steps.version.outputs.version }}
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: "0" # passer a 1 pour bloquer la publication
format: table
- name: Recapitulatif
run: |
echo "Publie : ${{ env.REGISTRY }}/${{ env.PROJET }}/web:${{ steps.version.outputs.version }}"
echo "Sur le serveur : ajuster ENCLUME_TAG dans .env puis docker compose up -d"
-70
View File
@@ -1,70 +0,0 @@
# Template
name: Deployment Verification
on:
push:
branches:
- develop
tags:
- "v[0-9]+.[0-9]+.[0-9]"
pull_request:
branches:
- main
workflow_dispatch:
jobs:
deploy-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# https://github.com/docker/setup-qemu-action#usage
- name: Set up QEMU
uses: docker/[email protected]
# https://github.com/marketplace/actions/docker-setup-buildx
- name: Set up Docker Buildx
id: buildx
uses: docker/[email protected]
# https://github.com/docker/build-push-action
- name: Build and push
uses: docker/build-push-action@v6
#
- name: Create necessary Docker networks
run: |
docker network create back_network || true
docker network create traefik_front_network || true
- name: Start up services using Docker Compose
run: docker compose -f docker-compose.yml up -d
# - name: Modify /etc/hosts for internal routing
# run: |
# echo "127.0.0.1 gitea.tips-of-mine.com" | sudo tee -a /etc/hosts
- name: Print Docker Compose services status
run: docker ps
- name: Wait for the application to be ready via Traefik
run: |
echo "Checking the routing and availability of application via Traefik..."
timeout 5m bash -c 'while ! curl -fsSLk "https://metabase.tips-of-mine.com"; do echo "Waiting for the application to be ready..."; sleep 10; done'
- name: Inspect Network Configuration
run: |
docker network inspect back_network
docker network inspect traefik_front_network
- name: Shutdown Docker Compose services
if: always()
run: docker compose -f docker-compose.yml down
- name: Cleanup
if: always()
run: |
docker compose --profile setup down
rm -rf /workspace/tips-of-mine/metabase/*
-51
View File
@@ -1,51 +0,0 @@
# Template
name: Deployment Verification
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
deploy-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Create necessary Docker networks
run: |
docker network create back_network_{ vars.APPLICATION_NAME } || true
docker network create traefik_front_network || true
- name: Start up services using Docker Compose
run: docker compose -f docker-compose.yml up -d
# - name: Modify /etc/hosts for internal routing
# run: |
# echo "127.0.0.1 { vars.APPLICATION_URL }" | sudo tee -a /etc/hosts
# echo "127.0.0.1 dashboard.tips-of-mine.com" | sudo tee -a /etc/hosts
- name: Print Docker Compose services status
run: docker ps
- name: Wait for the application to be ready via Traefik
run: |
echo "Checking the routing and availability of application via Traefik..."
timeout 5m bash -c 'while ! curl -fsSLk "https://{ vars.APPLICATION_URL }"; do echo "Waiting for the application to be ready..."; sleep 10; done'
- name: Inspect Network Configuration
run: |
docker network inspect back_network_{ vars.APPLICATION_NAME }
docker network inspect traefik_front_network
- name: Shutdown Docker Compose services
if: always()
run: docker compose -f docker-compose.yml down
+9 -14
View File
@@ -1,14 +1,9 @@
# ---> VisualStudioCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
.venv/
__pycache__/
*.pyc
.pytest_cache/
.env
.env.*
!.env.example
sortie/
*.zip
+50
View File
@@ -0,0 +1,50 @@
# syntax=docker/dockerfile:1
# ---------------------------------------------------------------- construction
FROM python:3.12-slim AS build
ENV PIP_NO_CACHE_DIR=1 PIP_DISABLE_PIP_VERSION_CHECK=1
WORKDIR /build
COPY requirements.txt ./
RUN python -m venv /opt/venv \
&& /opt/venv/bin/pip install --no-cache-dir -r requirements.txt
# ------------------------------------------------------------------- execution
FROM python:3.12-slim AS runtime
ARG ENCLUME_VERSION=dev
ARG ENCLUME_COMMIT=inconnu
ARG BUILD_DATE=inconnue
LABEL org.opencontainers.image.title="Enclume" \
org.opencontainers.image.description="Generateur de configuration CLAPI pour Centreon" \
org.opencontainers.image.version="${ENCLUME_VERSION}" \
org.opencontainers.image.revision="${ENCLUME_COMMIT}" \
org.opencontainers.image.created="${BUILD_DATE}" \
org.opencontainers.image.licenses="MIT"
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PATH="/opt/venv/bin:$PATH" \
ENCLUME_VERSION="${ENCLUME_VERSION}" \
ENCLUME_COMMIT="${ENCLUME_COMMIT}" \
ENCLUME_CATALOG=/app/catalog \
GUNICORN_WORKERS=2 \
GUNICORN_TIMEOUT=30
RUN groupadd --gid 10001 enclume \
&& useradd --uid 10001 --gid enclume --home /app --no-create-home --shell /usr/sbin/nologin enclume
COPY --from=build /opt/venv /opt/venv
WORKDIR /app
COPY --chown=root:root enclume/ /app/enclume/
COPY --chown=root:root catalog/ /app/catalog/
USER enclume
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8080/sante', timeout=4).status==200 else 1)"
CMD ["sh", "-c", "exec gunicorn --bind 0.0.0.0:8080 --workers ${GUNICORN_WORKERS} --timeout ${GUNICORN_TIMEOUT} --access-logfile - --error-logfile - --forwarded-allow-ips '*' 'enclume.webapp:create_app()'"]
+22
View File
@@ -0,0 +1,22 @@
# syntax=docker/dockerfile:1
# Synchronise le depot du catalogue dans un volume partage avec l'application,
# puis demande a celle-ci de le relire. Le catalogue evolue ainsi sans nouvelle
# image de l'application.
FROM alpine:3.20
RUN apk add --no-cache git curl tini \
&& addgroup -g 10001 enclume \
&& adduser -u 10001 -G enclume -D -H enclume
COPY docker/catalog-sync.sh /usr/local/bin/catalog-sync
RUN chmod +x /usr/local/bin/catalog-sync
ENV CATALOG_REPO="" \
CATALOG_BRANCH=main \
CATALOG_SUBDIR=catalog \
CATALOG_DEST=/catalogue \
SYNC_INTERVAL=300 \
RELOAD_URL=http://web:8080/recharger
USER enclume
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/catalog-sync"]
+93 -27
View File
@@ -1,41 +1,107 @@
# Enclume
Générateur de configuration CLAPI pour Centreon : créez vos commandes, modèles de service et modèles d'hôte, repartez avec les fichiers prêts à jouer. Sans inscription. Projet indépendant de Centreon SAS.
Site public de génération de configuration CLAPI pour Centreon. On y construit ses commandes
de contrôle, ses modèles de service et ses modèles d'hôte — de zéro ou en partant d'un pack
du catalogue — et on repart avec les fichiers à jouer sur son central.
# A propos de
Projet indépendant, sans lien avec Centreon SAS.
# Prérequis
## Ce que fait le site
Vous avez besoin d'une installation docker fonctionnelle et de docker-compose sur votre machine.
- **Éditeur libre** : commandes, modèles de service, modèles d'hôte, hôtes. Héritage,
duplication, rattachement des services aux modèles d'hôte.
- **Catalogue de départ** : 26 packs, environ 124 sondes, importables puis modifiables.
- **Validation** avant génération : noms en double, références manquantes, point-virgule
interdit, services rattachés à aucun modèle d'hôte, mots de passe en clair.
- **Quatre fichiers** : `deploy.sh` (avec `--dry-run`), `deploy.clapi` (import CLAPI),
et les deux `rollback` correspondants.
- **Version de Centreon** au niveau du projet : les macros sortent en 3 ou 5 champs selon
la cible.
# Démarrage
## Ce que le serveur ne fait pas
~~~bash
git clone https://gitea.tips-of-mine.com/Tips-Of-Mine/Enclume.git
cd Enclume
~~~
Le projet vit dans le navigateur du visiteur (`localStorage`), s'exporte en JSON et se
réimporte. Le serveur valide et génère, sans rien conserver. Les macros marquées « mot de
passe » ne sont jamais stockées ni partagées : elles sont vidées avant toute écriture côté
serveur.
## Personnalisation
## Architecture
Le fichier .env pour les variable d'environnement
~~~bash
nano .env
~~~
```
Cloudflare Tunnel → Traefik ─┬→ web (image maison, sans état)
│
volume catalogue ┴← catalog-sync (git → volume, puis /recharger)
db (postgres, à partir du chantier 2)
```
Modification des labels pour traefik
~~~bash
nano docker-compose.yml
~~~
Chaque évolution produit une image versionnée. Le déploiement et le retour arrière se font
en changeant une ligne dans `.env` : voir [docs/EXPLOITATION.md](docs/EXPLOITATION.md).
## Lancement
## Développement
~~~bash
docker compose up -d
~~~
```bash
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
ENCLUME_DEBUG=1 python3 run.py # http://127.0.0.1:8080
python3 -m pytest -q
```
~~~bash
docker compose logs -f
~~~
Ou en conteneur, avec le code monté en direct :
# Buy me a coffe
<a href='https://ko-fi.com/R5R2KNI3N' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://storage.ko-fi.com/cdn/kofi4.png?v=3' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
```bash
docker compose -f compose.dev.yaml up --build
```
## Ligne de commande et API
Le même moteur sert au site, à la CLI et à la CI :
```bash
python3 -m enclume.cli --list
python3 -m enclume.cli --pack os-linux-snmp --prefix ACME- --out ./sortie --strict
python3 -m enclume.cli --projet mon-projet.json --out ./sortie
```
| Route | Usage |
|---|---|
| `GET /api/packs` | catalogue |
| `GET /api/packs/<id>/projet` | un pack converti en objets de projet |
| `POST /api/valider` | validation seule |
| `POST /api/generer` | validation et génération des quatre fichiers |
| `POST /telecharger` | archive zip |
| `GET /sante` | version, commit, état du catalogue |
| `POST /recharger` | relecture du catalogue (appelé par `catalog-sync`) |
## Ajouter un pack au catalogue
Un pack est un fichier YAML déposé dans `catalog/<catégorie>/`. Le format et un exemple
commenté sont dans `catalog/exemple-pack.yml.example`. Aucun code à modifier, aucune image à
reconstruire : `catalog-sync` le prend en charge au prochain cycle.
## Structure
```
run.py point d'entrée de développement
enclume/model.py modèle de projet, validation, retrait des secrets
enclume/generator.py projet → opérations CLAPI → quatre fichiers
enclume/catalog.py lecture du catalogue YAML
enclume/packs.py pack du catalogue → objets de projet
enclume/webapp.py routes Flask
enclume/cli.py usage en ligne de commande et en CI
enclume/static/editeur.js éditeur, sans dépendance ni étape de build
tests/ garde-fous sur la génération
Dockerfile image de l'application
Dockerfile.catalog-sync image du synchroniseur
compose.yaml production, derrière Traefik
compose.dev.yaml développement local
.gitea/workflows/build.yaml construction, tests, publication sur Harbor
```
## Chantiers
1. Éditeur libre — **fait**
2. Empaquetage Docker, CI, exploitation — **fait**
3. Partage par lien et projets enregistrés (PostgreSQL)
4. Connexion optionnelle via Authentik
5. Soumission de packs et modération, écriture dans Gitea
6. Habillage public : page d'accueil, mentions, documentation d'usage
+49
View File
@@ -0,0 +1,49 @@
id: app-apache
name: Apache HTTP Server
category: application
status: verified
plugin: centreon_apache_serverstatus.pl
description: Supervision d'un serveur Apache via la page mod_status (charge CPU, requetes, slots, temps de reponse).
host_template:
name: App-Apache-custom
alias: Applicatif Apache
parents: [generic-active-host-custom]
macros:
- {name: APACHEPORT, value: "80", description: "Port d'ecoute"}
- {name: APACHEPROTO, value: "http", description: "Protocole (http ou https)"}
- {name: APACHEURLPATH, value: "/server-status/?auto", description: "Chemin de la page mod_status"}
- {name: APACHEEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Cpu-Load
alias: Cpu-Load
description: Charge CPU declaree par Apache
line: "$CENTREONPLUGINS$/centreon_apache_serverstatus.pl --plugin=apps::apache::serverstatus::plugin --mode=cpuload --hostname=$HOSTADDRESS$ --port='$_HOSTAPACHEPORT$' --proto='$_HOSTAPACHEPROTO$' --urlpath='$_HOSTAPACHEURLPATH$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTAPACHEEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Requests
alias: Requests
description: Requetes et debit par seconde
line: "$CENTREONPLUGINS$/centreon_apache_serverstatus.pl --plugin=apps::apache::serverstatus::plugin --mode=requests --hostname=$HOSTADDRESS$ --port='$_HOSTAPACHEPORT$' --proto='$_HOSTAPACHEPROTO$' --urlpath='$_HOSTAPACHEURLPATH$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTAPACHEEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning"}
- {name: CRITICAL, value: "", description: "Seuil critical"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Slot-States
alias: Slot-States
description: Occupation des slots de traitement
line: "$CENTREONPLUGINS$/centreon_apache_serverstatus.pl --plugin=apps::apache::serverstatus::plugin --mode=slotstates --hostname=$HOSTADDRESS$ --port='$_HOSTAPACHEPORT$' --proto='$_HOSTAPACHEPROTO$' --urlpath='$_HOSTAPACHEURLPATH$' --warning-busy='$_SERVICEWARNING$' --critical-busy='$_SERVICECRITICAL$' $_HOSTAPACHEEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning slots occupes (%)"}
- {name: CRITICAL, value: "90", description: "Seuil critical slots occupes (%)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Response-Time
alias: Response-Time
description: Temps de reponse de la page de statut
default: false
line: "$CENTREONPLUGINS$/centreon_apache_serverstatus.pl --plugin=apps::apache::serverstatus::plugin --mode=responsetime --hostname=$HOSTADDRESS$ --port='$_HOSTAPACHEPORT$' --proto='$_HOSTAPACHEPROTO$' --urlpath='$_HOSTAPACHEURLPATH$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTAPACHEEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "1", description: "Seuil warning en secondes"}
- {name: CRITICAL, value: "3", description: "Seuil critical en secondes"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+38
View File
@@ -0,0 +1,38 @@
id: app-docker
name: Docker (API REST)
category: application
status: to-verify
plugin: centreon_docker_restapi.pl
description: Supervision d'un hote Docker via son API REST (etat des conteneurs, consommation CPU et memoire). Verifier le nom du plugin selon la version installee.
host_template:
name: App-Docker-custom
alias: Applicatif Docker
parents: [generic-active-host-custom]
macros:
- {name: DOCKERPORT, value: "2376", description: "Port de l'API Docker"}
- {name: DOCKERPROTO, value: "https", description: "Protocole d'acces a l'API"}
- {name: DOCKEREXTRAOPTIONS, value: "", description: "Options supplementaires (certificats, api-version)"}
services:
- name: Container-Status
alias: Container-Status
description: Etat des conteneurs
line: "$CENTREONPLUGINS$/centreon_docker_restapi.pl --plugin=cloud::docker::restapi::plugin --mode=container-status --hostname=$HOSTADDRESS$ --port='$_HOSTDOCKERPORT$' --proto='$_HOSTDOCKERPROTO$' --container-name='$_SERVICECONTAINERNAME$' $_HOSTDOCKEREXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: CONTAINERNAME, value: "", description: "Filtre sur le nom du conteneur"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Container-Usage
alias: Container-Usage
description: Consommation CPU et memoire des conteneurs
line: "$CENTREONPLUGINS$/centreon_docker_restapi.pl --plugin=cloud::docker::restapi::plugin --mode=container-usage --hostname=$HOSTADDRESS$ --port='$_HOSTDOCKERPORT$' --proto='$_HOSTDOCKERPROTO$' --container-name='$_SERVICECONTAINERNAME$' --warning-cpu='$_SERVICEWARNINGCPU$' --critical-cpu='$_SERVICECRITICALCPU$' $_HOSTDOCKEREXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: CONTAINERNAME, value: "", description: "Filtre sur le nom du conteneur"}
- {name: WARNINGCPU, value: "80", description: "Seuil warning CPU en %"}
- {name: CRITICALCPU, value: "90", description: "Seuil critical CPU en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Node-Status
alias: Node-Status
description: Etat du noeud Docker
default: false
line: "$CENTREONPLUGINS$/centreon_docker_restapi.pl --plugin=cloud::docker::restapi::plugin --mode=node-status --hostname=$HOSTADDRESS$ --port='$_HOSTDOCKERPORT$' --proto='$_HOSTDOCKERPROTO$' $_HOSTDOCKEREXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+56
View File
@@ -0,0 +1,56 @@
id: app-mssql
name: Microsoft SQL Server
category: application
status: verified
plugin: centreon_mssql.pl
description: Supervision d'une instance SQL Server (connexion, taille des bases, sauvegardes, jobs, sessions, verrous).
host_template:
name: App-DB-MSSQL-custom
alias: Applicatif MSSQL
parents: [generic-active-host-custom]
macros:
- {name: MSSQLPORT, value: "1433", description: "Port d'ecoute de l'instance"}
- {name: MSSQLUSERNAME, value: "centreon", description: "Compte de supervision"}
- {name: MSSQLPASSWORD, value: "", description: "Mot de passe du compte de supervision", is_password: true}
- {name: MSSQLEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Connection-Time
alias: Connection-Time
description: Temps d'etablissement de la connexion
line: "$CENTREONPLUGINS$/centreon_mssql.pl --plugin=database::mssql::plugin --mode=connection-time --host=$HOSTADDRESS$ --port='$_HOSTMSSQLPORT$' --username='$_HOSTMSSQLUSERNAME$' --password='$_HOSTMSSQLPASSWORD$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTMSSQLEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "1000", description: "Seuil warning en ms"}
- {name: CRITICAL, value: "5000", description: "Seuil critical en ms"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Databases-Size
alias: Databases-Size
description: Taille et occupation des bases
line: "$CENTREONPLUGINS$/centreon_mssql.pl --plugin=database::mssql::plugin --mode=databases-size --host=$HOSTADDRESS$ --port='$_HOSTMSSQLPORT$' --username='$_HOSTMSSQLUSERNAME$' --password='$_HOSTMSSQLPASSWORD$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTMSSQLEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning"}
- {name: CRITICAL, value: "", description: "Seuil critical"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Backup-Age
alias: Backup-Age
description: Anciennete de la derniere sauvegarde
line: "$CENTREONPLUGINS$/centreon_mssql.pl --plugin=database::mssql::plugin --mode=backup-age --host=$HOSTADDRESS$ --port='$_HOSTMSSQLPORT$' --username='$_HOSTMSSQLUSERNAME$' --password='$_HOSTMSSQLPASSWORD$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTMSSQLEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "86400", description: "Seuil warning en secondes"}
- {name: CRITICAL, value: "172800", description: "Seuil critical en secondes"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Failed-Jobs
alias: Failed-Jobs
description: Jobs de l'agent SQL en echec
default: false
line: "$CENTREONPLUGINS$/centreon_mssql.pl --plugin=database::mssql::plugin --mode=failed-jobs --host=$HOSTADDRESS$ --port='$_HOSTMSSQLPORT$' --username='$_HOSTMSSQLUSERNAME$' --password='$_HOSTMSSQLPASSWORD$' $_HOSTMSSQLEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Connected-Users
alias: Connected-Users
description: Nombre de sessions ouvertes
default: false
line: "$CENTREONPLUGINS$/centreon_mssql.pl --plugin=database::mssql::plugin --mode=connected-users --host=$HOSTADDRESS$ --port='$_HOSTMSSQLPORT$' --username='$_HOSTMSSQLUSERNAME$' --password='$_HOSTMSSQLPASSWORD$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTMSSQLEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning"}
- {name: CRITICAL, value: "", description: "Seuil critical"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+65
View File
@@ -0,0 +1,65 @@
id: app-mysql
name: MySQL / MariaDB
category: application
status: verified
plugin: centreon_mysql.pl
description: Supervision d'une instance MySQL ou MariaDB (connexion, requetes, threads, replication, taille des bases).
host_template:
name: App-DB-MySQL-custom
alias: Applicatif MySQL
parents: [generic-active-host-custom]
macros:
- {name: MYSQLPORT, value: "3306", description: "Port d'ecoute du serveur"}
- {name: MYSQLUSERNAME, value: "centreon", description: "Compte de supervision"}
- {name: MYSQLPASSWORD, value: "", description: "Mot de passe du compte de supervision", is_password: true}
- {name: MYSQLEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Connection-Time
alias: Connection-Time
description: Temps d'etablissement de la connexion
line: "$CENTREONPLUGINS$/centreon_mysql.pl --plugin=database::mysql::plugin --mode=connection-time --host=$HOSTADDRESS$ --port='$_HOSTMYSQLPORT$' --username='$_HOSTMYSQLUSERNAME$' --password='$_HOSTMYSQLPASSWORD$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTMYSQLEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "1000", description: "Seuil warning en ms"}
- {name: CRITICAL, value: "5000", description: "Seuil critical en ms"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Queries
alias: Queries
description: Nombre de requetes par seconde
line: "$CENTREONPLUGINS$/centreon_mysql.pl --plugin=database::mysql::plugin --mode=queries --host=$HOSTADDRESS$ --port='$_HOSTMYSQLPORT$' --username='$_HOSTMYSQLUSERNAME$' --password='$_HOSTMYSQLPASSWORD$' $_HOSTMYSQLEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Connections
alias: Connections
description: Nombre de connexions clientes ouvertes
line: "$CENTREONPLUGINS$/centreon_mysql.pl --plugin=database::mysql::plugin --mode=threads-connected --host=$HOSTADDRESS$ --port='$_HOSTMYSQLPORT$' --username='$_HOSTMYSQLUSERNAME$' --password='$_HOSTMYSQLPASSWORD$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTMYSQLEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning"}
- {name: CRITICAL, value: "90", description: "Seuil critical"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Slave-Status
alias: Slave-Status
description: Etat de la replication cote replica
default: false
line: "$CENTREONPLUGINS$/centreon_mysql.pl --plugin=database::mysql::plugin --mode=slave-status --host=$HOSTADDRESS$ --port='$_HOSTMYSQLPORT$' --username='$_HOSTMYSQLUSERNAME$' --password='$_HOSTMYSQLPASSWORD$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTMYSQLEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "60", description: "Retard warning en secondes"}
- {name: CRITICAL, value: "300", description: "Retard critical en secondes"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Databases-Size
alias: Databases-Size
description: Taille des bases de donnees
default: false
line: "$CENTREONPLUGINS$/centreon_mysql.pl --plugin=database::mysql::plugin --mode=databases-size --host=$HOSTADDRESS$ --port='$_HOSTMYSQLPORT$' --username='$_HOSTMYSQLUSERNAME$' --password='$_HOSTMYSQLPASSWORD$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTMYSQLEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning en octets"}
- {name: CRITICAL, value: "", description: "Seuil critical en octets"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Uptime
alias: Uptime
description: Duree de fonctionnement de l'instance
default: false
line: "$CENTREONPLUGINS$/centreon_mysql.pl --plugin=database::mysql::plugin --mode=uptime --host=$HOSTADDRESS$ --port='$_HOSTMYSQLPORT$' --username='$_HOSTMYSQLUSERNAME$' --password='$_HOSTMYSQLPASSWORD$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTMYSQLEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning en secondes"}
- {name: CRITICAL, value: "@0:600", description: "Seuil critical en secondes"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+41
View File
@@ -0,0 +1,41 @@
id: app-nginx
name: Nginx
category: application
status: verified
plugin: centreon_nginx_serverstatus.pl
description: Supervision d'un serveur Nginx via le module stub_status (connexions, requetes, temps de reponse).
host_template:
name: App-Nginx-custom
alias: Applicatif Nginx
parents: [generic-active-host-custom]
macros:
- {name: NGINXPORT, value: "80", description: "Port d'ecoute"}
- {name: NGINXPROTO, value: "http", description: "Protocole (http ou https)"}
- {name: NGINXURLPATH, value: "/nginx_status", description: "Chemin de la page stub_status"}
- {name: NGINXEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Connections
alias: Connections
description: Connexions actives, en lecture et en attente
line: "$CENTREONPLUGINS$/centreon_nginx_serverstatus.pl --plugin=apps::nginx::serverstatus::plugin --mode=connections --hostname=$HOSTADDRESS$ --port='$_HOSTNGINXPORT$' --proto='$_HOSTNGINXPROTO$' --urlpath='$_HOSTNGINXURLPATH$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTNGINXEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning"}
- {name: CRITICAL, value: "", description: "Seuil critical"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Requests
alias: Requests
description: Requetes traitees par seconde
line: "$CENTREONPLUGINS$/centreon_nginx_serverstatus.pl --plugin=apps::nginx::serverstatus::plugin --mode=requests --hostname=$HOSTADDRESS$ --port='$_HOSTNGINXPORT$' --proto='$_HOSTNGINXPROTO$' --urlpath='$_HOSTNGINXURLPATH$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTNGINXEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning"}
- {name: CRITICAL, value: "", description: "Seuil critical"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Response-Time
alias: Response-Time
description: Temps de reponse de la page de statut
default: false
line: "$CENTREONPLUGINS$/centreon_nginx_serverstatus.pl --plugin=apps::nginx::serverstatus::plugin --mode=responsetime --hostname=$HOSTADDRESS$ --port='$_HOSTNGINXPORT$' --proto='$_HOSTNGINXPROTO$' --urlpath='$_HOSTNGINXURLPATH$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTNGINXEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "1", description: "Seuil warning en secondes"}
- {name: CRITICAL, value: "3", description: "Seuil critical en secondes"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+48
View File
@@ -0,0 +1,48 @@
id: app-oracle
name: Oracle Database
category: application
status: to-verify
plugin: centreon_oracle.pl
description: Supervision d'une instance Oracle (connexion, tablespaces, sessions, processus, sauvegardes RMAN). Necessite un client Oracle sur le collecteur.
host_template:
name: App-DB-Oracle-custom
alias: Applicatif Oracle
parents: [generic-active-host-custom]
macros:
- {name: ORACLEPORT, value: "1521", description: "Port d'ecoute du listener"}
- {name: ORACLESID, value: "", description: "SID ou service name"}
- {name: ORACLEUSERNAME, value: "centreon", description: "Compte de supervision"}
- {name: ORACLEPASSWORD, value: "", description: "Mot de passe du compte de supervision", is_password: true}
- {name: ORACLEEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Connection-Time
alias: Connection-Time
description: Temps d'etablissement de la connexion
line: "$CENTREONPLUGINS$/centreon_oracle.pl --plugin=database::oracle::plugin --mode=connection-time --hostname=$HOSTADDRESS$ --port='$_HOSTORACLEPORT$' --sid='$_HOSTORACLESID$' --username='$_HOSTORACLEUSERNAME$' --password='$_HOSTORACLEPASSWORD$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTORACLEEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "1000", description: "Seuil warning en ms"}
- {name: CRITICAL, value: "5000", description: "Seuil critical en ms"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Tablespace-Usage
alias: Tablespace-Usage
description: Occupation des tablespaces
line: "$CENTREONPLUGINS$/centreon_oracle.pl --plugin=database::oracle::plugin --mode=tablespace-usage --hostname=$HOSTADDRESS$ --port='$_HOSTORACLEPORT$' --sid='$_HOSTORACLESID$' --username='$_HOSTORACLEUSERNAME$' --password='$_HOSTORACLEPASSWORD$' --warning-usage='$_SERVICEWARNING$' --critical-usage='$_SERVICECRITICAL$' $_HOSTORACLEEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Sessions-Usage
alias: Sessions-Usage
description: Sessions ouvertes par rapport au maximum
line: "$CENTREONPLUGINS$/centreon_oracle.pl --plugin=database::oracle::plugin --mode=sessions-usage --hostname=$HOSTADDRESS$ --port='$_HOSTORACLEPORT$' --sid='$_HOSTORACLESID$' --username='$_HOSTORACLEUSERNAME$' --password='$_HOSTORACLEPASSWORD$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTORACLEEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Rman-Backup-Problems
alias: Rman-Backup-Problems
description: Erreurs remontees par RMAN
default: false
line: "$CENTREONPLUGINS$/centreon_oracle.pl --plugin=database::oracle::plugin --mode=rman-backup-problems --hostname=$HOSTADDRESS$ --port='$_HOSTORACLEPORT$' --sid='$_HOSTORACLESID$' --username='$_HOSTORACLEUSERNAME$' --password='$_HOSTORACLEPASSWORD$' $_HOSTORACLEEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+57
View File
@@ -0,0 +1,57 @@
id: app-postgresql
name: PostgreSQL
category: application
status: verified
plugin: centreon_postgres.pl
description: Supervision d'une instance PostgreSQL (connexion, sessions, taille des bases, verrous, temps de requete).
host_template:
name: App-DB-PostgreSQL-custom
alias: Applicatif PostgreSQL
parents: [generic-active-host-custom]
macros:
- {name: PGPORT, value: "5432", description: "Port d'ecoute du serveur"}
- {name: PGUSERNAME, value: "centreon", description: "Compte de supervision"}
- {name: PGPASSWORD, value: "", description: "Mot de passe du compte de supervision", is_password: true}
- {name: PGDATABASE, value: "postgres", description: "Base de connexion"}
- {name: PGEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Connection-Time
alias: Connection-Time
description: Temps d'etablissement de la connexion
line: "$CENTREONPLUGINS$/centreon_postgres.pl --plugin=database::postgres::plugin --mode=connection-time --host=$HOSTADDRESS$ --port='$_HOSTPGPORT$' --username='$_HOSTPGUSERNAME$' --password='$_HOSTPGPASSWORD$' --database='$_HOSTPGDATABASE$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTPGEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "1000", description: "Seuil warning en ms"}
- {name: CRITICAL, value: "5000", description: "Seuil critical en ms"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Backends
alias: Backends
description: Nombre de connexions actives
line: "$CENTREONPLUGINS$/centreon_postgres.pl --plugin=database::postgres::plugin --mode=backends --host=$HOSTADDRESS$ --port='$_HOSTPGPORT$' --username='$_HOSTPGUSERNAME$' --password='$_HOSTPGPASSWORD$' --database='$_HOSTPGDATABASE$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTPGEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning"}
- {name: CRITICAL, value: "90", description: "Seuil critical"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Database-Size
alias: Database-Size
description: Taille des bases de donnees
line: "$CENTREONPLUGINS$/centreon_postgres.pl --plugin=database::postgres::plugin --mode=database-size --host=$HOSTADDRESS$ --port='$_HOSTPGPORT$' --username='$_HOSTPGUSERNAME$' --password='$_HOSTPGPASSWORD$' --database='$_HOSTPGDATABASE$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTPGEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning en octets"}
- {name: CRITICAL, value: "", description: "Seuil critical en octets"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Locks
alias: Locks
description: Verrous poses sur l'instance
default: false
line: "$CENTREONPLUGINS$/centreon_postgres.pl --plugin=database::postgres::plugin --mode=locks --host=$HOSTADDRESS$ --port='$_HOSTPGPORT$' --username='$_HOSTPGUSERNAME$' --password='$_HOSTPGPASSWORD$' --database='$_HOSTPGDATABASE$' $_HOSTPGEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Query-Time
alias: Query-Time
description: Duree de la requete la plus longue
default: false
line: "$CENTREONPLUGINS$/centreon_postgres.pl --plugin=database::postgres::plugin --mode=query-time --host=$HOSTADDRESS$ --port='$_HOSTPGPORT$' --username='$_HOSTPGUSERNAME$' --password='$_HOSTPGPASSWORD$' --database='$_HOSTPGDATABASE$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTPGEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "60", description: "Seuil warning en secondes"}
- {name: CRITICAL, value: "300", description: "Seuil critical en secondes"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+68
View File
@@ -0,0 +1,68 @@
id: app-protocols
name: Services reseau standards (HTTP, TCP, DNS, NTP, SSH)
category: application
status: verified
plugin: check_http
description: Sondes generiques basees sur les plugins Nagios livres avec Centreon. Utile pour tout service expose sans pack dedie.
host_template:
name: App-Protocols-custom
alias: Applicatif Protocoles reseau
parents: [generic-active-host-custom]
macros: []
services:
- name: Http
alias: Http
description: Disponibilite et temps de reponse d'une URL
line: "$USER1$/check_http -H $HOSTADDRESS$ -p $_SERVICEPORT$ -u '$_SERVICEURLPATH$' -w $_SERVICEWARNING$ -c $_SERVICECRITICAL$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: PORT, value: "80", description: "Port d'ecoute"}
- {name: URLPATH, value: "/", description: "Chemin a interroger"}
- {name: WARNING, value: "1", description: "Temps de reponse warning (s)"}
- {name: CRITICAL, value: "3", description: "Temps de reponse critical (s)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires (ex: -S pour https)"}
- name: Https-Certificate
alias: Https-Certificate
description: Validite du certificat TLS
line: "$USER1$/check_http -H $_SERVICEHOSTNAME$ -I $HOSTADDRESS$ -p $_SERVICEPORT$ -S -C $_SERVICEDAYS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: HOSTNAME, value: "$HOSTADDRESS$", description: "Nom presente dans le certificat (SNI)"}
- {name: PORT, value: "443", description: "Port TLS"}
- {name: DAYS, value: "30,14", description: "Jours restants warning,critical"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Tcp
alias: Tcp
description: Ouverture d'un port TCP
line: "$USER1$/check_tcp -H $HOSTADDRESS$ -p $_SERVICEPORT$ -w $_SERVICEWARNING$ -c $_SERVICECRITICAL$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: PORT, value: "22", description: "Port TCP a tester"}
- {name: WARNING, value: "1", description: "Temps de connexion warning (s)"}
- {name: CRITICAL, value: "3", description: "Temps de connexion critical (s)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Dns
alias: Dns
description: Resolution d'un nom par le serveur
default: false
line: "$USER1$/check_dns -H $_SERVICELOOKUP$ -s $HOSTADDRESS$ -w $_SERVICEWARNING$ -c $_SERVICECRITICAL$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: LOOKUP, value: "www.google.com", description: "Nom a resoudre"}
- {name: WARNING, value: "1", description: "Temps de reponse warning (s)"}
- {name: CRITICAL, value: "3", description: "Temps de reponse critical (s)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Ntp
alias: Ntp
description: Derive de l'horloge par rapport au serveur
default: false
line: "$USER1$/check_ntp_time -H $HOSTADDRESS$ -w $_SERVICEWARNING$ -c $_SERVICECRITICAL$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "1", description: "Derive warning (s)"}
- {name: CRITICAL, value: "3", description: "Derive critical (s)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Ssh
alias: Ssh
description: Disponibilite du service SSH
default: false
line: "$USER1$/check_ssh -H $HOSTADDRESS$ -p $_SERVICEPORT$ -t $_SERVICETIMEOUT$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: PORT, value: "22", description: "Port SSH"}
- {name: TIMEOUT, value: "10", description: "Timeout en secondes"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+54
View File
@@ -0,0 +1,54 @@
id: app-vmware-esx
name: VMware ESXi (connecteur Centreon VMware)
category: application
status: to-verify
plugin: centreon_vmware_connector_client.pl
description: Supervision d'un hote ESXi via le connecteur Centreon VMware. Le connecteur doit etre installe et configure avec l'acces vCenter.
host_template:
name: App-VMware-ESX-custom
alias: Applicatif VMware ESX
parents: [generic-active-host-custom]
macros:
- {name: VMWARECONNECTORHOSTNAME, value: "localhost", description: "Adresse du connecteur Centreon VMware"}
- {name: VMWARECONNECTORPORT, value: "5700", description: "Port du connecteur"}
- {name: VMWARECONTAINER, value: "default", description: "Nom du container declare dans le connecteur"}
- {name: VMWAREESXHOSTNAME, value: "", description: "Nom de l'hote ESXi dans le vCenter"}
- {name: VMWAREEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Host-Status
alias: Host-Status
description: Etat general de l'hote ESXi
line: "$CENTREONPLUGINS$/centreon_vmware_connector_client.pl --plugin=apps::vmware::connector::plugin --mode=status-host --custommode=connector --connector-hostname='$_HOSTVMWARECONNECTORHOSTNAME$' --connector-port='$_HOSTVMWARECONNECTORPORT$' --container='$_HOSTVMWARECONTAINER$' --esx-hostname='$_HOSTVMWAREESXHOSTNAME$' $_HOSTVMWAREEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Host-Cpu
alias: Host-Cpu
description: Consommation CPU de l'hote
line: "$CENTREONPLUGINS$/centreon_vmware_connector_client.pl --plugin=apps::vmware::connector::plugin --mode=cpu-host --custommode=connector --connector-hostname='$_HOSTVMWARECONNECTORHOSTNAME$' --connector-port='$_HOSTVMWARECONNECTORPORT$' --container='$_HOSTVMWARECONTAINER$' --esx-hostname='$_HOSTVMWAREESXHOSTNAME$' --warning-total-cpu='$_SERVICEWARNING$' --critical-total-cpu='$_SERVICECRITICAL$' $_HOSTVMWAREEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Host-Memory
alias: Host-Memory
description: Consommation memoire de l'hote
line: "$CENTREONPLUGINS$/centreon_vmware_connector_client.pl --plugin=apps::vmware::connector::plugin --mode=memory-host --custommode=connector --connector-hostname='$_HOSTVMWARECONNECTORHOSTNAME$' --connector-port='$_HOSTVMWARECONNECTORPORT$' --container='$_HOSTVMWARECONTAINER$' --esx-hostname='$_HOSTVMWAREESXHOSTNAME$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTVMWAREEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Host-Health
alias: Host-Health
description: Etat materiel remonte par l'hyperviseur
line: "$CENTREONPLUGINS$/centreon_vmware_connector_client.pl --plugin=apps::vmware::connector::plugin --mode=health-host --custommode=connector --connector-hostname='$_HOSTVMWARECONNECTORHOSTNAME$' --connector-port='$_HOSTVMWARECONNECTORPORT$' --container='$_HOSTVMWARECONTAINER$' --esx-hostname='$_HOSTVMWAREESXHOSTNAME$' $_HOSTVMWAREEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Datastore-Usage
alias: Datastore-Usage
description: Occupation des banques de donnees
default: false
line: "$CENTREONPLUGINS$/centreon_vmware_connector_client.pl --plugin=apps::vmware::connector::plugin --mode=datastore-usage --custommode=connector --connector-hostname='$_HOSTVMWARECONNECTORHOSTNAME$' --connector-port='$_HOSTVMWARECONNECTORPORT$' --container='$_HOSTVMWARECONTAINER$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTVMWAREEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+21
View File
@@ -0,0 +1,21 @@
id: app-rabbitmq
name: RabbitMQ
category: application
status: to-verify
plugin: centreon_rabbitmq_restapi.pl
description: Supervision d'un broker RabbitMQ via son API de management.
host_template:
name: App-RabbitMQ-custom
alias: Applicatif RabbitMQ
parents: [generic-active-host-custom]
macros:
- {name: RABBITPORT, value: "15672", description: "Port de l'API"}
- {name: RABBITPASSWORD, value: "", description: "Mot de passe", is_password: true}
services:
- name: Queues
alias: Queues
description: Messages en attente dans les files
default: true # décoché par défaut si false
line: "$CENTREONPLUGINS$/centreon_rabbitmq_restapi.pl --plugin=… --mode=queues …"
macros:
- {name: WARNING, value: "1000", description: "Seuil warning"}
+37
View File
@@ -0,0 +1,37 @@
id: hw-apc-ups-snmp
name: Onduleur APC (SNMP)
category: hardware
status: to-verify
plugin: centreon_apc_ups_snmp.pl
description: Supervision d'un onduleur APC en SNMP (batterie, lignes d'entree et de sortie). Verifier les modes disponibles selon le modele.
host_template:
name: HW-APC-UPS-SNMP-custom
alias: Materiel APC UPS SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Battery-Status
alias: Battery-Status
description: Etat, charge et autonomie de la batterie
line: "$CENTREONPLUGINS$/centreon_apc_ups_snmp.pl --plugin=hardware::ups::apc::snmp::plugin --mode=battery-status --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-charge-remaining='$_SERVICEWARNINGCHARGE$' --critical-charge-remaining='$_SERVICECRITICALCHARGE$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNINGCHARGE, value: "50:", description: "Seuil warning sur la charge restante (%)"}
- {name: CRITICALCHARGE, value: "25:", description: "Seuil critical sur la charge restante (%)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Input-Lines
alias: Input-Lines
description: Tension et frequence des lignes d'entree
line: "$CENTREONPLUGINS$/centreon_apc_ups_snmp.pl --plugin=hardware::ups::apc::snmp::plugin --mode=input-lines --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Output-Lines
alias: Output-Lines
description: Charge et tension des lignes de sortie
line: "$CENTREONPLUGINS$/centreon_apc_ups_snmp.pl --plugin=hardware::ups::apc::snmp::plugin --mode=output-lines --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-load='$_SERVICEWARNINGLOAD$' --critical-load='$_SERVICECRITICALLOAD$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNINGLOAD, value: "70", description: "Seuil warning sur la charge (%)"}
- {name: CRITICALLOAD, value: "85", description: "Seuil critical sur la charge (%)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+58
View File
@@ -0,0 +1,58 @@
id: hw-cisco-snmp
name: Switch / routeur Cisco (SNMP)
category: hardware
status: verified
plugin: centreon_cisco_standard_snmp.pl
description: Supervision d'un equipement Cisco IOS / IOS-XE en SNMP (CPU, memoire, environnement materiel, interfaces).
host_template:
name: HW-Cisco-SNMP-custom
alias: Materiel Cisco SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Cpu
alias: Cpu
description: Charge CPU du chassis
line: "$CENTREONPLUGINS$/centreon_cisco_standard_snmp.pl --plugin=network::cisco::standard::snmp::plugin --mode=cpu --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-average='$_SERVICEWARNING$' --critical-average='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Memory
alias: Memory
description: Utilisation memoire des pools
line: "$CENTREONPLUGINS$/centreon_cisco_standard_snmp.pl --plugin=network::cisco::standard::snmp::plugin --mode=memory --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Hardware
alias: Hardware
description: Alimentations, ventilateurs, sondes et modules
line: "$CENTREONPLUGINS$/centreon_cisco_standard_snmp.pl --plugin=network::cisco::standard::snmp::plugin --mode=environment --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --component='$_SERVICECOMPONENT$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: COMPONENT, value: ".*", description: "Composants a verifier (regex)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Interfaces
alias: Interfaces
description: Etat et trafic des interfaces
line: "$CENTREONPLUGINS$/centreon_cisco_standard_snmp.pl --plugin=network::cisco::standard::snmp::plugin --mode=interfaces --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --interface='$_SERVICEINTERFACE$' --add-status --add-traffic --add-errors --warning-in-traffic='$_SERVICEWARNINGIN$' --critical-in-traffic='$_SERVICECRITICALIN$' --warning-out-traffic='$_SERVICEWARNINGOUT$' --critical-out-traffic='$_SERVICECRITICALOUT$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: INTERFACE, value: "", description: "Filtre sur le nom de l'interface"}
- {name: WARNINGIN, value: "80", description: "Seuil warning trafic entrant (%)"}
- {name: CRITICALIN, value: "90", description: "Seuil critical trafic entrant (%)"}
- {name: WARNINGOUT, value: "80", description: "Seuil warning trafic sortant (%)"}
- {name: CRITICALOUT, value: "90", description: "Seuil critical trafic sortant (%)"}
- {name: EXTRAOPTIONS, value: "--interface-use-adminstatus --name", description: "Options supplementaires"}
- name: Uptime
alias: Uptime
description: Duree depuis le dernier redemarrage
default: false
line: "$CENTREONPLUGINS$/centreon_cisco_standard_snmp.pl --plugin=network::cisco::standard::snmp::plugin --mode=uptime --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-uptime='$_SERVICEWARNING$' --critical-uptime='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning (secondes)"}
- {name: CRITICAL, value: "@0:600", description: "Seuil critical (secondes)"}
- {name: EXTRAOPTIONS, value: "--unit=s", description: "Options supplementaires"}
+53
View File
@@ -0,0 +1,53 @@
id: hw-dell-idrac-snmp
name: Serveur Dell (iDRAC SNMP)
category: hardware
status: verified
plugin: centreon_dell_idrac_snmp.pl
description: Etat materiel d'un serveur Dell PowerEdge via la carte iDRAC en SNMP (global, alimentations, ventilateurs, temperatures, disques, memoire).
host_template:
name: HW-Dell-iDRAC-SNMP-custom
alias: Materiel Dell iDRAC SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Hardware
alias: Hardware
description: Etat global de tous les composants materiels
line: "$CENTREONPLUGINS$/centreon_dell_idrac_snmp.pl --plugin=hardware::server::dell::idrac::snmp::plugin --mode=hardware --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --component='$_SERVICECOMPONENT$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: COMPONENT, value: ".*", description: "Composants a verifier (regex)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Hardware-Psu
alias: Hardware-Psu
description: Etat des alimentations
default: false
line: "$CENTREONPLUGINS$/centreon_dell_idrac_snmp.pl --plugin=hardware::server::dell::idrac::snmp::plugin --mode=hardware --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --component='psu' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Hardware-Fan
alias: Hardware-Fan
description: Etat des ventilateurs
default: false
line: "$CENTREONPLUGINS$/centreon_dell_idrac_snmp.pl --plugin=hardware::server::dell::idrac::snmp::plugin --mode=hardware --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --component='fan' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Hardware-Temperature
alias: Hardware-Temperature
description: Sondes de temperature
default: false
line: "$CENTREONPLUGINS$/centreon_dell_idrac_snmp.pl --plugin=hardware::server::dell::idrac::snmp::plugin --mode=hardware --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --component='temperature' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning (ex: temperature,.*,40)"}
- {name: CRITICAL, value: "", description: "Seuil critical (ex: temperature,.*,50)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Hardware-Disk
alias: Hardware-Disk
description: Etat des disques physiques et des volumes logiques
default: false
line: "$CENTREONPLUGINS$/centreon_dell_idrac_snmp.pl --plugin=hardware::server::dell::idrac::snmp::plugin --mode=hardware --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --component='$_SERVICECOMPONENT$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: COMPONENT, value: "pdisk,vdisk", description: "Composants disques"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
@@ -0,0 +1,60 @@
id: hw-fortinet-fortigate-snmp
name: Firewall Fortinet FortiGate (SNMP)
category: hardware
status: verified
plugin: centreon_fortinet_fortigate.pl
description: Supervision d'un FortiGate en SNMP (CPU, memoire, disque, sessions, tunnels VPN, cluster HA, interfaces).
host_template:
name: HW-Fortinet-FortiGate-SNMP-custom
alias: Materiel Fortinet FortiGate SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Cpu
alias: Cpu
description: Charge CPU du boitier
line: "$CENTREONPLUGINS$/centreon_fortinet_fortigate.pl --plugin=network::fortinet::fortigate::snmp::plugin --mode=cpu --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-average='$_SERVICEWARNING$' --critical-average='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Memory
alias: Memory
description: Utilisation memoire
line: "$CENTREONPLUGINS$/centreon_fortinet_fortigate.pl --plugin=network::fortinet::fortigate::snmp::plugin --mode=memory --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Sessions
alias: Sessions
description: Nombre de sessions actives
line: "$CENTREONPLUGINS$/centreon_fortinet_fortigate.pl --plugin=network::fortinet::fortigate::snmp::plugin --mode=sessions --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning sur le nombre de sessions"}
- {name: CRITICAL, value: "", description: "Seuil critical sur le nombre de sessions"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Vpn
alias: Vpn
description: Etat des tunnels VPN IPSec
default: false
line: "$CENTREONPLUGINS$/centreon_fortinet_fortigate.pl --plugin=network::fortinet::fortigate::snmp::plugin --mode=vpn --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Ha
alias: Ha
description: Etat du cluster haute disponibilite
default: false
line: "$CENTREONPLUGINS$/centreon_fortinet_fortigate.pl --plugin=network::fortinet::fortigate::snmp::plugin --mode=ha --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Interfaces
alias: Interfaces
description: Etat et trafic des interfaces
line: "$CENTREONPLUGINS$/centreon_fortinet_fortigate.pl --plugin=network::fortinet::fortigate::snmp::plugin --mode=interfaces --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --interface='$_SERVICEINTERFACE$' --add-status --add-traffic $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: INTERFACE, value: "", description: "Filtre sur le nom de l'interface"}
- {name: EXTRAOPTIONS, value: "--interface-use-adminstatus --name", description: "Options supplementaires"}
+44
View File
@@ -0,0 +1,44 @@
id: hw-hp-proliant-snmp
name: Serveur HP ProLiant (iLO SNMP)
category: hardware
status: verified
plugin: centreon_hp_proliant_snmp.pl
description: Etat materiel d'un serveur HP ProLiant via les agents Insight / iLO en SNMP.
host_template:
name: HW-HP-Proliant-SNMP-custom
alias: Materiel HP ProLiant SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Hardware
alias: Hardware
description: Etat global de tous les composants materiels
line: "$CENTREONPLUGINS$/centreon_hp_proliant_snmp.pl --plugin=hardware::server::hp::proliant::snmp::plugin --mode=hardware --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --component='$_SERVICECOMPONENT$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: COMPONENT, value: ".*", description: "Composants a verifier (regex)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Hardware-Psu
alias: Hardware-Psu
description: Etat des alimentations
default: false
line: "$CENTREONPLUGINS$/centreon_hp_proliant_snmp.pl --plugin=hardware::server::hp::proliant::snmp::plugin --mode=hardware --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --component='psu' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Hardware-Fan
alias: Hardware-Fan
description: Etat des ventilateurs
default: false
line: "$CENTREONPLUGINS$/centreon_hp_proliant_snmp.pl --plugin=hardware::server::hp::proliant::snmp::plugin --mode=hardware --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --component='fan' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Hardware-Storage
alias: Hardware-Storage
description: Controleurs, disques physiques et volumes logiques
default: false
line: "$CENTREONPLUGINS$/centreon_hp_proliant_snmp.pl --plugin=hardware::server::hp::proliant::snmp::plugin --mode=hardware --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --component='$_SERVICECOMPONENT$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: COMPONENT, value: "pdrive,ldrive,ctrl", description: "Composants stockage"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
@@ -0,0 +1,45 @@
id: hw-juniper-junos-snmp
name: Juniper JunOS (SNMP)
category: hardware
status: to-verify
plugin: centreon_juniper_junos_snmp.pl
description: Supervision d'un equipement Juniper sous JunOS en SNMP. Verifier le nom du script (centreon_juniper_junos_snmp.pl) sur l'installation cible.
host_template:
name: HW-Juniper-JunOS-SNMP-custom
alias: Materiel Juniper JunOS SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Cpu
alias: Cpu
description: Charge CPU du routing engine
line: "$CENTREONPLUGINS$/centreon_juniper_junos_snmp.pl --plugin=network::juniper::common::junos::snmp::plugin --mode=cpu --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Memory
alias: Memory
description: Utilisation memoire
line: "$CENTREONPLUGINS$/centreon_juniper_junos_snmp.pl --plugin=network::juniper::common::junos::snmp::plugin --mode=memory --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Hardware
alias: Hardware
description: Etat des composants materiels
line: "$CENTREONPLUGINS$/centreon_juniper_junos_snmp.pl --plugin=network::juniper::common::junos::snmp::plugin --mode=hardware --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --component='$_SERVICECOMPONENT$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: COMPONENT, value: ".*", description: "Composants a verifier (regex)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Interfaces
alias: Interfaces
description: Etat et trafic des interfaces
line: "$CENTREONPLUGINS$/centreon_juniper_junos_snmp.pl --plugin=network::juniper::common::junos::snmp::plugin --mode=interfaces --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --interface='$_SERVICEINTERFACE$' --add-status --add-traffic $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: INTERFACE, value: "", description: "Filtre sur le nom de l'interface"}
- {name: EXTRAOPTIONS, value: "--interface-use-adminstatus --name", description: "Options supplementaires"}
+42
View File
@@ -0,0 +1,42 @@
id: hw-netapp-ontap-snmp
name: Baie NetApp ONTAP (SNMP)
category: hardware
status: to-verify
plugin: centreon_netapp_ontap_snmp.pl
description: Supervision d'une baie NetApp ONTAP en SNMP (etat global, volumes, disques, replication). Verifier les modes selon la version ONTAP.
host_template:
name: HW-NetApp-ONTAP-SNMP-custom
alias: Materiel NetApp ONTAP SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Global-Status
alias: Global-Status
description: Etat global de la baie
line: "$CENTREONPLUGINS$/centreon_netapp_ontap_snmp.pl --plugin=storage::netapp::ontap::snmp::plugin --mode=global-status --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Volume-Usage
alias: Volume-Usage
description: Occupation des volumes
line: "$CENTREONPLUGINS$/centreon_netapp_ontap_snmp.pl --plugin=storage::netapp::ontap::snmp::plugin --mode=volume-usage --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Disk-Failed
alias: Disk-Failed
description: Nombre de disques en defaut
line: "$CENTREONPLUGINS$/centreon_netapp_ontap_snmp.pl --plugin=storage::netapp::ontap::snmp::plugin --mode=disk-failed --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Snapmirror-Status
alias: Snapmirror-Status
description: Etat des relations SnapMirror
default: false
line: "$CENTREONPLUGINS$/centreon_netapp_ontap_snmp.pl --plugin=storage::netapp::ontap::snmp::plugin --mode=snapmirror-status --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+37
View File
@@ -0,0 +1,37 @@
id: hw-printer-rfc3805
name: Imprimante reseau (RFC 3805)
category: hardware
status: to-verify
plugin: centreon_printer_standard_rfc3805.pl
description: Supervision d'une imprimante compatible RFC 3805 (niveaux de consommables, bacs papier, etat general). Verifier les modes disponibles.
host_template:
name: HW-Printer-RFC3805-custom
alias: Materiel Imprimante RFC3805
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Marker-Supply
alias: Marker-Supply
description: Niveau des consommables (toner, tambour)
line: "$CENTREONPLUGINS$/centreon_printer_standard_rfc3805.pl --plugin=hardware::printers::standard::rfc3805::plugin --mode=marker-supply --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "20:", description: "Seuil warning sur le niveau restant (%)"}
- {name: CRITICAL, value: "10:", description: "Seuil critical sur le niveau restant (%)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Papertray
alias: Papertray
description: Niveau des bacs a papier
line: "$CENTREONPLUGINS$/centreon_printer_standard_rfc3805.pl --plugin=hardware::printers::standard::rfc3805::plugin --mode=papertray --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning='$_SERVICEWARNING$' --critical='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "20:", description: "Seuil warning sur le niveau restant (%)"}
- {name: CRITICAL, value: "5:", description: "Seuil critical sur le niveau restant (%)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Printer-Status
alias: Printer-Status
description: Etat general de l'imprimante
line: "$CENTREONPLUGINS$/centreon_printer_standard_rfc3805.pl --plugin=hardware::printers::standard::rfc3805::plugin --mode=printer --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+46
View File
@@ -0,0 +1,46 @@
id: hw-synology-snmp
name: NAS Synology (SNMP)
category: hardware
status: to-verify
plugin: centreon_synology_snmp.pl
description: Supervision d'un NAS Synology en SNMP (composants materiels, CPU, memoire, volumes). Verifier les modes selon la version DSM.
host_template:
name: HW-Synology-SNMP-custom
alias: Materiel Synology SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Hardware
alias: Hardware
description: Etat des composants (ventilateurs, disques, alimentation)
line: "$CENTREONPLUGINS$/centreon_synology_snmp.pl --plugin=storage::synology::snmp::plugin --mode=components --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --component='$_SERVICECOMPONENT$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: COMPONENT, value: ".*", description: "Composants a verifier (regex)"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Cpu
alias: Cpu
description: Charge CPU du NAS
line: "$CENTREONPLUGINS$/centreon_synology_snmp.pl --plugin=storage::synology::snmp::plugin --mode=cpu --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-average='$_SERVICEWARNING$' --critical-average='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Memory
alias: Memory
description: Utilisation memoire
line: "$CENTREONPLUGINS$/centreon_synology_snmp.pl --plugin=storage::synology::snmp::plugin --mode=memory --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Storage
alias: Storage
description: Occupation des volumes
line: "$CENTREONPLUGINS$/centreon_synology_snmp.pl --plugin=storage::synology::snmp::plugin --mode=storage --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
+54
View File
@@ -0,0 +1,54 @@
id: os-aix-snmp
name: AIX (SNMP)
category: system
status: to-verify
plugin: centreon_aix_snmp.pl
description: Supervision d'un serveur AIX via SNMP. Verifier le nom exact du plugin sur la version de centreon-plugins installee.
host_template:
name: OS-AIX-SNMP-custom
alias: OS AIX SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Cpu
alias: Cpu
description: Charge CPU globale
line: "$CENTREONPLUGINS$/centreon_aix_snmp.pl --plugin=os::aix::snmp::plugin --mode=cpu --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-average='$_SERVICEWARNING$' --critical-average='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Memory
alias: Memory
description: Utilisation de la memoire physique
line: "$CENTREONPLUGINS$/centreon_aix_snmp.pl --plugin=os::aix::snmp::plugin --mode=memory --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Swap
alias: Swap
description: Utilisation du swap
line: "$CENTREONPLUGINS$/centreon_aix_snmp.pl --plugin=os::aix::snmp::plugin --mode=swap --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "50", description: "Seuil warning en %"}
- {name: CRITICAL, value: "75", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Storage
alias: Storage
description: Occupation des systemes de fichiers
line: "$CENTREONPLUGINS$/centreon_aix_snmp.pl --plugin=os::aix::snmp::plugin --mode=storage --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Interfaces
alias: Interfaces
description: Etat et trafic des interfaces reseau
line: "$CENTREONPLUGINS$/centreon_aix_snmp.pl --plugin=os::aix::snmp::plugin --mode=interfaces --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --interface='$_SERVICEINTERFACE$' --add-status --add-traffic $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: INTERFACE, value: "", description: "Filtre sur le nom de l'interface"}
- {name: EXTRAOPTIONS, value: "--interface-use-adminstatus --name", description: "Options supplementaires"}
+54
View File
@@ -0,0 +1,54 @@
id: os-freebsd-snmp
name: FreeBSD (SNMP)
category: system
status: to-verify
plugin: centreon_freebsd_snmp.pl
description: Supervision d'un serveur FreeBSD via SNMP. Verifier le nom exact du plugin sur la version de centreon-plugins installee.
host_template:
name: OS-FreeBSD-SNMP-custom
alias: OS FreeBSD SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Cpu
alias: Cpu
description: Charge CPU globale
line: "$CENTREONPLUGINS$/centreon_freebsd_snmp.pl --plugin=os::freebsd::snmp::plugin --mode=cpu --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-average='$_SERVICEWARNING$' --critical-average='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Memory
alias: Memory
description: Utilisation de la memoire physique
line: "$CENTREONPLUGINS$/centreon_freebsd_snmp.pl --plugin=os::freebsd::snmp::plugin --mode=memory --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Swap
alias: Swap
description: Utilisation du swap
line: "$CENTREONPLUGINS$/centreon_freebsd_snmp.pl --plugin=os::freebsd::snmp::plugin --mode=swap --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "50", description: "Seuil warning en %"}
- {name: CRITICAL, value: "75", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Storage
alias: Storage
description: Occupation des systemes de fichiers
line: "$CENTREONPLUGINS$/centreon_freebsd_snmp.pl --plugin=os::freebsd::snmp::plugin --mode=storage --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Interfaces
alias: Interfaces
description: Etat et trafic des interfaces reseau
line: "$CENTREONPLUGINS$/centreon_freebsd_snmp.pl --plugin=os::freebsd::snmp::plugin --mode=interfaces --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --interface='$_SERVICEINTERFACE$' --add-status --add-traffic $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: INTERFACE, value: "", description: "Filtre sur le nom de l'interface"}
- {name: EXTRAOPTIONS, value: "--interface-use-adminstatus --name", description: "Options supplementaires"}
+58
View File
@@ -0,0 +1,58 @@
id: os-generic-snmp
name: Equipement generique (SNMP standard)
category: system
status: verified
plugin: centreon_generic_snmp.pl
description: Supervision de tout equipement repondant aux MIB standard (HOST-RESOURCES, UCD, IF-MIB). Base de repli quand aucun pack constructeur ne correspond.
host_template:
name: OS-Generic-SNMP-custom
alias: Generique SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Cpu
alias: Cpu
description: Charge CPU via HOST-RESOURCES-MIB
line: "$CENTREONPLUGINS$/centreon_generic_snmp.pl --plugin=snmp_standard::plugin --mode=cpu --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-average='$_SERVICEWARNING$' --critical-average='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Memory
alias: Memory
description: Memoire via UCD-SNMP-MIB
line: "$CENTREONPLUGINS$/centreon_generic_snmp.pl --plugin=snmp_standard::plugin --mode=memory --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Storage
alias: Storage
description: Occupation des volumes declares en HOST-RESOURCES
line: "$CENTREONPLUGINS$/centreon_generic_snmp.pl --plugin=snmp_standard::plugin --mode=storage --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Interfaces
alias: Interfaces
description: Etat et trafic des interfaces (IF-MIB)
line: "$CENTREONPLUGINS$/centreon_generic_snmp.pl --plugin=snmp_standard::plugin --mode=interfaces --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --interface='$_SERVICEINTERFACE$' --add-status --add-traffic --warning-in-traffic='$_SERVICEWARNINGIN$' --critical-in-traffic='$_SERVICECRITICALIN$' --warning-out-traffic='$_SERVICEWARNINGOUT$' --critical-out-traffic='$_SERVICECRITICALOUT$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: INTERFACE, value: "", description: "Filtre sur le nom de l'interface"}
- {name: WARNINGIN, value: "80", description: "Seuil warning trafic entrant (%)"}
- {name: CRITICALIN, value: "90", description: "Seuil critical trafic entrant (%)"}
- {name: WARNINGOUT, value: "80", description: "Seuil warning trafic sortant (%)"}
- {name: CRITICALOUT, value: "90", description: "Seuil critical trafic sortant (%)"}
- {name: EXTRAOPTIONS, value: "--interface-use-adminstatus --name", description: "Options supplementaires"}
- name: Uptime
alias: Uptime
description: Duree depuis le dernier redemarrage
line: "$CENTREONPLUGINS$/centreon_generic_snmp.pl --plugin=snmp_standard::plugin --mode=uptime --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-uptime='$_SERVICEWARNING$' --critical-uptime='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning (secondes)"}
- {name: CRITICAL, value: "@0:600", description: "Seuil critical (secondes)"}
- {name: EXTRAOPTIONS, value: "--unit=s", description: "Options supplementaires"}
+56
View File
@@ -0,0 +1,56 @@
id: os-linux-nrpe
name: Linux (NRPE)
category: system
status: verified
plugin: check_centreon_nrpe
description: Supervision Linux par agent NRPE (commandes definies cote client dans nrpe.cfg).
host_template:
name: OS-Linux-NRPE-custom
alias: OS Linux NRPE
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: NRPEPORT, value: "5666", description: "Port d'ecoute de l'agent NRPE"}
services:
- name: Cpu
alias: Cpu
description: Charge CPU via la commande NRPE distante
line: "$USER1$/check_centreon_nrpe -H $HOSTADDRESS$ -p $_HOSTNRPEPORT$ -t $_SERVICETIMEOUT$ -c $_SERVICECOMMAND$ -a '$_SERVICEARGS$'"
macros:
- {name: COMMAND, value: "check_cpu", description: "Nom de la commande NRPE"}
- {name: ARGS, value: "80!90", description: "Arguments passes a la commande"}
- {name: TIMEOUT, value: "30", description: "Timeout en secondes"}
- name: Load
alias: Load
description: Charge systeme via NRPE
line: "$USER1$/check_centreon_nrpe -H $HOSTADDRESS$ -p $_HOSTNRPEPORT$ -t $_SERVICETIMEOUT$ -c $_SERVICECOMMAND$ -a '$_SERVICEARGS$'"
macros:
- {name: COMMAND, value: "check_load", description: "Nom de la commande NRPE"}
- {name: ARGS, value: "4,3,2!6,5,4", description: "Arguments passes a la commande"}
- {name: TIMEOUT, value: "30", description: "Timeout en secondes"}
- name: Memory
alias: Memory
description: Memoire disponible via NRPE
line: "$USER1$/check_centreon_nrpe -H $HOSTADDRESS$ -p $_HOSTNRPEPORT$ -t $_SERVICETIMEOUT$ -c $_SERVICECOMMAND$ -a '$_SERVICEARGS$'"
macros:
- {name: COMMAND, value: "check_memory", description: "Nom de la commande NRPE"}
- {name: ARGS, value: "80!90", description: "Arguments passes a la commande"}
- {name: TIMEOUT, value: "30", description: "Timeout en secondes"}
- name: Disk
alias: Disk
description: Occupation d'un point de montage via NRPE
line: "$USER1$/check_centreon_nrpe -H $HOSTADDRESS$ -p $_HOSTNRPEPORT$ -t $_SERVICETIMEOUT$ -c $_SERVICECOMMAND$ -a '$_SERVICEARGS$'"
macros:
- {name: COMMAND, value: "check_disk", description: "Nom de la commande NRPE"}
- {name: ARGS, value: "80!90!/", description: "Arguments passes a la commande"}
- {name: TIMEOUT, value: "30", description: "Timeout en secondes"}
- name: Users
alias: Users
description: Nombre d'utilisateurs connectes
default: false
line: "$USER1$/check_centreon_nrpe -H $HOSTADDRESS$ -p $_HOSTNRPEPORT$ -t $_SERVICETIMEOUT$ -c $_SERVICECOMMAND$ -a '$_SERVICEARGS$'"
macros:
- {name: COMMAND, value: "check_users", description: "Nom de la commande NRPE"}
- {name: ARGS, value: "5!10", description: "Arguments passes a la commande"}
- {name: TIMEOUT, value: "30", description: "Timeout en secondes"}
+86
View File
@@ -0,0 +1,86 @@
id: os-linux-snmp
name: Linux (SNMP)
category: system
status: verified
plugin: centreon_linux_snmp.pl
description: Supervision d'un serveur Linux via SNMP avec centreon-plugins (CPU, memoire, swap, systemes de fichiers, charge, interfaces, uptime).
host_template:
name: OS-Linux-SNMP-custom
alias: OS Linux SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Cpu
alias: Cpu
description: Charge CPU moyenne toutes unites confondues
line: "$CENTREONPLUGINS$/centreon_linux_snmp.pl --plugin=os::linux::snmp::plugin --mode=cpu --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-average='$_SERVICEWARNING$' --critical-average='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en % de CPU"}
- {name: CRITICAL, value: "90", description: "Seuil critical en % de CPU"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Memory
alias: Memory
description: Utilisation de la memoire physique
line: "$CENTREONPLUGINS$/centreon_linux_snmp.pl --plugin=os::linux::snmp::plugin --mode=memory --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en % utilise"}
- {name: CRITICAL, value: "90", description: "Seuil critical en % utilise"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Swap
alias: Swap
description: Utilisation du swap
line: "$CENTREONPLUGINS$/centreon_linux_snmp.pl --plugin=os::linux::snmp::plugin --mode=swap --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "50", description: "Seuil warning en % utilise"}
- {name: CRITICAL, value: "75", description: "Seuil critical en % utilise"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Load
alias: Load
description: Charge systeme 1/5/15 minutes
line: "$CENTREONPLUGINS$/centreon_linux_snmp.pl --plugin=os::linux::snmp::plugin --mode=load --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-load='$_SERVICEWARNING$' --critical-load='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "4,3,2", description: "Seuils warning load 1,5,15"}
- {name: CRITICAL, value: "6,5,4", description: "Seuils critical load 1,5,15"}
- {name: EXTRAOPTIONS, value: "--average", description: "Options supplementaires"}
- name: Storage
alias: Storage
description: Occupation de tous les systemes de fichiers locaux
line: "$CENTREONPLUGINS$/centreon_linux_snmp.pl --plugin=os::linux::snmp::plugin --mode=storage --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --filter-storage-type='$_SERVICEFILTERSTORAGETYPE$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en % utilise"}
- {name: CRITICAL, value: "90", description: "Seuil critical en % utilise"}
- {name: FILTERSTORAGETYPE, value: "^(hrStorageFixedDisk|hrStorageNetworkDisk)$", description: "Filtre sur le type de stockage"}
- {name: EXTRAOPTIONS, value: "--units=% --free", description: "Options supplementaires"}
- name: Interfaces
alias: Interfaces
description: Etat et trafic des interfaces reseau
line: "$CENTREONPLUGINS$/centreon_linux_snmp.pl --plugin=os::linux::snmp::plugin --mode=interfaces --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --interface='$_SERVICEINTERFACE$' --add-status --add-traffic --warning-in-traffic='$_SERVICEWARNINGIN$' --critical-in-traffic='$_SERVICECRITICALIN$' --warning-out-traffic='$_SERVICEWARNINGOUT$' --critical-out-traffic='$_SERVICECRITICALOUT$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: INTERFACE, value: "", description: "Filtre sur le nom de l'interface"}
- {name: WARNINGIN, value: "80", description: "Seuil warning trafic entrant (%)"}
- {name: CRITICALIN, value: "90", description: "Seuil critical trafic entrant (%)"}
- {name: WARNINGOUT, value: "80", description: "Seuil warning trafic sortant (%)"}
- {name: CRITICALOUT, value: "90", description: "Seuil critical trafic sortant (%)"}
- {name: EXTRAOPTIONS, value: "--interface-use-adminstatus --name", description: "Options supplementaires"}
- name: Uptime
alias: Uptime
description: Duree depuis le dernier redemarrage
default: false
line: "$CENTREONPLUGINS$/centreon_linux_snmp.pl --plugin=os::linux::snmp::plugin --mode=uptime --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-uptime='$_SERVICEWARNING$' --critical-uptime='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning (secondes)"}
- {name: CRITICAL, value: "@0:600", description: "Seuil critical (secondes)"}
- {name: EXTRAOPTIONS, value: "--unit=s", description: "Options supplementaires"}
- name: Process-Count
alias: Process-Count
description: Nombre de processus correspondant a un filtre
default: false
line: "$CENTREONPLUGINS$/centreon_linux_snmp.pl --plugin=os::linux::snmp::plugin --mode=processcount --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --process-name='$_SERVICEPROCESSNAME$' --warning-number='$_SERVICEWARNING$' --critical-number='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: PROCESSNAME, value: "", description: "Nom du processus a compter"}
- {name: WARNING, value: "", description: "Seuil warning sur le nombre"}
- {name: CRITICAL, value: "1:", description: "Seuil critical sur le nombre"}
- {name: EXTRAOPTIONS, value: "--regexp", description: "Options supplementaires"}
+54
View File
@@ -0,0 +1,54 @@
id: os-solaris-snmp
name: Solaris (SNMP)
category: system
status: to-verify
plugin: centreon_solaris_snmp.pl
description: Supervision d'un serveur Solaris via SNMP. Verifier le nom exact du plugin sur la version de centreon-plugins installee.
host_template:
name: OS-Solaris-SNMP-custom
alias: OS Solaris SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Cpu
alias: Cpu
description: Charge CPU globale
line: "$CENTREONPLUGINS$/centreon_solaris_snmp.pl --plugin=os::solaris::snmp::plugin --mode=cpu --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-average='$_SERVICEWARNING$' --critical-average='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Memory
alias: Memory
description: Utilisation de la memoire physique
line: "$CENTREONPLUGINS$/centreon_solaris_snmp.pl --plugin=os::solaris::snmp::plugin --mode=memory --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Swap
alias: Swap
description: Utilisation du swap
line: "$CENTREONPLUGINS$/centreon_solaris_snmp.pl --plugin=os::solaris::snmp::plugin --mode=swap --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "50", description: "Seuil warning en %"}
- {name: CRITICAL, value: "75", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Storage
alias: Storage
description: Occupation des systemes de fichiers
line: "$CENTREONPLUGINS$/centreon_solaris_snmp.pl --plugin=os::solaris::snmp::plugin --mode=storage --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Interfaces
alias: Interfaces
description: Etat et trafic des interfaces reseau
line: "$CENTREONPLUGINS$/centreon_solaris_snmp.pl --plugin=os::solaris::snmp::plugin --mode=interfaces --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --interface='$_SERVICEINTERFACE$' --add-status --add-traffic $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: INTERFACE, value: "", description: "Filtre sur le nom de l'interface"}
- {name: EXTRAOPTIONS, value: "--interface-use-adminstatus --name", description: "Options supplementaires"}
+49
View File
@@ -0,0 +1,49 @@
id: os-windows-nsclient
name: Windows (NSClient++ / check_nt)
category: system
status: verified
plugin: check_nt
description: Supervision Windows par l'agent NSClient++ en protocole NSC (check_nt), utile quand SNMP n'est pas ouvert.
host_template:
name: OS-Windows-NSClient-custom
alias: OS Windows NSClient
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: NSCLIENTPORT, value: "12489", description: "Port d'ecoute NSClient++"}
- {name: NSCLIENTPASSWORD, value: "", description: "Mot de passe NSClient++", is_password: true}
services:
- name: Cpu
alias: Cpu
description: Charge CPU sur 1, 5 et 15 minutes
line: "$USER1$/check_nt -H $HOSTADDRESS$ -p $_HOSTNSCLIENTPORT$ -s '$_HOSTNSCLIENTPASSWORD$' -v CPULOAD -l $_SERVICECPULOAD$"
macros:
- {name: CPULOAD, value: "1,80,90,5,80,90,15,75,85", description: "Triplets minutes,warning,critical"}
- name: Memory
alias: Memory
description: Utilisation de la memoire
line: "$USER1$/check_nt -H $HOSTADDRESS$ -p $_HOSTNSCLIENTPORT$ -s '$_HOSTNSCLIENTPASSWORD$' -v MEMUSE -w $_SERVICEWARNING$ -c $_SERVICECRITICAL$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- name: Disk-C
alias: Disk-C
description: Occupation d'un lecteur
line: "$USER1$/check_nt -H $HOSTADDRESS$ -p $_HOSTNSCLIENTPORT$ -s '$_HOSTNSCLIENTPASSWORD$' -v USEDDISKSPACE -l $_SERVICEDRIVE$ -w $_SERVICEWARNING$ -c $_SERVICECRITICAL$"
macros:
- {name: DRIVE, value: "C", description: "Lettre du lecteur"}
- {name: WARNING, value: "80", description: "Seuil warning en %"}
- {name: CRITICAL, value: "90", description: "Seuil critical en %"}
- name: Service
alias: Service
description: Etat d'un service Windows
line: "$USER1$/check_nt -H $HOSTADDRESS$ -p $_HOSTNSCLIENTPORT$ -s '$_HOSTNSCLIENTPASSWORD$' -v SERVICESTATE -l $_SERVICESERVICENAME$"
macros:
- {name: SERVICENAME, value: "", description: "Nom court du service Windows"}
- name: Uptime
alias: Uptime
description: Duree depuis le dernier redemarrage
default: false
line: "$USER1$/check_nt -H $HOSTADDRESS$ -p $_HOSTNSCLIENTPORT$ -s '$_HOSTNSCLIENTPASSWORD$' -v UPTIME"
macros: []
+78
View File
@@ -0,0 +1,78 @@
id: os-windows-snmp
name: Windows (SNMP)
category: system
status: verified
plugin: centreon_windows_snmp.pl
description: Supervision d'un serveur Windows via SNMP (CPU, memoire, disques, services, interfaces, uptime).
host_template:
name: OS-Windows-SNMP-custom
alias: OS Windows SNMP
parents: [generic-active-host-custom]
check_command: check_centreon_ping
check_command_args: "!3!200,20%!400,50%"
macros:
- {name: SNMPEXTRAOPTIONS, value: "", description: "Options supplementaires passees au plugin"}
services:
- name: Cpu
alias: Cpu
description: Charge CPU globale
line: "$CENTREONPLUGINS$/centreon_windows_snmp.pl --plugin=os::windows::snmp::plugin --mode=cpu --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-average='$_SERVICEWARNING$' --critical-average='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en % de CPU"}
- {name: CRITICAL, value: "90", description: "Seuil critical en % de CPU"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Memory
alias: Memory
description: Utilisation de la memoire physique
line: "$CENTREONPLUGINS$/centreon_windows_snmp.pl --plugin=os::windows::snmp::plugin --mode=memory --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en % utilise"}
- {name: CRITICAL, value: "90", description: "Seuil critical en % utilise"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Swap
alias: Swap
description: Utilisation du fichier d'echange
default: false
line: "$CENTREONPLUGINS$/centreon_windows_snmp.pl --plugin=os::windows::snmp::plugin --mode=swap --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "50", description: "Seuil warning en % utilise"}
- {name: CRITICAL, value: "75", description: "Seuil critical en % utilise"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Storage
alias: Storage
description: Occupation des disques locaux
line: "$CENTREONPLUGINS$/centreon_windows_snmp.pl --plugin=os::windows::snmp::plugin --mode=storage --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --filter-storage-type='$_SERVICEFILTERSTORAGETYPE$' --warning-usage-prct='$_SERVICEWARNING$' --critical-usage-prct='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "80", description: "Seuil warning en % utilise"}
- {name: CRITICAL, value: "90", description: "Seuil critical en % utilise"}
- {name: FILTERSTORAGETYPE, value: "^(hrStorageFixedDisk)$", description: "Filtre sur le type de stockage"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Service
alias: Service
description: Etat d'un service Windows
line: "$CENTREONPLUGINS$/centreon_windows_snmp.pl --plugin=os::windows::snmp::plugin --mode=service --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --service='$_SERVICESERVICENAME$' --warning-status='$_SERVICEWARNINGSTATUS$' --critical-status='$_SERVICECRITICALSTATUS$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: SERVICENAME, value: "", description: "Nom du service Windows a superviser"}
- {name: WARNINGSTATUS, value: "", description: "Regle warning sur le statut"}
- {name: CRITICALSTATUS, value: "%{state} !~ /running/i", description: "Regle critical sur le statut"}
- {name: EXTRAOPTIONS, value: "", description: "Options supplementaires"}
- name: Interfaces
alias: Interfaces
description: Etat et trafic des interfaces reseau
line: "$CENTREONPLUGINS$/centreon_windows_snmp.pl --plugin=os::windows::snmp::plugin --mode=interfaces --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --interface='$_SERVICEINTERFACE$' --add-status --add-traffic --warning-in-traffic='$_SERVICEWARNINGIN$' --critical-in-traffic='$_SERVICECRITICALIN$' --warning-out-traffic='$_SERVICEWARNINGOUT$' --critical-out-traffic='$_SERVICECRITICALOUT$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: INTERFACE, value: "", description: "Filtre sur le nom de l'interface"}
- {name: WARNINGIN, value: "80", description: "Seuil warning trafic entrant (%)"}
- {name: CRITICALIN, value: "90", description: "Seuil critical trafic entrant (%)"}
- {name: WARNINGOUT, value: "80", description: "Seuil warning trafic sortant (%)"}
- {name: CRITICALOUT, value: "90", description: "Seuil critical trafic sortant (%)"}
- {name: EXTRAOPTIONS, value: "--interface-use-adminstatus --name", description: "Options supplementaires"}
- name: Uptime
alias: Uptime
description: Duree depuis le dernier redemarrage
default: false
line: "$CENTREONPLUGINS$/centreon_windows_snmp.pl --plugin=os::windows::snmp::plugin --mode=uptime --hostname=$HOSTADDRESS$ --snmp-version='$_HOSTSNMPVERSION$' --snmp-community='$_HOSTSNMPCOMMUNITY$' --warning-uptime='$_SERVICEWARNING$' --critical-uptime='$_SERVICECRITICAL$' $_HOSTSNMPEXTRAOPTIONS$ $_SERVICEEXTRAOPTIONS$"
macros:
- {name: WARNING, value: "", description: "Seuil warning (secondes)"}
- {name: CRITICAL, value: "@0:600", description: "Seuil critical (secondes)"}
- {name: EXTRAOPTIONS, value: "--unit=s", description: "Options supplementaires"}
+24
View File
@@ -0,0 +1,24 @@
# Developpement local : image construite sur place, code monte en direct,
# rechargement automatique, port publie.
#
# docker compose -f compose.dev.yaml up --build
name: enclume-dev
services:
web:
build:
context: .
args:
ENCLUME_VERSION: dev
command: >-
sh -c "exec python -m flask --app 'enclume.webapp:create_app()' run
--host 0.0.0.0 --port 8080 --debug"
environment:
ENCLUME_CATALOG: /app/catalog
FLASK_DEBUG: "1"
volumes:
- ./enclume:/app/enclume:ro
- ./catalog:/app/catalog:ro
ports:
- "127.0.0.1:8080:8080"
+100
View File
@@ -0,0 +1,100 @@
# Deploiement de production, derriere le Traefik existant.
# Aucun port n'est publie : le trafic entre par le tunnel Cloudflare puis Traefik.
#
# cp .env.example .env puis renseigner ENCLUME_TAG et ENCLUME_DOMAINE
# docker compose pull && docker compose up -d
#
# Retour arriere : remettre l'ancienne valeur de ENCLUME_TAG, puis up -d.
name: enclume
services:
web:
image: ${ENCLUME_REGISTRY:-harbor.tips-of-mine.com}/enclume/web:${ENCLUME_TAG:?definir ENCLUME_TAG dans .env}
restart: unless-stopped
environment:
ENCLUME_CATALOG: /catalogue
GUNICORN_WORKERS: ${GUNICORN_WORKERS:-2}
TZ: ${TZ:-Europe/Paris}
volumes:
- catalogue:/catalogue:ro
networks: [interne, traefik]
read_only: true
tmpfs:
- /tmp:size=32m,mode=1777
security_opt: ["no-new-privileges:true"]
cap_drop: [ALL]
deploy:
resources:
limits: {cpus: "1.0", memory: 512M}
logging:
driver: json-file
options: {max-size: "10m", max-file: "3"}
labels:
traefik.enable: "true"
traefik.docker.network: traefik
traefik.http.routers.enclume.rule: Host(`${ENCLUME_DOMAINE:?definir ENCLUME_DOMAINE}`)
traefik.http.routers.enclume.entrypoints: websecure
traefik.http.routers.enclume.tls.certresolver: ${TRAEFIK_RESOLVER:-cloudflare}
traefik.http.routers.enclume.middlewares: enclume-entetes,enclume-limite
traefik.http.services.enclume.loadbalancer.server.port: "8080"
traefik.http.middlewares.enclume-entetes.headers.stsSeconds: "31536000"
traefik.http.middlewares.enclume-entetes.headers.stsIncludeSubdomains: "true"
traefik.http.middlewares.enclume-entetes.headers.frameDeny: "true"
traefik.http.middlewares.enclume-entetes.headers.contentTypeNosniff: "true"
traefik.http.middlewares.enclume-limite.ratelimit.average: "60"
traefik.http.middlewares.enclume-limite.ratelimit.burst: "120"
catalog-sync:
image: ${ENCLUME_REGISTRY:-harbor.tips-of-mine.com}/enclume/catalog-sync:${ENCLUME_TAG:?definir ENCLUME_TAG dans .env}
restart: unless-stopped
environment:
CATALOG_REPO: ${CATALOG_REPO:-}
CATALOG_BRANCH: ${CATALOG_BRANCH:-main}
CATALOG_SUBDIR: ${CATALOG_SUBDIR:-catalog}
CATALOG_DEST: /catalogue
SYNC_INTERVAL: ${SYNC_INTERVAL:-300}
RELOAD_URL: http://web:8080/recharger
volumes:
- catalogue:/catalogue
networks: [interne]
security_opt: ["no-new-privileges:true"]
deploy:
resources:
limits: {cpus: "0.25", memory: 128M}
logging:
driver: json-file
options: {max-size: "5m", max-file: "2"}
# Utilisee a partir du chantier 2 (liens de partage, projets enregistres).
# L'application fonctionne sans, tant que ENCLUME_DATABASE_URL n'est pas defini.
db:
image: postgres:16.4-alpine
restart: unless-stopped
environment:
POSTGRES_DB: enclume
POSTGRES_USER: enclume
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?definir POSTGRES_PASSWORD}
TZ: ${TZ:-Europe/Paris}
volumes:
- donnees:/var/lib/postgresql/data
networks: [interne]
security_opt: ["no-new-privileges:true"]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U enclume -d enclume"]
interval: 20s
timeout: 5s
retries: 5
deploy:
resources:
limits: {cpus: "1.0", memory: 512M}
volumes:
catalogue:
donnees:
networks:
interne:
internal: true
traefik:
external: true
-42
View File
@@ -1,42 +0,0 @@
#### NETWORKS
networks:
traefik_front_network:
external: true
back_network_:
driver: bridge
attachable: true
#### SERVICES
services:
### hello_world
hello_world:
container_name: gitea-app
hostname: gitea-app
image: hello-world
environment:
restart: always
networks:
# - back_network_gitea
- traefik_front_network
volumes:
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik_front_network"
# HTTP
- "traefik.http.routers.hello-world-http.rule=Host(`hello-world.tips-of-mine.com`)"
- "traefik.http.routers.hello-world-http.entrypoints=http"
- "traefik.http.routers.hello-world-http.priority=49"
# HTTPS
- "traefik.http.routers.hello-world-https.rule=Host(`hello-world.tips-of-mine.com`)"
- "traefik.http.routers.hello-world-https.entrypoints=https"
- "traefik.http.routers.hello-world-https.tls=true"
- "traefik.http.routers.hello-world-https.priority=50"
- "traefik.http.routers.gitea.service=gitea-https-service"
# Middleware
# Service
# - "traefik.http.services.gitea-https-service.loadbalancer.server.port=3000"
# - "traefik.http.services.gitea-https-service.loadbalancer.server.scheme=https"
# - "traefik.http.services.gitea-https-service.loadbalancer.healthcheck.hostname=gitea.traefik.me"
# - "traefik.http.services.gitea-https-service.loadbalancer.healthcheck.method=foobar"
# - "traefik.http.services.gitea-https-service.loadbalancer.healthcheck.timeout=10"
# - "traefik.http.services.gitea-https-service.loadbalancer.healthcheck.interval=30"
+42
View File
@@ -0,0 +1,42 @@
#!/bin/sh
# Clone ou met a jour le depot du catalogue, publie son contenu dans le volume
# partage, puis notifie l'application. Boucle jusqu'a l'arret du conteneur.
set -eu
if [ -z "${CATALOG_REPO}" ]; then
echo "catalog-sync : CATALOG_REPO non defini, rien a synchroniser." >&2
exit 0
fi
TRAVAIL=/tmp/depot
sync_une_fois() {
if [ -d "$TRAVAIL/.git" ]; then
git -C "$TRAVAIL" fetch --quiet --depth 1 origin "$CATALOG_BRANCH"
git -C "$TRAVAIL" reset --quiet --hard "origin/$CATALOG_BRANCH"
else
git clone --quiet --depth 1 --branch "$CATALOG_BRANCH" "$CATALOG_REPO" "$TRAVAIL"
fi
SOURCE="$TRAVAIL/$CATALOG_SUBDIR"
[ -d "$SOURCE" ] || { echo "catalog-sync : $CATALOG_SUBDIR absent du depot." >&2; return 1; }
# Publication atomique : on ecrit a cote, puis on bascule.
NEUF="${CATALOG_DEST}.neuf"
rm -rf "$NEUF"
mkdir -p "$NEUF"
cp -a "$SOURCE"/. "$NEUF"/
rm -rf "${CATALOG_DEST}.ancien"
[ -d "$CATALOG_DEST" ] && mv "$CATALOG_DEST" "${CATALOG_DEST}.ancien"
mv "$NEUF" "$CATALOG_DEST"
rm -rf "${CATALOG_DEST}.ancien"
REVISION=$(git -C "$TRAVAIL" rev-parse --short HEAD)
echo "catalog-sync : catalogue a jour ($REVISION)"
curl -fsS -X POST "$RELOAD_URL" >/dev/null 2>&1 || echo "catalog-sync : rechargement non confirme"
}
while true; do
sync_une_fois || echo "catalog-sync : echec de la synchronisation, nouvelle tentative dans ${SYNC_INTERVAL}s" >&2
sleep "$SYNC_INTERVAL"
done
+91
View File
@@ -0,0 +1,91 @@
# Exploitation
## Composants
| Conteneur | Image | Rôle | État |
|---|---|---|---|
| `web` | `harbor…/enclume/web:<version>` | l'application : éditeur, validation, génération | sans état |
| `catalog-sync` | `harbor…/enclume/catalog-sync:<version>` | clone le dépôt du catalogue dans un volume, prévient l'application | sans état |
| `db` | `postgres:16.4-alpine` | partages et projets enregistrés (à partir du chantier 2) | volume `donnees` |
Traefik, Authentik et le tunnel Cloudflare sont ceux du homelab : ils ne font pas partie
de cette pile. `web` rejoint le réseau `traefik` existant et n'expose aucun port.
Le réseau `interne` est marqué `internal: true` : `db` et `catalog-sync` n'ont aucun accès
sortant depuis Docker, et personne ne les joint de l'extérieur.
## Première installation
```bash
git clone https://gitea.tips-of-mine.com/hubert/enclume.git
cd enclume
cp .env.example .env # renseigner ENCLUME_TAG, ENCLUME_DOMAINE, POSTGRES_PASSWORD
docker compose pull
docker compose up -d
curl -s https://clapi.tips-of-mine.com/sante
```
`/sante` renvoie la version et le commit de l'image en cours : c'est la source de vérité
sur ce qui tourne réellement.
## Mise à jour
Une évolution donne une image, jamais un `docker compose build` sur le serveur.
```bash
sed -i 's/^ENCLUME_TAG=.*/ENCLUME_TAG=1.4.0/' .env
docker compose pull && docker compose up -d
curl -s https://clapi.tips-of-mine.com/sante | grep 1.4.0
```
## Retour arrière
```bash
sed -i 's/^ENCLUME_TAG=.*/ENCLUME_TAG=1.3.2/' .env
docker compose up -d web catalog-sync
```
Quelques secondes, sans reconstruction. Trois conditions le garantissent :
1. **Aucun `latest` en production.** Le compose refuse de démarrer si `ENCLUME_TAG` est absent.
2. **Aucun état dans `web`.** Le système de fichiers est en lecture seule, le projet vit dans
le navigateur du visiteur. Revenir en arrière ne perd rien.
3. **Migrations compatibles dans les deux sens.** À partir du chantier 2 : on ajoute une
colonne, on ne la supprime jamais dans la même version que le code qui cesse de l'utiliser.
La suppression arrive une version plus tard, quand le retour arrière n'est plus envisagé.
Sans cette règle, revenir à l'image précédente casse l'application.
Conserver au minimum les trois dernières versions dans Harbor (politique de rétention par
nombre de tags, pas par date).
## Catalogue
`catalog-sync` clone le dépôt du catalogue toutes les cinq minutes, publie le contenu de
façon atomique dans le volume partagé, puis appelle `/recharger`. Ajouter un pack ne demande
donc ni image ni redémarrage : un commit suffit.
Si `CATALOG_REPO` est vide, l'application utilise le catalogue livré dans son image.
## Sauvegarde
Le volume `catalogue` est jetable, il se reconstruit depuis Git. Seul `donnees` compte :
```bash
docker compose exec -T db pg_dump -U enclume enclume | zstd > enclume-$(date +%F).sql.zst
```
À brancher sur la sauvegarde existante du homelab.
## Journaux et diagnostic
```bash
docker compose logs -f web
docker compose exec web python -m enclume.cli --list # le catalogue vu par l'application
docker compose ps # état des healthchecks
```
## Ce qui est verrouillé dans `web`
Utilisateur non root (uid 10001), racine en lecture seule, `/tmp` en tmpfs, toutes les
capacités retirées, `no-new-privileges`, corps de requête plafonné à 2 Mo, en-têtes CSP,
`nosniff` et `no-referrer` posés par l'application, limitation de débit au niveau de Traefik.
+86
View File
@@ -0,0 +1,86 @@
# Création des emplacements — Gitea et Harbor
À faire une seule fois, avant le premier `git push`. Les valeurs ci-dessous sont celles
qu'attendent `compose.yaml` et `.gitea/workflows/build.yaml`.
## Gitea
**Deux dépôts, pas un.**
| Dépôt | Contenu | Visibilité |
|---|---|---|
| `enclume` | l'application, les images, la CI | public |
| `enclume-catalogue` | uniquement `catalog/`, les packs YAML | public |
Le catalogue vit à part parce que `catalog-sync` le tire directement : ajouter un pack devient
un commit, sans reconstruction d'image. C'est aussi ce qui rendra les contributions par pull
request lisibles — on relit dix lignes de YAML, pas un diff mêlé au code.
Sur `enclume` :
- branche par défaut `main`, protégée : pas de push direct, fusion par pull request
- Actions activées, avec un runner ayant accès au démon Docker
- secrets du dépôt : `HARBOR_USER` et `HARBOR_TOKEN` (le compte robot créé plus bas)
- licence MIT à la racine
Sur `enclume-catalogue` : même protection de branche. C'est là qu'arriveront les soumissions
modérées du chantier 5.
## Harbor
Projet **`enclume`**, en accès **public** : n'importe qui peut alors tirer les images pour
s'auto-héberger, sans compte chez toi. Seule la publication demande une authentification.
Dépôts créés automatiquement au premier push : `enclume/web` et `enclume/catalog-sync`.
### Compte robot
Créer un robot au niveau du projet, nommé `robot$enclume-ci`, avec les droits
**push** et **pull** sur les dépôts — rien d'autre, pas de suppression. Le secret généré
va dans `HARBOR_TOKEN` côté Gitea, le nom complet du robot dans `HARBOR_USER`.
### Immuabilité des versions
Règle d'immuabilité sur les tags correspondant à `[0-9]*.[0-9]*.[0-9]*`.
C'est le réglage qui donne sa valeur au retour arrière : une version publiée ne peut plus
être écrasée, donc `ENCLUME_TAG=1.3.2` désigne aujourd'hui exactement la même image
qu'au jour de sa publication.
### Rétention
Conserver les **10 derniers tags** correspondant à `[0-9]*.[0-9]*.[0-9]*`, et purger les tags
`0.0.0-main.*` de plus de 30 jours. Les tags `sha-*` servent au diagnostic, une rétention de
30 jours suffit.
### Analyse
Scanner automatiquement à la publication. Ne pas activer « empêcher le déploiement d'images
vulnérables » au démarrage : une CVE sans correctif dans une image de base bloquerait un
déploiement urgent. Le rapport Trivy dans la CI suffit à surveiller.
## Enchaînement
```bash
git remote add origin https://gitea.tips-of-mine.com/hubert/enclume.git
git push -u origin main
```
La CI construit, teste et publie `0.0.0-main.<commit>`. Pour une vraie version :
```bash
git tag v1.0.0 && git push origin v1.0.0
```
Puis sur le serveur, dans le répertoire du compose :
```bash
cp .env.example .env # renseigner ENCLUME_TAG=1.0.0 et le domaine
docker compose pull && docker compose up -d
curl -s https://<domaine>/sante
```
## Réseau Traefik
Le compose attend un réseau externe nommé `traefik`. S'il porte un autre nom dans ton
installation, ajuster la section `networks` et l'étiquette `traefik.docker.network`.
View File
Whitespace-only changes.
+180
View File
@@ -0,0 +1,180 @@
"""Chargement du catalogue de packs de supervision.
Un pack est un fichier YAML decrivant, pour une technologie donnee :
- un modele d'hote (host template) et ses macros,
- une liste de services, chacun portant sa ligne de commande et ses macros.
Le catalogue est volontairement en donnees et non en code : ajouter un pack
revient a deposer un fichier YAML dans catalog/<categorie>/.
"""
from __future__ import annotations
import os
from dataclasses import dataclass, field
from typing import Any
import yaml
CATEGORIES = {
"system": "Systemes d'exploitation",
"hardware": "Materiel",
"application": "Applications",
}
@dataclass
class Macro:
name: str
value: str = ""
description: str = ""
is_password: bool = False
@classmethod
def from_dict(cls, raw: dict[str, Any]) -> "Macro":
return cls(
name=str(raw["name"]).strip().upper(),
value="" if raw.get("value") is None else str(raw.get("value")),
description=str(raw.get("description", "")),
is_password=bool(raw.get("is_password", False)),
)
@dataclass
class Service:
name: str
alias: str
description: str
line: str
macros: list[Macro] = field(default_factory=list)
enabled_by_default: bool = True
check_interval: int | None = None
retry_interval: int | None = None
max_attempts: int | None = None
@classmethod
def from_dict(cls, raw: dict[str, Any]) -> "Service":
return cls(
name=str(raw["name"]).strip(),
alias=str(raw.get("alias") or raw["name"]).strip(),
description=str(raw.get("description", "")),
line=str(raw["line"]).strip(),
macros=[Macro.from_dict(m) for m in raw.get("macros") or []],
enabled_by_default=bool(raw.get("default", True)),
check_interval=raw.get("check_interval"),
retry_interval=raw.get("retry_interval"),
max_attempts=raw.get("max_attempts"),
)
@dataclass
class HostTemplate:
name: str
alias: str
parents: list[str] = field(default_factory=list)
check_command: str = ""
check_command_args: str = ""
macros: list[Macro] = field(default_factory=list)
@classmethod
def from_dict(cls, raw: dict[str, Any]) -> "HostTemplate":
return cls(
name=str(raw["name"]).strip(),
alias=str(raw.get("alias") or raw["name"]).strip(),
parents=[str(p).strip() for p in raw.get("parents") or []],
check_command=str(raw.get("check_command", "")),
check_command_args=str(raw.get("check_command_args", "")),
macros=[Macro.from_dict(m) for m in raw.get("macros") or []],
)
@dataclass
class Pack:
id: str
name: str
category: str
description: str
plugin: str
status: str
host_template: HostTemplate
services: list[Service]
source_file: str = ""
@property
def prefix(self) -> str:
"""Racine des noms de modeles de service, deduite du modele d'hote.
OS-Linux-SNMP-custom -> OS-Linux-SNMP
"""
base = self.host_template.name
return base[: -len("-custom")] if base.endswith("-custom") else base
@property
def category_label(self) -> str:
return CATEGORIES.get(self.category, self.category)
def service(self, name: str) -> Service | None:
for svc in self.services:
if svc.name == name:
return svc
return None
@classmethod
def from_dict(cls, raw: dict[str, Any], source_file: str = "") -> "Pack":
return cls(
id=str(raw["id"]).strip(),
name=str(raw["name"]).strip(),
category=str(raw.get("category", "system")).strip(),
description=str(raw.get("description", "")),
plugin=str(raw.get("plugin", "")),
status=str(raw.get("status", "to-verify")),
host_template=HostTemplate.from_dict(raw["host_template"]),
services=[Service.from_dict(s) for s in raw.get("services") or []],
source_file=source_file,
)
class Catalog:
def __init__(self, root: str):
self.root = root
self.packs: dict[str, Pack] = {}
self.errors: list[str] = []
self.load()
def load(self) -> None:
self.packs = {}
self.errors = []
for dirpath, _dirnames, filenames in os.walk(self.root):
for filename in sorted(filenames):
if not filename.endswith((".yml", ".yaml")):
continue
path = os.path.join(dirpath, filename)
try:
with open(path, encoding="utf-8") as handle:
raw = yaml.safe_load(handle)
pack = Pack.from_dict(raw, source_file=path)
except Exception as exc: # noqa: BLE001 - remonte tel quel a l'UI
self.errors.append(f"{path} : {exc}")
continue
if pack.id in self.packs:
self.errors.append(
f"{path} : identifiant deja utilise par "
f"{self.packs[pack.id].source_file}"
)
continue
self.packs[pack.id] = pack
def get(self, pack_id: str) -> Pack | None:
return self.packs.get(pack_id)
def by_category(self) -> dict[str, list[Pack]]:
grouped: dict[str, list[Pack]] = {key: [] for key in CATEGORIES}
for pack in self.packs.values():
grouped.setdefault(pack.category, []).append(pack)
for packs in grouped.values():
packs.sort(key=lambda p: p.name.lower())
return grouped
@property
def service_count(self) -> int:
return sum(len(p.services) for p in self.packs.values())
+97
View File
@@ -0,0 +1,97 @@
"""Ligne de commande : meme moteur que le site, utilisable en CI.
python3 -m enclume.cli --list
python3 -m enclume.cli --pack os-linux-snmp --pack app-mysql --out ./sortie
python3 -m enclume.cli --projet projet.json --out ./sortie
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from .catalog import Catalog
from .generator import build_plan, render_bundle
from .model import Project
from .packs import pack_to_project
DEFAULT_CATALOG = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "catalog")
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Generateur CLAPI Centreon")
parser.add_argument("--catalog", default=os.environ.get("ENCLUME_CATALOG", DEFAULT_CATALOG))
parser.add_argument("--list", action="store_true", help="liste les packs disponibles")
parser.add_argument("--pack", action="append", default=[], help="pack a importer (repetable)")
parser.add_argument("--tout", action="store_true", help="importer toutes les sondes, pas seulement celles par defaut")
parser.add_argument("--projet", help="fichier de projet JSON exporte depuis le site")
parser.add_argument("--prefix", default="", help="prefixe applique aux objets crees")
parser.add_argument("--plugins-dir", default="/usr/lib/centreon/plugins")
parser.add_argument("--version-centreon", default="24.x")
parser.add_argument("--poller", default="Central")
parser.add_argument("--out", help="repertoire de sortie (defaut : stdout)")
parser.add_argument("--strict", action="store_true", help="code de retour non nul si le projet contient une erreur")
args = parser.parse_args(argv)
catalog = Catalog(args.catalog)
for erreur in catalog.errors:
print(f"catalogue : {erreur}", file=sys.stderr)
if args.list:
for pack in sorted(catalog.packs.values(), key=lambda p: (p.category, p.name)):
marque = " " if pack.status == "verified" else "!"
print(f"{marque} {pack.id:<28} {pack.category:<12} {len(pack.services):>2} sondes {pack.name}")
return 0
if args.projet:
with open(args.projet, encoding="utf-8") as handle:
project = Project.from_dict(json.load(handle))
elif args.pack:
project = Project(
name="Projet en ligne de commande",
centreon_version=args.version_centreon,
prefix=args.prefix,
plugins_dir=args.plugins_dir,
poller=args.poller,
)
for pack_id in args.pack:
pack = catalog.get(pack_id)
if pack is None:
print(f"pack inconnu : {pack_id}", file=sys.stderr)
return 2
for message in project.merge(pack_to_project(pack, only_defaults=not args.tout)):
print(f"fusion : {message}", file=sys.stderr)
else:
parser.error("indiquez --list, --pack ou --projet")
return 2
plan = build_plan(project)
fichiers = render_bundle(plan)
for probleme in plan.issues:
print(f"{probleme['level']} : {probleme['message']}", file=sys.stderr)
if not args.out:
sys.stdout.write(fichiers["deploy.clapi"])
else:
os.makedirs(args.out, exist_ok=True)
for nom, contenu in fichiers.items():
chemin = os.path.join(args.out, nom)
with open(chemin, "w", encoding="utf-8") as handle:
handle.write(contenu)
if nom.endswith(".sh"):
os.chmod(chemin, 0o755)
with open(os.path.join(args.out, "projet.json"), "w", encoding="utf-8") as handle:
json.dump(project.to_dict(), handle, indent=2, ensure_ascii=False)
print(f"{args.out} : " + ", ".join(f"{v} {k}" for k, v in plan.summary.items() if v))
return 1 if (args.strict and plan.has_errors) else 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except BrokenPipeError:
raise SystemExit(0)
+266
View File
@@ -0,0 +1,266 @@
"""Traduction d'un projet en operations CLAPI, puis en fichiers.
Un seul parcours du projet produit la liste d'operations ; les quatre fichiers
livres a l'utilisateur ne sont que des rendus differents de cette liste.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any
from .model import Macro, Project, validate
CLAPI_BIN = "/usr/share/centreon/bin/centreon"
@dataclass
class Op:
obj: str
action: str
values: list[str] = field(default_factory=list)
comment: str = ""
def import_line(self) -> str:
return ";".join([self.obj, self.action, *self.values])
def shell_line(self) -> str:
# Les valeurs contiennent des macros Centreon ($HOSTADDRESS$, $_SERVICEWARNING$).
# Guillemets simples pour que bash ne les interprete pas ; apostrophes echappees.
payload = ";".join(self.values).replace("'", "'\\''")
return f"clapi -o {self.obj} -a {self.action} -v '{payload}'"
@dataclass
class Plan:
project: Project
ops: list[Op] = field(default_factory=list)
rollback_ops: list[Op] = field(default_factory=list)
issues: list[dict[str, str]] = field(default_factory=list)
@property
def summary(self) -> dict[str, int]:
return {
"commandes": len(self.project.commands),
"modeles de service": len(self.project.service_templates),
"modeles d'hote": len(self.project.host_templates),
"hotes": len(self.project.hosts),
"operations": len([o for o in self.ops if o.obj != "#"]),
}
@property
def has_errors(self) -> bool:
return any(i["level"] == "error" for i in self.issues)
def _macro_values(target: str, macro: Macro, fields: int) -> list[str]:
if fields == 3:
return [target, macro.name, macro.value]
return [target, macro.name, macro.value, "1" if macro.is_password else "0", macro.description]
def build_plan(project: Project) -> Plan:
plan = Plan(project=project, issues=validate(project))
prefix = project.prefix
fields = project.macro_fields
def full(name: str) -> str:
return f"{prefix}{name}" if name else name
own_commands = {c.name for c in project.commands}
own_htpl = {h.name for h in project.host_templates}
def ref_command(name: str) -> str:
"""Un objet du projet est prefixe ; un objet deja present sur le central ne l'est pas."""
return full(name) if name in own_commands else name
def ref_htpl(name: str) -> str:
return full(name) if name in own_htpl else name
del_cmd: list[Op] = []
del_stpl: list[Op] = []
del_htpl: list[Op] = []
del_host: list[Op] = []
if project.commands:
plan.ops.append(Op("#", "#", comment="Commandes de controle"))
for command in project.commands:
line = command.line.replace("$CENTREONPLUGINS$", project.plugins_dir)
plan.ops.append(Op("CMD", "ADD", [full(command.name), command.type, line]))
if command.comment:
plan.ops.append(Op("CMD", "setparam", [full(command.name), "comment", command.comment]))
del_cmd.append(Op("CMD", "DEL", [full(command.name)]))
if project.service_templates:
plan.ops.append(Op("#", "#", comment="Modeles de service"))
for stpl in project.service_templates:
plan.ops.append(
Op(
"STPL",
"ADD",
[
full(stpl.name),
stpl.alias or stpl.name,
stpl.parent,
ref_command(stpl.command),
stpl.command_args,
str(stpl.check_interval),
str(stpl.retry_interval),
str(stpl.max_attempts),
str(stpl.active_checks),
str(stpl.passive_checks),
],
)
)
for macro in stpl.macros:
plan.ops.append(Op("STPL", "setmacro", _macro_values(full(stpl.name), macro, fields)))
del_stpl.append(Op("STPL", "DEL", [full(stpl.name)]))
if project.host_templates:
plan.ops.append(Op("#", "#", comment="Modeles d'hote"))
for htpl in project.host_templates:
parents = "|".join(htpl.parents)
plan.ops.append(Op("HTPL", "ADD", [full(htpl.name), htpl.alias or htpl.name, "", parents, "", ""]))
for param, value in (
("check_command", htpl.check_command),
("check_command_arguments", htpl.check_command_args),
("snmp_version", htpl.snmp_version),
("snmp_community", htpl.snmp_community),
("timezone", htpl.timezone),
):
if value:
plan.ops.append(Op("HTPL", "setparam", [full(htpl.name), param, value]))
for macro in htpl.macros:
plan.ops.append(Op("HTPL", "setmacro", _macro_values(full(htpl.name), macro, fields)))
del_htpl.append(Op("HTPL", "DEL", [full(htpl.name)]))
links = [(s, h) for s in project.service_templates for h in s.host_templates]
if links:
plan.ops.append(Op("#", "#", comment="Rattachement des modeles de service aux modeles d'hote"))
for stpl, htpl_name in links:
plan.ops.append(Op("STPL", "addhosttemplate", [full(stpl.name), ref_htpl(htpl_name)]))
if project.hosts:
plan.ops.append(Op("#", "#", comment="Hotes"))
for host in project.hosts:
plan.ops.append(
Op(
"HOST",
"ADD",
[
host.name,
host.alias or host.name,
host.address,
"|".join(ref_htpl(t) for t in host.templates),
host.poller or project.poller,
host.hostgroups,
],
)
)
for macro in host.macros:
plan.ops.append(Op("HOST", "setmacro", _macro_values(host.name, macro, fields)))
plan.ops.append(Op("HOST", "applytpl", [host.name]))
del_host.append(Op("HOST", "DEL", [host.name]))
plan.rollback_ops = [*del_host, *del_stpl, *del_htpl, *del_cmd]
return plan
# --------------------------------------------------------------------------- #
# Rendus
# --------------------------------------------------------------------------- #
def _header(title: str, plan: Plan) -> list[str]:
stamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
summary = ", ".join(f"{v} {k}" for k, v in plan.summary.items() if v)
return [
f"# {title} - {plan.project.name}",
f"# Genere le {stamp}",
f"# Cible : {plan.project.centreon_version} | {summary}",
"#",
]
def render_import(plan: Plan, ops: list[Op], title: str) -> str:
lines = _header(title, plan)
for op in ops:
lines.append(f"# {op.comment}" if op.obj == "#" else op.import_line())
return "\n".join(lines) + "\n"
def render_shell(plan: Plan, ops: list[Op], title: str, apply_config: bool) -> str:
lines = [
"#!/usr/bin/env bash",
"#",
*_header(title, plan),
"# Usage :",
"# export CENTREON_USER=admin",
"# ./deploy.sh --dry-run affiche les commandes sans les executer",
"# ./deploy.sh applique la configuration",
"",
"set -euo pipefail",
"",
f'CLAPI_BIN="${{CLAPI_BIN:-{CLAPI_BIN}}}"',
'CENTREON_USER="${CENTREON_USER:-admin}"',
"DRY_RUN=0",
'[ "${1:-}" = "--dry-run" ] && DRY_RUN=1',
"",
'if [ "$DRY_RUN" -eq 0 ] && [ -z "${CENTREON_PASS:-}" ]; then',
' read -r -s -p "Mot de passe Centreon pour $CENTREON_USER : " CENTREON_PASS',
" echo",
"fi",
"",
"clapi() {",
' if [ "$DRY_RUN" -eq 1 ]; then',
' local out="$CLAPI_BIN -u $CENTREON_USER -p *** " arg',
' for arg in "$@"; do',
' case "$arg" in',
" *[!A-Za-z0-9_.:/=-]*) out=\"$out '$arg'\" ;;",
' *) out="$out $arg" ;;',
" esac",
" done",
' echo "$out"',
" else",
' "$CLAPI_BIN" -u "$CENTREON_USER" -p "$CENTREON_PASS" "$@"',
" fi",
"}",
"",
]
for op in ops:
if op.obj == "#":
lines.extend(["", f"# {op.comment}"])
else:
lines.append(op.shell_line())
if apply_config:
lines.extend(
[
"",
"# Generation et deploiement de la configuration du collecteur",
f"clapi -a APPLYCFG -v '{plan.project.poller}'",
]
)
lines.extend(["", 'echo "Termine."', ""])
return "\n".join(lines)
def render_bundle(plan: Plan) -> dict[str, str]:
apply_config = bool(plan.project.apply_config)
return {
"deploy.clapi": render_import(plan, plan.ops, "Deploiement CLAPI"),
"deploy.sh": render_shell(plan, plan.ops, "Deploiement CLAPI", apply_config),
"rollback.clapi": render_import(plan, plan.rollback_ops, "Suppression des objets"),
"rollback.sh": render_shell(plan, plan.rollback_ops, "Suppression des objets", apply_config),
}
def generate(raw_project: dict[str, Any]) -> dict[str, Any]:
"""Point d'entree unique utilise par l'API et la CLI."""
project = Project.from_dict(raw_project)
plan = build_plan(project)
return {
"summary": plan.summary,
"issues": plan.issues,
"has_errors": plan.has_errors,
"files": render_bundle(plan),
}
+346
View File
@@ -0,0 +1,346 @@
"""Modele de donnees d'un projet.
Un projet est ce que l'utilisateur construit dans l'editeur : des commandes de
controle, des modeles de service, des modeles d'hote, et facultativement des
hotes. Les objets se referencent par leur nom, comme dans Centreon.
Le projet est serialisable en JSON : c'est ce que le navigateur conserve, ce
qu'on exporte, ce qu'on partage et ce que la CI rejoue.
"""
from __future__ import annotations
from dataclasses import asdict, dataclass, field
from typing import Any
SCHEMA_VERSION = 1
# Versions de Centreon proposees dans l'editeur et ce qu'elles impliquent.
CENTREON_VERSIONS: dict[str, dict[str, Any]] = {
"24.x": {"label": "Centreon 24.x", "macro_fields": 5},
"23.x": {"label": "Centreon 23.x", "macro_fields": 5},
"22.x": {"label": "Centreon 22.x", "macro_fields": 5},
"21.x": {"label": "Centreon 21.x", "macro_fields": 5},
"20.04": {"label": "Centreon 20.04", "macro_fields": 5},
"legacy": {"label": "Centreon anterieur a 20.04", "macro_fields": 3},
}
COMMAND_TYPES = {"check": "Controle", "notif": "Notification", "misc": "Divers", "discovery": "Decouverte"}
FORBIDDEN = ";"
def _clean(value: Any) -> str:
return "" if value is None else str(value).strip()
@dataclass
class Macro:
name: str = ""
value: str = ""
description: str = ""
is_password: bool = False
@classmethod
def from_dict(cls, raw: dict[str, Any]) -> "Macro":
return cls(
name=_clean(raw.get("name")).upper(),
value="" if raw.get("value") is None else str(raw.get("value")),
description=_clean(raw.get("description")),
is_password=bool(raw.get("is_password")),
)
@dataclass
class Command:
name: str = ""
type: str = "check"
line: str = ""
comment: str = ""
@classmethod
def from_dict(cls, raw: dict[str, Any]) -> "Command":
return cls(
name=_clean(raw.get("name")),
type=_clean(raw.get("type")) or "check",
line=_clean(raw.get("line")),
comment=_clean(raw.get("comment")),
)
@dataclass
class ServiceTemplate:
name: str = ""
alias: str = ""
parent: str = "generic-active-service-custom"
command: str = ""
command_args: str = ""
check_interval: int = 5
retry_interval: int = 1
max_attempts: int = 3
active_checks: int = 1
passive_checks: int = 0
host_templates: list[str] = field(default_factory=list)
macros: list[Macro] = field(default_factory=list)
@classmethod
def from_dict(cls, raw: dict[str, Any]) -> "ServiceTemplate":
def as_int(key: str, default: int) -> int:
try:
return int(raw.get(key, default))
except (TypeError, ValueError):
return default
return cls(
name=_clean(raw.get("name")),
alias=_clean(raw.get("alias")),
parent=_clean(raw.get("parent")),
command=_clean(raw.get("command")),
command_args=_clean(raw.get("command_args")),
check_interval=as_int("check_interval", 5),
retry_interval=as_int("retry_interval", 1),
max_attempts=as_int("max_attempts", 3),
active_checks=as_int("active_checks", 1),
passive_checks=as_int("passive_checks", 0),
host_templates=[_clean(h) for h in raw.get("host_templates") or [] if _clean(h)],
macros=[Macro.from_dict(m) for m in raw.get("macros") or []],
)
@dataclass
class HostTemplate:
name: str = ""
alias: str = ""
parents: list[str] = field(default_factory=list)
check_command: str = ""
check_command_args: str = ""
snmp_version: str = ""
snmp_community: str = ""
timezone: str = ""
macros: list[Macro] = field(default_factory=list)
@classmethod
def from_dict(cls, raw: dict[str, Any]) -> "HostTemplate":
return cls(
name=_clean(raw.get("name")),
alias=_clean(raw.get("alias")),
parents=[_clean(p) for p in raw.get("parents") or [] if _clean(p)],
check_command=_clean(raw.get("check_command")),
check_command_args=_clean(raw.get("check_command_args")),
snmp_version=_clean(raw.get("snmp_version")),
snmp_community=_clean(raw.get("snmp_community")),
timezone=_clean(raw.get("timezone")),
macros=[Macro.from_dict(m) for m in raw.get("macros") or []],
)
@dataclass
class Host:
name: str = ""
alias: str = ""
address: str = ""
templates: list[str] = field(default_factory=list)
poller: str = "Central"
hostgroups: str = ""
macros: list[Macro] = field(default_factory=list)
@classmethod
def from_dict(cls, raw: dict[str, Any]) -> "Host":
return cls(
name=_clean(raw.get("name")),
alias=_clean(raw.get("alias")),
address=_clean(raw.get("address")),
templates=[_clean(t) for t in raw.get("templates") or [] if _clean(t)],
poller=_clean(raw.get("poller")) or "Central",
hostgroups=_clean(raw.get("hostgroups")),
macros=[Macro.from_dict(m) for m in raw.get("macros") or []],
)
@dataclass
class Project:
name: str = "Nouveau projet"
centreon_version: str = "24.x"
prefix: str = ""
plugins_dir: str = "/usr/lib/centreon/plugins"
poller: str = "Central"
apply_config: bool = True
commands: list[Command] = field(default_factory=list)
service_templates: list[ServiceTemplate] = field(default_factory=list)
host_templates: list[HostTemplate] = field(default_factory=list)
hosts: list[Host] = field(default_factory=list)
schema: int = SCHEMA_VERSION
@property
def macro_fields(self) -> int:
return CENTREON_VERSIONS.get(self.centreon_version, {}).get("macro_fields", 5)
@classmethod
def from_dict(cls, raw: dict[str, Any] | None) -> "Project":
raw = raw or {}
version = _clean(raw.get("centreon_version")) or "24.x"
if version not in CENTREON_VERSIONS:
version = "24.x"
return cls(
name=_clean(raw.get("name")) or "Nouveau projet",
centreon_version=version,
prefix=_clean(raw.get("prefix")),
plugins_dir=_clean(raw.get("plugins_dir")) or "/usr/lib/centreon/plugins",
poller=_clean(raw.get("poller")) or "Central",
apply_config=bool(raw.get("apply_config", True)),
commands=[Command.from_dict(c) for c in raw.get("commands") or []],
service_templates=[ServiceTemplate.from_dict(s) for s in raw.get("service_templates") or []],
host_templates=[HostTemplate.from_dict(h) for h in raw.get("host_templates") or []],
hosts=[Host.from_dict(h) for h in raw.get("hosts") or []],
)
def to_dict(self) -> dict[str, Any]:
return asdict(self)
def merge(self, other: "Project") -> list[str]:
"""Ajoute les objets d'un autre projet, en ignorant les noms deja pris."""
skipped: list[str] = []
def add(target: list[Any], items: list[Any], label: str) -> None:
existing = {obj.name for obj in target}
for item in items:
if item.name in existing:
skipped.append(f"{label} deja present, ignore : {item.name}")
continue
target.append(item)
existing.add(item.name)
add(self.commands, other.commands, "Commande")
add(self.service_templates, other.service_templates, "Modele de service")
add(self.host_templates, other.host_templates, "Modele d'hote")
add(self.hosts, other.hosts, "Hote")
return skipped
# --------------------------------------------------------------------------- #
# Validation
# --------------------------------------------------------------------------- #
def validate(project: Project) -> list[dict[str, str]]:
"""Retourne la liste des problemes, du bloquant au simple avertissement."""
issues: list[dict[str, str]] = []
def error(message: str) -> None:
issues.append({"level": "error", "message": message})
def warn(message: str) -> None:
issues.append({"level": "warning", "message": message})
def check_names(items: list[Any], label: str) -> None:
seen: set[str] = set()
for index, item in enumerate(items, start=1):
if not item.name:
error(f"{label} n{chr(176)}{index} : le nom est vide.")
continue
if item.name in seen:
error(f"{label} : nom en double, {item.name}")
seen.add(item.name)
if FORBIDDEN in item.name:
error(f"{label} {item.name} : le point-virgule est le separateur CLAPI.")
check_names(project.commands, "Commande")
check_names(project.service_templates, "Modele de service")
check_names(project.host_templates, "Modele d'hote")
check_names(project.hosts, "Hote")
command_names = {c.name for c in project.commands}
htpl_names = {h.name for h in project.host_templates}
for command in project.commands:
if not command.line:
error(f"Commande {command.name} : la ligne de commande est vide.")
elif FORBIDDEN in command.line:
error(
f"Commande {command.name} : la ligne contient un point-virgule, "
"ce qui casserait le fichier d'import."
)
for stpl in project.service_templates:
if not stpl.command:
warn(f"Modele de service {stpl.name} : aucune commande de controle associee.")
elif stpl.command not in command_names:
warn(
f"Modele de service {stpl.name} : la commande {stpl.command} n'est pas dans "
"le projet, elle devra exister deja sur le central."
)
if not stpl.host_templates:
warn(
f"Modele de service {stpl.name} : rattache a aucun modele d'hote, "
"il ne sera deploye sur aucun hote."
)
for htpl in stpl.host_templates:
if htpl not in htpl_names:
warn(f"Modele de service {stpl.name} : modele d'hote inconnu, {htpl}")
_check_macros(stpl.macros, f"Modele de service {stpl.name}", issues)
for htpl in project.host_templates:
_check_macros(htpl.macros, f"Modele d'hote {htpl.name}", issues)
for host in project.hosts:
if not host.address:
error(f"Hote {host.name} : l'adresse est vide.")
for template in host.templates:
if template not in htpl_names:
warn(f"Hote {host.name} : modele d'hote inconnu, {template}")
_check_macros(host.macros, f"Hote {host.name}", issues)
if not project.commands and not project.service_templates and not project.host_templates:
warn("Le projet est vide.")
return issues
def _check_macros(macros: list[Macro], label: str, issues: list[dict[str, str]]) -> None:
seen: set[str] = set()
for macro in macros:
if not macro.name:
issues.append({"level": "error", "message": f"{label} : une macro n'a pas de nom."})
continue
if macro.name in seen:
issues.append({"level": "error", "message": f"{label} : macro en double, {macro.name}"})
seen.add(macro.name)
if FORBIDDEN in macro.value or FORBIDDEN in macro.description:
issues.append(
{
"level": "error",
"message": f"{label}, macro {macro.name} : le point-virgule est interdit.",
}
)
if macro.is_password and macro.value:
issues.append(
{
"level": "warning",
"message": (
f"{label}, macro {macro.name} : un mot de passe est renseigne. "
"Il apparaitra en clair dans les fichiers generes."
),
}
)
def detect_macros(line: str) -> dict[str, list[str]]:
"""Repere les macros personnalisees utilisees dans une ligne de commande."""
import re
service = sorted(set(re.findall(r"\$_SERVICE([A-Z0-9_]+)\$", line)))
host = sorted(set(re.findall(r"\$_HOST([A-Z0-9_]+)\$", line)))
return {"service": service, "host": host}
def strip_secrets(project: Project) -> Project:
"""Vide toutes les macros marquees mot de passe.
Applique avant tout stockage cote serveur : partage, projet enregistre,
soumission de pack. Le site n'a aucune raison de detenir ces valeurs.
"""
for holder in (*project.service_templates, *project.host_templates, *project.hosts):
for macro in holder.macros:
if macro.is_password:
macro.value = ""
return project
+60
View File
@@ -0,0 +1,60 @@
"""Passerelle entre le catalogue de packs et le modele de projet.
Le catalogue reste un point de depart : importer un pack copie ses objets dans
le projet de l'utilisateur, qui les modifie ensuite librement.
"""
from __future__ import annotations
from .catalog import Pack
from .model import Command, HostTemplate, Macro, Project, ServiceTemplate
def _macro(macro) -> Macro:
return Macro(
name=macro.name,
value=macro.value,
description=macro.description,
is_password=macro.is_password,
)
def pack_to_project(pack: Pack, only_defaults: bool = True, service_parent: str = "generic-active-service-custom") -> Project:
"""Construit un projet ne contenant que les objets du pack."""
project = Project(name=pack.name)
project.commands = []
project.service_templates = []
htpl = HostTemplate(
name=pack.host_template.name,
alias=pack.host_template.alias,
parents=list(pack.host_template.parents),
check_command=pack.host_template.check_command,
check_command_args=pack.host_template.check_command_args,
macros=[_macro(m) for m in pack.host_template.macros],
)
if any("--snmp-" in svc.line for svc in pack.services):
htpl.snmp_version = "2c"
htpl.snmp_community = "public"
project.host_templates = [htpl]
for svc in pack.services:
if only_defaults and not svc.enabled_by_default:
continue
name = f"{pack.prefix}-{svc.name}"
project.commands.append(Command(name=name, type="check", line=svc.line, comment=svc.description))
project.service_templates.append(
ServiceTemplate(
name=name,
alias=svc.alias,
parent=service_parent,
command=name,
check_interval=svc.check_interval or 5,
retry_interval=svc.retry_interval or 1,
max_attempts=svc.max_attempts or 3,
host_templates=[htpl.name],
macros=[_macro(m) for m in svc.macros],
)
)
return project
+744
View File
@@ -0,0 +1,744 @@
/* Editeur de projet Enclume.
Aucune dependance : le projet vit dans le navigateur, le serveur ne fait que
valider et generer. */
'use strict';
var CLE = 'enclume.projet';
var TYPES = JSON.parse(document.getElementById('donnees-types').textContent);
var projet = charger();
var selection = { type: 'reglages', index: 0 };
var packsCache = null;
// ------------------------------------------------------------------ modele
function projetVide() {
return {
name: 'Nouveau projet',
centreon_version: '24.x',
prefix: '',
plugins_dir: '/usr/lib/centreon/plugins',
poller: 'Central',
apply_config: true,
commands: [],
service_templates: [],
host_templates: [],
hosts: []
};
}
function charger() {
try {
var brut = localStorage.getItem(CLE);
if (!brut) return projetVide();
var p = JSON.parse(brut);
var vide = projetVide();
Object.keys(vide).forEach(function (k) { if (p[k] === undefined) p[k] = vide[k]; });
return p;
} catch (e) {
return projetVide();
}
}
var minuteurSauvegarde = null;
function sauver() {
clearTimeout(minuteurSauvegarde);
minuteurSauvegarde = setTimeout(function () {
try {
localStorage.setItem(CLE, JSON.stringify(projet));
etat('Enregistre dans ce navigateur');
} catch (e) {
etat('Enregistrement impossible : espace du navigateur sature');
}
}, 250);
}
function etat(message) {
var cible = document.getElementById('etat-sauvegarde');
cible.textContent = message;
clearTimeout(etat.minuteur);
etat.minuteur = setTimeout(function () { cible.textContent = ''; }, 2500);
}
var LISTES = {
command: { cle: 'commands', titre: 'Commandes', singulier: 'commande' },
stpl: { cle: 'service_templates', titre: 'Modeles de service', singulier: 'modele de service' },
htpl: { cle: 'host_templates', titre: "Modeles d'hote", singulier: "modele d'hote" },
host: { cle: 'hosts', titre: 'Hotes', singulier: 'hote' }
};
function liste(type) { return projet[LISTES[type].cle]; }
function nouvelObjet(type) {
if (type === 'command') return { name: '', type: 'check', line: '', comment: '' };
if (type === 'stpl') return {
name: '', alias: '', parent: 'generic-active-service-custom', command: '', command_args: '',
check_interval: 5, retry_interval: 1, max_attempts: 3, active_checks: 1, passive_checks: 0,
host_templates: [], macros: []
};
if (type === 'htpl') return {
name: '', alias: '', parents: ['generic-active-host-custom'], check_command: '',
check_command_args: '', snmp_version: '', snmp_community: '', timezone: '', macros: []
};
return { name: '', alias: '', address: '', templates: [], poller: projet.poller, hostgroups: '', macros: [] };
}
// -------------------------------------------------------------------- rail
function rendreRail() {
var rail = document.getElementById('rail');
rail.innerHTML = '';
rail.appendChild(elt('button', {
className: 'rail-reglages' + (selection.type === 'reglages' ? ' actif' : ''),
type: 'button',
textContent: 'Reglages du projet',
onclick: function () { selection = { type: 'reglages', index: 0 }; rendre(); }
}));
Object.keys(LISTES).forEach(function (type) {
var section = elt('section', { className: 'rail-groupe' });
var head = elt('div', { className: 'rail-head' });
head.appendChild(elt('h2', { textContent: LISTES[type].titre }));
head.appendChild(elt('span', { className: 'count', textContent: liste(type).length }));
head.appendChild(elt('button', {
className: 'small', type: 'button', textContent: '+', title: 'Ajouter un ' + LISTES[type].singulier,
onclick: function () {
liste(type).push(nouvelObjet(type));
selection = { type: type, index: liste(type).length - 1 };
sauver(); rendre();
}
}));
section.appendChild(head);
liste(type).forEach(function (objet, index) {
var actif = selection.type === type && selection.index === index;
var item = elt('button', {
className: 'rail-item' + (actif ? ' actif' : ''),
type: 'button',
onclick: function () { selection = { type: type, index: index }; rendre(); }
});
item.appendChild(elt('span', {
className: 'nom', textContent: objet.name || '(sans nom)',
dataset: { railNom: type + ':' + index }
}));
if (type === 'stpl' && objet.host_templates.length === 0) {
item.appendChild(elt('span', { className: 'tag warn', textContent: 'non rattache' }));
}
section.appendChild(item);
});
rail.appendChild(section);
});
}
// -------------------------------------------------------------------- main
function rendreMain() {
var main = document.getElementById('main');
main.innerHTML = '';
if (selection.type === 'generation') return rendreGeneration(main);
if (selection.type === 'reglages') return rendreReglages(main);
var items = liste(selection.type);
if (!items.length) {
if (projetTotal() === 0) {
main.appendChild(document.getElementById('tpl-vide').content.cloneNode(true));
} else {
main.appendChild(elt('p', { className: 'lede', textContent: 'Aucun objet dans cette liste.' }));
}
return;
}
if (selection.index >= items.length) selection.index = items.length - 1;
var objet = items[selection.index];
var entete = elt('div', { className: 'objet-head' });
entete.appendChild(elt('h2', { textContent: LISTES[selection.type].titre }));
entete.appendChild(elt('span', { className: 'grow' }));
entete.appendChild(elt('button', {
className: 'small', type: 'button', textContent: 'Dupliquer',
onclick: function () {
var copie = JSON.parse(JSON.stringify(objet));
copie.name = copie.name + '-copie';
items.splice(selection.index + 1, 0, copie);
selection.index += 1; sauver(); rendre();
}
}));
entete.appendChild(elt('button', {
className: 'small danger', type: 'button', textContent: 'Supprimer',
onclick: function () {
if (!confirm('Supprimer ' + (objet.name || 'cet objet') + ' ?')) return;
items.splice(selection.index, 1);
selection.index = Math.max(0, selection.index - 1);
sauver(); rendre();
}
}));
main.appendChild(entete);
if (selection.type === 'command') rendreCommande(main, objet);
if (selection.type === 'stpl') rendreStpl(main, objet);
if (selection.type === 'htpl') rendreHtpl(main, objet);
if (selection.type === 'host') rendreHost(main, objet);
}
function projetTotal() {
return projet.commands.length + projet.service_templates.length +
projet.host_templates.length + projet.hosts.length;
}
function rendreReglages(main) {
main.appendChild(elt('h2', { textContent: 'Reglages du projet' }));
main.appendChild(elt('p', {
className: 'lede',
textContent: "Ces valeurs s'appliquent a l'ensemble du code genere. Le prefixe evite les collisions " +
'avec les packs officiels deja installes sur votre central.'
}));
var grille = elt('div', { className: 'grille' });
grille.appendChild(champ('Prefixe applique aux objets crees', projet, 'prefix', { placeholder: 'ex. ACME-' }));
grille.appendChild(champ('Repertoire des plugins', projet, 'plugins_dir'));
grille.appendChild(champ('Collecteur cible', projet, 'poller'));
main.appendChild(grille);
main.appendChild(caseACocher("Ajouter APPLYCFG a la fin des scripts", projet, 'apply_config'));
var info = elt('div', { className: 'notice' });
info.appendChild(elt('p', {
textContent: 'Le projet est conserve dans ce navigateur uniquement. Rien n\u2019est envoye au serveur ' +
'tant que vous ne demandez pas la generation, et aucune macro de type mot de passe n\u2019est stockee ' +
'ni partagee.'
}));
main.appendChild(info);
}
function rendreCommande(main, cmd) {
var grille = elt('div', { className: 'grille' });
grille.appendChild(champ('Nom de la commande', cmd, 'name', { rail: true, placeholder: 'OS-Linux-SNMP-Cpu' }));
grille.appendChild(champSelect('Type', cmd, 'type', TYPES));
main.appendChild(grille);
main.appendChild(champ('Commentaire', cmd, 'comment'));
main.appendChild(champZone('Ligne de commande', cmd, 'line', {
aide: '$CENTREONPLUGINS$ sera remplace par le repertoire des plugins. ' +
'Utilisez $HOSTADDRESS$, $_HOSTMACRO$ et $_SERVICEMACRO$ comme dans Centreon.'
}));
var barre = elt('div', { className: 'barre' });
barre.appendChild(elt('button', {
type: 'button', className: 'small', textContent: 'Creer un modele de service depuis cette commande',
onclick: function () {
var stpl = nouvelObjet('stpl');
stpl.name = cmd.name;
stpl.alias = cmd.name.split('-').pop();
stpl.command = cmd.name;
stpl.macros = macrosDepuisLigne(cmd.line).service;
projet.service_templates.push(stpl);
selection = { type: 'stpl', index: projet.service_templates.length - 1 };
sauver(); rendre();
}
}));
main.appendChild(barre);
}
function macrosDepuisLigne(ligne) {
var service = (ligne.match(/\$_SERVICE([A-Z0-9_]+)\$/g) || []).map(function (m) {
return m.replace('$_SERVICE', '').replace('$', '');
});
var host = (ligne.match(/\$_HOST([A-Z0-9_]+)\$/g) || []).map(function (m) {
return m.replace('$_HOST', '').replace('$', '');
});
var uniq = function (noms) {
var vus = {}, sortie = [];
noms.forEach(function (n) {
if (vus[n]) return;
vus[n] = 1;
sortie.push({ name: n, value: '', description: '', is_password: /(PASSWORD|SECRET|TOKEN|APIKEY)/.test(n) });
});
return sortie;
};
return { service: uniq(service), host: uniq(host) };
}
function rendreStpl(main, stpl) {
var grille = elt('div', { className: 'grille' });
grille.appendChild(champ('Nom du modele', stpl, 'name', { rail: true }));
grille.appendChild(champ('Alias', stpl, 'alias'));
grille.appendChild(champ('Modele parent', stpl, 'parent'));
grille.appendChild(champSelectListe('Commande de controle', stpl, 'command',
projet.commands.map(function (c) { return c.name; })));
main.appendChild(grille);
var grille2 = elt('div', { className: 'grille quatre' });
grille2.appendChild(champ('Intervalle', stpl, 'check_interval', { type: 'number' }));
grille2.appendChild(champ('Intervalle de reprise', stpl, 'retry_interval', { type: 'number' }));
grille2.appendChild(champ('Tentatives', stpl, 'max_attempts', { type: 'number' }));
grille2.appendChild(champ('Arguments', stpl, 'command_args', { placeholder: '!80!90' }));
main.appendChild(grille2);
main.appendChild(sousTitre("Rattachement aux modeles d'hote",
"Un hote heritant d'un de ces modeles recevra ce service."));
if (!projet.host_templates.length) {
main.appendChild(elt('p', { className: 'lede', textContent: "Aucun modele d'hote dans le projet." }));
} else {
var cases = elt('div', { className: 'cases' });
projet.host_templates.forEach(function (htpl) {
cases.appendChild(caseListe(htpl.name || '(sans nom)', stpl.host_templates, htpl.name));
});
main.appendChild(cases);
}
main.appendChild(sousTitre('Macros du service', 'Seuils et parametres propres a cette sonde.'));
main.appendChild(editeurMacros(stpl, function () {
var cmd = projet.commands.filter(function (c) { return c.name === stpl.command; })[0];
return cmd ? macrosDepuisLigne(cmd.line).service : [];
}));
}
function rendreHtpl(main, htpl) {
var grille = elt('div', { className: 'grille' });
grille.appendChild(champ('Nom du modele', htpl, 'name', { rail: true, placeholder: 'OS-Linux-SNMP-custom' }));
grille.appendChild(champ('Alias', htpl, 'alias'));
grille.appendChild(champListe('Modeles parents', htpl, 'parents'));
grille.appendChild(champ('Fuseau horaire', htpl, 'timezone', { placeholder: ':Europe/Paris' }));
main.appendChild(grille);
var grille2 = elt('div', { className: 'grille' });
grille2.appendChild(champ('Commande de verification', htpl, 'check_command', { placeholder: 'check_centreon_ping' }));
grille2.appendChild(champ('Arguments', htpl, 'check_command_args', { placeholder: '!3!200,20%!400,50%' }));
grille2.appendChild(champ('Version SNMP', htpl, 'snmp_version', { placeholder: '2c' }));
grille2.appendChild(champ('Communaute SNMP', htpl, 'snmp_community'));
main.appendChild(grille2);
main.appendChild(sousTitre("Macros du modele d'hote",
'Heritees par tous les hotes rattaches : identifiants, ports, options du plugin.'));
main.appendChild(editeurMacros(htpl, function () { return []; }));
var services = projet.service_templates.filter(function (s) {
return s.host_templates.indexOf(htpl.name) !== -1;
});
main.appendChild(sousTitre('Services deployes par ce modele', services.length + ' modele(s) de service'));
var liste_ = elt('div', { className: 'puces' });
services.forEach(function (s) { liste_.appendChild(elt('span', { className: 'puce', textContent: s.name })); });
main.appendChild(liste_);
}
function rendreHost(main, host) {
var grille = elt('div', { className: 'grille' });
grille.appendChild(champ("Nom de l'hote", host, 'name', { rail: true }));
grille.appendChild(champ('Alias', host, 'alias'));
grille.appendChild(champ('Adresse IP ou DNS', host, 'address'));
grille.appendChild(champ('Collecteur', host, 'poller'));
grille.appendChild(champ("Groupes d'hotes", host, 'hostgroups'));
main.appendChild(grille);
main.appendChild(sousTitre("Modeles d'hote appliques", ''));
var cases = elt('div', { className: 'cases' });
projet.host_templates.forEach(function (htpl) {
cases.appendChild(caseListe(htpl.name || '(sans nom)', host.templates, htpl.name));
});
main.appendChild(cases);
main.appendChild(sousTitre("Macros propres a l'hote", 'Elles surchargent celles du modele.'));
main.appendChild(editeurMacros(host, function () { return []; }));
}
// --------------------------------------------------------------- generation
function rendreGeneration(main) {
main.appendChild(elt('h2', { textContent: 'Code CLAPI' }));
var zone = elt('div', { id: 'zone-generation' });
zone.appendChild(elt('p', { className: 'lede', textContent: 'Generation en cours...' }));
main.appendChild(zone);
fetch('/api/generer', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ project: projet })
}).then(function (r) { return r.json(); }).then(function (data) {
zone.innerHTML = '';
var resume = elt('div', { className: 'resume' });
Object.keys(data.summary).forEach(function (cle) {
var bloc = elt('div');
bloc.appendChild(elt('b', { textContent: data.summary[cle] }));
bloc.appendChild(elt('span', { textContent: cle }));
resume.appendChild(bloc);
});
zone.appendChild(resume);
if (data.issues.length) {
var erreurs = data.issues.filter(function (i) { return i.level === 'error'; });
var note = elt('div', { className: 'notice' + (erreurs.length ? ' bad' : '') });
note.appendChild(elt('b', {
textContent: erreurs.length
? erreurs.length + ' erreur(s) a corriger avant de deployer'
: data.issues.length + ' point(s) de vigilance'
}));
var ul = elt('ul');
data.issues.forEach(function (i) {
ul.appendChild(elt('li', { textContent: i.message, className: i.level }));
});
note.appendChild(ul);
zone.appendChild(note);
}
var noms = Object.keys(data.files);
var onglets = elt('div', { className: 'tabs' });
var panneaux = {};
noms.forEach(function (nom, i) {
var bouton = elt('button', {
type: 'button', textContent: nom,
onclick: function () {
noms.forEach(function (n) {
panneaux[n].hidden = n !== nom;
onglets.querySelectorAll('button').forEach(function (b) {
b.setAttribute('aria-selected', String(b.textContent === nom));
});
});
}
});
bouton.setAttribute('aria-selected', String(i === 0));
onglets.appendChild(bouton);
});
zone.appendChild(onglets);
noms.forEach(function (nom, i) {
var pre = elt('pre', { className: 'code', textContent: data.files[nom] });
pre.hidden = i !== 0;
panneaux[nom] = pre;
zone.appendChild(pre);
});
var barre = elt('div', { className: 'barre' });
noms.forEach(function (nom) {
barre.appendChild(elt('button', {
type: 'button', textContent: nom,
onclick: function () { telecharger(nom, data.files[nom]); }
}));
});
barre.appendChild(elt('button', {
type: 'button', className: 'primary auto', textContent: 'Archive complete (.zip)',
onclick: function () {
document.getElementById('form-zip-project').value = JSON.stringify(projet);
document.getElementById('form-zip').submit();
}
}));
zone.appendChild(barre);
}).catch(function () {
zone.innerHTML = '';
zone.appendChild(elt('div', { className: 'notice bad', textContent: 'La generation a echoue. Reessayez.' }));
});
}
function telecharger(nom, contenu) {
var blob = new Blob([contenu], { type: 'text/plain;charset=utf-8' });
var url = URL.createObjectURL(blob);
var a = elt('a', { href: url, download: nom });
document.body.appendChild(a); a.click(); a.remove();
setTimeout(function () { URL.revokeObjectURL(url); }, 1000);
}
// ------------------------------------------------------------- composants
function elt(tag, props) {
var noeud = document.createElement(tag);
Object.keys(props || {}).forEach(function (cle) {
if (cle === 'dataset') Object.assign(noeud.dataset, props[cle]);
else if (cle === 'onclick') noeud.addEventListener('click', props[cle]);
else noeud[cle] = props[cle];
});
return noeud;
}
function champ(libelle, objet, cle, options) {
options = options || {};
var label = elt('label', { className: 'field' });
label.appendChild(elt('span', { textContent: libelle }));
var input = elt('input', { type: options.type || 'text', value: objet[cle] });
if (options.placeholder) input.placeholder = options.placeholder;
input.addEventListener('input', function () {
objet[cle] = options.type === 'number' ? parseInt(input.value || '0', 10) : input.value;
if (options.rail) majNomRail();
sauver();
});
label.appendChild(input);
return label;
}
function champZone(libelle, objet, cle, options) {
options = options || {};
var label = elt('label', { className: 'field' });
label.appendChild(elt('span', { textContent: libelle }));
var zone = elt('textarea', { rows: 5, value: objet[cle] });
zone.addEventListener('input', function () { objet[cle] = zone.value; sauver(); });
label.appendChild(zone);
if (options.aide) label.appendChild(elt('small', { textContent: options.aide }));
return label;
}
function champSelect(libelle, objet, cle, choix) {
var label = elt('label', { className: 'field' });
label.appendChild(elt('span', { textContent: libelle }));
var select = elt('select');
Object.keys(choix).forEach(function (valeur) {
var option = elt('option', { value: valeur, textContent: choix[valeur] });
if (objet[cle] === valeur) option.selected = true;
select.appendChild(option);
});
select.addEventListener('change', function () { objet[cle] = select.value; sauver(); });
label.appendChild(select);
return label;
}
function champSelectListe(libelle, objet, cle, valeurs) {
var label = elt('label', { className: 'field' });
label.appendChild(elt('span', { textContent: libelle }));
var input = elt('input', { type: 'text', value: objet[cle], list: 'liste-' + cle });
var datalist = elt('datalist', { id: 'liste-' + cle });
valeurs.forEach(function (v) { datalist.appendChild(elt('option', { value: v })); });
input.addEventListener('input', function () { objet[cle] = input.value; sauver(); });
label.appendChild(input);
label.appendChild(datalist);
return label;
}
function champListe(libelle, objet, cle) {
var label = elt('label', { className: 'field' });
label.appendChild(elt('span', { textContent: libelle + ' (separes par une virgule)' }));
var input = elt('input', { type: 'text', value: (objet[cle] || []).join(', ') });
input.addEventListener('input', function () {
objet[cle] = input.value.split(',').map(function (v) { return v.trim(); }).filter(Boolean);
sauver();
});
label.appendChild(input);
return label;
}
function caseACocher(libelle, objet, cle) {
var label = elt('label', { className: 'check' });
var input = elt('input', { type: 'checkbox', checked: !!objet[cle] });
input.addEventListener('change', function () { objet[cle] = input.checked; sauver(); });
label.appendChild(input);
label.appendChild(elt('span', { textContent: libelle }));
return label;
}
function caseListe(libelle, tableau, valeur) {
var label = elt('label', { className: 'check' });
var input = elt('input', { type: 'checkbox', checked: tableau.indexOf(valeur) !== -1 });
input.addEventListener('change', function () {
var i = tableau.indexOf(valeur);
if (input.checked && i === -1) tableau.push(valeur);
if (!input.checked && i !== -1) tableau.splice(i, 1);
sauver(); rendreRail();
});
label.appendChild(input);
label.appendChild(elt('span', { textContent: libelle }));
return label;
}
function sousTitre(titre, aide) {
var bloc = elt('div', { className: 'sous-titre' });
bloc.appendChild(elt('h3', { textContent: titre }));
if (aide) bloc.appendChild(elt('span', { textContent: aide }));
return bloc;
}
function editeurMacros(objet, suggestions) {
var bloc = elt('div');
var table = elt('div', { className: 'macros-table' });
objet.macros.forEach(function (macro, index) {
var ligne = elt('div', { className: 'macro-ligne' });
var nom = elt('input', { type: 'text', value: macro.name, placeholder: 'NOM' });
nom.addEventListener('input', function () { macro.name = nom.value.toUpperCase(); nom.value = macro.name; sauver(); });
var valeur = elt('input', { type: 'text', value: macro.value, placeholder: 'valeur' });
valeur.addEventListener('input', function () { macro.value = valeur.value; sauver(); });
var desc = elt('input', { type: 'text', value: macro.description, placeholder: 'description' });
desc.addEventListener('input', function () { macro.description = desc.value; sauver(); });
var mdp = elt('label', { className: 'check', title: 'Macro de type mot de passe' });
var coche = elt('input', { type: 'checkbox', checked: !!macro.is_password });
coche.addEventListener('change', function () { macro.is_password = coche.checked; sauver(); rendreMain(); });
mdp.appendChild(coche);
mdp.appendChild(elt('span', { textContent: 'mdp' }));
var sup = elt('button', {
type: 'button', className: 'small danger', textContent: 'x',
onclick: function () { objet.macros.splice(index, 1); sauver(); rendreMain(); }
});
ligne.appendChild(nom); ligne.appendChild(valeur); ligne.appendChild(desc);
ligne.appendChild(mdp); ligne.appendChild(sup);
if (macro.is_password && macro.value) {
ligne.appendChild(elt('small', { className: 'avert', textContent: 'sortira en clair dans les fichiers' }));
}
table.appendChild(ligne);
});
bloc.appendChild(table);
var barre = elt('div', { className: 'barre' });
barre.appendChild(elt('button', {
type: 'button', className: 'small', textContent: 'Ajouter une macro',
onclick: function () {
objet.macros.push({ name: '', value: '', description: '', is_password: false });
sauver(); rendreMain();
}
}));
var manquantes = suggestions().filter(function (m) {
return !objet.macros.some(function (existante) { return existante.name === m.name; });
});
if (manquantes.length) {
barre.appendChild(elt('button', {
type: 'button', className: 'small', textContent: 'Creer les macros de la commande (' + manquantes.length + ')',
onclick: function () { manquantes.forEach(function (m) { objet.macros.push(m); }); sauver(); rendreMain(); }
}));
}
bloc.appendChild(barre);
return bloc;
}
function majNomRail() {
var cible = document.querySelector('[data-rail-nom="' + selection.type + ':' + selection.index + '"]');
if (cible) cible.textContent = liste(selection.type)[selection.index].name || '(sans nom)';
}
// ------------------------------------------------------------------ modale
function ouvrirModale(titre, construire) {
document.getElementById('modal-titre').textContent = titre;
var corps = document.getElementById('modal-corps');
corps.innerHTML = '';
construire(corps);
document.getElementById('modal').hidden = false;
}
function fermerModale() { document.getElementById('modal').hidden = true; }
function ouvrirCataloguePacks() {
ouvrirModale('Importer un pack', function (corps) {
var recherche = elt('input', { type: 'text', placeholder: 'Filtrer par nom, technologie, plugin...' });
corps.appendChild(recherche);
var resultats = elt('div', { className: 'packs' });
corps.appendChild(resultats);
function afficher(packs) {
resultats.innerHTML = '';
packs.forEach(function (pack) {
var ligne = elt('div', { className: 'pack-ligne' });
var texte = elt('div');
texte.appendChild(elt('b', { textContent: pack.name }));
if (pack.status !== 'verified') {
texte.appendChild(elt('span', { className: 'tag warn', textContent: 'a valider' }));
}
texte.appendChild(elt('div', { className: 'desc', textContent: pack.description }));
texte.appendChild(elt('div', { className: 'plugin', textContent: pack.plugin + ' | ' + pack.services.length + ' sondes' }));
ligne.appendChild(texte);
var actions = elt('div', { className: 'barre' });
actions.appendChild(elt('button', {
type: 'button', className: 'small', textContent: 'Sondes par defaut',
onclick: function () { importerPack(pack.id, false); }
}));
actions.appendChild(elt('button', {
type: 'button', className: 'small', textContent: 'Toutes les sondes',
onclick: function () { importerPack(pack.id, true); }
}));
ligne.appendChild(actions);
resultats.appendChild(ligne);
});
if (!packs.length) resultats.appendChild(elt('p', { className: 'lede', textContent: 'Aucun pack ne correspond.' }));
}
function filtrer() {
var q = recherche.value.toLowerCase();
afficher(packsCache.filter(function (p) {
return (p.name + ' ' + p.description + ' ' + p.plugin + ' ' + p.category_label).toLowerCase().indexOf(q) !== -1;
}));
}
recherche.addEventListener('input', filtrer);
if (packsCache) { afficher(packsCache); }
else {
resultats.appendChild(elt('p', { className: 'lede', textContent: 'Chargement du catalogue...' }));
fetch('/api/packs').then(function (r) { return r.json(); }).then(function (packs) {
packsCache = packs; afficher(packs);
});
}
});
}
function importerPack(id, tout) {
fetch('/api/packs/' + id + '/projet' + (tout ? '?tout=1' : ''))
.then(function (r) { return r.json(); })
.then(function (fragment) {
var ignores = [];
['commands', 'service_templates', 'host_templates'].forEach(function (cle) {
var existants = {};
projet[cle].forEach(function (o) { existants[o.name] = true; });
fragment[cle].forEach(function (o) {
if (existants[o.name]) { ignores.push(o.name); return; }
projet[cle].push(o);
});
});
fermerModale();
selection = { type: 'htpl', index: Math.max(0, projet.host_templates.length - 1) };
sauver(); rendre();
etat(ignores.length ? ignores.length + ' objet(s) deja present(s), ignore(s)' : 'Pack importe');
});
}
// ------------------------------------------------------------------ actions
function rendre() {
rendreRail();
rendreMain();
document.getElementById('projet-nom').value = projet.name;
document.getElementById('projet-version').value = projet.centreon_version;
}
document.getElementById('projet-nom').addEventListener('input', function (e) {
projet.name = e.target.value; sauver();
});
document.getElementById('projet-version').addEventListener('change', function (e) {
projet.centreon_version = e.target.value; sauver();
});
document.getElementById('btn-pack').addEventListener('click', ouvrirCataloguePacks);
document.getElementById('btn-generer').addEventListener('click', function () {
selection = { type: 'generation', index: 0 }; rendre();
});
document.getElementById('btn-export').addEventListener('click', function () {
telecharger((projet.name || 'projet').replace(/[^\w-]+/g, '-') + '.json', JSON.stringify(projet, null, 2));
});
document.getElementById('btn-import').addEventListener('click', function () {
var input = elt('input', { type: 'file', accept: '.json,application/json' });
input.addEventListener('change', function () {
var fichier = input.files[0];
if (!fichier) return;
var lecteur = new FileReader();
lecteur.onload = function () {
try {
var charge = JSON.parse(lecteur.result);
var vide = projetVide();
Object.keys(vide).forEach(function (k) { if (charge[k] === undefined) charge[k] = vide[k]; });
projet = charge;
selection = { type: 'reglages', index: 0 };
sauver(); rendre();
etat('Projet ouvert');
} catch (e) {
alert("Ce fichier n'est pas un projet valide.");
}
};
lecteur.readAsText(fichier);
});
input.click();
});
document.getElementById('btn-vider').addEventListener('click', function () {
if (!confirm('Vider le projet en cours ? Cette action est definitive.')) return;
projet = projetVide();
selection = { type: 'reglages', index: 0 };
sauver(); rendre();
});
document.querySelectorAll('[data-fermer-modal]').forEach(function (b) {
b.addEventListener('click', fermerModale);
});
document.getElementById('modal').addEventListener('click', function (e) {
if (e.target.id === 'modal') fermerModale();
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') fermerModale();
});
rendre();
+366
View File
@@ -0,0 +1,366 @@
:root {
--bg: #0f151c;
--panel: #161f29;
--panel-2: #1b2532;
--line: #253243;
--ink: #dde5ee;
--ink-dim: #8ca0b6;
--accent: #e7a93f;
--ok: #46b98a;
--alert: #e0575b;
--radius: 3px;
--mono: "JetBrains Mono", "SFMono-Regular", Menlo, Consolas, monospace;
--sans: "Inter", "Segoe UI", Roboto, system-ui, sans-serif;
--h-top: 52px;
--h-tool: 52px;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: var(--sans);
font-size: 14px;
line-height: 1.5;
}
h1, h2, h3 { margin: 0; font-weight: 600; }
h1 { font-size: 16px; }
h2 { font-size: 14px; }
h3 { font-size: 13px; }
small { color: var(--ink-dim); font-size: 12px; display: block; margin-top: 4px; }
/* --------------------------------------------------------------- bandeaux */
.topbar {
display: flex;
align-items: baseline;
gap: 14px;
height: var(--h-top);
padding: 0 20px;
border-bottom: 1px solid var(--line);
background: var(--panel);
}
.topbar .meta { color: var(--ink-dim); font-size: 12.5px; }
.toolbar {
display: flex;
align-items: center;
gap: 8px;
height: var(--h-tool);
padding: 0 20px;
border-bottom: 1px solid var(--line);
background: var(--panel-2);
position: sticky;
top: 0;
z-index: 4;
}
.toolbar .titre {
width: 260px;
font-family: var(--sans);
font-size: 14px;
font-weight: 600;
}
.toolbar .sep { width: 1px; height: 22px; background: var(--line); margin: 0 4px; }
.toolbar .grow { flex: 1; text-align: right; color: var(--ok); font-size: 12.5px; }
/* ---------------------------------------------------------------- atelier */
.atelier {
display: grid;
grid-template-columns: 280px minmax(0, 1fr);
min-height: calc(100vh - var(--h-top) - var(--h-tool));
align-items: start;
}
#rail {
border-right: 1px solid var(--line);
background: var(--panel);
padding: 10px 0 30px;
position: sticky;
top: var(--h-tool);
max-height: calc(100vh - var(--h-tool));
overflow-y: auto;
}
.rail-reglages {
display: block;
width: calc(100% - 20px);
margin: 0 10px 14px;
text-align: left;
}
.rail-reglages.actif { border-color: var(--accent); color: var(--accent); }
.rail-groupe { margin-bottom: 14px; }
.rail-head {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 10px 4px 14px;
border-bottom: 1px solid var(--line);
}
.rail-head h2 { font-size: 12.5px; color: var(--ink-dim); font-weight: 600; }
.rail-head .count { color: var(--ink-dim); font-size: 12px; margin-left: auto; }
.rail-item {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
text-align: left;
background: none;
border: none;
border-left: 2px solid transparent;
border-radius: 0;
padding: 6px 10px 6px 12px;
color: var(--ink);
font-family: var(--mono);
font-size: 12px;
cursor: pointer;
}
.rail-item:hover { background: var(--panel-2); }
.rail-item.actif { background: var(--panel-2); border-left-color: var(--accent); }
.rail-item .nom { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ------------------------------------------------------------------- main */
main {
padding: 22px 26px 60px;
max-width: 1000px;
}
p.lede { color: var(--ink-dim); margin: 0 0 18px; max-width: 70ch; }
.objet-head {
display: flex;
align-items: center;
gap: 8px;
padding-bottom: 10px;
border-bottom: 1px solid var(--line);
margin-bottom: 18px;
}
.objet-head .grow { flex: 1; }
.sous-titre {
display: flex;
align-items: baseline;
gap: 10px;
margin: 22px 0 10px;
padding-bottom: 6px;
border-bottom: 1px solid var(--line);
}
.sous-titre span { color: var(--ink-dim); font-size: 12.5px; }
.grille { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 16px; }
.grille.quatre { grid-template-columns: repeat(4, minmax(0, 1fr)); }
/* ------------------------------------------------------------ formulaires */
label.field { display: block; margin-bottom: 12px; }
label.field > span { display: block; font-size: 12px; color: var(--ink-dim); margin-bottom: 3px; }
input[type="text"], input[type="number"], select, textarea {
width: 100%;
background: var(--bg);
border: 1px solid var(--line);
border-radius: var(--radius);
color: var(--ink);
font-family: var(--mono);
font-size: 12.5px;
padding: 6px 8px;
}
textarea { line-height: 1.55; resize: vertical; }
input:focus, select:focus, textarea:focus, button:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
input[type="checkbox"] { accent-color: var(--accent); width: 15px; height: 15px; }
.check { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; margin: 0 16px 8px 0; }
.check span { color: var(--ink); }
.cases { display: flex; flex-wrap: wrap; gap: 0 4px; }
button {
font-family: var(--sans);
font-size: 13px;
border-radius: var(--radius);
border: 1px solid var(--line);
background: var(--panel);
color: var(--ink);
padding: 6px 11px;
cursor: pointer;
}
button:hover { border-color: var(--accent); }
button.primary { background: var(--accent); border-color: var(--accent); color: #17202b; font-weight: 600; }
button.small { padding: 3px 8px; font-size: 12px; }
button.danger:hover { border-color: var(--alert); color: var(--alert); }
button.auto { width: auto; }
.barre { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
/* ----------------------------------------------------------------- macros */
.macros-table { display: flex; flex-direction: column; gap: 6px; }
.macro-ligne {
display: grid;
grid-template-columns: 180px minmax(0, 1fr) minmax(0, 1fr) 74px 30px;
gap: 8px;
align-items: center;
}
.macro-ligne .check { margin: 0; font-size: 12px; }
.macro-ligne .avert { grid-column: 1 / -1; color: var(--accent); margin: 0 0 4px; }
.puces { display: flex; flex-wrap: wrap; gap: 6px; }
.puce {
font-family: var(--mono);
font-size: 11.5px;
color: var(--ink-dim);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 2px 7px;
}
.tag {
font-size: 11px;
padding: 1px 6px;
border-radius: var(--radius);
border: 1px solid var(--line);
color: var(--ink-dim);
}
.tag.warn { color: var(--accent); border-color: rgba(231, 169, 63, 0.4); }
/* ------------------------------------------------------------- generation */
.resume { display: flex; gap: 24px; margin: 14px 0 18px; }
.resume div { display: flex; flex-direction: column; }
.resume b { font-family: var(--mono); font-size: 20px; font-weight: 500; }
.resume span { color: var(--ink-dim); font-size: 12px; }
.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--line); }
.tabs button {
border-bottom: none;
border-radius: var(--radius) var(--radius) 0 0;
background: var(--panel);
color: var(--ink-dim);
font-family: var(--mono);
font-size: 12.5px;
}
.tabs button[aria-selected="true"] { background: var(--panel-2); color: var(--ink); }
pre.code {
background: var(--panel-2);
border: 1px solid var(--line);
border-top: none;
margin: 0;
padding: 14px;
font-family: var(--mono);
font-size: 12px;
line-height: 1.55;
max-height: 58vh;
overflow: auto;
white-space: pre;
}
.notice {
border-left: 2px solid var(--accent);
background: var(--panel);
padding: 10px 14px;
margin: 14px 0;
font-size: 13px;
}
.notice.bad { border-left-color: var(--alert); }
.notice p { margin: 0; color: var(--ink-dim); }
.notice ul { margin: 8px 0 0; padding-left: 18px; }
.notice li { color: var(--ink-dim); margin-bottom: 3px; }
.notice li.error { color: var(--alert); }
.vide { padding: 40px 0; max-width: 60ch; }
.vide h2 { margin-bottom: 8px; }
.vide p { color: var(--ink-dim); }
/* ----------------------------------------------------------------- modale */
.modal {
position: fixed;
inset: 0;
background: rgba(8, 12, 17, 0.72);
display: flex;
align-items: flex-start;
justify-content: center;
padding: 60px 20px;
z-index: 20;
}
.modal[hidden] { display: none; }
.modal-box {
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--radius);
width: min(760px, 100%);
max-height: 78vh;
display: flex;
flex-direction: column;
}
.modal-box > header {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--line);
}
.modal-box > header h2 { flex: 1; }
#modal-corps { padding: 14px 16px; overflow-y: auto; }
#modal-corps > input { margin-bottom: 12px; }
.packs { display: flex; flex-direction: column; }
.pack-ligne {
display: flex;
align-items: center;
gap: 14px;
padding: 10px 0;
border-bottom: 1px solid var(--line);
}
.pack-ligne > div:first-child { flex: 1; min-width: 0; }
.pack-ligne b { margin-right: 8px; }
.pack-ligne .desc { color: var(--ink-dim); font-size: 12.5px; }
.pack-ligne .plugin { font-family: var(--mono); font-size: 11.5px; color: var(--ink-dim); margin-top: 2px; }
.pack-ligne .barre { margin: 0; flex-wrap: nowrap; }
/* -------------------------------------------------------------- adaptatif */
@media (max-width: 900px) {
.atelier { grid-template-columns: 1fr; }
#rail { position: static; max-height: none; border-right: none; border-bottom: 1px solid var(--line); }
.grille, .grille.quatre { grid-template-columns: 1fr; }
.macro-ligne { grid-template-columns: 1fr 1fr; }
.toolbar { flex-wrap: wrap; height: auto; padding: 10px 16px; }
.toolbar .titre { width: 100%; }
main { padding: 18px 16px 50px; }
}
@media (prefers-reduced-motion: reduce) {
* { transition: none !important; animation: none !important; }
}
/* -------------------------------------------------------------------- pied */
.topbar .version {
margin-left: auto;
font-family: var(--mono);
font-size: 11.5px;
color: var(--ink-dim);
}
.pied {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 14px;
padding: 14px 20px;
border-top: 1px solid var(--line);
background: var(--panel);
color: var(--ink-dim);
font-size: 12px;
}
.pied .grow { flex: 1; }
.pied a { color: var(--ink-dim); }
.pied a:hover { color: var(--accent); }
+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img" aria-label="Enclume">
<rect width="32" height="32" rx="4" fill="#161f29"/>
<path d="M7 12h11c0 3.2-1.6 4.6-3.4 5.2H21l4-3.4V12h2v4.3l-4.6 3.9h-3.1l1.6 3.3H10.5l1.6-3.3H10c-2.3 0-4-1.9-4-4.2V12z"
fill="#e7a93f"/>
<rect x="9" y="8" width="7" height="2.4" rx="1" fill="#8ca0b6"/>
</svg>

After

Width:  |  Height:  |  Size: 369 B

+29
View File
@@ -0,0 +1,29 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Enclume — Générateur de configuration CLAPI pour Centreon{% endblock %}</title>
<meta name="description" content="Créez vos commandes de contrôle, modèles de service et modèles d'hôte Centreon, et repartez avec le code CLAPI prêt à jouer sur votre central. Gratuit, sans inscription.">
<link rel="canonical" href="{{ request.url_root }}">
<meta property="og:title" content="Enclume — Générateur de configuration CLAPI pour Centreon">
<meta property="og:description" content="Créez vos commandes, modèles de service et modèles d'hôte, repartez avec le code CLAPI.">
<meta property="og:type" content="website">
<link rel="icon" href="{{ url_for('static', filename='enclume.svg') }}" type="image/svg+xml">
<link rel="stylesheet" href="{{ url_for('static', filename='enclume.css') }}">
</head>
<body>
<header class="topbar">
<h1>Enclume</h1>
<span class="meta">Générateur de configuration CLAPI pour Centreon</span>
<span class="version">{{ version }}</span>
</header>
{% block body %}{% endblock %}
<footer class="pied">
<span>Projet indépendant, sans lien avec Centreon SAS. Centreon est une marque déposée de son propriétaire.</span>
<span class="grow"></span>
<a href="https://gitea.tips-of-mine.com/hubert/enclume">Code source</a>
<a href="/sante">État du service</a>
</footer>
</body>
</html>
+46
View File
@@ -0,0 +1,46 @@
{% extends "base.html" %}
{% block body %}
<div class="toolbar">
<input type="text" id="projet-nom" class="titre" value="Nouveau projet" aria-label="Nom du projet">
<select id="projet-version" aria-label="Version de Centreon ciblee">
{% for key, meta in versions.items() %}<option value="{{ key }}">{{ meta.label }}</option>{% endfor %}
</select>
<span class="sep"></span>
<button type="button" id="btn-pack">Importer un pack</button>
<button type="button" id="btn-import">Ouvrir un projet</button>
<button type="button" id="btn-export">Enregistrer le projet</button>
<button type="button" id="btn-vider" class="danger">Vider</button>
<span class="grow" id="etat-sauvegarde"></span>
<button type="button" id="btn-generer" class="primary">Generer le CLAPI</button>
</div>
<div class="atelier">
<nav id="rail" aria-label="Objets du projet"></nav>
<main id="main"></main>
</div>
<div class="modal" id="modal" hidden>
<div class="modal-box">
<header>
<h2 id="modal-titre">Importer un pack</h2>
<button type="button" class="small" data-fermer-modal>Fermer</button>
</header>
<div id="modal-corps"></div>
</div>
</div>
<form id="form-zip" method="post" action="/telecharger" hidden>
<input type="hidden" name="project" id="form-zip-project">
</form>
<template id="tpl-vide">
<div class="vide">
<h2>Un projet vide, trois facons de le remplir</h2>
<p>Partez d'un pack du catalogue ({{ pack_count }} packs, {{ service_count }} sondes) et modifiez-le,
rouvrez un projet enregistre, ou creez vos objets un par un dans la colonne de gauche.</p>
</div>
</template>
<script id="donnees-types" type="application/json">{{ command_types|tojson }}</script>
<script src="{{ url_for('static', filename='editor.js') }}"></script>
{% endblock %}
+163
View File
@@ -0,0 +1,163 @@
"""Routes de l'application.
Le serveur est volontairement sans etat a ce stade : il valide, il genere, il
ne conserve rien. Le projet vit dans le navigateur. Le partage, les projets
enregistres et la connexion viendront s'ajouter par-dessus sans changer ce
noyau.
"""
from __future__ import annotations
import io
import json
import os
import zipfile
from flask import Flask, Response, jsonify, render_template, request
from .catalog import CATEGORIES, Catalog
from .generator import build_plan, generate, render_bundle
from .model import CENTREON_VERSIONS, COMMAND_TYPES, Project, detect_macros, strip_secrets
from .packs import pack_to_project
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
CATALOG_DIR = os.environ.get("ENCLUME_CATALOG", os.path.join(BASE_DIR, "catalog"))
MAX_BODY = 2 * 1024 * 1024 # un projet honnete pese quelques dizaines de ko
def create_app() -> Flask:
app = Flask(__name__)
app.config["MAX_CONTENT_LENGTH"] = MAX_BODY
app.config["CATALOG"] = Catalog(CATALOG_DIR)
def catalog() -> Catalog:
return app.config["CATALOG"]
def project_from_request() -> Project:
payload = request.get_json(force=True, silent=True) or {}
return Project.from_dict(payload.get("project", payload))
@app.after_request
def security_headers(response: Response) -> Response:
response.headers.setdefault(
"Content-Security-Policy",
"default-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self'; "
"frame-ancestors 'none'; base-uri 'none'; form-action 'self'",
)
response.headers.setdefault("X-Content-Type-Options", "nosniff")
response.headers.setdefault("Referrer-Policy", "no-referrer")
return response
# ----------------------------------------------------------------- pages
@app.context_processor
def contexte_commun() -> dict:
return {"version": os.environ.get("ENCLUME_VERSION", "dev")}
@app.get("/")
def editor() -> str:
cat = catalog()
return render_template(
"editeur.html",
versions=CENTREON_VERSIONS,
command_types=COMMAND_TYPES,
categories=CATEGORIES,
grouped=cat.by_category(),
pack_count=len(cat.packs),
service_count=cat.service_count,
)
@app.get("/sante")
def health() -> Response:
cat = catalog()
return jsonify(
{
"statut": "ok",
"version": os.environ.get("ENCLUME_VERSION", "dev"),
"revision": os.environ.get("ENCLUME_COMMIT", "inconnu"),
"packs": len(cat.packs),
"erreurs_catalogue": cat.errors,
}
)
# ------------------------------------------------------------------- api
@app.get("/api/packs")
def api_packs() -> Response:
return jsonify(
[
{
"id": p.id,
"name": p.name,
"category": p.category,
"category_label": p.category_label,
"status": p.status,
"plugin": p.plugin,
"description": p.description,
"host_template": p.host_template.name,
"services": [
{"name": s.name, "description": s.description, "default": s.enabled_by_default}
for s in p.services
],
}
for p in sorted(catalog().packs.values(), key=lambda p: (p.category, p.name))
]
)
@app.get("/api/packs/<pack_id>/projet")
def api_pack_project(pack_id: str) -> Response:
pack = catalog().get(pack_id)
if pack is None:
return jsonify({"erreur": "pack inconnu"}), 404
only_defaults = request.args.get("tout") != "1"
return jsonify(pack_to_project(pack, only_defaults=only_defaults).to_dict())
@app.post("/api/valider")
def api_validate() -> Response:
plan = build_plan(project_from_request())
return jsonify({"summary": plan.summary, "issues": plan.issues, "has_errors": plan.has_errors})
@app.post("/api/generer")
def api_generate() -> Response:
payload = request.get_json(force=True, silent=True) or {}
return jsonify(generate(payload.get("project", payload)))
@app.post("/api/macros")
def api_macros() -> Response:
payload = request.get_json(force=True, silent=True) or {}
return jsonify(detect_macros(str(payload.get("line", ""))))
@app.post("/telecharger")
def download_zip() -> Response:
raw = request.form.get("project") or "{}"
project = Project.from_dict(json.loads(raw))
plan = build_plan(project)
files = render_bundle(plan)
buffer = io.BytesIO()
with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as archive:
for name, content in files.items():
archive.writestr(name, content)
# Le projet joint a l'archive ne contient aucun mot de passe.
archive.writestr(
"projet.json",
json.dumps(
strip_secrets(Project.from_dict(json.loads(raw))).to_dict(),
indent=2,
ensure_ascii=False,
),
)
buffer.seek(0)
slug = "".join(c if c.isalnum() or c in "-_" else "-" for c in project.name).strip("-") or "projet"
return Response(
buffer.read(),
mimetype="application/zip",
headers={"Content-Disposition": f'attachment; filename="{slug}-clapi.zip"'},
)
@app.post("/recharger")
def reload_catalog() -> Response:
app.config["CATALOG"] = Catalog(CATALOG_DIR)
cat = catalog()
return jsonify({"packs": len(cat.packs), "erreurs": cat.errors})
return app
+3
View File
@@ -0,0 +1,3 @@
Flask>=3.0
PyYAML>=6.0
gunicorn>=22.0
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env python3
"""Point d'entree de Centreon Forge.
python3 run.py # http://127.0.0.1:8080
ENCLUME_PORT=9000 python3 run.py
"""
import os
from enclume.webapp import create_app
app = create_app()
if __name__ == "__main__":
app.run(
host=os.environ.get("ENCLUME_HOST", "127.0.0.1"),
port=int(os.environ.get("ENCLUME_PORT", "8080")),
debug=os.environ.get("ENCLUME_DEBUG") == "1",
)
+78
View File
@@ -0,0 +1,78 @@
"""Garde-fous minimaux : ce qui casserait silencieusement le code genere."""
import json
from enclume.catalog import Catalog
from enclume.generator import build_plan, render_bundle
from enclume.model import Project, strip_secrets, validate
from enclume.packs import pack_to_project
from enclume.webapp import create_app
def projet_exemple() -> Project:
catalog = Catalog("catalog")
return pack_to_project(catalog.get("os-linux-snmp"))
def test_catalogue_lisible():
catalog = Catalog("catalog")
assert not catalog.errors
assert len(catalog.packs) >= 20
def test_plan_complet():
plan = build_plan(projet_exemple())
lignes = plan.ops
assert any(o.obj == "CMD" and o.action == "ADD" for o in lignes)
assert any(o.obj == "STPL" and o.action == "addhosttemplate" for o in lignes)
assert not plan.has_errors
def test_prefixe_applique_aux_references():
projet = projet_exemple()
projet.prefix = "ACME-"
lignes = [o.import_line() for o in build_plan(projet).ops if o.obj != "#"]
liaison = [l for l in lignes if l.startswith("STPL;addhosttemplate")][0]
assert liaison.count("ACME-") == 2
def test_macros_trois_champs_sur_ancienne_version():
projet = projet_exemple()
projet.centreon_version = "legacy"
macro = [o for o in build_plan(projet).ops if o.action == "setmacro"][0]
assert len(macro.values) == 3
def test_echappement_shell_des_macros_centreon():
fichiers = render_bundle(build_plan(projet_exemple()))
ligne = [l for l in fichiers["deploy.sh"].splitlines() if "HOSTADDRESS" in l][0]
assert ligne.startswith("clapi -o CMD -a ADD -v '")
def test_point_virgule_refuse():
projet = projet_exemple()
projet.commands[0].line += "; rm -rf /"
erreurs = [i for i in validate(projet) if i["level"] == "error"]
assert erreurs
def test_les_mots_de_passe_ne_sont_pas_conserves():
projet = projet_exemple()
projet.host_templates[0].macros.append(
type(projet.host_templates[0].macros[0])(name="PASSWORD", value="secret", is_password=True)
)
assert all(not m.value for m in strip_secrets(projet).host_templates[0].macros if m.is_password)
def test_api_de_generation():
client = create_app().test_client()
reponse = client.post("/api/generer", json={"project": projet_exemple().to_dict()})
donnees = reponse.get_json()
assert reponse.status_code == 200
assert donnees["files"]["deploy.clapi"].startswith("#")
assert donnees["summary"]["operations"] > 0
def test_sante():
reponse = create_app().test_client().get("/sante")
assert reponse.get_json()["statut"] == "ok"