diff --git a/Makefile b/Makefile index 1a41a06..b03ef9c 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,8 @@ validate-generated: $(ICINGA_FILES) bpm/Life-NOC.conf @grep -R 'check_life_noc_mock' icinga/templates icinga/commands icinga/services >/dev/null @$(PYTHON) -c 'import pathlib; p = pathlib.Path("bpm/Life-NOC.conf"); text = p.read_text(encoding="utf-8"); lines = [l.strip() for l in text.splitlines() if l.strip()]; assert any(l == "### Business Process Config File ###" for l in lines), "bpm/Life-NOC.conf invalide: en-tête BPM absent"; assert any(l.startswith("display 1;") for l in lines), "bpm/Life-NOC.conf invalide: aucun display absent"; print("Artefacts générés valides")' +check: generate validate + mock-ok: @bash checks/check_life_noc_mock.sh --state OK --message "Sous contrôle" --item-name "demo" --date "2026-03-13" diff --git a/ansible/roles/life_noc/templates/life-noc-input-api.service.j2 b/ansible/roles/life_noc/templates/life-noc-input-api.service.j2 index efdd2fc..15aeaa8 100644 --- a/ansible/roles/life_noc/templates/life-noc-input-api.service.j2 +++ b/ansible/roles/life_noc/templates/life-noc-input-api.service.j2 @@ -7,7 +7,7 @@ Type=simple User=root Group=root WorkingDirectory={{ life_noc_project_root }} -ExecStart=/usr/bin/python3 -m uvicorn scripts.life_noc_input_api:app --host 127.0.0.1 --port 8787 +ExecStart=/usr/bin/python3 -m uvicorn scripts.life_noc_input_api:app --host 0.0.0.0 --port 8787 Restart=on-failure RestartSec=3 diff --git a/apply_days_until_due_pilot.sh b/apply_days_until_due_pilot.sh deleted file mode 100755 index fd5a38f..0000000 --- a/apply_days_until_due_pilot.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT="${1:-.}" - -need_file() { - local f="$1" - if [[ ! -f "$ROOT/$f" ]]; then - echo "Fichier introuvable: $f" >&2 - exit 1 - fi -} - -echo "==> Vérification" -need_file "domains.yaml" -need_file "icinga/commands/check_life_noc_probe.conf" -need_file "scripts/generate_services.py" -need_file "Makefile" - -echo "==> Sauvegarde minimale" -mkdir -p "$ROOT/.backup-days-until-due-pilot" -cp -a "$ROOT/domains.yaml" "$ROOT/.backup-days-until-due-pilot/domains.yaml.bak" - -echo "==> Création du store d'intrants" -mkdir -p "$ROOT/data/inputs" -cat > "$ROOT/data/inputs/obligations-legales-personnelles.yaml" <<'EOF' -renouvellement-permis-conduire: - value: "2026-09-20" - captured_at: "2026-03-14T23:30:00Z" - origin: manual -EOF - -echo "==> Mise à jour de domains.yaml" -python3 - "$ROOT/domains.yaml" <<'PY' -from pathlib import Path -import sys -import yaml - -path = Path(sys.argv[1]) -data = yaml.safe_load(path.read_text(encoding="utf-8")) - -domains = data.get("domains") -if not isinstance(domains, dict): - raise SystemExit("domains.yaml invalide: clé racine 'domains' absente ou invalide") - -domain_name = "obligations-legales-personnelles" -items = domains.get(domain_name) -if not isinstance(items, list): - raise SystemExit(f"Domaine introuvable ou invalide: {domain_name}") - -target = None -for item in items: - if isinstance(item, dict) and item.get("name") == "renouvellement-permis-conduire": - target = item - break - -if target is None: - raise SystemExit("Item 'renouvellement-permis-conduire' introuvable dans domains.yaml") - -target["date"] = "2026-09-20" -target["notes"] = "Vérifier la proximité de l’échéance du permis" -target["probe"] = { - "type": "days_until_due", - "source": { - "type": "manual_date", - "inputs_file": "/opt/life-noc/data/inputs/obligations-legales-personnelles.yaml", - "item_key": "renouvellement-permis-conduire", - }, - "metric": { - "unit": "days", - }, - "thresholds": { - "critical_lt": 7, - "warning_lt": 30, - "ok_lt": 90, - "unknown_gte": 90, - }, - "policy": { - "on_error": "critical", - }, -} - -path.write_text(yaml.safe_dump(data, sort_keys=False, allow_unicode=True), encoding="utf-8") -print("domains.yaml mis à jour") -PY - -echo "==> Vérification rapide de la commande Icinga" -for needle in \ - '--unknown-gte' \ - '--ok-lt' \ - '--warning-lt' \ - '--critical-lt' -do - if ! grep -q -- "$needle" "$ROOT/icinga/commands/check_life_noc_probe.conf"; then - echo "ERREUR: argument manquant dans icinga/commands/check_life_noc_probe.conf : $needle" >&2 - exit 1 - fi -done - -echo "==> Génération" -( - cd "$ROOT" - python3 scripts/generate_services.py -) - -echo "==> Validation" -( - cd "$ROOT" - make check -) - -echo "==> Déploiement" -( - cd "$ROOT" - make deploy-with-bpm -) - -echo -echo "Terminé." -echo "Test utile ensuite :" -echo " python3 scripts/life_noc_input.py set obligations-legales-personnelles renouvellement-permis-conduire 2026-03-20" diff --git a/docs/architecture-life-noc.md b/docs/architecture-life-noc.md new file mode 100644 index 0000000..7edb7ba --- /dev/null +++ b/docs/architecture-life-noc.md @@ -0,0 +1,295 @@ +# Architecture et fonctionnement de Life-NOC + +## 1. Présentation générale + +### 1.1 Définition + +**Life-NOC** est un système de pilotage personnel qui applique à la vie réelle la logique d’un **NOC** (Network Operations Center / centre d’opérations). + +Son but n’est pas d’automatiser la vie à la place de la personne. Son but est de : + +- rendre visibles les suivis importants ; +- transformer la charge mentale en état observable ; +- soutenir la mémoire prospective ; +- orienter l’attention vers ce qui mérite réellement d’être vu ; +- réduire la friction entre la prise de conscience et l’action. + +Life-NOC peut être résumé ainsi : + +> **Voir clair, prioriser juste, agir au bon endroit.** + +### 1.2 Finalité + +Life-NOC sert à superviser des éléments de la vie personnelle, domestique, administrative, technique ou communautaire comme s’il s’agissait de services critiques dans une salle de contrôle. + +Il permet par exemple de suivre : + +- des revues périodiques ; +- des échéances ; +- des stocks ; +- des vérifications techniques ; +- des obligations administratives ; +- des contrôles de maintenance ; +- des routines de résilience ; +- des points d’attention dans l’environnement physique. + +### 1.3 Positionnement + +Life-NOC n’est pas : + +- un simple gestionnaire de tâches ; +- un agenda ; +- un ERP ; +- un moteur d’automatisation généraliste ; +- un outil d’inventaire pur. + +Life-NOC est : + +- un **système de supervision attentionnelle** ; +- un **cadre d’évaluation de suivis** ; +- un **point de convergence** entre intrants, règles, états et actions ; +- une **interface de réduction de charge mentale**. + +## 2. Principes de fonctionnement + +### 2.1 Séparation des couches + +Life-NOC repose sur quatre couches distinctes : + +1. **définition** ; +2. **ingestion** ; +3. **persistance** ; +4. **évaluation**. + +**Définition** +Contient les items à superviser, les méthodes, les seuils, les notes et les liens utiles. + +**Ingestion** +Contient les mécanismes par lesquels une donnée entre dans le système : +- saisie manuelle ; +- API ; +- page HTML ; +- QR code ; +- plus tard MQTT, GUI avancée, etc. + +**Persistance** +Contient les intrants vivants à jour. + +**Évaluation** +Contient la logique qui transforme un intrant en état Life-NOC. + +### 2.2 Philosophie générale + +Le système suit cette logique : + +- le **domaine** organise ; +- la **méthode** évalue ; +- l’**item** fait le lien entre les deux ; +- l’**intrant** fournit la valeur vivante ; +- la **page item** réduit la friction d’action ; +- **Icinga** rend les états visibles dans un cadre de supervision. + +## 3. États Life-NOC + +### 3.1 États + +- **UNKNOWN** : pas encore dû, pas encore à faire ; +- **OK** : actionnable maintenant ; +- **WARNING** : il faut se presser ; +- **CRITICAL** : en retard, anormal, ou impossible à évaluer. + +### 3.2 Politique d’erreur + +Par défaut, si le système ne peut pas conclure techniquement, il retourne **CRITICAL**. + +Exemples : + +- fichier introuvable ; +- donnée absente ; +- format invalide ; +- parsing impossible ; +- seuil incohérent ; +- type de sonde non pris en charge. + +## 4. Organisation métier par domaines + +Les domaines servent à organiser les suivis. + +Exemples : + +- revue ; +- focus ; +- finances-personnelles ; +- obligations-legales-personnelles ; +- maison ; +- energie ; +- voiture ; +- projets ; +- documentation ; +- animaux. + +Le domaine est une unité d’organisation métier, pas nécessairement une unité de calcul. + +## 5. Taxonomie des méthodes de sonde + +### 5.1 `elapsed_time` +Mesure le temps écoulé depuis une dernière exécution. + +### 5.2 `days_until_due` +Mesure le temps restant avant une échéance fixe. + +### 5.3 `elapsed_distance` +Mesure une distance écoulée depuis une action passée. + +### 5.4 `current_value` +Évalue une valeur instantanée contre des seuils. + +### 5.5 `remaining_quantity` +Évalue ce qu’il reste d’un stock ou d’une réserve. + +## 6. Portée actuellement validée + +Au stade actuel, les méthodes réellement validées sont : + +- `elapsed_time` ; +- `days_until_due`. + +## 7. Définition des items + +Les items sont définis dans : + +```text +domains.yaml +``` + +Un item peut contenir : + +- `name` +- `date` +- `notes` +- `notes_url` +- `instructions_url` +- `action_url` +- `probe` + +### 7.1 Rôle des champs descriptifs + +- `notes` : résumé court ; +- `notes_url` : contexte / référence ; +- `instructions_url` : procédure ; +- `action_url` : outil ou page d’action. + +## 8. Store des intrants + +Les intrants vivants sont stockés sous : + +```text +data/inputs/.yaml +``` + +Exemple : + +```yaml +revue-hebdomadaire-priorites: + value: "2026-03-14" + captured_at: "2026-03-14T23:16:41Z" + origin: manual +``` + +## 9. CLI des intrants + +Commandes principales : + +- `list` +- `get` +- `set` +- `complete` + +Exemples : + +```bash +life-noc-input list revue +life-noc-input get revue revue-hebdomadaire-priorites +life-noc-input set revue revue-hebdomadaire-priorites 2026-03-14 +life-noc-input complete revue revue-hebdomadaire-priorites +``` + +## 10. API locale des intrants + +API locale sur : + +```text +http://127.0.0.1:8787 +``` + +Endpoints JSON : + +- `GET /health` +- `GET /inputs/{domain}` +- `GET /inputs/{domain}/{item_key}` +- `POST /inputs/{domain}/{item_key}` +- `POST /inputs/{domain}/{item_key}/complete` + +Dépendances importantes : + +- `python3-fastapi` +- `python3-uvicorn` +- `python3-multipart` + +## 11. Pages HTML Life-NOC + +Route : + +```text +/life-noc/item// +``` + +La page affiche : + +- titre humain ; +- état actuel ; +- type de sonde ; +- dernier intrant ; +- domaine ; +- item key ; +- notes ; +- liens utiles ; +- bouton `Compléter`. + +Le bouton utilise : + +```text +POST /life-noc/item///complete +``` + +## 12. Intégration Icinga + +Life-NOC s’appuie sur Icinga pour : + +- la visualisation des états ; +- le `Check Now` ; +- les BPM natifs ; +- les servicegroups ; +- l’affichage des variables utiles. + +## 13. Flux complet + +1. définition dans `domains.yaml` ; +2. intrant dans `data/inputs/.yaml` ; +3. mise à jour par CLI, API ou page HTML ; +4. lecture par la sonde ; +5. calcul de l’état ; +6. affichage dans Icinga ; +7. action de l’usager ; +8. mise à jour de l’intrant. + +## 14. Déploiement + +Commandes usuelles : + +```bash +make check +make deploy-with-bpm +``` + +Le dépôt doit rester reproductible sans correctifs manuels cachés. diff --git a/docs/exploitation-life-noc.md b/docs/exploitation-life-noc.md new file mode 100644 index 0000000..453b6cb --- /dev/null +++ b/docs/exploitation-life-noc.md @@ -0,0 +1,83 @@ +# Exploitation et dépannage Life-NOC + +## 1. Vérifications utiles + +### Vérifier l’API + +```bash +curl http://127.0.0.1:8787/health +``` + +### Vérifier un intrant + +```bash +curl http://127.0.0.1:8787/inputs/revue/revue-hebdomadaire-priorites +``` + +### Vérifier une page item + +```bash +curl http://127.0.0.1:8787/life-noc/item/revue/revue-hebdomadaire-priorites +``` + +## 2. Cas fréquent : l’intrant change mais pas l’état dans Icinga + +Cela signifie souvent qu’Icinga n’a pas encore refait le check. + +Il faut alors : + +- lancer un `Check Now` ; +- ou attendre le prochain check ; +- ou tester directement le plugin à la main. + +## 3. Cas fréquent : route HTML absente après déploiement + +Vérifier : + +- que le service API tourne ; +- que le fichier déployé contient bien les routes HTML ; +- que les dépendances FastAPI sont installées, notamment `python3-multipart`. + +## 4. Vérifier le service API + +```bash +sudo systemctl status life-noc-input-api --no-pager +sudo journalctl -u life-noc-input-api -n 80 --no-pager +``` + +## 5. Vérifier directement un plugin + +Exemple pour `elapsed_time` : + +```bash +/usr/lib/nagios/plugins/check_life_noc_probe.py --probe-type elapsed_time --source-type manual_date --inputs-file /opt/life-noc/data/inputs/revue.yaml --item-key revue-hebdomadaire-priorites --unit days --unknown-lt 5 --ok-gte 5 --warning-gte 7 --critical-gte 10 --on-error critical --label revue-hebdomadaire-priorites +``` + +## 6. Déploiement + +```bash +make check +make deploy-with-bpm +``` + +## 7. Dépendances API importantes + +Le service API nécessite : + +- `python3-fastapi` +- `python3-uvicorn` +- `python3-multipart` + +## 8. État actuel validé + +Éléments validés en pratique : + +- Icinga Web 2 ; +- BPM natif ; +- servicegroups ; +- `Check Now` ; +- store d’intrants ; +- CLI ; +- API locale ; +- pages HTML d’item ; +- méthodes `elapsed_time` et `days_until_due`. diff --git a/docs/guide-usager-life-noc.md b/docs/guide-usager-life-noc.md new file mode 100644 index 0000000..2e35521 --- /dev/null +++ b/docs/guide-usager-life-noc.md @@ -0,0 +1,79 @@ +# Guide usager Life-NOC + +## 1. À quoi sert Life-NOC + +Life-NOC aide une personne à voir rapidement ce qui mérite son attention, sans tout garder dans sa tête. + +Il sert à suivre par exemple : + +- des revues régulières ; +- des dates importantes ; +- des entretiens ; +- des vérifications ; +- des obligations. + +## 2. Ce que signifient les états + +- **UNKNOWN** : pas encore le temps d’agir ; +- **OK** : c’est le bon moment pour s’en occuper ; +- **WARNING** : il faut accélérer ; +- **CRITICAL** : c’est urgent ou il y a un problème. + +## 3. Comment consulter un item + +Un item peut être consulté : + +- dans Icinga ; +- via une page Life-NOC ; +- plus tard via un QR code. + +La page Life-NOC affiche : + +- le nom du suivi ; +- son état ; +- la dernière valeur enregistrée ; +- les notes ; +- les instructions ; +- un bouton pour marquer l’action comme faite. + +## 4. Comment marquer une action comme faite + +Quand l’item le permet, il suffit de cliquer sur **Compléter**. + +Cela met à jour la valeur de l’intrant avec la date du jour. + +## 5. À quoi servent les liens utiles + +- **Notes** : pour comprendre le contexte ; +- **Instructions** : pour savoir quoi faire ; +- **Action** : pour aller directement au bon outil ou à la bonne page. + +## 6. Ce que l’usager n’a pas besoin de savoir + +L’usager n’a normalement pas besoin de comprendre : + +- les fichiers YAML ; +- le moteur de sonde ; +- l’architecture technique ; +- le fonctionnement interne d’Icinga. + +Il lui suffit de : + +- voir l’état ; +- consulter les instructions ; +- marquer l’action effectuée. + +## 7. QR codes — direction prévue + +Life-NOC est conçu pour permettre l’usage de QR codes pointant vers : + +```text +/life-noc/item// +``` + +Le scan permettra : + +- d’ouvrir directement l’item ; +- de lire les instructions ; +- de confirmer l’action ; +- de mettre à jour le système facilement. diff --git a/docs/life_noc_documentation_pack.md b/docs/life_noc_documentation_pack.md new file mode 100644 index 0000000..a54e521 --- /dev/null +++ b/docs/life_noc_documentation_pack.md @@ -0,0 +1,734 @@ +# Life-NOC — Documentation complète + +## 1. Présentation générale + +### 1.1 Définition + +**Life-NOC** est un système de pilotage personnel qui applique à la vie réelle la logique d’un **NOC** (Network Operations Center / centre d’opérations). + +Son but n’est pas d’automatiser la vie à la place de la personne. +Son but est de : + +- rendre visibles les suivis importants ; +- transformer la charge mentale en état observable ; +- soutenir la mémoire prospective ; +- orienter l’attention vers ce qui mérite réellement d’être vu ; +- réduire la friction entre la prise de conscience et l’action. + +Life-NOC peut être résumé ainsi : + +> **Voir clair, prioriser juste, agir au bon endroit.** + +--- + +### 1.2 Finalité + +Life-NOC sert à superviser des éléments de la vie personnelle, domestique, administrative, technique ou communautaire comme s’il s’agissait de services critiques dans une salle de contrôle. + +Il permet par exemple de suivre : + +- des revues périodiques ; +- des échéances ; +- des stocks ; +- des vérifications techniques ; +- des obligations administratives ; +- des contrôles de maintenance ; +- des routines de résilience ; +- des points d’attention dans l’environnement physique. + +--- + +### 1.3 Positionnement + +Life-NOC n’est pas : + +- un simple gestionnaire de tâches ; +- un agenda ; +- un ERP ; +- un moteur d’automatisation généraliste ; +- un outil d’inventaire pur. + +Life-NOC est : + +- un **système de supervision attentionnelle** ; +- un **cadre d’évaluation de suivis** ; +- un **point de convergence** entre intrants, règles, états et actions ; +- une **interface de réduction de charge mentale**. + +--- + +## 2. Principes de fonctionnement + +### 2.1 Séparation des couches + +Life-NOC repose sur quatre couches distinctes : + +1. **définition** ; +2. **ingestion** ; +3. **persistance** ; +4. **évaluation**. + +#### Définition +Contient les items à superviser, les méthodes, les seuils, les notes et les liens utiles. + +#### Ingestion +Contient les mécanismes par lesquels une donnée entre dans le système : +- saisie manuelle ; +- API ; +- page HTML ; +- QR code ; +- plus tard MQTT, GUI avancée, etc. + +#### Persistance +Contient les intrants vivants à jour. + +#### Évaluation +Contient la logique qui transforme un intrant en état Life-NOC. + +--- + +### 2.2 Philosophie générale + +Le système suit cette logique : + +- le **domaine** organise ; +- la **méthode** évalue ; +- l’**item** fait le lien entre les deux ; +- l’**intrant** fournit la valeur vivante ; +- la **page item** réduit la friction d’action ; +- **Icinga** rend les états visibles dans un cadre de supervision. + +--- + +## 3. États Life-NOC + +Life-NOC utilise une sémantique propre des états. + +### 3.1 États + +- **UNKNOWN** : pas encore dû, pas encore à faire ; +- **OK** : actionnable maintenant ; +- **WARNING** : il faut se presser ; +- **CRITICAL** : en retard, anormal, ou impossible à évaluer. + +### 3.2 Différence avec l’usage classique de Nagios/Icinga + +Dans Life-NOC : + +- le vert ne signifie pas seulement « tout va bien » ; +- le vert signifie souvent « c’est le bon moment pour agir » ; +- le gris signifie que l’item n’est pas encore entré dans sa fenêtre d’action. + +### 3.3 Politique d’erreur + +Par défaut, si le système ne peut pas conclure techniquement, il retourne **CRITICAL**. + +Exemples : + +- fichier introuvable ; +- donnée absente ; +- format invalide ; +- parsing impossible ; +- seuil incohérent ; +- type de sonde non pris en charge. + +Cette politique force la correction des faux positifs techniques plutôt que leur invisibilisation. + +--- + +## 4. Organisation métier par domaines + +Life-NOC regroupe les items dans des **domaines**. + +Exemples de domaines : + +- revue ; +- focus ; +- finances-personnelles ; +- fiscalite-personnelle ; +- obligations-legales-personnelles ; +- maison ; +- garage-et-rangement ; +- energie ; +- resilience ; +- stock-alimentaire ; +- sante ; +- voiture ; +- jardin ; +- informatique-personnelle ; +- infrastructure-chezlepro ; +- reseau-chezlepro ; +- securite-chezlepro ; +- exploitation-chezlepro ; +- projets ; +- communaute ; +- documentation ; +- animaux. + +Le domaine est une unité d’organisation métier, pas nécessairement une unité de calcul. + +Un même domaine peut contenir plusieurs méthodes de sonde. + +--- + +## 5. Taxonomie des méthodes de sonde + +Le modèle conceptuel v1 de Life-NOC prévoit cinq méthodes principales. + +### 5.1 elapsed_time + +Mesure le temps écoulé depuis un événement ou une exécution précédente. + +Exemples : +- revue hebdomadaire ; +- inspection trimestrielle ; +- test périodique. + +Intrant attendu : +- date de dernière exécution. + +Unité typique : +- jours. + +--- + +### 5.2 days_until_due + +Mesure le temps restant avant une échéance fixe. + +Exemples : +- renouvellement du permis ; +- passeport ; +- assurance ; +- obligation légale. + +Intrant attendu : +- date d’échéance. + +Unité typique : +- jours. + +--- + +### 5.3 elapsed_distance + +Mesure la distance écoulée depuis une action passée. + +Exemples : +- vidange ; +- entretien pneus ; +- entretien freins. + +Intrants attendus : +- compteur courant ; +- compteur au dernier entretien. + +Unité typique : +- kilomètres. + +--- + +### 5.4 current_value + +Évalue une valeur instantanée contre des seuils. + +Exemples : +- tension batterie ; +- température ; +- humidité ; +- espace libre. + +Intrant attendu : +- valeur actuelle. + +Unité typique : +- variable selon le cas. + +--- + +### 5.5 remaining_quantity + +Évalue ce qu’il reste d’un stock ou d’une réserve. + +Exemples : +- nourriture chats ; +- réserve d’eau ; +- carburant ; +- consommables. + +Intrant attendu : +- quantité restante. + +Unité typique : +- jours, litres, unités, pourcentage, etc. + +--- + +## 6. Portée actuellement validée + +Au stade actuel, les méthodes réellement validées dans le système sont : + +- `elapsed_time` ; +- `days_until_due`. + +Les autres méthodes sont conceptuellement définies, mais pas encore consolidées de la même manière dans le dépôt. + +--- + +## 7. Définition des items + +Les items supervisés sont définis dans : + +```text +domains.yaml +``` + +Chaque item peut contenir : + +- `name` ; +- `date` ; +- `notes` ; +- `notes_url` ; +- `instructions_url` ; +- `action_url` ; +- `probe` ; +- `thresholds` ; +- `policy`. + +### 7.1 Rôle des champs descriptifs + +- `notes` : résumé humain court ; +- `notes_url` : contexte / référence ; +- `instructions_url` : quoi faire / procédure ; +- `action_url` : où agir concrètement. + +### 7.2 Champ probe + +Le champ `probe` décrit : + +- la méthode ; +- la source ; +- l’unité ; +- les seuils ; +- la politique d’erreur. + +Exemple : + +```yaml +probe: + type: elapsed_time + source: + type: manual_date + inputs_file: "/opt/life-noc/data/inputs/revue.yaml" + item_key: "revue-hebdomadaire-priorites" + metric: + unit: days + thresholds: + unknown_lt: 5 + ok_gte: 5 + warning_gte: 7 + critical_gte: 10 + policy: + on_error: critical +``` + +--- + +## 8. Store des intrants + +### 8.1 Principe + +Les intrants vivants ne sont pas mélangés à la définition des items. + +Ils sont stockés sous : + +```text +data/inputs/.yaml +``` + +Exemples : + +- `data/inputs/revue.yaml` +- `data/inputs/obligations-legales-personnelles.yaml` + +### 8.2 Contenu minimal d’un intrant + +Un intrant contient en général : + +- `value` ; +- `captured_at` ; +- `origin`. + +Exemple : + +```yaml +revue-hebdomadaire-priorites: + value: "2026-03-14" + captured_at: "2026-03-14T23:16:41Z" + origin: manual +``` + +### 8.3 Rôle du store + +Le store d’intrants permet : + +- de changer une valeur sans toucher à `domains.yaml` ; +- d’alimenter les sondes avec des valeurs vivantes ; +- de préparer les futures interfaces GUI, API, QR et autres. + +--- + +## 9. CLI de gestion des intrants + +Life-NOC dispose d’une CLI de gestion des intrants. + +### 9.1 Commandes principales + +- `list` +- `get` +- `set` +- `complete` + +### 9.2 Exemples + +```bash +life-noc-input list revue +life-noc-input get revue revue-hebdomadaire-priorites +life-noc-input set revue revue-hebdomadaire-priorites 2026-03-14 +life-noc-input complete revue revue-hebdomadaire-priorites +``` + +### 9.3 Rôle de complete + +`complete` est particulièrement utile pour les items de type `elapsed_time/manual_date`. + +Il inscrit automatiquement : + +- `value = date du jour` +- `captured_at = maintenant` +- `origin = manual` + +--- + +## 10. API locale des intrants + +Life-NOC expose une API locale sur : + +```text +http://127.0.0.1:8787 +``` + +### 10.1 Endpoints JSON + +- `GET /health` +- `GET /inputs/{domain}` +- `GET /inputs/{domain}/{item_key}` +- `POST /inputs/{domain}/{item_key}` +- `POST /inputs/{domain}/{item_key}/complete` + +### 10.2 Exemple + +```bash +curl http://127.0.0.1:8787/health +curl http://127.0.0.1:8787/inputs/revue +curl http://127.0.0.1:8787/inputs/revue/revue-hebdomadaire-priorites +curl -X POST http://127.0.0.1:8787/inputs/revue/revue-hebdomadaire-priorites \ + -H 'Content-Type: application/json' \ + -d '{"value":"2026-03-14","origin":"manual"}' +``` + +### 10.3 Dépendances importantes + +Le service FastAPI nécessite notamment : + +- `python3-fastapi` +- `python3-uvicorn` +- `python3-multipart` + +--- + +## 11. Pages HTML Life-NOC + +Le serveur Life-NOC sert aussi des pages HTML simples destinées à l’action humaine. + +### 11.1 URL d’item + +Format : + +```text +/life-noc/item// +``` + +Exemple : + +```text +/life-noc/item/revue/revue-hebdomadaire-priorites +``` + +### 11.2 Contenu de la page + +La page affiche : + +- titre humain ; +- état actuel ; +- type de sonde ; +- dernier intrant ; +- domaine ; +- item key ; +- notes ; +- lien `notes_url` ; +- lien `instructions_url` ; +- lien `action_url` ; +- bouton `Compléter`. + +### 11.3 Route d’action HTML + +Le bouton `Compléter` utilise : + +```text +POST /life-noc/item///complete +``` + +Puis redirige vers la page de l’item. + +--- + +## 12. Intégration Icinga + +Life-NOC s’appuie sur Icinga 2 / Icinga Web 2 pour la visualisation des états supervisés. + +### 12.1 Ce que le système sait faire + +- déployer les définitions de services ; +- exposer les custom variables ; +- rendre les états lisibles dans Icinga ; +- supporter `Check Now` ; +- intégrer les BPM natifs ; +- générer des servicegroups cohérents. + +### 12.2 Informations visibles dans Icinga + +Selon l’item, on peut voir notamment : + +- l’état calculé ; +- les variables de seuil ; +- les variables de source ; +- les liens utiles ; +- `instructions_url` en variable personnalisée. + +--- + +## 13. BPM et servicegroups + +Life-NOC génère : + +- des services Icinga ; +- des groupes de services ; +- une configuration BPM native. + +L’objectif est de rendre visibles à la fois : + +- les items unitaires ; +- les regroupements par domaine ; +- la vision d’ensemble hiérarchisée. + +--- + +## 14. Flux de fonctionnement complet + +Le fonctionnement complet suit le chemin suivant : + +1. l’item est défini dans `domains.yaml` ; +2. l’intrant vivant est stocké dans `data/inputs/.yaml` ; +3. la CLI, l’API ou la page HTML modifient cet intrant ; +4. la sonde lit le store ; +5. la sonde calcule l’état ; +6. Icinga l’affiche ; +7. l’usager agit ; +8. l’intrant est mis à jour ; +9. le cycle recommence. + +--- + +## 15. Déploiement et reproductibilité + +Le dépôt doit permettre de reproduire le système sans correctifs manuels cachés. + +### 15.1 Principes + +- les correctifs doivent vivre dans le dépôt ; +- les dépendances nécessaires doivent être installées automatiquement ; +- les scripts de chantier temporaires doivent être supprimés une fois les changements intégrés proprement. + +### 15.2 Déploiement habituel + +```bash +make check +make deploy-with-bpm +``` + +--- + +## 16. Documentation pour les usagers + +# Guide usager Life-NOC + +## 16.1 À quoi sert Life-NOC pour l’usager + +Life-NOC aide une personne à voir rapidement ce qui mérite son attention, sans tout garder dans sa tête. + +Il sert à suivre des choses comme : + +- des revues régulières ; +- des dates importantes ; +- des entretiens ; +- des vérifications ; +- des obligations. + +## 16.2 Ce que signifient les états + +- **UNKNOWN** : pas encore le temps d’agir ; +- **OK** : c’est le bon moment pour s’en occuper ; +- **WARNING** : il faut accélérer ; +- **CRITICAL** : c’est urgent ou il y a un problème. + +## 16.3 Comment consulter un item + +Un item peut être consulté : + +- dans Icinga ; +- via une page Life-NOC ; +- plus tard via QR code. + +La page Life-NOC affiche : + +- le nom du suivi ; +- son état ; +- la dernière valeur enregistrée ; +- les notes ; +- les instructions ; +- un bouton pour marquer l’action comme faite. + +## 16.4 Comment marquer une action comme faite + +Quand l’item le permet, il suffit de cliquer sur **Compléter**. + +Cela met à jour la valeur de l’intrant avec la date du jour. + +## 16.5 Quand utiliser les liens utiles + +- **Notes** : pour comprendre le contexte ; +- **Instructions** : pour savoir quoi faire ; +- **Action** : pour aller directement au bon outil ou à la bonne page. + +## 16.6 Ce que l’usager n’a pas besoin de faire + +L’usager n’a normalement pas besoin de comprendre : + +- le moteur de sonde ; +- les fichiers YAML ; +- la structure technique ; +- l’architecture Icinga. + +Il lui suffit de : + +- voir l’état ; +- consulter les instructions ; +- marquer l’action effectuée. + +--- + +## 17. Documentation d’exploitation + +### 17.1 Vérifications utiles + +#### Vérifier l’API + +```bash +curl http://127.0.0.1:8787/health +``` + +#### Vérifier un intrant + +```bash +curl http://127.0.0.1:8787/inputs/revue/revue-hebdomadaire-priorites +``` + +#### Vérifier une page item + +```bash +curl http://127.0.0.1:8787/life-noc/item/revue/revue-hebdomadaire-priorites +``` + +### 17.2 Cas fréquent : l’intrant change mais pas l’état dans Icinga + +Cela signifie souvent qu’Icinga n’a pas encore refait le check. + +Il faut alors : + +- lancer un `Check Now` ; +- ou attendre le prochain check ; +- ou tester directement le plugin à la main. + +### 17.3 Cas fréquent : route HTML absente après déploiement + +Vérifier : + +- que le service API tourne ; +- que le fichier déployé contient bien les routes HTML ; +- que les dépendances FastAPI sont installées, notamment `python3-multipart`. + +--- + +## 18. QR codes — direction prévue + +Life-NOC est conçu pour pouvoir pointer des QR codes vers des pages item. + +Format visé : + +```text +/life-noc/item// +``` + +Le scan permettrait : + +- d’ouvrir l’item ; +- de lire les instructions ; +- de confirmer l’action ; +- de mettre à jour le système facilement. + +--- + +## 19. État actuel du projet + +Le système a déjà validé en pratique : + +- Icinga Web 2 fonctionnel ; +- BPM natif fonctionnel ; +- servicegroups fonctionnels ; +- `Check Now` fonctionnel ; +- store d’intrants séparé ; +- CLI des intrants ; +- API locale ; +- pages HTML d’item ; +- méthodes réelles `elapsed_time` et `days_until_due`. + +--- + +## 20. Conclusion + +Life-NOC dispose désormais d’un noyau opératoire réel. + +Il ne s’agit plus seulement d’un concept ou d’une maquette. +Le système sait déjà : + +- définir des suivis ; +- stocker des intrants ; +- évaluer des états ; +- exposer ces états ; +- fournir des instructions ; +- permettre l’action ; +- réintégrer cette action dans la supervision. + +C’est la base d’un véritable système de pilotage personnel orienté action. + diff --git a/docs/plan-demockage-life-noc.md b/docs/plan-demockage-life-noc.md new file mode 100644 index 0000000..95c8ad6 --- /dev/null +++ b/docs/plan-demockage-life-noc.md @@ -0,0 +1,657 @@ +# Plan de démockage Life-NOC + +## 1. Objet + +Ce document sert de plan directeur pour faire évoluer Life-NOC d’une preuve de concept partiellement mockée vers un système où **chaque item** : + +- possède une **méthode réelle** ; +- possède une **source d’intrants réelle** ; +- possède une **page web canonique** ; +- possède une **URL stable** ; +- peut être **consulté**, **compris** et **renseigné** de manière interactive. + +L’objectif final est : + +> **1 item = 1 URL = 1 page = 1 logique = 1 source de vérité** + +--- + +## 2. Principes retenus + +### 2.1 Tout item doit être démocké + +À terme, aucun item ne devrait dépendre d’un état mocké durablement. + +Le mock a servi à : + +- prouver l’architecture ; +- faire exister la structure BPM et Icinga ; +- valider les flux de déploiement ; +- permettre une mise en place progressive. + +Mais la cible est désormais : + +- **zéro item purement mocké**. + +### 2.2 Chaque item doit avoir sa page web + +Chaque item doit avoir une URL canonique de la forme : + +```text +/life-noc/item// +``` + +Cette page doit devenir : + +- la page d’information ; +- la page d’instructions ; +- la page d’action ; +- la page de saisie des intrants. + +### 2.3 La page web d’un item est l’instruction par défaut + +Pour tout item : + +- si `instructions_url` est défini, il surcharge ; +- sinon, la page canonique de l’item est la page d’instructions implicite. + +Règle : + +```text +instructions_url implicite = /life-noc/item// +``` + +### 2.4 Les domaines organisent, les méthodes évaluent + +- le **domaine** organise les items par sens métier ; +- la **méthode** exprime la logique de calcul ; +- l’**item** relie domaine, méthode, intrant, UI et action. + +Donc : + +- plusieurs items d’un même domaine peuvent utiliser des méthodes différentes ; +- plusieurs domaines peuvent réutiliser la même méthode. + +--- + +## 3. Méthodes de sonde cibles + +### 3.1 Méthodes déjà validées + +#### `elapsed_time` +Mesure le temps écoulé depuis une dernière exécution. + +#### `days_until_due` +Mesure le temps restant avant une échéance fixe. + +### 3.2 Méthodes encore à industrialiser + +#### `elapsed_distance` +Mesure une distance écoulée depuis une action passée. + +#### `current_value` +Évalue une valeur instantanée contre des seuils. + +#### `remaining_quantity` +Évalue une quantité restante contre des seuils. + +--- + +## 4. Types d’intrants cibles + +Les intrants vivants continueront d’être stockés dans : + +```text +data/inputs/.yaml +``` + +Les types principaux d’intrants à supporter sont : + +- `manual_date` +- `manual_counter` +- `manual_value` + +À terme, d’autres sources pourront exister, mais le démockage initial se concentre sur les intrants simples et robustes. + +--- + +## 5. Formulaires web cibles + +Chaque item doit pouvoir afficher un formulaire adapté à son type réel. + +### 5.1 `complete_date` +Pour les items de type `elapsed_time` avec saisie de date de dernière exécution. + +Exemples : +- revue périodique ; +- inspection ; +- test ; +- vérification périodique. + +Fonctions attendues : +- bouton `Compléter` +- saisie manuelle d’une date +- bouton `Aujourd’hui` + +### 5.2 `due_date` +Pour les items de type `days_until_due`. + +Exemples : +- permis ; +- assurance ; +- passeport ; +- échéance administrative. + +Fonctions attendues : +- saisie d’une date d’échéance +- bouton `Enregistrer` + +### 5.3 `counter_pair` +Pour les items de type `elapsed_distance`. + +Exemples : +- huile moteur ; +- pneus ; +- freins ; +- timing belt. + +Fonctions attendues : +- compteur courant +- compteur au dernier entretien +- bouton `Enregistrer` + +### 5.4 `numeric_value` +Pour les items de type `current_value`. + +Exemples : +- tension batterie +- capacité libre +- température +- mesure technique + +Fonctions attendues : +- saisie numérique +- unité affichée +- bouton `Enregistrer` + +### 5.5 `remaining_quantity` +Pour les items de type `remaining_quantity`. + +Exemples : +- réserve d’eau +- stock nourriture chats +- stock consommables +- carburant + +Fonctions attendues : +- quantité restante +- unité +- bouton `Enregistrer` + +--- + +## 6. Modèle cible d’un item réel + +Le modèle canonique minimal d’un item réel devrait converger vers ceci : + +```yaml +- name: remplacement-filtre-fournaise + title: "Remplacement du filtre de la fournaise" + summary: "Suivi périodique du filtre principal." + notes: "À faire selon la fréquence d’usage et la saison." + notes_url: "" + instructions: | + 1. Couper le système si nécessaire. + 2. Retirer le filtre usagé. + 3. Vérifier le sens du flux d’air. + 4. Installer le nouveau filtre. + 5. Confirmer la date dans Life-NOC. + instructions_url: "" + action_url: "" + probe: + type: elapsed_time + source: + type: manual_date + inputs_file: "/opt/life-noc/data/inputs/maison.yaml" + item_key: "remplacement-filtre-fournaise" + metric: + unit: days + thresholds: + unknown_lt: 30 + ok_gte: 30 + warning_gte: 60 + critical_gte: 90 + policy: + on_error: critical + ui: + form_mode: complete_date + allow_complete: true + allow_manual_edit: true +``` + +Tous les champs ne seront pas obligatoires immédiatement, mais cette structure donne la direction. + +--- + +## 7. Tableau de migration + +## 7.1 REVUE + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| revue-quotidienne-life-noc | elapsed_time | manual_date | complete_date | réel | +| revue-hebdomadaire-priorites | elapsed_time | manual_date | complete_date | réel | +| revue-mensuelle-systeme-vie | elapsed_time | manual_date | complete_date | à migrer | +| revue-trimestrielle-orientation | elapsed_time | manual_date | complete_date | à migrer | + +## 7.2 FOCUS + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| entretien-processus-focus | elapsed_time | manual_date | complete_date | à migrer | +| nettoyage-inbox-mentale | elapsed_time | manual_date | complete_date | à migrer | +| revue-limitation-engagements | elapsed_time | manual_date | complete_date | à migrer | + +## 7.3 FINANCES-PERSONNELLES + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| revision-comptes-bancaires | elapsed_time | manual_date | complete_date | à migrer | +| verification-cartes-credit | elapsed_time | manual_date | complete_date | à migrer | +| paiement-cartes-credit | elapsed_time | manual_date | complete_date | à migrer | +| revision-budget-personnel | elapsed_time | manual_date | complete_date | à migrer | +| verification-prelevements-automatiques | elapsed_time | manual_date | complete_date | à migrer | +| verification-placements | elapsed_time | manual_date | complete_date | à migrer | +| verification-cotisations-reer | elapsed_time | manual_date | complete_date | à migrer | +| verification-celi | elapsed_time | manual_date | complete_date | à migrer | +| preparation-dossier-financier-annuel | elapsed_time | manual_date | complete_date | à migrer | + +## 7.4 FISCALITE-PERSONNELLE + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| preparation-documents-impots | elapsed_time | manual_date | complete_date | à migrer | +| production-impots-federal | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser | +| production-impots-quebec | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser | +| paiement-solde-impots | days_until_due | manual_date | due_date | à migrer | +| verification-avis-cotisation | elapsed_time | manual_date | complete_date | à migrer | +| archivage-documents-fiscaux | elapsed_time | manual_date | complete_date | à migrer | +| verification-acomptes-provisionnels | days_until_due | manual_date | due_date | à migrer | + +## 7.5 OBLIGATIONS-LEGALES-PERSONNELLES + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| verification-testament | elapsed_time | manual_date | complete_date | à migrer | +| verification-mandat-inaptitude | elapsed_time | manual_date | complete_date | à migrer | +| verification-directives-medicales | elapsed_time | manual_date | complete_date | à migrer | +| verification-papiers-identite | elapsed_time | manual_date | complete_date | à migrer | +| renouvellement-permis-conduire | days_until_due | manual_date | due_date | réel | +| verification-carte-assurance-maladie | days_until_due | manual_date | due_date | à migrer | +| verification-passeport | days_until_due | manual_date | due_date | à migrer | + +## 7.6 MAISON + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| remplacement-filtre-fournaise | elapsed_time | manual_date | complete_date | à migrer | +| inspection-fournaise | elapsed_time | manual_date | complete_date | à migrer | +| inspection-thermopompe | elapsed_time | manual_date | complete_date | à migrer | +| nettoyage-unites-exterieures | elapsed_time | manual_date | complete_date | à migrer | +| inspection-toiture | elapsed_time | manual_date | complete_date | à migrer | +| inspection-gouttieres | elapsed_time | manual_date | complete_date | à migrer | +| inspection-fondation | elapsed_time | manual_date | complete_date | à migrer | +| verification-drainage-terrain | elapsed_time | manual_date | complete_date | à migrer | +| test-detecteurs-fumee | elapsed_time | manual_date | complete_date | à migrer | +| test-detecteurs-co | elapsed_time | manual_date | complete_date | à migrer | +| remplacement-piles-detecteurs | elapsed_time | manual_date | complete_date | à migrer | +| verification-plomberie-visible | elapsed_time | manual_date | complete_date | à migrer | +| inspection-sump-pump | elapsed_time | manual_date | complete_date | à migrer | +| verification-calfetrage | elapsed_time | manual_date | complete_date | à migrer | +| inspection-portes-garage | elapsed_time | manual_date | complete_date | à migrer | +| verification-extincteurs | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser | + +## 7.7 GARAGE-ET-RANGEMENT + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| inspection-rangement-plafond | elapsed_time | manual_date | complete_date | à migrer | +| revue-inventaire-garage | elapsed_time | manual_date | complete_date | à migrer | +| verification-rouille-outillage | elapsed_time | manual_date | complete_date | à migrer | +| inspection-securite-garage | elapsed_time | manual_date | complete_date | à migrer | +| rotation-stockage-bacs | elapsed_time | manual_date | complete_date | à migrer | + +## 7.8 ENERGIE + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| inspection-batteries-lifepo4 | elapsed_time | manual_date | complete_date | à migrer | +| verification-tension-batteries | current_value | manual_value | numeric_value | à migrer | +| verification-smartshunt | current_value | manual_value | numeric_value | à préciser | +| verification-cerbo-gx | elapsed_time / current_value | manual_date / manual_value | complete_date / numeric_value | à préciser | +| verification-can-rs485 | elapsed_time | manual_date | complete_date | à migrer | +| inspection-cablage-energie | elapsed_time | manual_date | complete_date | à migrer | +| verification-busbars | elapsed_time | manual_date | complete_date | à migrer | +| verification-parametres-charge | elapsed_time | manual_date | complete_date | à migrer | +| test-charge-batteries | elapsed_time | manual_date | complete_date | à migrer | +| test-comportement-onduleur | elapsed_time | manual_date | complete_date | à migrer | +| verification-journal-evenements-victron | elapsed_time | manual_date | complete_date | à migrer | + +## 7.9 RESILIENCE + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| test-generatrice-propane | elapsed_time | manual_date | complete_date | à migrer | +| verification-carburant-generatrice | remaining_quantity | manual_value | remaining_quantity | à migrer | +| verification-procedure-bascule | elapsed_time | manual_date | complete_date | à migrer | +| verification-stock-lampes-piles | remaining_quantity | manual_value | remaining_quantity | à migrer | +| verification-trousses-urgence | remaining_quantity / elapsed_time | manual_value / manual_date | remaining_quantity / complete_date | à préciser | +| revision-plan-urgence | elapsed_time | manual_date | complete_date | à migrer | +| test-autonomie-base | elapsed_time | manual_date | complete_date | à migrer | +| verification-moyens-cuisson-secours | remaining_quantity / elapsed_time | manual_value / manual_date | remaining_quantity / complete_date | à préciser | + +## 7.10 STOCK-ALIMENTAIRE + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| rotation-nourriture-seche | elapsed_time | manual_date | complete_date | à migrer | +| verification-reserve-eau | remaining_quantity | manual_value | remaining_quantity | à migrer | +| verification-mylar-absorbeurs | remaining_quantity | manual_value | remaining_quantity | à migrer | +| revue-inventaire-conserves | remaining_quantity | manual_value | remaining_quantity | à migrer | +| revue-inventaire-farine-riz-pates | remaining_quantity | manual_value | remaining_quantity | à migrer | +| verification-bacs-legumes-racines | remaining_quantity | manual_value | remaining_quantity | à migrer | +| revue-supplements-vitamines | remaining_quantity | manual_value | remaining_quantity | à migrer | + +## 7.11 SANTE + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| verification-trousse-premiers-soins | remaining_quantity / elapsed_time | manual_value / manual_date | remaining_quantity / complete_date | à préciser | +| verification-expiration-medicaments | days_until_due | manual_date | due_date | à migrer | +| revision-rendez-vous-medicaux | days_until_due | manual_date | due_date | à migrer | +| verification-lunettes-prescriptions | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser | +| verification-dossier-sante | elapsed_time | manual_date | complete_date | à migrer | + +## 7.12 VOITURE + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| verification-huile-moteur | elapsed_distance | manual_counter | counter_pair | à migrer | +| inspection-freins | elapsed_distance | manual_counter | counter_pair | à migrer | +| verification-pneus | elapsed_distance | manual_counter | counter_pair | à migrer | +| ajustement-valves | elapsed_distance / elapsed_time | manual_counter / manual_date | counter_pair / complete_date | à préciser | +| verification-timing-belt | elapsed_distance | manual_counter | counter_pair | à migrer | +| verification-batterie-voiture | current_value | manual_value | numeric_value | à migrer | +| verification-balais-essuie-glace | elapsed_time | manual_date | complete_date | à migrer | +| verification-liquides | current_value / elapsed_time | manual_value / manual_date | numeric_value / complete_date | à préciser | +| verification-eclairage | elapsed_time | manual_date | complete_date | à migrer | +| verification-immatriculation-assurance | days_until_due | manual_date | due_date | à migrer | + +## 7.13 JARDIN + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| inspection-vignes | elapsed_time | manual_date | complete_date | à migrer | +| entretien-bleuetiers | elapsed_time | manual_date | complete_date | à migrer | +| entretien-framboisiers | elapsed_time | manual_date | complete_date | à migrer | +| inspection-kiwis-nordiques | elapsed_time | manual_date | complete_date | à migrer | +| entretien-rhubarbe | elapsed_time | manual_date | complete_date | à migrer | +| inspection-groseillier | elapsed_time | manual_date | complete_date | à migrer | +| inspection-muriers | elapsed_time | manual_date | complete_date | à migrer | +| entretien-houblon | elapsed_time | manual_date | complete_date | à migrer | +| revision-pharmacopee-jardin | remaining_quantity | manual_value | remaining_quantity | à migrer | +| revision-armoire-produits-jardin | remaining_quantity | manual_value | remaining_quantity | à migrer | + +## 7.14 OUTILS-ET-EQUIPEMENTS + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| entretien-outils-jardin | elapsed_time | manual_date | complete_date | à migrer | +| inspection-outils-electriques | elapsed_time | manual_date | complete_date | à migrer | +| verification-rallonges-et-connecteurs | elapsed_time | manual_date | complete_date | à migrer | +| verification-compresseur-et-accessoires | elapsed_time | manual_date | complete_date | à migrer | +| revision-inventaire-outillage | elapsed_time | manual_date | complete_date | à migrer | + +## 7.15 INFORMATIQUE-PERSONNELLE + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| verification-sauvegardes-personnelles | elapsed_time | manual_date | complete_date | à migrer | +| nettoyage-stockage-personnel | elapsed_time | manual_date | complete_date | à migrer | +| verification-comptes-importants | elapsed_time | manual_date | complete_date | à migrer | +| revue-mots-de-passe-personnels | elapsed_time | manual_date | complete_date | à migrer | +| verification-documents-cloud-personnels | elapsed_time | manual_date | complete_date | à migrer | + +## 7.16 INFRASTRUCTURE-CHEZLEPRO + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| verification-proxmox | elapsed_time | manual_date | complete_date | à migrer | +| verification-ceph | elapsed_time / current_value | manual_date / manual_value | complete_date / numeric_value | à préciser | +| test-restauration-backups | elapsed_time | manual_date | complete_date | à migrer | +| verification-pbs-truenas | elapsed_time | manual_date | complete_date | à migrer | +| verification-ups-infrastructure | current_value | manual_value | numeric_value | à migrer | +| verification-capacite-stockage | current_value | manual_value | numeric_value | à migrer | +| verification-apt-cacher-ng | elapsed_time | manual_date | complete_date | à migrer | +| verification-icinga2 | elapsed_time | manual_date | complete_date | à migrer | +| verification-keycloak-openldap | elapsed_time | manual_date | complete_date | à migrer | +| verification-nextcloud | elapsed_time | manual_date | complete_date | à migrer | +| verification-mailcow | elapsed_time | manual_date | complete_date | à migrer | +| verification-openvas | elapsed_time | manual_date | complete_date | à migrer | +| verification-journaux-systemes | elapsed_time | manual_date | complete_date | à migrer | + +## 7.17 RESEAU-CHEZLEPRO + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| verification-firewalls | elapsed_time | manual_date | complete_date | à migrer | +| verification-vpn-openvpn | elapsed_time | manual_date | complete_date | à migrer | +| verification-crl-certificats | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser | +| verification-switches | elapsed_time | manual_date | complete_date | à migrer | +| verification-vlans-segmentation | elapsed_time | manual_date | complete_date | à migrer | +| verification-dns | elapsed_time | manual_date | complete_date | à migrer | +| verification-dynamic-dns | elapsed_time | manual_date | complete_date | à migrer | +| audit-regles-firewall | elapsed_time | manual_date | complete_date | à migrer | +| verification-geoblocking | elapsed_time | manual_date | complete_date | à migrer | +| verification-certificats-publics | days_until_due | manual_date | due_date | à migrer | + +## 7.18 SECURITE-CHEZLEPRO + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| verification-mises-a-jour-securite | elapsed_time | manual_date | complete_date | à migrer | +| audit-comptes-acces | elapsed_time | manual_date | complete_date | à migrer | +| verification-ids-snort | elapsed_time | manual_date | complete_date | à migrer | +| revue-pki-privee | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser | +| revue-politiques-securite | elapsed_time | manual_date | complete_date | à migrer | +| verification-sauvegardes-configuration | elapsed_time | manual_date | complete_date | à migrer | + +## 7.19 EXPLOITATION-CHEZLEPRO + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| revision-playbooks-ansible | elapsed_time | manual_date | complete_date | à migrer | +| verification-inventaires-ansible | elapsed_time | manual_date | complete_date | à migrer | +| revision-documentation-technique | elapsed_time | manual_date | complete_date | à migrer | +| verification-procedures-reprise | elapsed_time | manual_date | complete_date | à migrer | +| revue-capacite-ressources | elapsed_time | manual_date | complete_date | à migrer | +| verification-jobs-automatises | elapsed_time | manual_date | complete_date | à migrer | + +## 7.20 OBLIGATIONS-CHEZLEPRO + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| verification-registraire-entreprise | days_until_due | manual_date | due_date | à migrer | +| verification-declarations-taxes | days_until_due | manual_date | due_date | à migrer | +| verification-dossier-comptable-entreprise | elapsed_time | manual_date | complete_date | à migrer | +| verification-facturation-clients | elapsed_time | manual_date | complete_date | à migrer | +| verification-paiements-fournisseurs | elapsed_time | manual_date | complete_date | à migrer | +| verification-contrats-ententes | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser | +| verification-assurances-entreprise | days_until_due | manual_date | due_date | à migrer | +| archivage-documents-entreprise | elapsed_time | manual_date | complete_date | à migrer | + +## 7.21 ACTIFS-CHEZLEPRO + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| revision-inventaire-materiel | elapsed_time | manual_date | complete_date | à migrer | +| verification-amortissables | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser | +| verification-serveurs-et-composants | elapsed_time | manual_date | complete_date | à migrer | +| verification-reserve-pieces | remaining_quantity | manual_value | remaining_quantity | à migrer | +| revision-actifs-transferts | elapsed_time | manual_date | complete_date | à migrer | + +## 7.22 PROJETS + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| revision-life-noc | elapsed_time | manual_date | complete_date | à migrer | +| revision-alliance-boreale | elapsed_time | manual_date | complete_date | à migrer | +| revision-erplibre | elapsed_time | manual_date | complete_date | à migrer | +| revision-semence-numerique | elapsed_time | manual_date | complete_date | à migrer | +| revision-ortrux-1 | elapsed_time | manual_date | complete_date | à migrer | +| revision-district16 | elapsed_time | manual_date | complete_date | à migrer | + +## 7.23 COMMUNAUTE + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| verification-outils-aa | elapsed_time | manual_date | complete_date | à migrer | +| verification-espace-87-16 | elapsed_time | manual_date | complete_date | à migrer | +| verification-salles-visio-groupes | elapsed_time | manual_date | complete_date | à migrer | +| verification-depots-rapports-rsg | elapsed_time | manual_date | complete_date | à migrer | +| revision-outils-communication-communaute | elapsed_time | manual_date | complete_date | à migrer | + +## 7.24 DOCUMENTATION + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| revue-docs-personnelles-importantes | elapsed_time | manual_date | complete_date | à migrer | +| revue-wiki-technique | elapsed_time | manual_date | complete_date | à migrer | +| revue-base-connaissances | elapsed_time | manual_date | complete_date | à migrer | +| verification-sauvegarde-docs-cles | elapsed_time | manual_date | complete_date | à migrer | + +## 7.25 ANIMAUX + +| item_key | méthode cible | intrant | formulaire | statut | +|---|---|---|---|---| +| verification-stock-nourriture-chats | remaining_quantity | manual_value | remaining_quantity | à migrer | +| verification-friandises-et-litiere | remaining_quantity | manual_value | remaining_quantity | à migrer | +| revision-routine-soins-animaux | elapsed_time | manual_date | complete_date | à migrer | + +--- + +## 8. Priorité de migration + +### Vague 1 — tout ce qui est date simple +Objectif : +- finir `elapsed_time` +- finir `days_until_due` + +C’est la vague la plus simple, la plus rentable et la moins risquée. + +### Vague 2 — voiture +Objectif : +- introduire `elapsed_distance` + +### Vague 3 — stocks et réserves +Objectif : +- introduire `remaining_quantity` + +### Vague 4 — mesures techniques +Objectif : +- introduire `current_value` + +--- + +## 9. Pages web cibles + +Chaque item doit converger vers une page web canonique : + +```text +/life-noc/item// +``` + +Cette page doit devenir : + +- la fiche de contexte ; +- la fiche d’instructions ; +- la fiche d’action ; +- la fiche de saisie des intrants. + +### 9.1 Contenu minimal + +- titre humain +- état actuel +- dernier intrant +- résumé +- notes +- instructions +- liens utiles +- formulaire adapté +- action disponible + +### 9.2 Règle sur `instructions_url` + +La page de l’item est la page d’instructions par défaut. + +Règle : + +- si `instructions_url` est explicite, on l’utilise ; +- sinon, on considère implicitement : + +```text +/life-noc/item// +``` + +--- + +## 10. Critère de fin de démockage + +Le démockage d’un item sera considéré terminé quand : + +- l’item possède un `probe` réel ; +- l’item possède un intrant réel ; +- l’item possède une page web opérationnelle ; +- l’item possède un formulaire web adapté ; +- l’item possède une URL canonique stable ; +- l’état Icinga est calculé à partir du vrai store. + +--- + +## 11. Stratégie de mise en œuvre + +### Phase 1 +Finir toutes les migrations `elapsed_time` et `days_until_due`. + +### Phase 2 +Faire évoluer la page item pour afficher : +- `title` +- `summary` +- `instructions` +- formulaire adapté au type d’intrant. + +### Phase 3 +Ajouter les endpoints HTML de mise à jour interactive des intrants. + +### Phase 4 +Introduire successivement : +- `elapsed_distance` +- `remaining_quantity` +- `current_value` + +### Phase 5 +Éliminer complètement les items mockés restants. + +--- + +## 12. Conclusion + +La cible Life-NOC retenue est désormais claire : + +- **tout item doit être réel** +- **tout item doit avoir sa page** +- **tout item doit avoir son URL** +- **tout item doit être renseignable** +- **tout item doit pouvoir guider l’action** + +Ce plan sert de carte de migration pour converger progressivement vers ce modèle. diff --git a/domains.yaml b/domains.yaml index d17b0cc..a42e463 100644 --- a/domains.yaml +++ b/domains.yaml @@ -21,6 +21,7 @@ domains: - name: revue-hebdomadaire-priorites date: '2026-03-10' notes: Revoir les priorités de la semaine + instructions_url: "https://chezlepro.ca/life-noc/test-instructions" probe: type: elapsed_time source: diff --git a/icinga/services/actifs-chezlepro.conf b/icinga/services/actifs-chezlepro.conf index 7277ff0..dd8a163 100644 --- a/icinga/services/actifs-chezlepro.conf +++ b/icinga/services/actifs-chezlepro.conf @@ -11,6 +11,7 @@ apply Service "actifs-chezlepro-revision-inventaire-materiel" { vars.mock_message = "Sous contrôle" groups = [ "ACTIFS-CHEZLEPRO" ] notes = "Revoir l'inventaire du matériel de Chezlepro" + vars.instructions_url = "/life-noc/item/actifs-chezlepro/revision-inventaire-materiel" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "actifs-chezlepro-verification-amortissables" { vars.mock_message = "Sous contrôle" groups = [ "ACTIFS-CHEZLEPRO" ] notes = "Vérifier le registre des biens amortissables" + vars.instructions_url = "/life-noc/item/actifs-chezlepro/verification-amortissables" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "actifs-chezlepro-verification-serveurs-et-composants" { vars.mock_message = "Sous contrôle" groups = [ "ACTIFS-CHEZLEPRO" ] notes = "Vérifier l'état des serveurs et composants matériels" + vars.instructions_url = "/life-noc/item/actifs-chezlepro/verification-serveurs-et-composants" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "actifs-chezlepro-verification-reserve-pieces" { vars.mock_message = "Sous contrôle" groups = [ "ACTIFS-CHEZLEPRO" ] notes = "Vérifier les pièces de rechange critiques" + vars.instructions_url = "/life-noc/item/actifs-chezlepro/verification-reserve-pieces" assign where host.name == "life-noc" } @@ -51,5 +55,6 @@ apply Service "actifs-chezlepro-revision-actifs-transferts" { vars.mock_message = "Sous contrôle" groups = [ "ACTIFS-CHEZLEPRO" ] notes = "Réviser les actifs transférés ou à transférer à l'entreprise" + vars.instructions_url = "/life-noc/item/actifs-chezlepro/revision-actifs-transferts" assign where host.name == "life-noc" } diff --git a/icinga/services/animaux.conf b/icinga/services/animaux.conf index 82160df..90482e0 100644 --- a/icinga/services/animaux.conf +++ b/icinga/services/animaux.conf @@ -11,6 +11,7 @@ apply Service "animaux-verification-stock-nourriture-chats" { vars.mock_message = "Sous contrôle" groups = [ "ANIMAUX" ] notes = "Vérification du stock de nourriture pour chats" + vars.instructions_url = "/life-noc/item/animaux/verification-stock-nourriture-chats" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "animaux-verification-friandises-et-litiere" { vars.mock_message = "Sous contrôle" groups = [ "ANIMAUX" ] notes = "Vérification des stocks de friandises et de litière" + vars.instructions_url = "/life-noc/item/animaux/verification-friandises-et-litiere" assign where host.name == "life-noc" } @@ -31,5 +33,6 @@ apply Service "animaux-revision-routine-soins-animaux" { vars.mock_message = "Sous contrôle" groups = [ "ANIMAUX" ] notes = "Revue de la routine de soins et des besoins matériels des chats" + vars.instructions_url = "/life-noc/item/animaux/revision-routine-soins-animaux" assign where host.name == "life-noc" } diff --git a/icinga/services/communaute.conf b/icinga/services/communaute.conf index fec6ba3..19171cc 100644 --- a/icinga/services/communaute.conf +++ b/icinga/services/communaute.conf @@ -11,6 +11,7 @@ apply Service "communaute-verification-outils-aa" { vars.mock_message = "Sous contrôle" groups = [ "COMMUNAUTE" ] notes = "Vérifier les outils numériques liés aux activités communautaires" + vars.instructions_url = "/life-noc/item/communaute/verification-outils-aa" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "communaute-verification-espace-87-16" { vars.mock_message = "Sous contrôle" groups = [ "COMMUNAUTE" ] notes = "Vérifier l'espace Nextcloud du district" + vars.instructions_url = "/life-noc/item/communaute/verification-espace-87-16" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "communaute-verification-salles-visio-groupes" { vars.mock_message = "Sous contrôle" groups = [ "COMMUNAUTE" ] notes = "Vérifier les salles de visioconférence des groupes" + vars.instructions_url = "/life-noc/item/communaute/verification-salles-visio-groupes" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "communaute-verification-depots-rapports-rsg" { vars.mock_message = "Sous contrôle" groups = [ "COMMUNAUTE" ] notes = "Vérifier les dépôts de rapports RSG" + vars.instructions_url = "/life-noc/item/communaute/verification-depots-rapports-rsg" assign where host.name == "life-noc" } @@ -51,5 +55,6 @@ apply Service "communaute-revision-outils-communication-communaute" { vars.mock_message = "Sous contrôle" groups = [ "COMMUNAUTE" ] notes = "Réviser les moyens de communication communautaires" + vars.instructions_url = "/life-noc/item/communaute/revision-outils-communication-communaute" assign where host.name == "life-noc" } diff --git a/icinga/services/documentation.conf b/icinga/services/documentation.conf index 6c17d4a..4e16ff6 100644 --- a/icinga/services/documentation.conf +++ b/icinga/services/documentation.conf @@ -11,6 +11,7 @@ apply Service "documentation-revue-docs-personnelles-importantes" { vars.mock_message = "Sous contrôle" groups = [ "DOCUMENTATION" ] notes = "Revoir l'organisation des documents personnels importants" + vars.instructions_url = "/life-noc/item/documentation/revue-docs-personnelles-importantes" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "documentation-revue-wiki-technique" { vars.mock_message = "Sous contrôle" groups = [ "DOCUMENTATION" ] notes = "Revoir le wiki technique et sa cohérence" + vars.instructions_url = "/life-noc/item/documentation/revue-wiki-technique" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "documentation-revue-base-connaissances" { vars.mock_message = "Sous contrôle" groups = [ "DOCUMENTATION" ] notes = "Revoir la base de connaissances globale" + vars.instructions_url = "/life-noc/item/documentation/revue-base-connaissances" assign where host.name == "life-noc" } @@ -41,5 +44,6 @@ apply Service "documentation-verification-sauvegarde-docs-cles" { vars.mock_message = "Sous contrôle" groups = [ "DOCUMENTATION" ] notes = "Vérifier la sauvegarde des documents clés" + vars.instructions_url = "/life-noc/item/documentation/verification-sauvegarde-docs-cles" assign where host.name == "life-noc" } diff --git a/icinga/services/energie.conf b/icinga/services/energie.conf index 763d683..8a9d777 100644 --- a/icinga/services/energie.conf +++ b/icinga/services/energie.conf @@ -11,6 +11,7 @@ apply Service "energie-inspection-batteries-lifepo4" { vars.mock_message = "Sous contrôle" groups = [ "ENERGIE" ] notes = "Inspection physique et logique des batteries LiFePO4" + vars.instructions_url = "/life-noc/item/energie/inspection-batteries-lifepo4" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "energie-verification-tension-batteries" { vars.mock_message = "Sous contrôle" groups = [ "ENERGIE" ] notes = "Vérification des tensions et cohérence des batteries" + vars.instructions_url = "/life-noc/item/energie/verification-tension-batteries" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "energie-verification-smartshunt" { vars.mock_message = "Sous contrôle" groups = [ "ENERGIE" ] notes = "Vérification SmartShunt et cohérence du monitoring" + vars.instructions_url = "/life-noc/item/energie/verification-smartshunt" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "energie-verification-cerbo-gx" { vars.mock_message = "Sous contrôle" groups = [ "ENERGIE" ] notes = "Vérification Cerbo GX et remontée des données" + vars.instructions_url = "/life-noc/item/energie/verification-cerbo-gx" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "energie-verification-can-rs485" { vars.mock_message = "Sous contrôle" groups = [ "ENERGIE" ] notes = "Vérification des communications CAN ou RS485" + vars.instructions_url = "/life-noc/item/energie/verification-can-rs485" assign where host.name == "life-noc" } @@ -61,6 +66,7 @@ apply Service "energie-inspection-cablage-energie" { vars.mock_message = "Sous contrôle" groups = [ "ENERGIE" ] notes = "Inspection du câblage et des connexions énergétiques" + vars.instructions_url = "/life-noc/item/energie/inspection-cablage-energie" assign where host.name == "life-noc" } @@ -71,6 +77,7 @@ apply Service "energie-verification-busbars" { vars.mock_message = "Sous contrôle" groups = [ "ENERGIE" ] notes = "Vérification du serrage, corrosion et échauffement des busbars" + vars.instructions_url = "/life-noc/item/energie/verification-busbars" assign where host.name == "life-noc" } @@ -81,6 +88,7 @@ apply Service "energie-verification-parametres-charge" { vars.mock_message = "Sous contrôle" groups = [ "ENERGIE" ] notes = "Vérification des paramètres de charge et float" + vars.instructions_url = "/life-noc/item/energie/verification-parametres-charge" assign where host.name == "life-noc" } @@ -91,6 +99,7 @@ apply Service "energie-test-charge-batteries" { vars.mock_message = "Sous contrôle" groups = [ "ENERGIE" ] notes = "Test de comportement en charge des batteries" + vars.instructions_url = "/life-noc/item/energie/test-charge-batteries" assign where host.name == "life-noc" } @@ -101,6 +110,7 @@ apply Service "energie-test-comportement-onduleur" { vars.mock_message = "Sous contrôle" groups = [ "ENERGIE" ] notes = "Vérifier le comportement du Multiplus sous charge légère" + vars.instructions_url = "/life-noc/item/energie/test-comportement-onduleur" assign where host.name == "life-noc" } @@ -111,5 +121,6 @@ apply Service "energie-verification-journal-evenements-victron" { vars.mock_message = "Sous contrôle" groups = [ "ENERGIE" ] notes = "Vérification des événements et anomalies Victron" + vars.instructions_url = "/life-noc/item/energie/verification-journal-evenements-victron" assign where host.name == "life-noc" } diff --git a/icinga/services/exploitation-chezlepro.conf b/icinga/services/exploitation-chezlepro.conf index 3240348..aedb773 100644 --- a/icinga/services/exploitation-chezlepro.conf +++ b/icinga/services/exploitation-chezlepro.conf @@ -11,6 +11,7 @@ apply Service "exploitation-chezlepro-revision-playbooks-ansible" { vars.mock_message = "Sous contrôle" groups = [ "EXPLOITATION-CHEZLEPRO" ] notes = "Revue des playbooks Ansible" + vars.instructions_url = "/life-noc/item/exploitation-chezlepro/revision-playbooks-ansible" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "exploitation-chezlepro-verification-inventaires-ansible" { vars.mock_message = "Sous contrôle" groups = [ "EXPLOITATION-CHEZLEPRO" ] notes = "Vérification des inventaires Ansible" + vars.instructions_url = "/life-noc/item/exploitation-chezlepro/verification-inventaires-ansible" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "exploitation-chezlepro-revision-documentation-technique" { vars.mock_message = "Sous contrôle" groups = [ "EXPLOITATION-CHEZLEPRO" ] notes = "Revue de la documentation technique Chezlepro" + vars.instructions_url = "/life-noc/item/exploitation-chezlepro/revision-documentation-technique" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "exploitation-chezlepro-verification-procedures-reprise" { vars.mock_message = "Sous contrôle" groups = [ "EXPLOITATION-CHEZLEPRO" ] notes = "Vérifier les procédures de reprise et restauration" + vars.instructions_url = "/life-noc/item/exploitation-chezlepro/verification-procedures-reprise" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "exploitation-chezlepro-revue-capacite-ressources" { vars.mock_message = "Sous contrôle" groups = [ "EXPLOITATION-CHEZLEPRO" ] notes = "Revue CPU RAM stockage et charge de l'infrastructure" + vars.instructions_url = "/life-noc/item/exploitation-chezlepro/revue-capacite-ressources" assign where host.name == "life-noc" } @@ -61,5 +66,6 @@ apply Service "exploitation-chezlepro-verification-jobs-automatises" { vars.mock_message = "Sous contrôle" groups = [ "EXPLOITATION-CHEZLEPRO" ] notes = "Vérification des jobs automatisés et scripts périodiques" + vars.instructions_url = "/life-noc/item/exploitation-chezlepro/verification-jobs-automatises" assign where host.name == "life-noc" } diff --git a/icinga/services/finances-personnelles.conf b/icinga/services/finances-personnelles.conf index 03f3be9..138ad58 100644 --- a/icinga/services/finances-personnelles.conf +++ b/icinga/services/finances-personnelles.conf @@ -11,6 +11,7 @@ apply Service "finances-personnelles-revision-comptes-bancaires" { vars.mock_message = "Sous contrôle" groups = [ "FINANCES-PERSONNELLES" ] notes = "Révision mensuelle des comptes bancaires personnels" + vars.instructions_url = "/life-noc/item/finances-personnelles/revision-comptes-bancaires" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "finances-personnelles-verification-cartes-credit" { vars.mock_message = "Sous contrôle" groups = [ "FINANCES-PERSONNELLES" ] notes = "Vérification des soldes et transactions des cartes de crédit" + vars.instructions_url = "/life-noc/item/finances-personnelles/verification-cartes-credit" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "finances-personnelles-paiement-cartes-credit" { vars.mock_message = "Sous contrôle" groups = [ "FINANCES-PERSONNELLES" ] notes = "Paiement des cartes de crédit personnelles" + vars.instructions_url = "/life-noc/item/finances-personnelles/paiement-cartes-credit" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "finances-personnelles-revision-budget-personnel" { vars.mock_message = "Sous contrôle" groups = [ "FINANCES-PERSONNELLES" ] notes = "Révision mensuelle du budget personnel" + vars.instructions_url = "/life-noc/item/finances-personnelles/revision-budget-personnel" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "finances-personnelles-verification-prelevements-automatiques" { vars.mock_message = "Sous contrôle" groups = [ "FINANCES-PERSONNELLES" ] notes = "Vérification des prélèvements automatiques personnels" + vars.instructions_url = "/life-noc/item/finances-personnelles/verification-prelevements-automatiques" assign where host.name == "life-noc" } @@ -61,6 +66,7 @@ apply Service "finances-personnelles-verification-placements" { vars.mock_message = "Sous contrôle" groups = [ "FINANCES-PERSONNELLES" ] notes = "Vérification périodique des placements et comptes enregistrés" + vars.instructions_url = "/life-noc/item/finances-personnelles/verification-placements" assign where host.name == "life-noc" } @@ -71,6 +77,7 @@ apply Service "finances-personnelles-verification-cotisations-reer" { vars.mock_message = "Sous contrôle" groups = [ "FINANCES-PERSONNELLES" ] notes = "Vérification des cotisations REER" + vars.instructions_url = "/life-noc/item/finances-personnelles/verification-cotisations-reer" assign where host.name == "life-noc" } @@ -81,6 +88,7 @@ apply Service "finances-personnelles-verification-celi" { vars.mock_message = "Sous contrôle" groups = [ "FINANCES-PERSONNELLES" ] notes = "Vérification de l'espace CELI et de son utilisation" + vars.instructions_url = "/life-noc/item/finances-personnelles/verification-celi" assign where host.name == "life-noc" } @@ -91,5 +99,6 @@ apply Service "finances-personnelles-preparation-dossier-financier-annuel" { vars.mock_message = "Sous contrôle" groups = [ "FINANCES-PERSONNELLES" ] notes = "Préparer le dossier financier annuel personnel" + vars.instructions_url = "/life-noc/item/finances-personnelles/preparation-dossier-financier-annuel" assign where host.name == "life-noc" } diff --git a/icinga/services/fiscalite-personnelle.conf b/icinga/services/fiscalite-personnelle.conf index 66f5a52..135db7b 100644 --- a/icinga/services/fiscalite-personnelle.conf +++ b/icinga/services/fiscalite-personnelle.conf @@ -11,6 +11,7 @@ apply Service "fiscalite-personnelle-preparation-documents-impots" { vars.mock_message = "Sous contrôle" groups = [ "FISCALITE-PERSONNELLE" ] notes = "Rassembler tous les documents fiscaux personnels" + vars.instructions_url = "/life-noc/item/fiscalite-personnelle/preparation-documents-impots" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "fiscalite-personnelle-production-impots-federal" { vars.mock_message = "Sous contrôle" groups = [ "FISCALITE-PERSONNELLE" ] notes = "Produire la déclaration de revenus fédérale" + vars.instructions_url = "/life-noc/item/fiscalite-personnelle/production-impots-federal" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "fiscalite-personnelle-production-impots-quebec" { vars.mock_message = "Sous contrôle" groups = [ "FISCALITE-PERSONNELLE" ] notes = "Produire la déclaration de revenus Québec" + vars.instructions_url = "/life-noc/item/fiscalite-personnelle/production-impots-quebec" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "fiscalite-personnelle-paiement-solde-impots" { vars.mock_message = "Sous contrôle" groups = [ "FISCALITE-PERSONNELLE" ] notes = "Payer tout solde d'impôt exigible" + vars.instructions_url = "/life-noc/item/fiscalite-personnelle/paiement-solde-impots" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "fiscalite-personnelle-verification-avis-cotisation" { vars.mock_message = "Sous contrôle" groups = [ "FISCALITE-PERSONNELLE" ] notes = "Vérifier les avis de cotisation ARC et Revenu Québec" + vars.instructions_url = "/life-noc/item/fiscalite-personnelle/verification-avis-cotisation" assign where host.name == "life-noc" } @@ -61,6 +66,7 @@ apply Service "fiscalite-personnelle-archivage-documents-fiscaux" { vars.mock_message = "Sous contrôle" groups = [ "FISCALITE-PERSONNELLE" ] notes = "Archiver les documents fiscaux personnels" + vars.instructions_url = "/life-noc/item/fiscalite-personnelle/archivage-documents-fiscaux" assign where host.name == "life-noc" } @@ -71,5 +77,6 @@ apply Service "fiscalite-personnelle-verification-acomptes-provisionnels" { vars.mock_message = "Sous contrôle" groups = [ "FISCALITE-PERSONNELLE" ] notes = "Vérifier si des acomptes provisionnels sont requis" + vars.instructions_url = "/life-noc/item/fiscalite-personnelle/verification-acomptes-provisionnels" assign where host.name == "life-noc" } diff --git a/icinga/services/focus.conf b/icinga/services/focus.conf index ac5137b..4037af6 100644 --- a/icinga/services/focus.conf +++ b/icinga/services/focus.conf @@ -11,6 +11,7 @@ apply Service "focus-entretien-processus-focus" { vars.mock_message = "Sous contrôle" groups = [ "FOCUS" ] notes = "Vérifier que les cartouches FOCUS sont à jour" + vars.instructions_url = "/life-noc/item/focus/entretien-processus-focus" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "focus-nettoyage-inbox-mentale" { vars.mock_message = "Sous contrôle" groups = [ "FOCUS" ] notes = "Vider et clarifier les éléments capturés hors système" + vars.instructions_url = "/life-noc/item/focus/nettoyage-inbox-mentale" assign where host.name == "life-noc" } @@ -31,5 +33,6 @@ apply Service "focus-revue-limitation-engagements" { vars.mock_message = "Sous contrôle" groups = [ "FOCUS" ] notes = "Vérifier que les engagements actifs demeurent réalistes" + vars.instructions_url = "/life-noc/item/focus/revue-limitation-engagements" assign where host.name == "life-noc" } diff --git a/icinga/services/garage-et-rangement.conf b/icinga/services/garage-et-rangement.conf index be78f07..5fa2205 100644 --- a/icinga/services/garage-et-rangement.conf +++ b/icinga/services/garage-et-rangement.conf @@ -11,6 +11,7 @@ apply Service "garage-et-rangement-inspection-rangement-plafond" { vars.mock_message = "Sous contrôle" groups = [ "GARAGE-ET-RANGEMENT" ] notes = "Vérification des supports suspendus au plafond du garage" + vars.instructions_url = "/life-noc/item/garage-et-rangement/inspection-rangement-plafond" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "garage-et-rangement-revue-inventaire-garage" { vars.mock_message = "Sous contrôle" groups = [ "GARAGE-ET-RANGEMENT" ] notes = "Revue de l'inventaire et de l'ordre du garage" + vars.instructions_url = "/life-noc/item/garage-et-rangement/revue-inventaire-garage" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "garage-et-rangement-verification-rouille-outillage" { vars.mock_message = "Sous contrôle" groups = [ "GARAGE-ET-RANGEMENT" ] notes = "Vérification de la corrosion sur les outils et équipements" + vars.instructions_url = "/life-noc/item/garage-et-rangement/verification-rouille-outillage" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "garage-et-rangement-inspection-securite-garage" { vars.mock_message = "Sous contrôle" groups = [ "GARAGE-ET-RANGEMENT" ] notes = "Vérification des conditions de sécurité du garage" + vars.instructions_url = "/life-noc/item/garage-et-rangement/inspection-securite-garage" assign where host.name == "life-noc" } @@ -51,5 +55,6 @@ apply Service "garage-et-rangement-rotation-stockage-bacs" { vars.mock_message = "Sous contrôle" groups = [ "GARAGE-ET-RANGEMENT" ] notes = "Revue des contenus de bacs de rangement" + vars.instructions_url = "/life-noc/item/garage-et-rangement/rotation-stockage-bacs" assign where host.name == "life-noc" } diff --git a/icinga/services/informatique-personnelle.conf b/icinga/services/informatique-personnelle.conf index 4b1d56e..b0c05be 100644 --- a/icinga/services/informatique-personnelle.conf +++ b/icinga/services/informatique-personnelle.conf @@ -11,6 +11,7 @@ apply Service "informatique-personnelle-verification-sauvegardes-personnelles" { vars.mock_message = "Sous contrôle" groups = [ "INFORMATIQUE-PERSONNELLE" ] notes = "Vérification des sauvegardes personnelles" + vars.instructions_url = "/life-noc/item/informatique-personnelle/verification-sauvegardes-personnelles" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "informatique-personnelle-nettoyage-stockage-personnel" { vars.mock_message = "Sous contrôle" groups = [ "INFORMATIQUE-PERSONNELLE" ] notes = "Nettoyage du stockage numérique personnel" + vars.instructions_url = "/life-noc/item/informatique-personnelle/nettoyage-stockage-personnel" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "informatique-personnelle-verification-comptes-importants" { vars.mock_message = "Sous contrôle" groups = [ "INFORMATIQUE-PERSONNELLE" ] notes = "Vérification des comptes numériques personnels importants" + vars.instructions_url = "/life-noc/item/informatique-personnelle/verification-comptes-importants" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "informatique-personnelle-revue-mots-de-passe-personnels" { vars.mock_message = "Sous contrôle" groups = [ "INFORMATIQUE-PERSONNELLE" ] notes = "Revue des mots de passe personnels critiques" + vars.instructions_url = "/life-noc/item/informatique-personnelle/revue-mots-de-passe-personnels" assign where host.name == "life-noc" } @@ -51,5 +55,6 @@ apply Service "informatique-personnelle-verification-documents-cloud-personnels" vars.mock_message = "Sous contrôle" groups = [ "INFORMATIQUE-PERSONNELLE" ] notes = "Vérifier la cohérence des documents personnels sauvegardés" + vars.instructions_url = "/life-noc/item/informatique-personnelle/verification-documents-cloud-personnels" assign where host.name == "life-noc" } diff --git a/icinga/services/infrastructure-chezlepro.conf b/icinga/services/infrastructure-chezlepro.conf index fe0e7ed..813bd88 100644 --- a/icinga/services/infrastructure-chezlepro.conf +++ b/icinga/services/infrastructure-chezlepro.conf @@ -11,6 +11,7 @@ apply Service "infrastructure-chezlepro-verification-proxmox" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Vérification générale de l'environnement Proxmox" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-proxmox" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "infrastructure-chezlepro-verification-ceph" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Vérification générale de l'état Ceph" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-ceph" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "infrastructure-chezlepro-test-restauration-backups" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Test de restauration des sauvegardes" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/test-restauration-backups" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "infrastructure-chezlepro-verification-pbs-truenas" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Vérification des solutions de sauvegarde et synchronisation" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-pbs-truenas" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "infrastructure-chezlepro-verification-ups-infrastructure" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Vérification des UPS de l'infrastructure" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-ups-infrastructure" assign where host.name == "life-noc" } @@ -61,6 +66,7 @@ apply Service "infrastructure-chezlepro-verification-capacite-stockage" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Vérifier capacité et croissance du stockage" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-capacite-stockage" assign where host.name == "life-noc" } @@ -71,6 +77,7 @@ apply Service "infrastructure-chezlepro-verification-apt-cacher-ng" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Vérification du bon fonctionnement d'apt-cacher-ng" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-apt-cacher-ng" assign where host.name == "life-noc" } @@ -81,6 +88,7 @@ apply Service "infrastructure-chezlepro-verification-icinga2" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Vérification de la supervision Icinga2" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-icinga2" assign where host.name == "life-noc" } @@ -91,6 +99,7 @@ apply Service "infrastructure-chezlepro-verification-keycloak-openldap" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Vérification de l'identité et de l'authentification centralisées" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-keycloak-openldap" assign where host.name == "life-noc" } @@ -101,6 +110,7 @@ apply Service "infrastructure-chezlepro-verification-nextcloud" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Vérification de l'état Nextcloud" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-nextcloud" assign where host.name == "life-noc" } @@ -111,6 +121,7 @@ apply Service "infrastructure-chezlepro-verification-mailcow" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Vérification de l'infrastructure Mailcow" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-mailcow" assign where host.name == "life-noc" } @@ -121,6 +132,7 @@ apply Service "infrastructure-chezlepro-verification-openvas" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Vérification de la plateforme OpenVAS" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-openvas" assign where host.name == "life-noc" } @@ -131,5 +143,6 @@ apply Service "infrastructure-chezlepro-verification-journaux-systemes" { vars.mock_message = "Sous contrôle" groups = [ "INFRASTRUCTURE-CHEZLEPRO" ] notes = "Revue des journaux systèmes critiques" + vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-journaux-systemes" assign where host.name == "life-noc" } diff --git a/icinga/services/jardin.conf b/icinga/services/jardin.conf index fc2341a..7240a23 100644 --- a/icinga/services/jardin.conf +++ b/icinga/services/jardin.conf @@ -11,6 +11,7 @@ apply Service "jardin-inspection-vignes" { vars.mock_message = "Sous contrôle" groups = [ "JARDIN" ] notes = "Inspection et entretien des vignes" + vars.instructions_url = "/life-noc/item/jardin/inspection-vignes" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "jardin-entretien-bleuetiers" { vars.mock_message = "Sous contrôle" groups = [ "JARDIN" ] notes = "Entretien des bleuetiers" + vars.instructions_url = "/life-noc/item/jardin/entretien-bleuetiers" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "jardin-entretien-framboisiers" { vars.mock_message = "Sous contrôle" groups = [ "JARDIN" ] notes = "Entretien des framboisiers" + vars.instructions_url = "/life-noc/item/jardin/entretien-framboisiers" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "jardin-inspection-kiwis-nordiques" { vars.mock_message = "Sous contrôle" groups = [ "JARDIN" ] notes = "Vérification des kiwis nordiques" + vars.instructions_url = "/life-noc/item/jardin/inspection-kiwis-nordiques" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "jardin-entretien-rhubarbe" { vars.mock_message = "Sous contrôle" groups = [ "JARDIN" ] notes = "Entretien de la rhubarbe" + vars.instructions_url = "/life-noc/item/jardin/entretien-rhubarbe" assign where host.name == "life-noc" } @@ -61,6 +66,7 @@ apply Service "jardin-inspection-groseillier" { vars.mock_message = "Sous contrôle" groups = [ "JARDIN" ] notes = "Inspection du groseillier" + vars.instructions_url = "/life-noc/item/jardin/inspection-groseillier" assign where host.name == "life-noc" } @@ -71,6 +77,7 @@ apply Service "jardin-inspection-muriers" { vars.mock_message = "Sous contrôle" groups = [ "JARDIN" ] notes = "Inspection des mûriers" + vars.instructions_url = "/life-noc/item/jardin/inspection-muriers" assign where host.name == "life-noc" } @@ -81,6 +88,7 @@ apply Service "jardin-entretien-houblon" { vars.mock_message = "Sous contrôle" groups = [ "JARDIN" ] notes = "Vérification et entretien du houblon" + vars.instructions_url = "/life-noc/item/jardin/entretien-houblon" assign where host.name == "life-noc" } @@ -91,6 +99,7 @@ apply Service "jardin-revision-pharmacopée-jardin" { vars.mock_message = "Sous contrôle" groups = [ "JARDIN" ] notes = "Vérification du stock de soins pour végétaux" + vars.instructions_url = "/life-noc/item/jardin/revision-pharmacopée-jardin" assign where host.name == "life-noc" } @@ -101,5 +110,6 @@ apply Service "jardin-revision-armoire-produits-jardin" { vars.mock_message = "Sous contrôle" groups = [ "JARDIN" ] notes = "Vérification et ordre de l'armoire des produits pour végétaux" + vars.instructions_url = "/life-noc/item/jardin/revision-armoire-produits-jardin" assign where host.name == "life-noc" } diff --git a/icinga/services/maison.conf b/icinga/services/maison.conf index ad4c998..1d2a81b 100644 --- a/icinga/services/maison.conf +++ b/icinga/services/maison.conf @@ -11,6 +11,7 @@ apply Service "maison-remplacement-filtre-fournaise" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Remplacer le filtre de la fournaise" + vars.instructions_url = "/life-noc/item/maison/remplacement-filtre-fournaise" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "maison-inspection-fournaise" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Inspection générale de la fournaise avant saison froide" + vars.instructions_url = "/life-noc/item/maison/inspection-fournaise" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "maison-inspection-thermopompe" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Inspection et entretien de la thermopompe" + vars.instructions_url = "/life-noc/item/maison/inspection-thermopompe" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "maison-nettoyage-unites-exterieures" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Nettoyer les unités extérieures et vérifier le dégagement" + vars.instructions_url = "/life-noc/item/maison/nettoyage-unites-exterieures" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "maison-inspection-toiture" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Inspection annuelle de la toiture" + vars.instructions_url = "/life-noc/item/maison/inspection-toiture" assign where host.name == "life-noc" } @@ -61,6 +66,7 @@ apply Service "maison-inspection-gouttieres" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Inspection et nettoyage des gouttières" + vars.instructions_url = "/life-noc/item/maison/inspection-gouttieres" assign where host.name == "life-noc" } @@ -71,6 +77,7 @@ apply Service "maison-inspection-fondation" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Inspection de la fondation et des fissures" + vars.instructions_url = "/life-noc/item/maison/inspection-fondation" assign where host.name == "life-noc" } @@ -81,6 +88,7 @@ apply Service "maison-verification-drainage-terrain" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Vérification du drainage autour de la maison" + vars.instructions_url = "/life-noc/item/maison/verification-drainage-terrain" assign where host.name == "life-noc" } @@ -91,6 +99,7 @@ apply Service "maison-test-detecteurs-fumee" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Test des détecteurs de fumée" + vars.instructions_url = "/life-noc/item/maison/test-detecteurs-fumee" assign where host.name == "life-noc" } @@ -101,6 +110,7 @@ apply Service "maison-test-detecteurs-co" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Test des détecteurs de monoxyde de carbone" + vars.instructions_url = "/life-noc/item/maison/test-detecteurs-co" assign where host.name == "life-noc" } @@ -111,6 +121,7 @@ apply Service "maison-remplacement-piles-detecteurs" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Remplacement préventif des piles des détecteurs" + vars.instructions_url = "/life-noc/item/maison/remplacement-piles-detecteurs" assign where host.name == "life-noc" } @@ -121,6 +132,7 @@ apply Service "maison-verification-plomberie-visible" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Vérification visuelle de la plomberie accessible" + vars.instructions_url = "/life-noc/item/maison/verification-plomberie-visible" assign where host.name == "life-noc" } @@ -131,6 +143,7 @@ apply Service "maison-inspection-sump-pump" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Vérification de la pompe de puisard si applicable" + vars.instructions_url = "/life-noc/item/maison/inspection-sump-pump" assign where host.name == "life-noc" } @@ -141,6 +154,7 @@ apply Service "maison-verification-calfetrage" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Vérification du calfeutrage portes et fenêtres" + vars.instructions_url = "/life-noc/item/maison/verification-calfetrage" assign where host.name == "life-noc" } @@ -151,6 +165,7 @@ apply Service "maison-inspection-portes-garage" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Vérification mécanique et sécurité des portes de garage" + vars.instructions_url = "/life-noc/item/maison/inspection-portes-garage" assign where host.name == "life-noc" } @@ -161,5 +176,6 @@ apply Service "maison-verification-extincteurs" { vars.mock_message = "Sous contrôle" groups = [ "MAISON" ] notes = "Vérification des extincteurs" + vars.instructions_url = "/life-noc/item/maison/verification-extincteurs" assign where host.name == "life-noc" } diff --git a/icinga/services/obligations-chezlepro.conf b/icinga/services/obligations-chezlepro.conf index 0bec604..22b50f0 100644 --- a/icinga/services/obligations-chezlepro.conf +++ b/icinga/services/obligations-chezlepro.conf @@ -11,6 +11,7 @@ apply Service "obligations-chezlepro-verification-registraire-entreprise" { vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-CHEZLEPRO" ] notes = "Vérifier les obligations au Registraire des entreprises" + vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-registraire-entreprise" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "obligations-chezlepro-verification-declarations-taxes" { vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-CHEZLEPRO" ] notes = "Vérifier les obligations de taxes de l'entreprise si applicables" + vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-declarations-taxes" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "obligations-chezlepro-verification-dossier-comptable-entreprise" vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-CHEZLEPRO" ] notes = "Vérifier l'ordre du dossier comptable Chezlepro" + vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-dossier-comptable-entreprise" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "obligations-chezlepro-verification-facturation-clients" { vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-CHEZLEPRO" ] notes = "Vérifier la facturation et son suivi" + vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-facturation-clients" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "obligations-chezlepro-verification-paiements-fournisseurs" { vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-CHEZLEPRO" ] notes = "Vérifier les paiements fournisseurs" + vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-paiements-fournisseurs" assign where host.name == "life-noc" } @@ -61,6 +66,7 @@ apply Service "obligations-chezlepro-verification-contrats-ententes" { vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-CHEZLEPRO" ] notes = "Vérifier contrats, ententes et obligations associées" + vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-contrats-ententes" assign where host.name == "life-noc" } @@ -71,6 +77,7 @@ apply Service "obligations-chezlepro-verification-assurances-entreprise" { vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-CHEZLEPRO" ] notes = "Vérifier les assurances de l'entreprise" + vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-assurances-entreprise" assign where host.name == "life-noc" } @@ -81,5 +88,6 @@ apply Service "obligations-chezlepro-archivage-documents-entreprise" { vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-CHEZLEPRO" ] notes = "Archiver les documents administratifs et financiers de l'entreprise" + vars.instructions_url = "/life-noc/item/obligations-chezlepro/archivage-documents-entreprise" assign where host.name == "life-noc" } diff --git a/icinga/services/obligations-legales-personnelles.conf b/icinga/services/obligations-legales-personnelles.conf index 55ae0a7..f63322f 100644 --- a/icinga/services/obligations-legales-personnelles.conf +++ b/icinga/services/obligations-legales-personnelles.conf @@ -11,6 +11,7 @@ apply Service "obligations-legales-personnelles-verification-testament" { vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-LEGALES-PERSONNELLES" ] notes = "Vérifier la pertinence et l'actualité du testament" + vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/verification-testament" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "obligations-legales-personnelles-verification-mandat-inaptitude" vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-LEGALES-PERSONNELLES" ] notes = "Vérifier le mandat de protection et documents connexes" + vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/verification-mandat-inaptitude" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "obligations-legales-personnelles-verification-directives-medicale vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-LEGALES-PERSONNELLES" ] notes = "Vérifier les directives médicales anticipées" + vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/verification-directives-medicales" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "obligations-legales-personnelles-verification-papiers-identite" { vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-LEGALES-PERSONNELLES" ] notes = "Vérifier l'expiration des papiers d'identité" + vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/verification-papiers-identite" assign where host.name == "life-noc" } @@ -60,6 +64,7 @@ apply Service "obligations-legales-personnelles-renouvellement-permis-conduire" vars.life_noc_threshold_critical_lt = "7" vars.life_noc_on_error = "critical" notes = "Vérifier la proximité de l’échéance du permis" + vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/renouvellement-permis-conduire" assign where host.name == "life-noc" } @@ -70,6 +75,7 @@ apply Service "obligations-legales-personnelles-verification-carte-assurance-mal vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-LEGALES-PERSONNELLES" ] notes = "Vérifier l'expiration de la carte d'assurance maladie" + vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/verification-carte-assurance-maladie" assign where host.name == "life-noc" } @@ -80,5 +86,6 @@ apply Service "obligations-legales-personnelles-verification-passeport" { vars.mock_message = "Sous contrôle" groups = [ "OBLIGATIONS-LEGALES-PERSONNELLES" ] notes = "Vérifier l'expiration du passeport" + vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/verification-passeport" assign where host.name == "life-noc" } diff --git a/icinga/services/outils-et-equipements.conf b/icinga/services/outils-et-equipements.conf index 1009e93..baa2150 100644 --- a/icinga/services/outils-et-equipements.conf +++ b/icinga/services/outils-et-equipements.conf @@ -11,6 +11,7 @@ apply Service "outils-et-equipements-entretien-outils-jardin" { vars.mock_message = "Sous contrôle" groups = [ "OUTILS-ET-EQUIPEMENTS" ] notes = "Nettoyage et entretien des outils de jardin" + vars.instructions_url = "/life-noc/item/outils-et-equipements/entretien-outils-jardin" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "outils-et-equipements-inspection-outils-electriques" { vars.mock_message = "Sous contrôle" groups = [ "OUTILS-ET-EQUIPEMENTS" ] notes = "Vérification des outils électriques" + vars.instructions_url = "/life-noc/item/outils-et-equipements/inspection-outils-electriques" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "outils-et-equipements-verification-rallonges-et-connecteurs" { vars.mock_message = "Sous contrôle" groups = [ "OUTILS-ET-EQUIPEMENTS" ] notes = "Vérification des rallonges et connecteurs" + vars.instructions_url = "/life-noc/item/outils-et-equipements/verification-rallonges-et-connecteurs" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "outils-et-equipements-verification-compresseur-et-accessoires" { vars.mock_message = "Sous contrôle" groups = [ "OUTILS-ET-EQUIPEMENTS" ] notes = "Vérification du compresseur et accessoires" + vars.instructions_url = "/life-noc/item/outils-et-equipements/verification-compresseur-et-accessoires" assign where host.name == "life-noc" } @@ -51,5 +55,6 @@ apply Service "outils-et-equipements-revision-inventaire-outillage" { vars.mock_message = "Sous contrôle" groups = [ "OUTILS-ET-EQUIPEMENTS" ] notes = "Revue de l'inventaire d'outillage" + vars.instructions_url = "/life-noc/item/outils-et-equipements/revision-inventaire-outillage" assign where host.name == "life-noc" } diff --git a/icinga/services/projets.conf b/icinga/services/projets.conf index 25b9ba8..d163de8 100644 --- a/icinga/services/projets.conf +++ b/icinga/services/projets.conf @@ -11,6 +11,7 @@ apply Service "projets-revision-life-noc" { vars.mock_message = "Sous contrôle" groups = [ "PROJETS" ] notes = "Révision du projet Life-NOC" + vars.instructions_url = "/life-noc/item/projets/revision-life-noc" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "projets-revision-alliance-boreale" { vars.mock_message = "Sous contrôle" groups = [ "PROJETS" ] notes = "Révision du projet Alliance Boréale" + vars.instructions_url = "/life-noc/item/projets/revision-alliance-boreale" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "projets-revision-erplibre" { vars.mock_message = "Sous contrôle" groups = [ "PROJETS" ] notes = "Révision du projet ERPLibre" + vars.instructions_url = "/life-noc/item/projets/revision-erplibre" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "projets-revision-semence-numerique" { vars.mock_message = "Sous contrôle" groups = [ "PROJETS" ] notes = "Révision du projet semence numérique" + vars.instructions_url = "/life-noc/item/projets/revision-semence-numerique" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "projets-revision-ortrux-1" { vars.mock_message = "Sous contrôle" groups = [ "PROJETS" ] notes = "Révision du système Ortrux-1" + vars.instructions_url = "/life-noc/item/projets/revision-ortrux-1" assign where host.name == "life-noc" } @@ -61,5 +66,6 @@ apply Service "projets-revision-district16" { vars.mock_message = "Sous contrôle" groups = [ "PROJETS" ] notes = "Révision du projet DISTRICT16" + vars.instructions_url = "/life-noc/item/projets/revision-district16" assign where host.name == "life-noc" } diff --git a/icinga/services/reseau-chezlepro.conf b/icinga/services/reseau-chezlepro.conf index 8831478..18a32bb 100644 --- a/icinga/services/reseau-chezlepro.conf +++ b/icinga/services/reseau-chezlepro.conf @@ -11,6 +11,7 @@ apply Service "reseau-chezlepro-verification-firewalls" { vars.mock_message = "Sous contrôle" groups = [ "RESEAU-CHEZLEPRO" ] notes = "Vérification des firewalls" + vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-firewalls" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "reseau-chezlepro-verification-vpn-openvpn" { vars.mock_message = "Sous contrôle" groups = [ "RESEAU-CHEZLEPRO" ] notes = "Vérification des tunnels OpenVPN" + vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-vpn-openvpn" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "reseau-chezlepro-verification-crl-certificats" { vars.mock_message = "Sous contrôle" groups = [ "RESEAU-CHEZLEPRO" ] notes = "Vérification de la CRL et des certificats" + vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-crl-certificats" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "reseau-chezlepro-verification-switches" { vars.mock_message = "Sous contrôle" groups = [ "RESEAU-CHEZLEPRO" ] notes = "Vérification des switches et uplinks" + vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-switches" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "reseau-chezlepro-verification-vlans-segmentation" { vars.mock_message = "Sous contrôle" groups = [ "RESEAU-CHEZLEPRO" ] notes = "Vérification de la segmentation réseau" + vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-vlans-segmentation" assign where host.name == "life-noc" } @@ -61,6 +66,7 @@ apply Service "reseau-chezlepro-verification-dns" { vars.mock_message = "Sous contrôle" groups = [ "RESEAU-CHEZLEPRO" ] notes = "Vérification DNS interne et externe" + vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-dns" assign where host.name == "life-noc" } @@ -71,6 +77,7 @@ apply Service "reseau-chezlepro-verification-dynamic-dns" { vars.mock_message = "Sous contrôle" groups = [ "RESEAU-CHEZLEPRO" ] notes = "Vérification du DNS dynamique si utilisé" + vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-dynamic-dns" assign where host.name == "life-noc" } @@ -81,6 +88,7 @@ apply Service "reseau-chezlepro-audit-regles-firewall" { vars.mock_message = "Sous contrôle" groups = [ "RESEAU-CHEZLEPRO" ] notes = "Audit périodique des règles de firewall" + vars.instructions_url = "/life-noc/item/reseau-chezlepro/audit-regles-firewall" assign where host.name == "life-noc" } @@ -91,6 +99,7 @@ apply Service "reseau-chezlepro-verification-geoblocking" { vars.mock_message = "Sous contrôle" groups = [ "RESEAU-CHEZLEPRO" ] notes = "Vérifier le geoblocking et son effet attendu" + vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-geoblocking" assign where host.name == "life-noc" } @@ -101,5 +110,6 @@ apply Service "reseau-chezlepro-verification-certificats-publics" { vars.mock_message = "Sous contrôle" groups = [ "RESEAU-CHEZLEPRO" ] notes = "Vérifier les expirations de certificats publics" + vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-certificats-publics" assign where host.name == "life-noc" } diff --git a/icinga/services/resilience.conf b/icinga/services/resilience.conf index 0875dca..f291e18 100644 --- a/icinga/services/resilience.conf +++ b/icinga/services/resilience.conf @@ -11,6 +11,7 @@ apply Service "resilience-test-generatrice-propane" { vars.mock_message = "Sous contrôle" groups = [ "RESILIENCE" ] notes = "Test mensuel de la génératrice au propane" + vars.instructions_url = "/life-noc/item/resilience/test-generatrice-propane" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "resilience-verification-carburant-generatrice" { vars.mock_message = "Sous contrôle" groups = [ "RESILIENCE" ] notes = "Vérification de l'approvisionnement propane lié à la génératrice" + vars.instructions_url = "/life-noc/item/resilience/verification-carburant-generatrice" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "resilience-verification-procedure-bascule" { vars.mock_message = "Sous contrôle" groups = [ "RESILIENCE" ] notes = "Réviser la procédure de bascule en mode secours" + vars.instructions_url = "/life-noc/item/resilience/verification-procedure-bascule" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "resilience-verification-stock-lampes-piles" { vars.mock_message = "Sous contrôle" groups = [ "RESILIENCE" ] notes = "Vérification des lampes, piles et éclairage d'urgence" + vars.instructions_url = "/life-noc/item/resilience/verification-stock-lampes-piles" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "resilience-verification-trousses-urgence" { vars.mock_message = "Sous contrôle" groups = [ "RESILIENCE" ] notes = "Vérification des trousses d'urgence" + vars.instructions_url = "/life-noc/item/resilience/verification-trousses-urgence" assign where host.name == "life-noc" } @@ -61,6 +66,7 @@ apply Service "resilience-revision-plan-urgence" { vars.mock_message = "Sous contrôle" groups = [ "RESILIENCE" ] notes = "Réviser le plan d'urgence domestique" + vars.instructions_url = "/life-noc/item/resilience/revision-plan-urgence" assign where host.name == "life-noc" } @@ -71,6 +77,7 @@ apply Service "resilience-test-autonomie-base" { vars.mock_message = "Sous contrôle" groups = [ "RESILIENCE" ] notes = "Vérifier l'autonomie énergétique minimale attendue" + vars.instructions_url = "/life-noc/item/resilience/test-autonomie-base" assign where host.name == "life-noc" } @@ -81,5 +88,6 @@ apply Service "resilience-verification-moyens-cuisson-secours" { vars.mock_message = "Sous contrôle" groups = [ "RESILIENCE" ] notes = "Vérification des moyens de cuisson de secours" + vars.instructions_url = "/life-noc/item/resilience/verification-moyens-cuisson-secours" assign where host.name == "life-noc" } diff --git a/icinga/services/revue.conf b/icinga/services/revue.conf index c1119a5..2c36046 100644 --- a/icinga/services/revue.conf +++ b/icinga/services/revue.conf @@ -20,6 +20,7 @@ apply Service "revue-revue-quotidienne-life-noc" { vars.life_noc_threshold_critical_gte = "3" vars.life_noc_on_error = "critical" notes = "Vérifier le tableau de bord Life-NOC" + vars.instructions_url = "/life-noc/item/revue/revue-quotidienne-life-noc" assign where host.name == "life-noc" } @@ -39,6 +40,7 @@ apply Service "revue-revue-hebdomadaire-priorites" { vars.life_noc_threshold_critical_gte = "10" vars.life_noc_on_error = "critical" notes = "Revoir les priorités de la semaine" + vars.instructions_url = "https://chezlepro.ca/life-noc/test-instructions" assign where host.name == "life-noc" } @@ -49,6 +51,7 @@ apply Service "revue-revue-mensuelle-systeme-vie" { vars.mock_message = "Sous contrôle" groups = [ "REVUE" ] notes = "Revue mensuelle de l'ensemble du système Life-NOC" + vars.instructions_url = "/life-noc/item/revue/revue-mensuelle-systeme-vie" assign where host.name == "life-noc" } @@ -59,5 +62,6 @@ apply Service "revue-revue-trimestrielle-orientation" { vars.mock_message = "Sous contrôle" groups = [ "REVUE" ] notes = "Revue trimestrielle des orientations de vie et de Chezlepro" + vars.instructions_url = "/life-noc/item/revue/revue-trimestrielle-orientation" assign where host.name == "life-noc" } diff --git a/icinga/services/sante.conf b/icinga/services/sante.conf index fd4a2d9..686aa92 100644 --- a/icinga/services/sante.conf +++ b/icinga/services/sante.conf @@ -11,6 +11,7 @@ apply Service "sante-verification-trousse-premiers-soins" { vars.mock_message = "Sous contrôle" groups = [ "SANTE" ] notes = "Vérification de la trousse de premiers soins" + vars.instructions_url = "/life-noc/item/sante/verification-trousse-premiers-soins" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "sante-verification-expiration-medicaments" { vars.mock_message = "Sous contrôle" groups = [ "SANTE" ] notes = "Vérification des dates d'expiration des médicaments" + vars.instructions_url = "/life-noc/item/sante/verification-expiration-medicaments" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "sante-revision-rendez-vous-medicaux" { vars.mock_message = "Sous contrôle" groups = [ "SANTE" ] notes = "Revoir les rendez-vous médicaux requis" + vars.instructions_url = "/life-noc/item/sante/revision-rendez-vous-medicaux" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "sante-verification-lunettes-prescriptions" { vars.mock_message = "Sous contrôle" groups = [ "SANTE" ] notes = "Vérifier les prescriptions et besoins de renouvellement" + vars.instructions_url = "/life-noc/item/sante/verification-lunettes-prescriptions" assign where host.name == "life-noc" } @@ -51,5 +55,6 @@ apply Service "sante-verification-dossier-sante" { vars.mock_message = "Sous contrôle" groups = [ "SANTE" ] notes = "Vérifier l'ordre et l'accessibilité des documents de santé" + vars.instructions_url = "/life-noc/item/sante/verification-dossier-sante" assign where host.name == "life-noc" } diff --git a/icinga/services/securite-chezlepro.conf b/icinga/services/securite-chezlepro.conf index 33f0d85..b4e0c04 100644 --- a/icinga/services/securite-chezlepro.conf +++ b/icinga/services/securite-chezlepro.conf @@ -11,6 +11,7 @@ apply Service "securite-chezlepro-verification-mises-a-jour-securite" { vars.mock_message = "Sous contrôle" groups = [ "SECURITE-CHEZLEPRO" ] notes = "Vérification des mises à jour de sécurité" + vars.instructions_url = "/life-noc/item/securite-chezlepro/verification-mises-a-jour-securite" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "securite-chezlepro-audit-comptes-acces" { vars.mock_message = "Sous contrôle" groups = [ "SECURITE-CHEZLEPRO" ] notes = "Audit des comptes et accès privilégiés" + vars.instructions_url = "/life-noc/item/securite-chezlepro/audit-comptes-acces" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "securite-chezlepro-verification-ids-snort" { vars.mock_message = "Sous contrôle" groups = [ "SECURITE-CHEZLEPRO" ] notes = "Vérification des IDS et de leur état" + vars.instructions_url = "/life-noc/item/securite-chezlepro/verification-ids-snort" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "securite-chezlepro-revue-pki-privee" { vars.mock_message = "Sous contrôle" groups = [ "SECURITE-CHEZLEPRO" ] notes = "Revue de la PKI privée et des autorités intermédiaires" + vars.instructions_url = "/life-noc/item/securite-chezlepro/revue-pki-privee" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "securite-chezlepro-revue-politiques-securite" { vars.mock_message = "Sous contrôle" groups = [ "SECURITE-CHEZLEPRO" ] notes = "Revue des politiques et pratiques de sécurité numérique" + vars.instructions_url = "/life-noc/item/securite-chezlepro/revue-politiques-securite" assign where host.name == "life-noc" } @@ -61,5 +66,6 @@ apply Service "securite-chezlepro-verification-sauvegardes-configuration" { vars.mock_message = "Sous contrôle" groups = [ "SECURITE-CHEZLEPRO" ] notes = "Vérifier les sauvegardes des configurations critiques" + vars.instructions_url = "/life-noc/item/securite-chezlepro/verification-sauvegardes-configuration" assign where host.name == "life-noc" } diff --git a/icinga/services/stock-alimentaire.conf b/icinga/services/stock-alimentaire.conf index 2b06fab..b032cf9 100644 --- a/icinga/services/stock-alimentaire.conf +++ b/icinga/services/stock-alimentaire.conf @@ -11,6 +11,7 @@ apply Service "stock-alimentaire-rotation-nourriture-seche" { vars.mock_message = "Sous contrôle" groups = [ "STOCK-ALIMENTAIRE" ] notes = "Rotation des stocks de nourriture sèche" + vars.instructions_url = "/life-noc/item/stock-alimentaire/rotation-nourriture-seche" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "stock-alimentaire-verification-reserve-eau" { vars.mock_message = "Sous contrôle" groups = [ "STOCK-ALIMENTAIRE" ] notes = "Vérification de la réserve d'eau potable" + vars.instructions_url = "/life-noc/item/stock-alimentaire/verification-reserve-eau" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "stock-alimentaire-verification-mylar-absorbeurs" { vars.mock_message = "Sous contrôle" groups = [ "STOCK-ALIMENTAIRE" ] notes = "Vérifier l'intégrité des emballages Mylar et absorbeurs" + vars.instructions_url = "/life-noc/item/stock-alimentaire/verification-mylar-absorbeurs" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "stock-alimentaire-revue-inventaire-conserves" { vars.mock_message = "Sous contrôle" groups = [ "STOCK-ALIMENTAIRE" ] notes = "Revue des conserves et dates de rotation" + vars.instructions_url = "/life-noc/item/stock-alimentaire/revue-inventaire-conserves" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "stock-alimentaire-revue-inventaire-farine-riz-pates" { vars.mock_message = "Sous contrôle" groups = [ "STOCK-ALIMENTAIRE" ] notes = "Vérification des gros stocks alimentaires de base" + vars.instructions_url = "/life-noc/item/stock-alimentaire/revue-inventaire-farine-riz-pates" assign where host.name == "life-noc" } @@ -61,6 +66,7 @@ apply Service "stock-alimentaire-verification-bacs-legumes-racines" { vars.mock_message = "Sous contrôle" groups = [ "STOCK-ALIMENTAIRE" ] notes = "Vérification des bacs de stockage de légumes racines" + vars.instructions_url = "/life-noc/item/stock-alimentaire/verification-bacs-legumes-racines" assign where host.name == "life-noc" } @@ -71,5 +77,6 @@ apply Service "stock-alimentaire-revue-supplements-vitamines" { vars.mock_message = "Sous contrôle" groups = [ "STOCK-ALIMENTAIRE" ] notes = "Vérification du stock de suppléments et dates utiles" + vars.instructions_url = "/life-noc/item/stock-alimentaire/revue-supplements-vitamines" assign where host.name == "life-noc" } diff --git a/icinga/services/voiture.conf b/icinga/services/voiture.conf index ee03508..6a454b2 100644 --- a/icinga/services/voiture.conf +++ b/icinga/services/voiture.conf @@ -11,6 +11,7 @@ apply Service "voiture-verification-huile-moteur" { vars.mock_message = "Sous contrôle" groups = [ "VOITURE" ] notes = "Vérification de l'huile moteur" + vars.instructions_url = "/life-noc/item/voiture/verification-huile-moteur" assign where host.name == "life-noc" } @@ -21,6 +22,7 @@ apply Service "voiture-inspection-freins" { vars.mock_message = "Sous contrôle" groups = [ "VOITURE" ] notes = "Inspection des freins" + vars.instructions_url = "/life-noc/item/voiture/inspection-freins" assign where host.name == "life-noc" } @@ -31,6 +33,7 @@ apply Service "voiture-verification-pneus" { vars.mock_message = "Sous contrôle" groups = [ "VOITURE" ] notes = "Vérification usure, pression et permutation des pneus" + vars.instructions_url = "/life-noc/item/voiture/verification-pneus" assign where host.name == "life-noc" } @@ -41,6 +44,7 @@ apply Service "voiture-ajustement-valves" { vars.mock_message = "Sous contrôle" groups = [ "VOITURE" ] notes = "Vérification et ajustement des valves Honda Accord" + vars.instructions_url = "/life-noc/item/voiture/ajustement-valves" assign where host.name == "life-noc" } @@ -51,6 +55,7 @@ apply Service "voiture-verification-timing-belt" { vars.mock_message = "Sous contrôle" groups = [ "VOITURE" ] notes = "Vérification de la courroie de distribution et pompe à eau" + vars.instructions_url = "/life-noc/item/voiture/verification-timing-belt" assign where host.name == "life-noc" } @@ -61,6 +66,7 @@ apply Service "voiture-verification-batterie-voiture" { vars.mock_message = "Sous contrôle" groups = [ "VOITURE" ] notes = "Vérification de l'état de la batterie automobile" + vars.instructions_url = "/life-noc/item/voiture/verification-batterie-voiture" assign where host.name == "life-noc" } @@ -71,6 +77,7 @@ apply Service "voiture-verification-balais-essuie-glace" { vars.mock_message = "Sous contrôle" groups = [ "VOITURE" ] notes = "Vérification des essuie-glaces" + vars.instructions_url = "/life-noc/item/voiture/verification-balais-essuie-glace" assign where host.name == "life-noc" } @@ -81,6 +88,7 @@ apply Service "voiture-verification-liquides" { vars.mock_message = "Sous contrôle" groups = [ "VOITURE" ] notes = "Vérification des liquides de la voiture" + vars.instructions_url = "/life-noc/item/voiture/verification-liquides" assign where host.name == "life-noc" } @@ -91,6 +99,7 @@ apply Service "voiture-verification-eclairage" { vars.mock_message = "Sous contrôle" groups = [ "VOITURE" ] notes = "Vérification de l'éclairage et des phares" + vars.instructions_url = "/life-noc/item/voiture/verification-eclairage" assign where host.name == "life-noc" } @@ -101,5 +110,6 @@ apply Service "voiture-verification-immatriculation-assurance" { vars.mock_message = "Sous contrôle" groups = [ "VOITURE" ] notes = "Vérification papiers d'assurance et immatriculation" + vars.instructions_url = "/life-noc/item/voiture/verification-immatriculation-assurance" assign where host.name == "life-noc" } diff --git a/lib/life_noc_inputs.py b/lib/life_noc_inputs.py index 9439943..bbe3ba0 100644 --- a/lib/life_noc_inputs.py +++ b/lib/life_noc_inputs.py @@ -5,6 +5,7 @@ from pathlib import Path import yaml INPUTS_DIR = Path("data/inputs") +DOMAINS_FILE = Path("domains.yaml") def utc_now_iso() -> str: @@ -68,3 +69,76 @@ def set_item(domain: str, item_key: str, value: str, origin: str = "manual") -> def complete_item(domain: str, item_key: str, origin: str = "manual") -> dict: return set_item(domain, item_key, utc_today_date(), origin=origin) + + +def load_domains_definition() -> dict: + if not DOMAINS_FILE.exists(): + raise ValueError(f"Fichier introuvable: {DOMAINS_FILE}") + data = yaml.safe_load(DOMAINS_FILE.read_text(encoding="utf-8")) + if not isinstance(data, dict): + raise ValueError("domains.yaml invalide") + domains = data.get("domains") + if not isinstance(domains, dict): + raise ValueError("domains.yaml invalide: clé 'domains' absente ou invalide") + return domains + + +def get_defined_service(domain: str, item_key: str) -> dict: + domains = load_domains_definition() + items = domains.get(domain) + if not isinstance(items, list): + raise KeyError(domain) + + for item in items: + if isinstance(item, dict) and item.get("name") == item_key: + return item + + raise KeyError(item_key) + + +def compute_state_for_item(domain: str, item_key: str) -> str: + service = get_defined_service(domain, item_key) + probe = service.get("probe") + if not isinstance(probe, dict): + return "MOCK" + + probe_type = str(probe.get("type", "")).strip() + source = probe.get("source", {}) + thresholds = probe.get("thresholds", {}) + metric = probe.get("metric", {}) + + if str(source.get("type", "")).strip() != "manual_date": + return "CRITICAL" + if str(metric.get("unit", "")).strip() != "days": + return "CRITICAL" + + item = get_item(domain, item_key) + value = str(item.get("value", "")).strip() + dt = datetime.strptime(value, "%Y-%m-%d").replace(tzinfo=timezone.utc) + now = datetime.now(timezone.utc) + + if probe_type == "elapsed_time": + days = (now - dt).total_seconds() / 86400.0 + if "critical_gte" in thresholds and days >= float(thresholds["critical_gte"]): + return "CRITICAL" + if "warning_gte" in thresholds and days >= float(thresholds["warning_gte"]): + return "WARNING" + if "ok_gte" in thresholds and days >= float(thresholds["ok_gte"]): + return "OK" + if "unknown_lt" in thresholds and days < float(thresholds["unknown_lt"]): + return "UNKNOWN" + return "CRITICAL" + + if probe_type == "days_until_due": + days = (dt - now).total_seconds() / 86400.0 + if "critical_lt" in thresholds and days < float(thresholds["critical_lt"]): + return "CRITICAL" + if "warning_lt" in thresholds and days < float(thresholds["warning_lt"]): + return "WARNING" + if "ok_lt" in thresholds and days < float(thresholds["ok_lt"]): + return "OK" + if "unknown_gte" in thresholds and days >= float(thresholds["unknown_gte"]): + return "UNKNOWN" + return "CRITICAL" + + return "CRITICAL" diff --git a/patch_bpm_native.sh b/patch_bpm_native.sh deleted file mode 100755 index 6b5b1da..0000000 --- a/patch_bpm_native.sh +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT="${1:-.}" - -need_file() { - local f="$1" - if [[ ! -f "$ROOT/$f" ]]; then - echo "Fichier introuvable: $f" >&2 - exit 1 - fi -} - -echo "==> Vérification du dépôt" -need_file "domains.yaml" -need_file "scripts/generate_bpm.py" -need_file "Makefile" -need_file "ansible/roles/life_noc/defaults/main.yml" -need_file "ansible/roles/life_noc/tasks/main.yml" - -echo "==> Sauvegarde minimale" -mkdir -p "$ROOT/.patch-backup" -cp -a "$ROOT/scripts/generate_bpm.py" "$ROOT/.patch-backup/generate_bpm.py.bak" -cp -a "$ROOT/Makefile" "$ROOT/.patch-backup/Makefile.bak" -cp -a "$ROOT/ansible/roles/life_noc/defaults/main.yml" "$ROOT/.patch-backup/life_noc_defaults_main.yml.bak" -cp -a "$ROOT/ansible/roles/life_noc/tasks/main.yml" "$ROOT/.patch-backup/life_noc_tasks_main.yml.bak" - -[[ -f "$ROOT/preuve-de-reproductibilite.md" ]] && cp -a "$ROOT/preuve-de-reproductibilite.md" "$ROOT/.patch-backup/preuve-de-reproductibilite.md.bak" -[[ -f "$ROOT/decisions-techniques.md" ]] && cp -a "$ROOT/decisions-techniques.md" "$ROOT/.patch-backup/decisions-techniques.md.bak" - -echo "==> Réécriture de scripts/generate_bpm.py" -cat > "$ROOT/scripts/generate_bpm.py" <<'PYEOF' -#!/usr/bin/env python3 -from __future__ import annotations - -import re -from pathlib import Path -import sys -import yaml - -ROOT = Path(__file__).resolve().parents[1] -DOMAINS_YAML = ROOT / "domains.yaml" -OUTPUT_DIR = ROOT / "bpm" -OUTPUT_FILE = OUTPUT_DIR / "Life-NOC.conf" - -HOST_NAME = "life-noc" -TITLE = "Life-NOC" -OWNER = "icingadmin" - - -def slugify(value: str) -> str: - value = value.strip().lower() - replacements = { - "à": "a", "â": "a", "ä": "a", - "ç": "c", - "é": "e", "è": "e", "ê": "e", "ë": "e", - "î": "i", "ï": "i", - "ô": "o", "ö": "o", - "ù": "u", "û": "u", "ü": "u", - "ÿ": "y", - "œ": "oe", "æ": "ae", - "'": "", "’": "", - } - for old, new in replacements.items(): - value = value.replace(old, new) - value = re.sub(r"[^a-z0-9\-]+", "-", value) - value = re.sub(r"-{2,}", "-", value) - return value.strip("-") - - -def aliasify(value: str) -> str: - value = value.strip().upper() - replacements = { - "À": "A", "Â": "A", "Ä": "A", - "Ç": "C", - "É": "E", "È": "E", "Ê": "E", "Ë": "E", - "Î": "I", "Ï": "I", - "Ô": "O", "Ö": "O", - "Ù": "U", "Û": "U", "Ü": "U", - "Ÿ": "Y", - "Œ": "OE", "Æ": "AE", - "'": "", "’": "", - } - for old, new in replacements.items(): - value = value.replace(old, new) - value = re.sub(r"[^A-Z0-9\-]+", "-", value) - value = re.sub(r"-{2,}", "-", value) - return value.strip("-") - - -def read_domains() -> dict: - data = yaml.safe_load(DOMAINS_YAML.read_text(encoding="utf-8")) - if not isinstance(data, dict) or "domains" not in data: - raise ValueError("domains.yaml doit contenir une clé racine 'domains'") - domains = data["domains"] - if not isinstance(domains, dict): - raise ValueError("domains.yaml: 'domains' doit être un mapping") - return domains - - -def collect_services(domain_data: dict, domain_name: str) -> list[str]: - items = domain_data.get("items", []) - if not isinstance(items, list): - raise ValueError(f"Domaine '{domain_name}': 'items' doit être une liste") - - services: list[str] = [] - for item in items: - if not isinstance(item, dict): - raise ValueError(f"Domaine '{domain_name}': item invalide (dict attendu)") - item_name = item.get("name") - if not item_name: - raise ValueError(f"Domaine '{domain_name}': item sans champ 'name'") - service_name = f"{slugify(domain_name)}-{slugify(str(item_name))}" - services.append(f"{HOST_NAME};{service_name}") - - if not services: - raise ValueError(f"Domaine '{domain_name}' ne contient aucun item") - return services - - -def header() -> str: - return f"""### Business Process Config File ### -# -# Title : {TITLE} -# Description : -# Owner : {OWNER} -# AddToMenu : yes -# Backend : -# Statetype : soft -# -################################### - -""" - - -def main() -> int: - domains = read_domains() - OUTPUT_DIR.mkdir(parents=True, exist_ok=True) - - lines: list[str] = [header()] - aliases: list[tuple[str, str]] = [] - - for domain_name, domain_data in domains.items(): - if not isinstance(domain_data, dict): - raise ValueError(f"Domaine '{domain_name}': structure invalide") - alias = aliasify(domain_name) - label = domain_data.get("label", domain_name) - services = collect_services(domain_data, domain_name) - expr = " & ".join(services) - lines.append(f"{alias} = {expr}\n") - aliases.append((alias, str(label))) - - lines.append("\n") - root_expr = " & ".join(alias for alias, _label in aliases) - lines.append(f"LIFE-NOC = {root_expr}\n") - lines.append("display 1;LIFE-NOC;LIFE-NOC\n") - for alias, label in aliases: - lines.append(f"display 1;{alias};{label}\n") - - OUTPUT_FILE.write_text("".join(lines), encoding="utf-8") - print(f"BPM généré: {OUTPUT_FILE.relative_to(ROOT)}") - print("Processus générés :") - print(" - LIFE-NOC") - for alias, _label in aliases: - print(f" - {alias}") - return 0 - - -if __name__ == "__main__": - try: - raise SystemExit(main()) - except Exception as exc: - print(f"ERREUR: {exc}", file=sys.stderr) - raise -PYEOF -chmod +x "$ROOT/scripts/generate_bpm.py" - -echo "==> Ajustements texte ciblés" -python3 - "$ROOT" <<'PYEOF' -from pathlib import Path -import re -import sys - -root = Path(sys.argv[1]) - -def replace_text(path_str, replacements): - path = root / path_str - if not path.exists(): - return - text = path.read_text(encoding="utf-8") - original = text - for old, new in replacements: - text = text.replace(old, new) - if text != original: - path.write_text(text, encoding="utf-8") - print(f"Modifié: {path_str}") - -def regex_replace(path_str, pattern, repl, must_match=False): - path = root / path_str - if not path.exists(): - return - text = path.read_text(encoding="utf-8") - new_text, count = re.subn(pattern, repl, text, flags=re.MULTILINE) - if must_match and count == 0: - raise SystemExit(f"Aucun match dans {path_str} pour: {pattern}") - if new_text != text: - path.write_text(new_text, encoding="utf-8") - print(f"Modifié: {path_str}") - -# Makefile -replace_text("Makefile", [ - ("bpm/life-noc.json", "bpm/Life-NOC.conf"), -]) - -# defaults main.yml -replace_text("ansible/roles/life_noc/defaults/main.yml", [ - ("/etc/icingaweb2/modules/businessprocess/processes/life-noc.json", - "/etc/icingaweb2/modules/businessprocess/processes/Life-NOC.conf"), -]) - -# tasks main.yml -replace_text("ansible/roles/life_noc/tasks/main.yml", [ - ("bpm/life-noc.json", "bpm/Life-NOC.conf"), - ("life-noc.json", "Life-NOC.conf"), -]) - -# docs -for doc in ["preuve-de-reproductibilite.md", "decisions-techniques.md"]: - replace_text(doc, [ - ("bpm/life-noc.json", "bpm/Life-NOC.conf"), - ("life-noc.json", "Life-NOC.conf"), - ("export JSON", "export natif Business Process"), - ("artefact JSON", "artefact natif Business Process"), - ]) - -PYEOF - -echo "==> Nettoyage de l'ancien artefact si présent" -rm -f "$ROOT/bpm/life-noc.json" - -echo "==> Génération du nouveau BPM" -( - cd "$ROOT" - python3 scripts/generate_bpm.py -) - -echo "==> Vérification rapide" -if [[ -f "$ROOT/bpm/Life-NOC.conf" ]]; then - echo "OK: bpm/Life-NOC.conf généré" -else - echo "ERREUR: bpm/Life-NOC.conf absent" >&2 - exit 1 -fi - -echo -echo "Patch terminé." -echo "Fichiers sauvegardés dans: $ROOT/.patch-backup" -echo "Prochaine étape recommandée:" -echo " make check" -echo "Puis redéploiement avec BPM activé." diff --git a/patch_days_until_due.sh b/patch_days_until_due.sh deleted file mode 100755 index 4bd5dcc..0000000 --- a/patch_days_until_due.sh +++ /dev/null @@ -1,283 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT="${1:-.}" - -need_file() { - local f="$1" - if [[ ! -f "$ROOT/$f" ]]; then - echo "Fichier introuvable: $f" >&2 - exit 1 - fi -} - -echo "==> Vérification du dépôt" -need_file "checks/check_life_noc_probe.py" -need_file "icinga/commands/check_life_noc_probe.conf" -need_file "scripts/generate_services.py" -need_file "domains.yaml" - -echo "==> Sauvegarde" -mkdir -p "$ROOT/.patch-backup-days-until-due" -cp -a "$ROOT/checks/check_life_noc_probe.py" "$ROOT/.patch-backup-days-until-due/check_life_noc_probe.py.bak" -cp -a "$ROOT/icinga/commands/check_life_noc_probe.conf" "$ROOT/.patch-backup-days-until-due/check_life_noc_probe.conf.bak" -cp -a "$ROOT/scripts/generate_services.py" "$ROOT/.patch-backup-days-until-due/generate_services.py.bak" -cp -a "$ROOT/domains.yaml" "$ROOT/.patch-backup-days-until-due/domains.yaml.bak" - -echo "==> Réécriture de checks/check_life_noc_probe.py" -cat > "$ROOT/checks/check_life_noc_probe.py" <<'PYEOF' -#!/usr/bin/env python3 - -import argparse -from datetime import datetime, timezone -from pathlib import Path -import yaml - - -def exit_with(code: int, text: str) -> None: - print(text) - raise SystemExit(code) - - -def parse_date(value: str) -> datetime: - return datetime.strptime(value, "%Y-%m-%d").replace(tzinfo=timezone.utc) - - -def load_input_value(inputs_file: str, item_key: str) -> str: - path = Path(inputs_file) - if not path.exists(): - raise ValueError(f"inputs file introuvable: {inputs_file}") - - data = yaml.safe_load(path.read_text(encoding="utf-8")) - if not isinstance(data, dict): - raise ValueError(f"inputs file invalide: {inputs_file}") - - if item_key not in data: - raise ValueError(f"item_key introuvable dans le store: {item_key}") - - item = data[item_key] - if not isinstance(item, dict): - raise ValueError(f"entrée invalide pour item_key: {item_key}") - - if "value" not in item: - raise ValueError(f"champ 'value' absent pour item_key: {item_key}") - - return str(item["value"]).strip() - - -def main() -> int: - ap = argparse.ArgumentParser() - ap.add_argument("--probe-type", required=True) - ap.add_argument("--source-type", required=True) - ap.add_argument("--source-value") - ap.add_argument("--inputs-file") - ap.add_argument("--item-key") - ap.add_argument("--unit", required=True) - - ap.add_argument("--unknown-lt", type=float) - ap.add_argument("--unknown-gte", type=float) - - ap.add_argument("--ok-gte", type=float) - ap.add_argument("--ok-lt", type=float) - - ap.add_argument("--warning-gte", type=float) - ap.add_argument("--warning-lt", type=float) - - ap.add_argument("--critical-gte", type=float) - ap.add_argument("--critical-lt", type=float) - - ap.add_argument("--on-error", default="critical") - ap.add_argument("--label", default="life-noc") - args = ap.parse_args() - - try: - if args.source_type != "manual_date": - exit_with(2, f"CRITICAL - source_type non supporté: {args.source_type}") - - if args.unit != "days": - exit_with(2, f"CRITICAL - unité non supportée: {args.unit}") - - if args.inputs_file and args.item_key: - source_value = load_input_value(args.inputs_file, args.item_key) - elif args.source_value: - source_value = args.source_value - else: - raise ValueError("aucune source fournie: inputs-file/item-key ou source-value requis") - - dt = parse_date(source_value) - now = datetime.now(timezone.utc) - - if args.probe_type == "elapsed_time": - metric = (now - dt).total_seconds() / 86400.0 - - if args.critical_gte is not None and metric >= args.critical_gte: - exit_with(2, f"CRITICAL - {args.label}: {metric:.1f} jours") - if args.warning_gte is not None and metric >= args.warning_gte: - exit_with(1, f"WARNING - {args.label}: {metric:.1f} jours") - if args.ok_gte is not None and metric >= args.ok_gte: - exit_with(0, f"OK - {args.label}: {metric:.1f} jours") - if args.unknown_lt is not None and metric < args.unknown_lt: - exit_with(3, f"UNKNOWN - {args.label}: {metric:.1f} jours") - - exit_with(2, f"CRITICAL - {args.label}: seuils incohérents") - - elif args.probe_type == "days_until_due": - metric = (dt - now).total_seconds() / 86400.0 - - if args.critical_lt is not None and metric < args.critical_lt: - exit_with(2, f"CRITICAL - {args.label}: {metric:.1f} jours restants") - if args.warning_lt is not None and metric < args.warning_lt: - exit_with(1, f"WARNING - {args.label}: {metric:.1f} jours restants") - if args.ok_lt is not None and metric < args.ok_lt: - exit_with(0, f"OK - {args.label}: {metric:.1f} jours restants") - if args.unknown_gte is not None and metric >= args.unknown_gte: - exit_with(3, f"UNKNOWN - {args.label}: {metric:.1f} jours restants") - - exit_with(2, f"CRITICAL - {args.label}: seuils incohérents") - - else: - exit_with(2, f"CRITICAL - probe_type non supporté: {args.probe_type}") - - except Exception as exc: - if args.on_error == "critical": - exit_with(2, f"CRITICAL - erreur sonde: {exc}") - exit_with(3, f"UNKNOWN - erreur sonde: {exc}") - - -if __name__ == "__main__": - raise SystemExit(main()) -PYEOF -chmod +x "$ROOT/checks/check_life_noc_probe.py" - -echo "==> Réécriture de icinga/commands/check_life_noc_probe.conf" -cat > "$ROOT/icinga/commands/check_life_noc_probe.conf" <<'CONFEOF' -object CheckCommand "check_life_noc_probe" { - command = [ PluginDir + "/check_life_noc_probe.py" ] - - arguments = { - "--probe-type" = "$life_noc_probe_type$" - "--source-type" = "$life_noc_source_type$" - "--source-value" = "$life_noc_source_value$" - "--inputs-file" = "$life_noc_inputs_file$" - "--item-key" = "$life_noc_item_key$" - "--unit" = "$life_noc_metric_unit$" - - "--unknown-lt" = "$life_noc_threshold_unknown_lt$" - "--unknown-gte" = "$life_noc_threshold_unknown_gte$" - - "--ok-gte" = "$life_noc_threshold_ok_gte$" - "--ok-lt" = "$life_noc_threshold_ok_lt$" - - "--warning-gte" = "$life_noc_threshold_warning_gte$" - "--warning-lt" = "$life_noc_threshold_warning_lt$" - - "--critical-gte" = "$life_noc_threshold_critical_gte$" - "--critical-lt" = "$life_noc_threshold_critical_lt$" - - "--on-error" = "$life_noc_on_error$" - "--label" = "$life_noc_label$" - } -} -CONFEOF - -echo "==> Patch de scripts/generate_services.py" -python3 - "$ROOT/scripts/generate_services.py" <<'PYEOF' -from pathlib import Path -import sys - -path = Path(sys.argv[1]) -text = path.read_text(encoding="utf-8") - -old = ' if "critical_gte" in thresholds:\n lines.append(f\' vars.life_noc_threshold_critical_gte = "{icinga_escape(str(thresholds["critical_gte"]))}"\')\n' -new = old + ' if "unknown_gte" in thresholds:\n lines.append(f\' vars.life_noc_threshold_unknown_gte = "{icinga_escape(str(thresholds["unknown_gte"]))}"\')\n if "ok_lt" in thresholds:\n lines.append(f\' vars.life_noc_threshold_ok_lt = "{icinga_escape(str(thresholds["ok_lt"]))}"\')\n if "warning_lt" in thresholds:\n lines.append(f\' vars.life_noc_threshold_warning_lt = "{icinga_escape(str(thresholds["warning_lt"]))}"\')\n if "critical_lt" in thresholds:\n lines.append(f\' vars.life_noc_threshold_critical_lt = "{icinga_escape(str(thresholds["critical_lt"]))}"\')\n' -if old not in text: - raise SystemExit("Bloc seuils introuvable dans generate_services.py") -text = text.replace(old, new, 1) - -old2 = ' probe_type = str(probe.get("type", "")).strip()\n if probe_type not in {"elapsed_time"}:\n raise ValueError("seul probe.type=elapsed_time est supporté en v1")\n' -new2 = ' probe_type = str(probe.get("type", "")).strip()\n if probe_type not in {"elapsed_time", "days_until_due"}:\n raise ValueError("seuls probe.type=elapsed_time et days_until_due sont supportés")\n' -if old2 in text: - text = text.replace(old2, new2, 1) -else: - old3 = ' probe_type = str(probe.get("type", "")).strip()\n if probe_type not in {"elapsed_time", "days_until_due"}:\n raise ValueError("seuls probe.type=elapsed_time et days_until_due sont supportés")\n' - if old3 not in text: - raise SystemExit("Bloc validate_probe introuvable ou inattendu dans generate_services.py") - -path.write_text(text, encoding="utf-8") -print("generate_services.py patché") -PYEOF - -echo "==> Création du store data/inputs/obligations-legales-personnelles.yaml" -mkdir -p "$ROOT/data/inputs" -cat > "$ROOT/data/inputs/obligations-legales-personnelles.yaml" <<'YAMLEOF' -renouvellement-permis-conduire: - value: "2026-09-20" - captured_at: "2026-03-14T23:30:00Z" - origin: manual -YAMLEOF - -echo "==> Mise à jour de domains.yaml" -python3 - "$ROOT/domains.yaml" <<'PYEOF' -from pathlib import Path -import sys -import yaml - -path = Path(sys.argv[1]) -data = yaml.safe_load(path.read_text(encoding="utf-8")) - -domain = "obligations-legales-personnelles" -items = data.get("domains", {}).get(domain) -if not isinstance(items, list): - raise SystemExit(f"Domaine introuvable ou invalide: {domain}") - -target = None -for item in items: - if isinstance(item, dict) and item.get("name") == "renouvellement-permis-conduire": - target = item - break - -if target is None: - raise SystemExit("Item 'renouvellement-permis-conduire' introuvable dans domains.yaml") - -target["date"] = "2026-09-20" -target["notes"] = "Vérifier la proximité de l’échéance du permis" -target["probe"] = { - "type": "days_until_due", - "source": { - "type": "manual_date", - "inputs_file": "/opt/life-noc/data/inputs/obligations-legales-personnelles.yaml", - "item_key": "renouvellement-permis-conduire", - }, - "metric": { - "unit": "days", - }, - "thresholds": { - "critical_lt": 7, - "warning_lt": 30, - "ok_lt": 90, - "unknown_gte": 90, - }, - "policy": { - "on_error": "critical", - }, -} - -path.write_text(yaml.safe_dump(data, sort_keys=False, allow_unicode=True), encoding="utf-8") -print("domains.yaml mis à jour") -PYEOF - -echo "==> Régénération" -( - cd "$ROOT" - python3 scripts/generate_services.py -) - -echo -echo "Patch terminé." -echo "Étapes suivantes :" -echo " python3 scripts/generate_services.py" -echo " make check" -echo " make deploy-with-bpm" -echo -echo "Test utile ensuite :" -echo " python3 scripts/life_noc_input.py set obligations-legales-personnelles renouvellement-permis-conduire 2026-03-20" diff --git a/patch_days_until_due_v2.sh b/patch_days_until_due_v2.sh deleted file mode 100755 index 385d3f6..0000000 --- a/patch_days_until_due_v2.sh +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT="${1:-.}" - -need_file() { - local f="$1" - if [[ ! -f "$ROOT/$f" ]]; then - echo "Fichier introuvable: $f" >&2 - exit 1 - fi -} - -echo "==> Vérification" -need_file "scripts/generate_services.py" -need_file "domains.yaml" - -echo "==> Sauvegarde" -mkdir -p "$ROOT/.patch-backup-days-until-due-v2" -cp -a "$ROOT/scripts/generate_services.py" "$ROOT/.patch-backup-days-until-due-v2/generate_services.py.bak" -cp -a "$ROOT/domains.yaml" "$ROOT/.patch-backup-days-until-due-v2/domains.yaml.bak" - -echo "==> Patch de scripts/generate_services.py" -python3 - "$ROOT/scripts/generate_services.py" <<'PYEOF' -from pathlib import Path -import sys - -path = Path(sys.argv[1]) -text = path.read_text(encoding="utf-8") - -# 1) Ajouter les seuils *_lt / unknown_gte si absents -needle = ' if "critical_gte" in thresholds:\n lines.append(f\' vars.life_noc_threshold_critical_gte = "{icinga_escape(str(thresholds["critical_gte"]))}"\')\n' -addition = ( - ' if "unknown_gte" in thresholds:\n' - ' lines.append(f\' vars.life_noc_threshold_unknown_gte = "{icinga_escape(str(thresholds["unknown_gte"]))}"\')\n' - ' if "ok_lt" in thresholds:\n' - ' lines.append(f\' vars.life_noc_threshold_ok_lt = "{icinga_escape(str(thresholds["ok_lt"]))}"\')\n' - ' if "warning_lt" in thresholds:\n' - ' lines.append(f\' vars.life_noc_threshold_warning_lt = "{icinga_escape(str(thresholds["warning_lt"]))}"\')\n' - ' if "critical_lt" in thresholds:\n' - ' lines.append(f\' vars.life_noc_threshold_critical_lt = "{icinga_escape(str(thresholds["critical_lt"]))}"\')\n' -) - -if 'vars.life_noc_threshold_unknown_gte' not in text: - if needle not in text: - raise SystemExit("Impossible de trouver le bloc des seuils dans generate_services.py") - text = text.replace(needle, needle + addition, 1) - -# 2) Élargir validate_probe aux deux types -old_variants = [ - ' probe_type = str(probe.get("type", "")).strip()\n if probe_type not in {"elapsed_time"}:\n raise ValueError("seul probe.type=elapsed_time est supporté en v1")\n', - ' probe_type = str(probe.get("type", "")).strip()\n if probe_type not in {"elapsed_time"}:\n raise ValueError("seul probe.type=elapsed_time est supporté en v1")\r\n', -] -new_block = ' probe_type = str(probe.get("type", "")).strip()\n if probe_type not in {"elapsed_time", "days_until_due"}:\n raise ValueError("seuls probe.type=elapsed_time et days_until_due sont supportés")\n' - -replaced = False -for old in old_variants: - if old in text: - text = text.replace(old, new_block, 1) - replaced = True - break - -if not replaced: - # si déjà patché, on accepte - if new_block.strip() not in text: - raise SystemExit("Bloc validate_probe introuvable ou déjà trop différent dans generate_services.py") - -path.write_text(text, encoding="utf-8") -print("generate_services.py patché") -PYEOF - -echo "==> Création du store data/inputs/obligations-legales-personnelles.yaml" -mkdir -p "$ROOT/data/inputs" -cat > "$ROOT/data/inputs/obligations-legales-personnelles.yaml" <<'YAMLEOF' -renouvellement-permis-conduire: - value: "2026-09-20" - captured_at: "2026-03-14T23:30:00Z" - origin: manual -YAMLEOF - -echo "==> Mise à jour de domains.yaml" -python3 - "$ROOT/domains.yaml" <<'PYEOF' -from pathlib import Path -import sys -import yaml - -path = Path(sys.argv[1]) -data = yaml.safe_load(path.read_text(encoding="utf-8")) - -domain = "obligations-legales-personnelles" -items = data.get("domains", {}).get(domain) -if not isinstance(items, list): - raise SystemExit(f"Domaine introuvable ou invalide: {domain}") - -target = None -for item in items: - if isinstance(item, dict) and item.get("name") == "renouvellement-permis-conduire": - target = item - break - -if target is None: - raise SystemExit("Item 'renouvellement-permis-conduire' introuvable") - -target["date"] = "2026-09-20" -target["notes"] = "Vérifier la proximité de l’échéance du permis" -target["probe"] = { - "type": "days_until_due", - "source": { - "type": "manual_date", - "inputs_file": "/opt/life-noc/data/inputs/obligations-legales-personnelles.yaml", - "item_key": "renouvellement-permis-conduire", - }, - "metric": { - "unit": "days", - }, - "thresholds": { - "critical_lt": 7, - "warning_lt": 30, - "ok_lt": 90, - "unknown_gte": 90, - }, - "policy": { - "on_error": "critical", - }, -} - -path.write_text(yaml.safe_dump(data, sort_keys=False, allow_unicode=True), encoding="utf-8") -print("domains.yaml mis à jour") -PYEOF - -echo "==> Régénération" -( - cd "$ROOT" - python3 scripts/generate_services.py -) - -echo -echo "Patch terminé." -echo "Étapes suivantes :" -echo " python3 scripts/generate_services.py" -echo " make check" -echo " make deploy-with-bpm" -echo -echo "Test utile ensuite :" -echo " python3 scripts/life_noc_input.py set obligations-legales-personnelles renouvellement-permis-conduire 2026-03-20" diff --git a/patch_days_until_due_v3.sh b/patch_days_until_due_v3.sh deleted file mode 100755 index 818cf52..0000000 --- a/patch_days_until_due_v3.sh +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT="${1:-.}" - -need_file() { - local f="$1" - if [[ ! -f "$ROOT/$f" ]]; then - echo "Fichier introuvable: $f" >&2 - exit 1 - fi -} - -echo "==> Vérification" -need_file "scripts/generate_services.py" -need_file "domains.yaml" - -echo "==> Sauvegarde" -mkdir -p "$ROOT/.patch-backup-days-until-due-v3" -cp -a "$ROOT/scripts/generate_services.py" "$ROOT/.patch-backup-days-until-due-v3/generate_services.py.bak" -cp -a "$ROOT/domains.yaml" "$ROOT/.patch-backup-days-until-due-v3/domains.yaml.bak" - -echo "==> Patch robuste de scripts/generate_services.py" -python3 - "$ROOT/scripts/generate_services.py" <<'PYEOF' -from pathlib import Path -import re -import sys - -path = Path(sys.argv[1]) -text = path.read_text(encoding="utf-8") - -# 1) Ajouter les seuils lt/gte manquants dans render_probe_service, juste avant on_error -if 'vars.life_noc_threshold_unknown_gte' not in text: - marker = ' on_error = str(policy.get("on_error", "critical")).strip()\n' - inject = ( - ' if "unknown_gte" in thresholds:\n' - ' lines.append(f\' vars.life_noc_threshold_unknown_gte = "{icinga_escape(str(thresholds["unknown_gte"]))}"\')\n' - ' if "ok_lt" in thresholds:\n' - ' lines.append(f\' vars.life_noc_threshold_ok_lt = "{icinga_escape(str(thresholds["ok_lt"]))}"\')\n' - ' if "warning_lt" in thresholds:\n' - ' lines.append(f\' vars.life_noc_threshold_warning_lt = "{icinga_escape(str(thresholds["warning_lt"]))}"\')\n' - ' if "critical_lt" in thresholds:\n' - ' lines.append(f\' vars.life_noc_threshold_critical_lt = "{icinga_escape(str(thresholds["critical_lt"]))}"\')\n' - ) - if marker not in text: - raise SystemExit("Impossible de trouver le point d'injection des seuils dans render_probe_service") - text = text.replace(marker, inject + marker, 1) - -# 2) Remplacer entièrement validate_probe(...) -new_validate = '''def validate_probe(item: dict) -> None: - probe = item["probe"] - if not isinstance(probe, dict): - raise ValueError("probe doit être un objet") - - probe_type = str(probe.get("type", "")).strip() - if probe_type not in {"elapsed_time", "days_until_due"}: - raise ValueError("seuls probe.type=elapsed_time et days_until_due sont supportés") - - source = probe.get("source") - if not isinstance(source, dict): - raise ValueError("probe.source doit être un objet") - if str(source.get("type", "")).strip() != "manual_date": - raise ValueError("seul probe.source.type=manual_date est supporté en v1") - - has_inline_value = "value" in source - has_store_ref = "inputs_file" in source and "item_key" in source - if not has_inline_value and not has_store_ref: - raise ValueError("probe.source.value ou probe.source.inputs_file + item_key requis") - - metric = probe.get("metric") - if not isinstance(metric, dict): - raise ValueError("probe.metric doit être un objet") - if str(metric.get("unit", "")).strip() != "days": - raise ValueError("seul probe.metric.unit=days est supporté en v1") - - thresholds = probe.get("thresholds") - if not isinstance(thresholds, dict): - raise ValueError("probe.thresholds doit être un objet") -''' - -pattern = r'def validate_probe\(item: dict\) -> None:\n(?: .*\n)+?(?=\ndef render_servicegroup|\ndef main\()' -new_text, count = re.subn(pattern, new_validate + "\n", text, flags=re.MULTILINE) -if count == 0: - raise SystemExit("Impossible de remplacer validate_probe(...) dans generate_services.py") -text = new_text - -path.write_text(text, encoding="utf-8") -print("generate_services.py patché") -PYEOF - -echo "==> Création du store" -mkdir -p "$ROOT/data/inputs" -cat > "$ROOT/data/inputs/obligations-legales-personnelles.yaml" <<'YAMLEOF' -renouvellement-permis-conduire: - value: "2026-09-20" - captured_at: "2026-03-14T23:30:00Z" - origin: manual -YAMLEOF - -echo "==> Mise à jour de domains.yaml" -python3 - "$ROOT/domains.yaml" <<'PYEOF' -from pathlib import Path -import sys -import yaml - -path = Path(sys.argv[1]) -data = yaml.safe_load(path.read_text(encoding="utf-8")) - -domain = "obligations-legales-personnelles" -items = data.get("domains", {}).get(domain) -if not isinstance(items, list): - raise SystemExit(f"Domaine introuvable ou invalide: {domain}") - -target = None -for item in items: - if isinstance(item, dict) and item.get("name") == "renouvellement-permis-conduire": - target = item - break - -if target is None: - raise SystemExit("Item 'renouvellement-permis-conduire' introuvable") - -target["date"] = "2026-09-20" -target["notes"] = "Vérifier la proximité de l’échéance du permis" -target["probe"] = { - "type": "days_until_due", - "source": { - "type": "manual_date", - "inputs_file": "/opt/life-noc/data/inputs/obligations-legales-personnelles.yaml", - "item_key": "renouvellement-permis-conduire", - }, - "metric": { - "unit": "days", - }, - "thresholds": { - "critical_lt": 7, - "warning_lt": 30, - "ok_lt": 90, - "unknown_gte": 90, - }, - "policy": { - "on_error": "critical", - }, -} - -path.write_text(yaml.safe_dump(data, sort_keys=False, allow_unicode=True), encoding="utf-8") -print("domains.yaml mis à jour") -PYEOF - -echo "==> Régénération" -( - cd "$ROOT" - python3 scripts/generate_services.py -) - -echo -echo "Patch terminé." -echo "Étapes suivantes :" -echo " python3 scripts/generate_services.py" -echo " make check" -echo " make deploy-with-bpm" diff --git a/revue-hebdomadaire-priorites b/revue-hebdomadaire-priorites deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/generate_services.py b/scripts/generate_services.py index 691846c..308004d 100644 --- a/scripts/generate_services.py +++ b/scripts/generate_services.py @@ -79,6 +79,9 @@ def render_mock_service(domain: str, group_name: str, item: dict) -> str: date = str(item["date"]).strip() notes = item.get("notes", "").strip() notes_url = item.get("notes_url", "").strip() + instructions_url = item.get("instructions_url", "").strip() + if not instructions_url: + instructions_url = f"/life-noc/item/{domain}/{name}" action_url = item.get("action_url", "").strip() mock_state = normalize_mock_state(item.get("mock_state", DEFAULT_MOCK_STATE)) mock_message = item.get("mock_message", DEFAULT_MOCK_MESSAGE).strip() or DEFAULT_MOCK_MESSAGE @@ -98,6 +101,8 @@ def render_mock_service(domain: str, group_name: str, item: dict) -> str: lines.append(f' notes = "{icinga_escape(notes)}"') if notes_url: lines.append(f' notes_url = "{icinga_escape(notes_url)}"') + if instructions_url: + lines.append(f' vars.instructions_url = "{icinga_escape(instructions_url)}"') if action_url: lines.append(f' action_url = "{icinga_escape(action_url)}"') @@ -112,6 +117,9 @@ def render_probe_service(domain: str, group_name: str, item: dict) -> str: name = item["name"].strip() notes = item.get("notes", "").strip() notes_url = item.get("notes_url", "").strip() + instructions_url = item.get("instructions_url", "").strip() + if not instructions_url: + instructions_url = f"/life-noc/item/{domain}/{name}" action_url = item.get("action_url", "").strip() probe = item["probe"] @@ -160,6 +168,8 @@ def render_probe_service(domain: str, group_name: str, item: dict) -> str: lines.append(f' notes = "{icinga_escape(notes)}"') if notes_url: lines.append(f' notes_url = "{icinga_escape(notes_url)}"') + if instructions_url: + lines.append(f' vars.instructions_url = "{icinga_escape(instructions_url)}"') if action_url: lines.append(f' action_url = "{icinga_escape(action_url)}"') diff --git a/scripts/generate_services.py.bak b/scripts/generate_services.py.bak new file mode 100644 index 0000000..691846c --- /dev/null +++ b/scripts/generate_services.py.bak @@ -0,0 +1,307 @@ +#!/usr/bin/env python3 + +from pathlib import Path +import re +import sys +import yaml + +INPUT_FILE = Path("domains.yaml") +OUTPUT_DIR = Path("icinga/services") +SERVICEGROUPS_DIR = Path("icinga/servicegroups") +SERVICEGROUPS_FILE = SERVICEGROUPS_DIR / "life-noc.conf" +HOST_NAME = "life-noc" +SERVICE_TEMPLATE = "service_echeance" +PROBE_TEMPLATE = "service_life_noc_probe" +DEFAULT_MOCK_STATE = "OK" +DEFAULT_MOCK_MESSAGE = "Sous contrôle" + + +def slugify(value: str) -> str: + value = value.strip().lower() + replacements = { + "à": "a", "â": "a", "ä": "a", + "ç": "c", + "é": "e", "è": "e", "ê": "e", "ë": "e", + "î": "i", "ï": "i", + "ô": "o", "ö": "o", + "ù": "u", "û": "u", "ü": "u", + "ÿ": "y", + "œ": "oe", + "æ": "ae", + "'": "", + "’": "", + } + for old, new in replacements.items(): + value = value.replace(old, new) + value = re.sub(r"[^a-z0-9\-]+", "-", value) + value = re.sub(r"-{2,}", "-", value) + return value.strip("-") + + +def aliasify(value: str) -> str: + value = value.strip().upper() + replacements = { + "À": "A", "Â": "A", "Ä": "A", + "Ç": "C", + "É": "E", "È": "E", "Ê": "E", "Ë": "E", + "Î": "I", "Ï": "I", + "Ô": "O", "Ö": "O", + "Ù": "U", "Û": "U", "Ü": "U", + "Ÿ": "Y", + "Œ": "OE", + "Æ": "AE", + "'": "", + "’": "", + } + for old, new in replacements.items(): + value = value.replace(old, new) + value = re.sub(r"[^A-Z0-9\-]+", "-", value) + value = re.sub(r"-{2,}", "-", value) + return value.strip("-") + + +def icinga_escape(value: str) -> str: + return value.replace("\\", "\\\\").replace('"', '\\"') + + +def normalize_mock_state(value: str) -> str: + state = str(value).strip().upper() + allowed = {"OK", "WARNING", "CRITICAL"} + if state not in allowed: + raise ValueError( + f"mock_state invalide: {value}. Valeurs permises: {', '.join(sorted(allowed))}" + ) + return state + + +def render_mock_service(domain: str, group_name: str, item: dict) -> str: + name = item["name"].strip() + date = str(item["date"]).strip() + notes = item.get("notes", "").strip() + notes_url = item.get("notes_url", "").strip() + action_url = item.get("action_url", "").strip() + mock_state = normalize_mock_state(item.get("mock_state", DEFAULT_MOCK_STATE)) + mock_message = item.get("mock_message", DEFAULT_MOCK_MESSAGE).strip() or DEFAULT_MOCK_MESSAGE + + service_name = f"{domain}-{name}" + + lines = [ + f'apply Service "{icinga_escape(service_name)}" {{', + f' import "{SERVICE_TEMPLATE}"', + f' vars.date_echeance = "{icinga_escape(date)}"', + f' vars.mock_state = "{icinga_escape(mock_state)}"', + f' vars.mock_message = "{icinga_escape(mock_message)}"', + f' groups = [ "{icinga_escape(group_name)}" ]', + ] + + if notes: + lines.append(f' notes = "{icinga_escape(notes)}"') + if notes_url: + lines.append(f' notes_url = "{icinga_escape(notes_url)}"') + if action_url: + lines.append(f' action_url = "{icinga_escape(action_url)}"') + + lines.append(f' assign where host.name == "{HOST_NAME}"') + lines.append("}") + lines.append("") + + return "\n".join(lines) + + +def render_probe_service(domain: str, group_name: str, item: dict) -> str: + name = item["name"].strip() + notes = item.get("notes", "").strip() + notes_url = item.get("notes_url", "").strip() + action_url = item.get("action_url", "").strip() + probe = item["probe"] + + probe_type = str(probe["type"]).strip() + source = probe["source"] + metric = probe["metric"] + thresholds = probe["thresholds"] + policy = probe.get("policy", {}) + + service_name = f"{domain}-{name}" + + lines = [ + f'apply Service "{icinga_escape(service_name)}" {{', + f' import "{PROBE_TEMPLATE}"', + f' vars.life_noc_probe_type = "{icinga_escape(probe_type)}"', + f' vars.life_noc_source_type = "{icinga_escape(str(source["type"]).strip())}"', + f' vars.life_noc_source_value = "{icinga_escape(str(source.get("value", "")).strip())}"', + f' vars.life_noc_inputs_file = "{icinga_escape(str(source.get("inputs_file", "")).strip())}"', + f' vars.life_noc_item_key = "{icinga_escape(str(source.get("item_key", name)).strip())}"', + f' vars.life_noc_metric_unit = "{icinga_escape(str(metric["unit"]).strip())}"', + f' vars.life_noc_label = "{icinga_escape(name)}"', + f' groups = [ "{icinga_escape(group_name)}" ]', + ] + + if "unknown_lt" in thresholds: + lines.append(f' vars.life_noc_threshold_unknown_lt = "{icinga_escape(str(thresholds["unknown_lt"]))}"') + if "ok_gte" in thresholds: + lines.append(f' vars.life_noc_threshold_ok_gte = "{icinga_escape(str(thresholds["ok_gte"]))}"') + if "warning_gte" in thresholds: + lines.append(f' vars.life_noc_threshold_warning_gte = "{icinga_escape(str(thresholds["warning_gte"]))}"') + if "critical_gte" in thresholds: + lines.append(f' vars.life_noc_threshold_critical_gte = "{icinga_escape(str(thresholds["critical_gte"]))}"') + if "unknown_gte" in thresholds: + lines.append(f' vars.life_noc_threshold_unknown_gte = "{icinga_escape(str(thresholds["unknown_gte"]))}"') + if "ok_lt" in thresholds: + lines.append(f' vars.life_noc_threshold_ok_lt = "{icinga_escape(str(thresholds["ok_lt"]))}"') + if "warning_lt" in thresholds: + lines.append(f' vars.life_noc_threshold_warning_lt = "{icinga_escape(str(thresholds["warning_lt"]))}"') + if "critical_lt" in thresholds: + lines.append(f' vars.life_noc_threshold_critical_lt = "{icinga_escape(str(thresholds["critical_lt"]))}"') + + on_error = str(policy.get("on_error", "critical")).strip() + lines.append(f' vars.life_noc_on_error = "{icinga_escape(on_error)}"') + + if notes: + lines.append(f' notes = "{icinga_escape(notes)}"') + if notes_url: + lines.append(f' notes_url = "{icinga_escape(notes_url)}"') + if action_url: + lines.append(f' action_url = "{icinga_escape(action_url)}"') + + lines.append(f' assign where host.name == "{HOST_NAME}"') + lines.append("}") + lines.append("") + + return "\n".join(lines) + + +def validate_probe(item: dict) -> None: + probe = item["probe"] + if not isinstance(probe, dict): + raise ValueError("probe doit être un objet") + + probe_type = str(probe.get("type", "")).strip() + if probe_type not in {"elapsed_time", "days_until_due"}: + raise ValueError("seuls probe.type=elapsed_time et days_until_due sont supportés") + + source = probe.get("source") + if not isinstance(source, dict): + raise ValueError("probe.source doit être un objet") + if str(source.get("type", "")).strip() != "manual_date": + raise ValueError("seul probe.source.type=manual_date est supporté en v1") + + has_inline_value = "value" in source + has_store_ref = "inputs_file" in source and "item_key" in source + + if not has_inline_value and not has_store_ref: + raise ValueError("probe.source.value ou probe.source.inputs_file + item_key requis") + + metric = probe.get("metric") + if not isinstance(metric, dict): + raise ValueError("probe.metric doit être un objet") + if str(metric.get("unit", "")).strip() != "days": + raise ValueError("seul probe.metric.unit=days est supporté en v1") + + thresholds = probe.get("thresholds") + if not isinstance(thresholds, dict): + raise ValueError("probe.thresholds doit être un objet") + + +def render_servicegroup(group_name: str) -> str: + return "\n".join([ + f'object ServiceGroup "{icinga_escape(group_name)}" {{', + f' display_name = "{icinga_escape(group_name)}"', + "}", + "", + ]) + + +def main() -> int: + if not INPUT_FILE.exists(): + print(f"Erreur: fichier introuvable: {INPUT_FILE}", file=sys.stderr) + return 1 + + with INPUT_FILE.open("r", encoding="utf-8") as f: + data = yaml.safe_load(f) + + if not isinstance(data, dict) or "domains" not in data: + print("Erreur: le YAML doit contenir une clé racine 'domains'.", file=sys.stderr) + return 1 + + domains = data["domains"] + if not isinstance(domains, dict): + print("Erreur: 'domains' doit être un objet YAML.", file=sys.stderr) + return 1 + + OUTPUT_DIR.mkdir(parents=True, exist_ok=True) + SERVICEGROUPS_DIR.mkdir(parents=True, exist_ok=True) + + for existing_conf in OUTPUT_DIR.glob("*.conf"): + existing_conf.unlink() + + if SERVICEGROUPS_FILE.exists(): + SERVICEGROUPS_FILE.unlink() + + generated_files = [] + servicegroup_content = [ + "/*", + " AUTO-GENERATED FILE - SERVICE GROUPS", + " Ne pas modifier manuellement.", + " Source: domains.yaml", + "*/", + "", + ] + + for raw_domain, services in domains.items(): + domain = slugify(str(raw_domain)) + group_name = aliasify(str(raw_domain)) + + if not isinstance(services, list): + print(f"Erreur: le domaine '{raw_domain}' doit contenir une liste.", file=sys.stderr) + return 1 + + output_file = OUTPUT_DIR / f"{domain}.conf" + content = [ + "/*", + f" AUTO-GENERATED FILE - DOMAIN: {raw_domain}", + " Ne pas modifier manuellement.", + " Source: domains.yaml", + "*/", + "", + ] + + servicegroup_content.append(render_servicegroup(group_name)) + + for idx, item in enumerate(services, start=1): + if not isinstance(item, dict): + print(f"Erreur: entrée invalide dans '{raw_domain}' à la position {idx}.", file=sys.stderr) + return 1 + + missing = [key for key in ("name", "date", "notes") if key not in item] + if missing: + print( + f"Erreur: dans le domaine '{raw_domain}', entrée {idx}, champs manquants: {', '.join(missing)}", + file=sys.stderr, + ) + return 1 + + try: + if "probe" in item: + validate_probe(item) + content.append(render_probe_service(domain, group_name, item)) + else: + content.append(render_mock_service(domain, group_name, item)) + except ValueError as exc: + print(f"Erreur dans '{raw_domain}', entrée {idx}: {exc}", file=sys.stderr) + return 1 + + output_file.write_text("\n".join(content).rstrip() + "\n", encoding="utf-8") + generated_files.append(str(output_file)) + + SERVICEGROUPS_FILE.write_text("\n".join(servicegroup_content).rstrip() + "\n", encoding="utf-8") + generated_files.append(str(SERVICEGROUPS_FILE)) + + print("Fichiers générés :") + for file_path in generated_files: + print(f" - {file_path}") + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/life_noc_input_api.py b/scripts/life_noc_input_api.py index b10d118..fd665b9 100644 --- a/scripts/life_noc_input_api.py +++ b/scripts/life_noc_input_api.py @@ -5,14 +5,22 @@ from __future__ import annotations from pathlib import Path import sys -from fastapi import FastAPI, HTTPException +from fastapi import FastAPI, HTTPException, Form +from fastapi.responses import HTMLResponse, RedirectResponse from pydantic import BaseModel ROOT = Path(__file__).resolve().parents[1] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) -from lib.life_noc_inputs import list_items, get_item, set_item, complete_item # noqa: E402 +from lib.life_noc_inputs import ( # noqa: E402 + list_items, + get_item, + set_item, + complete_item, + get_defined_service, + compute_state_for_item, +) class SetInputRequest(BaseModel): @@ -27,6 +35,31 @@ class CompleteInputRequest(BaseModel): app = FastAPI(title="Life-NOC Input API", version="1.0.0") +def html_escape(value: str) -> str: + return ( + str(value) + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace('"', """) + ) + + +def link_row(label: str, url: str) -> str: + if not url: + return "" + u = html_escape(url) + l = html_escape(label) + return f'
  • {l}
  • ' + + +def resolve_instructions_url(domain: str, item_key: str, service: dict) -> str: + explicit = str(service.get("instructions_url", "")).strip() + if explicit: + return explicit + return f"/life-noc/item/{domain}/{item_key}" + + @app.get("/health") def health() -> dict: return {"status": "ok"} @@ -59,3 +92,191 @@ def api_complete_input(domain: str, item_key: str, req: CompleteInputRequest | N origin = "manual" if req is None else req.origin item = complete_item(domain, item_key, origin=origin) return {"domain": domain, "item_key": item_key, **item} + + +@app.get("/life-noc/item/{domain}/{item_key}", response_class=HTMLResponse) +def page_item(domain: str, item_key: str) -> HTMLResponse: + try: + service = get_defined_service(domain, item_key) + current_input = get_item(domain, item_key) + state = compute_state_for_item(domain, item_key) + except KeyError: + raise HTTPException(status_code=404, detail="item introuvable") + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) + + title = service.get("title") or service.get("display_name") or item_key.replace("-", " ").capitalize() + summary = service.get("summary", "") + notes = service.get("notes", "") + instructions = service.get("instructions", "") + notes_url = service.get("notes_url", "") + instructions_url = resolve_instructions_url(domain, item_key, service) + action_url = service.get("action_url", "") + probe_type = service.get("probe", {}).get("type", "unknown") + ui = service.get("ui", {}) if isinstance(service.get("ui", {}), dict) else {} + form_mode = ui.get("form_mode", "") + allow_complete = bool(ui.get("allow_complete", probe_type == "elapsed_time")) + allow_manual_edit = bool(ui.get("allow_manual_edit", True)) + + links_html = "" + for fragment in [ + link_row("Notes", notes_url), + link_row("Instructions", instructions_url), + link_row("Action", action_url), + ]: + if fragment: + links_html += fragment + + summary_html = "" + if summary: + summary_html = f'

    Résumé : {html_escape(summary)}

    ' + + instructions_html = "" + if instructions: + instructions_html = f""" +
    +

    Instructions

    +
    {html_escape(instructions)}
    +
    +""" + + manual_form_html = "" + if allow_manual_edit and form_mode == "complete_date": + manual_form_html = f""" +
    +

    Mettre à jour la date

    +
    + + + +
    +
    +""" + + complete_form_html = "" + if allow_complete: + complete_form_html = f""" +
    +

    Action rapide

    +
    + +
    +
    +""" + + html = f""" + + + + + Life-NOC — {html_escape(title)} + + + +
    +
    +

    {html_escape(title)}

    +

    État actuel : {html_escape(state)}

    +

    Type de sonde : {html_escape(probe_type)}

    + {summary_html} +
    + +
    +

    Dernier intrant

    +

    Valeur : {html_escape(current_input.get("value", ""))}

    +

    Capturé le : {html_escape(current_input.get("captured_at", ""))}

    +

    Origine : {html_escape(current_input.get("origin", ""))}

    +
    + +
    +

    Contexte

    +

    Domaine : {html_escape(domain)}

    +

    Item key : {html_escape(item_key)}

    +

    Notes : {html_escape(notes)}

    +
    + + {instructions_html} + +
    +

    Liens utiles

    +
      + {links_html} +
    +
    + + {manual_form_html} + {complete_form_html} +
    + +""" + return HTMLResponse(content=html) + + +@app.post("/life-noc/item/{domain}/{item_key}/set") +def page_set_item(domain: str, item_key: str, value: str = Form(...), origin: str = Form(default="manual")) -> RedirectResponse: + try: + set_item(domain, item_key, value=value, origin=origin) + except KeyError: + raise HTTPException(status_code=404, detail="item introuvable") + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) + + return RedirectResponse( + url=f"/life-noc/item/{domain}/{item_key}", + status_code=303, + ) + + +@app.post("/life-noc/item/{domain}/{item_key}/complete") +def page_complete_item(domain: str, item_key: str, origin: str = Form(default="manual")) -> RedirectResponse: + try: + complete_item(domain, item_key, origin=origin) + except KeyError: + raise HTTPException(status_code=404, detail="item introuvable") + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) + + return RedirectResponse( + url=f"/life-noc/item/{domain}/{item_key}", + status_code=303, + ) diff --git a/scripts/life_noc_input_api.py.bak b/scripts/life_noc_input_api.py.bak new file mode 100644 index 0000000..95388b6 --- /dev/null +++ b/scripts/life_noc_input_api.py.bak @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +from pathlib import Path +import sys + +from fastapi import FastAPI, HTTPException, Form +from fastapi.responses import HTMLResponse, RedirectResponse +from pydantic import BaseModel + +ROOT = Path(__file__).resolve().parents[1] +if str(ROOT) not in sys.path: + sys.path.insert(0, str(ROOT)) + +from lib.life_noc_inputs import ( # noqa: E402 + list_items, + get_item, + set_item, + complete_item, + get_defined_service, + compute_state_for_item, +) + + +class SetInputRequest(BaseModel): + value: str + origin: str = "manual" + + +class CompleteInputRequest(BaseModel): + origin: str = "manual" + + +app = FastAPI(title="Life-NOC Input API", version="1.0.0") + + +def html_escape(value: str) -> str: + return ( + str(value) + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace('"', """) + ) + + +def link_row(label: str, url: str) -> str: + if not url: + return "" + u = html_escape(url) + l = html_escape(label) + return f'
  • {l}
  • ' + +def resolve_instructions_url(domain: str, item_key: str, service: dict) -> str: + explicit = str(service.get("instructions_url", "")).strip() + if explicit: + return explicit + return f"/life-noc/item/{domain}/{item_key}" + +@app.get("/health") +def health() -> dict: + return {"status": "ok"} + + +@app.get("/inputs/{domain}") +def api_list_inputs(domain: str) -> dict: + return {"domain": domain, "items": list_items(domain)} + + +@app.get("/inputs/{domain}/{item_key}") +def api_get_input(domain: str, item_key: str) -> dict: + try: + item = get_item(domain, item_key) + except KeyError: + raise HTTPException(status_code=404, detail="item introuvable") + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) + return {"domain": domain, "item_key": item_key, **item} + + +@app.post("/inputs/{domain}/{item_key}") +def api_set_input(domain: str, item_key: str, req: SetInputRequest) -> dict: + item = set_item(domain, item_key, req.value, origin=req.origin) + return {"domain": domain, "item_key": item_key, **item} + + +@app.post("/inputs/{domain}/{item_key}/complete") +def api_complete_input(domain: str, item_key: str, req: CompleteInputRequest | None = None) -> dict: + origin = "manual" if req is None else req.origin + item = complete_item(domain, item_key, origin=origin) + return {"domain": domain, "item_key": item_key, **item} + + +@app.get("/life-noc/item/{domain}/{item_key}", response_class=HTMLResponse) +def page_item(domain: str, item_key: str) -> HTMLResponse: + try: + service = get_defined_service(domain, item_key) + current_input = get_item(domain, item_key) + state = compute_state_for_item(domain, item_key) + except KeyError: + raise HTTPException(status_code=404, detail="item introuvable") + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) + + title = service.get("title") or service.get("display_name") or item_key.replace("-", " ").capitalize() + summary = service.get("summary", "") + notes = service.get("notes", "") + instructions = service.get("instructions", "") + notes_url = service.get("notes_url", "") + instructions_url = resolve_instructions_url(domain, item_key, service) + action_url = service.get("action_url", "") + probe_type = service.get("probe", {}).get("type", "unknown") + ui = service.get("ui", {}) if isinstance(service.get("ui", {}), dict) else {} + form_mode = ui.get("form_mode", "") + allow_complete = bool(ui.get("allow_complete", probe_type == "elapsed_time")) + allow_manual_edit = bool(ui.get("allow_manual_edit", True)) + html = f""" + + + + + Life-NOC — {html_escape(title)} + + + +
    +
    +

    {html_escape(title)}

    +

    État actuel : {html_escape(state)}

    +

    Type de sonde : {html_escape(probe_type)}

    +
    + +
    +

    Dernier intrant

    +

    Valeur : {html_escape(current_input.get("value", ""))}

    +

    Capturé le : {html_escape(current_input.get("captured_at", ""))}

    +

    Origine : {html_escape(current_input.get("origin", ""))}

    +
    + +
    +

    Contexte

    +

    Domaine : {html_escape(domain)}

    +

    Item key : {html_escape(item_key)}

    +

    Notes : {html_escape(notes)}

    +
    + +
    +

    Liens utiles

    +
      + {link_row("Notes", notes_url)} + {link_row("Instructions", instructions_url)} + {link_row("Action", action_url)} +
    +
    + +
    +

    Action

    +
    + +
    +
    +
    + +""" + return HTMLResponse(content=html) + +@app.post("/life-noc/item/{domain}/{item_key}/set") +def page_set_item(domain: str, item_key: str, value: str = Form(...), origin: str = Form(default="manual")) -> RedirectResponse: + try: + set_item(domain, item_key, value=value, origin=origin) + except KeyError: + raise HTTPException(status_code=404, detail="item introuvable") + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) + + return RedirectResponse( + url=f"/life-noc/item/{domain}/{item_key}", + status_code=303, + ) + +@app.post("/life-noc/item/{domain}/{item_key}/complete") +def page_complete_item(domain: str, item_key: str, origin: str = Form(default="manual")) -> RedirectResponse: + try: + complete_item(domain, item_key, origin=origin) + except KeyError: + raise HTTPException(status_code=404, detail="item introuvable") + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) + + return RedirectResponse( + url=f"/life-noc/item/{domain}/{item_key}", + status_code=303, + )