4e vague de démockage des sondes

This commit is contained in:
Daniel Allaire 2026-03-17 09:31:06 -04:00
parent 3397a00d42
commit a64386db45
8 changed files with 208 additions and 18 deletions

View file

@ -135,6 +135,41 @@ def main() -> int:
exit_with(2, f"CRITICAL - {args.label}: seuils incohérents")
elif args.probe_type == "current_value":
if args.source_type != "manual_value":
exit_with(2, f"CRITICAL - source_type non supporté: {args.source_type}")
if args.unit not in {"%", "units", "litres", "volts", "watts"}:
exit_with(2, f"CRITICAL - unité non supportée: {args.unit}")
if args.inputs_file and args.item_key:
item = load_input_item(args.inputs_file, args.item_key)
metric = float(str(item.get("value", "")).strip())
elif args.source_value:
metric = float(str(args.source_value).strip())
else:
raise ValueError("manual_value exige inputs-file/item-key ou source-value")
unit_suffix = args.unit
if args.critical_lt is not None and metric < args.critical_lt:
exit_with(2, f"CRITICAL - {args.label}: {metric:.1f} {unit_suffix}")
if args.critical_gte is not None and metric >= args.critical_gte:
exit_with(2, f"CRITICAL - {args.label}: {metric:.1f} {unit_suffix}")
if args.warning_lt is not None and metric < args.warning_lt:
exit_with(1, f"WARNING - {args.label}: {metric:.1f} {unit_suffix}")
if args.warning_gte is not None and metric >= args.warning_gte:
exit_with(1, f"WARNING - {args.label}: {metric:.1f} {unit_suffix}")
if args.ok_gte is not None and metric >= args.ok_gte:
exit_with(0, f"OK - {args.label}: {metric:.1f} {unit_suffix}")
if args.ok_lt is not None and metric < args.ok_lt:
exit_with(0, f"OK - {args.label}: {metric:.1f} {unit_suffix}")
if args.unknown_gte is not None and metric >= args.unknown_gte:
exit_with(3, f"UNKNOWN - {args.label}: {metric:.1f} {unit_suffix}")
if args.unknown_lt is not None and metric < args.unknown_lt:
exit_with(3, f"UNKNOWN - {args.label}: {metric:.1f} {unit_suffix}")
exit_with(2, f"CRITICAL - {args.label}: seuils incohérents")
else:
exit_with(2, f"CRITICAL - probe_type non supporté: {args.probe_type}")

View file

@ -1014,6 +1014,24 @@ domains:
- name: verification-tension-batteries
date: 2026-03-15
notes: Vérification des tensions et cohérence des batteries
probe:
type: current_value
source:
type: manual_value
inputs_file: /opt/life-noc/data/inputs/energie.yaml
item_key: verification-tension-batteries
metric:
unit: volts
thresholds:
critical_lt: 50.0
warning_lt: 51.2
ok_gte: 51.2
policy:
on_error: critical
ui:
form_mode: numeric_value
allow_complete: false
allow_manual_edit: true
- name: verification-smartshunt
date: 2026-03-15
notes: Vérification SmartShunt et cohérence du monitoring
@ -1650,6 +1668,24 @@ domains:
- name: verification-batterie-voiture
date: 2026-10-01
notes: Vérification de l'état de la batterie automobile
probe:
type: current_value
source:
type: manual_value
inputs_file: /opt/life-noc/data/inputs/voiture.yaml
item_key: verification-batterie-voiture
metric:
unit: volts
thresholds:
critical_lt: 12.2
warning_lt: 12.5
ok_gte: 12.5
policy:
on_error: critical
ui:
form_mode: numeric_value
allow_complete: false
allow_manual_edit: true
- name: verification-balais-essuie-glace
date: 2026-05-01
notes: Vérification des essuie-glaces
@ -2244,9 +2280,45 @@ domains:
- name: verification-ups-infrastructure
date: 2026-04-15
notes: Vérification des UPS de l'infrastructure
probe:
type: current_value
source:
type: manual_value
inputs_file: /opt/life-noc/data/inputs/infrastructure-chezlepro.yaml
item_key: verification-ups-infrastructure
metric:
unit: '%'
thresholds:
critical_lt: 25
warning_lt: 50
ok_gte: 50
policy:
on_error: critical
ui:
form_mode: numeric_value
allow_complete: false
allow_manual_edit: true
- name: verification-capacite-stockage
date: 2026-04-15
notes: Vérifier capacité et croissance du stockage
probe:
type: current_value
source:
type: manual_value
inputs_file: /opt/life-noc/data/inputs/infrastructure-chezlepro.yaml
item_key: verification-capacite-stockage
metric:
unit: '%'
thresholds:
critical_lt: 15
warning_lt: 25
ok_gte: 25
policy:
on_error: critical
ui:
form_mode: numeric_value
allow_complete: false
allow_manual_edit: true
- name: verification-apt-cacher-ng
date: 2026-05-01
notes: Vérification du bon fonctionnement d'apt-cacher-ng

