Corrige l’intégration Icinga Web 2 et le transport de commandes
This commit is contained in:
parent
f865013a54
commit
3ea21ef27e
10 changed files with 247 additions and 19 deletions
|
|
@ -48,6 +48,14 @@
|
|||
- validate icinga2
|
||||
- restart icinga2
|
||||
|
||||
- name: enable command feature
|
||||
ansible.builtin.command: icinga2 feature enable command
|
||||
args:
|
||||
creates: /etc/icinga2/features-enabled/command.conf
|
||||
notify:
|
||||
- validate icinga2
|
||||
- restart icinga2
|
||||
|
||||
- name: enable and start icinga2
|
||||
ansible.builtin.service:
|
||||
name: icinga2
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
[icinga2]
|
||||
transport = "local"
|
||||
path = "{{ life_noc_icinga2_command_pipe }}"
|
||||
path = "/run/icinga2/cmd/icinga2.cmd"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
[global]
|
||||
show_stacktraces = "0"
|
||||
config_backend = "ini"
|
||||
config_backend = "db"
|
||||
config_resource = "icingaweb2"
|
||||
|
||||
[logging]
|
||||
log = "syslog"
|
||||
|
|
|
|||
|
|
@ -187,6 +187,18 @@
|
|||
- validate icinga
|
||||
- reload icinga
|
||||
|
||||
- name: install generic life-noc probe plugin
|
||||
ansible.builtin.copy:
|
||||
src: "{{ life_noc_project_root }}/checks/check_life_noc_probe.py"
|
||||
dest: /usr/lib/nagios/plugins/check_life_noc_probe.py
|
||||
remote_src: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
notify:
|
||||
- validate icinga
|
||||
- reload icinga
|
||||
|
||||
- name: ensure bpm destination directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ life_noc_bpm_destination_dir }}"
|
||||
|
|
|
|||
61
checks/check_life_noc_probe.py
Normal file
61
checks/check_life_noc_probe.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
from datetime import datetime, timezone
|
||||
|
||||
|
||||
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 main() -> int:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--probe-type", required=True)
|
||||
ap.add_argument("--source-type", required=True)
|
||||
ap.add_argument("--source-value", required=True)
|
||||
ap.add_argument("--unit", required=True)
|
||||
ap.add_argument("--unknown-lt", type=float)
|
||||
ap.add_argument("--ok-gte", type=float)
|
||||
ap.add_argument("--warning-gte", type=float)
|
||||
ap.add_argument("--critical-gte", type=float)
|
||||
ap.add_argument("--on-error", default="critical")
|
||||
ap.add_argument("--label", default="life-noc")
|
||||
args = ap.parse_args()
|
||||
|
||||
try:
|
||||
if args.probe_type != "elapsed_time":
|
||||
exit_with(2, f"CRITICAL - probe_type non supporté: {args.probe_type}")
|
||||
|
||||
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}")
|
||||
|
||||
dt = parse_date(args.source_value)
|
||||
now = datetime.now(timezone.utc)
|
||||
delta_days = (now - dt).total_seconds() / 86400.0
|
||||
|
||||
if args.critical_gte is not None and delta_days >= args.critical_gte:
|
||||
exit_with(2, f"CRITICAL - {args.label}: {delta_days:.1f} jours")
|
||||
if args.warning_gte is not None and delta_days >= args.warning_gte:
|
||||
exit_with(1, f"WARNING - {args.label}: {delta_days:.1f} jours")
|
||||
if args.ok_gte is not None and delta_days >= args.ok_gte:
|
||||
exit_with(0, f"OK - {args.label}: {delta_days:.1f} jours")
|
||||
if args.unknown_lt is not None and delta_days < args.unknown_lt:
|
||||
exit_with(3, f"UNKNOWN - {args.label}: {delta_days:.1f} jours")
|
||||
|
||||
exit_with(2, f"CRITICAL - {args.label}: seuils incohérents")
|
||||
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())
|
||||
38
domains.yaml
38
domains.yaml
|
|
@ -2,12 +2,40 @@ domains:
|
|||
|
||||
revue:
|
||||
- name: revue-quotidienne-life-noc
|
||||
date: 2026-03-07
|
||||
notes: Revue quotidienne du tableau Life-NOC
|
||||
|
||||
date: "2026-03-13"
|
||||
notes: Vérifier le tableau de bord Life-NOC
|
||||
probe:
|
||||
type: elapsed_time
|
||||
source:
|
||||
type: manual_date
|
||||
value: "2026-03-13"
|
||||
metric:
|
||||
unit: days
|
||||
thresholds:
|
||||
unknown_lt: 1
|
||||
ok_gte: 1
|
||||
warning_gte: 2
|
||||
critical_gte: 3
|
||||
policy:
|
||||
on_error: critical
|
||||
|
||||
- name: revue-hebdomadaire-priorites
|
||||
date: 2026-03-08
|
||||
notes: Revue hebdomadaire des priorités personnelles et professionnelles
|
||||
date: "2026-03-10"
|
||||
notes: Revoir les priorités de la semaine
|
||||
probe:
|
||||
type: elapsed_time
|
||||
source:
|
||||
type: manual_date
|
||||
value: "2026-03-10"
|
||||
metric:
|
||||
unit: days
|
||||
thresholds:
|
||||
unknown_lt: 5
|
||||
ok_gte: 5
|
||||
warning_gte: 7
|
||||
critical_gte: 10
|
||||
policy:
|
||||
on_error: critical
|
||||
|
||||
- name: revue-mensuelle-systeme-vie
|
||||
date: 2026-04-01
|
||||
|
|
|
|||
16
icinga/commands/check_life_noc_probe.conf
Normal file
16
icinga/commands/check_life_noc_probe.conf
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
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$"
|
||||
"--unit" = "$life_noc_metric_unit$"
|
||||
"--unknown-lt" = "$life_noc_threshold_unknown_lt$"
|
||||
"--ok-gte" = "$life_noc_threshold_ok_gte$"
|
||||
"--warning-gte" = "$life_noc_threshold_warning_gte$"
|
||||
"--critical-gte" = "$life_noc_threshold_critical_gte$"
|
||||
"--on-error" = "$life_noc_on_error$"
|
||||
"--label" = "$life_noc_label$"
|
||||
}
|
||||
}
|
||||
|
|
@ -5,22 +5,36 @@
|
|||
*/
|
||||
|
||||
apply Service "revue-revue-quotidienne-life-noc" {
|
||||
import "service_echeance"
|
||||
vars.date_echeance = "2026-03-07"
|
||||
vars.mock_state = "OK"
|
||||
vars.mock_message = "Sous contrôle"
|
||||
import "service_life_noc_probe"
|
||||
vars.life_noc_probe_type = "elapsed_time"
|
||||
vars.life_noc_source_type = "manual_date"
|
||||
vars.life_noc_source_value = "2026-03-13"
|
||||
vars.life_noc_metric_unit = "days"
|
||||
vars.life_noc_label = "revue-quotidienne-life-noc"
|
||||
groups = [ "REVUE" ]
|
||||
notes = "Revue quotidienne du tableau Life-NOC"
|
||||
vars.life_noc_threshold_unknown_lt = "1"
|
||||
vars.life_noc_threshold_ok_gte = "1"
|
||||
vars.life_noc_threshold_warning_gte = "2"
|
||||
vars.life_noc_threshold_critical_gte = "3"
|
||||
vars.life_noc_on_error = "critical"
|
||||
notes = "Vérifier le tableau de bord Life-NOC"
|
||||
assign where host.name == "life-noc"
|
||||
}
|
||||
|
||||
apply Service "revue-revue-hebdomadaire-priorites" {
|
||||
import "service_echeance"
|
||||
vars.date_echeance = "2026-03-08"
|
||||
vars.mock_state = "OK"
|
||||
vars.mock_message = "Sous contrôle"
|
||||
import "service_life_noc_probe"
|
||||
vars.life_noc_probe_type = "elapsed_time"
|
||||
vars.life_noc_source_type = "manual_date"
|
||||
vars.life_noc_source_value = "2026-03-10"
|
||||
vars.life_noc_metric_unit = "days"
|
||||
vars.life_noc_label = "revue-hebdomadaire-priorites"
|
||||
groups = [ "REVUE" ]
|
||||
notes = "Revue hebdomadaire des priorités personnelles et professionnelles"
|
||||
vars.life_noc_threshold_unknown_lt = "5"
|
||||
vars.life_noc_threshold_ok_gte = "5"
|
||||
vars.life_noc_threshold_warning_gte = "7"
|
||||
vars.life_noc_threshold_critical_gte = "10"
|
||||
vars.life_noc_on_error = "critical"
|
||||
notes = "Revoir les priorités de la semaine"
|
||||
assign where host.name == "life-noc"
|
||||
}
|
||||
|
||||
|
|
|
|||
4
icinga/templates/service_life_noc_probe.conf
Normal file
4
icinga/templates/service_life_noc_probe.conf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
template Service "service_life_noc_probe" {
|
||||
check_command = "check_life_noc_probe"
|
||||
vars.life_noc_on_error = "critical"
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ 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"
|
||||
|
||||
|
|
@ -73,7 +74,7 @@ def normalize_mock_state(value: str) -> str:
|
|||
return state
|
||||
|
||||
|
||||
def render_service(domain: str, group_name: str, item: dict) -> str:
|
||||
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()
|
||||
|
|
@ -107,6 +108,85 @@ def render_service(domain: str, group_name: str, item: dict) -> str:
|
|||
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["value"]).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"]))}"')
|
||||
|
||||
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")
|
||||
|
||||
if str(probe.get("type", "")).strip() != "elapsed_time":
|
||||
raise ValueError("seul probe.type=elapsed_time est supporté en v1")
|
||||
|
||||
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")
|
||||
if "value" not in source:
|
||||
raise ValueError("probe.source.value est 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)}" {{',
|
||||
|
|
@ -186,7 +266,11 @@ def main() -> int:
|
|||
return 1
|
||||
|
||||
try:
|
||||
content.append(render_service(domain, group_name, item))
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in a new issue