View file

@ -25,11 +25,19 @@ apply Service "energie-inspection-batteries-lifepo4" {
}
apply Service "energie-verification-tension-batteries" {
import "service_echeance"
vars.date_echeance = "2026-03-15"
vars.mock_state = "OK"
vars.mock_message = "Sous contrôle"
import "service_life_noc_probe"
vars.life_noc_probe_type = "current_value"
vars.life_noc_source_type = "manual_value"
vars.life_noc_source_value = ""
vars.life_noc_inputs_file = "/opt/life-noc/data/inputs/energie.yaml"
vars.life_noc_item_key = "verification-tension-batteries"
vars.life_noc_metric_unit = "volts"
vars.life_noc_label = "verification-tension-batteries"
groups = [ "ENERGIE" ]
vars.life_noc_threshold_ok_gte = "51.2"
vars.life_noc_threshold_warning_lt = "51.2"
vars.life_noc_threshold_critical_lt = "50.0"
vars.life_noc_on_error = "critical"
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"

View file

@ -76,22 +76,38 @@ apply Service "infrastructure-chezlepro-verification-pbs-truenas" {
}
apply Service "infrastructure-chezlepro-verification-ups-infrastructure" {
import "service_echeance"
vars.date_echeance = "2026-04-15"
vars.mock_state = "OK"
vars.mock_message = "Sous contrôle"
import "service_life_noc_probe"
vars.life_noc_probe_type = "current_value"
vars.life_noc_source_type = "manual_value"
vars.life_noc_source_value = ""
vars.life_noc_inputs_file = "/opt/life-noc/data/inputs/infrastructure-chezlepro.yaml"
vars.life_noc_item_key = "verification-ups-infrastructure"
vars.life_noc_metric_unit = "%"
vars.life_noc_label = "verification-ups-infrastructure"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
vars.life_noc_threshold_ok_gte = "50"
vars.life_noc_threshold_warning_lt = "50"
vars.life_noc_threshold_critical_lt = "25"
vars.life_noc_on_error = "critical"
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"
}
apply Service "infrastructure-chezlepro-verification-capacite-stockage" {
import "service_echeance"
vars.date_echeance = "2026-04-15"
vars.mock_state = "OK"
vars.mock_message = "Sous contrôle"
import "service_life_noc_probe"
vars.life_noc_probe_type = "current_value"
vars.life_noc_source_type = "manual_value"
vars.life_noc_source_value = ""
vars.life_noc_inputs_file = "/opt/life-noc/data/inputs/infrastructure-chezlepro.yaml"
vars.life_noc_item_key = "verification-capacite-stockage"
vars.life_noc_metric_unit = "%"
vars.life_noc_label = "verification-capacite-stockage"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
vars.life_noc_threshold_ok_gte = "25"
vars.life_noc_threshold_warning_lt = "25"
vars.life_noc_threshold_critical_lt = "15"
vars.life_noc_on_error = "critical"
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"

View file

@ -96,11 +96,19 @@ apply Service "voiture-verification-timing-belt" {
}
apply Service "voiture-verification-batterie-voiture" {
import "service_echeance"
vars.date_echeance = "2026-10-01"
vars.mock_state = "OK"
vars.mock_message = "Sous contrôle"
import "service_life_noc_probe"
vars.life_noc_probe_type = "current_value"
vars.life_noc_source_type = "manual_value"
vars.life_noc_source_value = ""
vars.life_noc_inputs_file = "/opt/life-noc/data/inputs/voiture.yaml"
vars.life_noc_item_key = "verification-batterie-voiture"
vars.life_noc_metric_unit = "volts"
vars.life_noc_label = "verification-batterie-voiture"
groups = [ "VOITURE" ]
vars.life_noc_threshold_ok_gte = "12.5"
vars.life_noc_threshold_warning_lt = "12.5"
vars.life_noc_threshold_critical_lt = "12.2"
vars.life_noc_on_error = "critical"
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"

View file

@ -194,4 +194,30 @@ def compute_state_for_item(domain: str, item_key: str) -> str:
return "UNKNOWN"
return "CRITICAL"
if probe_type == "current_value":
if source_type != "manual_value":
return "CRITICAL"
if metric_unit not in {"%", "units", "litres", "volts", "watts"}:
return "CRITICAL"
value = float(str(item.get("value", "")).strip())
if "critical_lt" in thresholds and value < float(thresholds["critical_lt"]):
return "CRITICAL"
if "critical_gte" in thresholds and value >= float(thresholds["critical_gte"]):
return "CRITICAL"
if "warning_lt" in thresholds and value < float(thresholds["warning_lt"]):
return "WARNING"
if "warning_gte" in thresholds and value >= float(thresholds["warning_gte"]):
return "WARNING"
if "ok_gte" in thresholds and value >= float(thresholds["ok_gte"]):
return "OK"
if "ok_lt" in thresholds and value < float(thresholds["ok_lt"]):
return "OK"
if "unknown_gte" in thresholds and value >= float(thresholds["unknown_gte"]):
return "UNKNOWN"
if "unknown_lt" in thresholds and value < float(thresholds["unknown_lt"]):
return "UNKNOWN"
return "CRITICAL"
return "CRITICAL"

View file

@ -202,8 +202,8 @@ def validate_probe(item: dict) -> None:
raise ValueError("probe doit être un objet")
probe_type = str(probe.get("type", "")).strip()
if probe_type not in {"elapsed_time", "days_until_due", "elapsed_distance", "remaining_quantity"}:
raise ValueError("seuls probe.type=elapsed_time, days_until_due, elapsed_distance et remaining_quantity sont supportés")
if probe_type not in {"elapsed_time", "days_until_due", "elapsed_distance", "remaining_quantity", "current_value"}:
raise ValueError("seuls probe.type=elapsed_time, days_until_due, elapsed_distance, remaining_quantity et current_value sont supportés")
source = probe.get("source")
if not isinstance(source, dict):
@ -228,6 +228,11 @@ def validate_probe(item: dict) -> None:
raise ValueError("seul probe.source.type=manual_value est supporté pour remaining_quantity")
if not has_store_ref:
raise ValueError("probe.source.inputs_file + item_key requis pour remaining_quantity")
elif probe_type == "current_value":
if source_type != "manual_value":
raise ValueError("seul probe.source.type=manual_value est supporté pour current_value")
if not has_store_ref:
raise ValueError("probe.source.inputs_file + item_key requis pour current_value")
metric = probe.get("metric")
if not isinstance(metric, dict):
@ -239,6 +244,8 @@ def validate_probe(item: dict) -> None:
raise ValueError("seul probe.metric.unit=km est supporté pour elapsed_distance")
if probe_type == "remaining_quantity" and metric_unit not in {"%", "units", "litres"}:
raise ValueError("probe.metric.unit doit être %, units ou litres pour remaining_quantity")
if probe_type == "current_value" and metric_unit not in {"%", "units", "litres", "volts", "watts"}:
raise ValueError("probe.metric.unit doit être %, units, litres, volts ou watts pour current_value")
thresholds = probe.get("thresholds")
if not isinstance(thresholds, dict):

View file

@ -663,6 +663,24 @@ def page_item(domain: str, item_key: str) -> HTMLResponse:
</form>
</section>
"""
elif allow_manual_edit and form_mode == "numeric_value":
unit = str(service.get("probe", {}).get("metric", {}).get("unit", "")).strip()
unit_hint = f" ({html_escape(unit)})" if unit else ""
manual_form_html = f"""
<section class="card">
<h2>Mettre à jour la valeur</h2>
<p class="muted">Saisir la valeur observée{unit_hint}.</p>
<form method="post" action="/life-noc/item/{html_escape(domain)}/{html_escape(item_key)}/set" class="form-grid">
<label>Valeur courante{unit_hint}
<input type="text" inputmode="decimal" name="value" value="{html_escape(current_input.get('value', ''))}" required>
</label>
<input type="hidden" name="origin" value="manual">
<div class="actions">
<button type="submit">Enregistrer</button>
</div>
</form>
</section>
"""
complete_form_html = ""
if allow_complete: