chore: Update validation script and generate Exchange docs

- tools: Fixed warning collection in validate_zabbix_template.py.
- docs: Generated template_exchange_gold_ptbr_generated.md with latest template changes.
This commit is contained in:
João Pedro Toledo Goncalves 2026-01-13 13:08:27 -03:00
parent 7c5cab088f
commit 0bc4984f89
2 changed files with 157 additions and 0 deletions

View File

@ -0,0 +1,74 @@
# Documentação: Microsoft Exchange Server 2016 by Zabbix agent active
**Template:** Microsoft Exchange Server 2016 by Zabbix agent active
**Descrição:**
Template Gold para Microsoft Exchange Server (2016/2019).
Monitoramento 360º com foco em performance e disponibilidade.
Inclui:
- Filas de Transporte (Submission, Delivery, Poison)
- Banco de Dados (Latência, Page Faults, Status)
- Serviços MSExchange (Discovery Automático)
- Web Access (OWA/ECP)
- Métricas de ActiveSync e WebServices
Desenvolvido por Arthur "O Farol" Mendes.
## Itens Monitorados
### Itens Globais
- **Bancos de Dados: Total Montados** (`perf_counter_en["\MSExchange Active Manager(_total)\Database Mounted"]`)
- **ActiveSync: Comandos Ping Pendentes** (`perf_counter_en["\MSExchange ActiveSync\Ping Commands Pending", {$MS.EXCHANGE.PERF.INTERVAL}]`)
- **ActiveSync: Requisições por Segundo** (`perf_counter_en["\MSExchange ActiveSync\Requests/sec", {$MS.EXCHANGE.PERF.INTERVAL}]`)
- **OWA: Usuários Únicos Atuais** (`perf_counter_en["\MSExchange OWA\Current Unique Users", {$MS.EXCHANGE.PERF.INTERVAL}]`)
- **OWA: Requisições por Segundo** (`perf_counter_en["\MSExchange OWA\Requests/sec", {$MS.EXCHANGE.PERF.INTERVAL}]`)
- **WebServices: Requisições por Segundo** (`perf_counter_en["\MSExchangeWS\Requests/sec", {$MS.EXCHANGE.PERF.INTERVAL}]`)
- **Zabbix: Disponibilidade do Agente (Active)** (`zabbix[host,active_agent,available]`)
- **Fila de Transporte: Submission Queue** (`perf_counter_en["\MSExchangeTransport Queues(_Total)\Submission Queue Length"]`)
- **Fila de Transporte: Active Mailbox Delivery** (`perf_counter_en["\MSExchangeTransport Queues(_Total)\Active Mailbox Delivery Queue Length"]`)
- **Fila de Transporte: Poison Queue** (`perf_counter_en["\MSExchangeTransport Queues(_Total)\Poison Queue Length"]`)
- **Fila de Transporte: Retry Mailbox Delivery** (`perf_counter_en["\MSExchangeTransport Queues(_Total)\Retry Mailbox Delivery Queue Length"]`)
- **Service: Status do Microsoft Exchange Transport Submission** (`service.info["MSExchangeSubmission",state]`)
- **Service: Status do Microsoft Exchange Transport** (`service.info["MSExchangeTransport",state]`)
### Regras de Descoberta (LLD)
#### Descoberta de Serviços Exchange (`service.discovery`)
- **Protótipos de Itens:**
- Serviço: Status do {#SERVICE.DISPLAYNAME} (`service.info["{#SERVICE.NAME}",state]`)
#### Descoberta de Bancos de Dados (`perf_instance.discovery["MSExchange Active Manager"]`)
- **Protótipos de Itens:**
- Banco [{#INSTANCE}]: Função da Cópia (`perf_counter_en["\MSExchange Active Manager({#INSTANCE})\Database Copy Role Active"]`)
- Banco [{#INSTANCE}]: Page Faults/seg (`perf_counter_en["\MSExchange Database({#INF.STORE})\Database Page Fault Stalls/sec", {$MS.EXCHANGE.PERF.INTERVAL}]`)
- Banco [{#INSTANCE}]: Logs Aguardando (Stalled) (`perf_counter_en["\MSExchange Database({#INF.STORE})\Log Record Stalls/sec", {$MS.EXCHANGE.PERF.INTERVAL}]`)
- Banco [{#INSTANCE}]: Latência de Leitura (Ativo) (`perf_counter_en["\MSExchange Database ==> Instances({#INF.STORE}/_Total)\I/O Database Reads (Attached) Average Latency", {$MS.EXCHANGE.PERF.INTERVAL}]`)
- Banco [{#INSTANCE}]: Latência de Escrita (Ativo) (`perf_counter_en["\MSExchange Database ==> Instances({#INF.STORE}/_Total)\I/O Database Writes (Attached) Average Latency", {$MS.EXCHANGE.PERF.INTERVAL}]`)
- Banco [{#INSTANCE}]: Estado (`perf_counter_en["\MSExchangeIS Store({#INSTANCE})\Database State"]`)
- Banco [{#INSTANCE}]: Tamanho do Arquivo (`perf_counter_en["\MSExchange Database({#INF.STORE})\Database File Size In Bytes", {$MS.EXCHANGE.PERF.INTERVAL}]`)
- Banco [{#INSTANCE}]: Total de Itens (`perf_counter_en["\MSExchange IS Store({#INF.STORE})\Item Count", {$MS.EXCHANGE.PERF.INTERVAL}]`)
## Alertas (Triggers)
### Triggers Globais
- [HIGH] **📉 Exchange: Queda Anômala de Tráfego OWA**
- [HIGH] **🚨 Exchange: Agente Zabbix Indisponível**
- [AVERAGE] **⚠️ Exchange: Fila de Submissão Alta**
- [HIGH] **🔮 Exchange: Fila de Submissão em Crescimento Rápido**
- [AVERAGE] **⚠️ Exchange: Fila de Entrega Local Alta**
- [HIGH] **🔮 Exchange: Fila de Entrega em Crescimento Rápido**
- [HIGH] **🚨 Exchange: Mensagens em Quarentena (Poison Queue) detectadas**
- [HIGH] **🚨 Serviço Parado: Microsoft Exchange Transport Submission**
- [HIGH] **🚨 Serviço Parado: Microsoft Exchange Transport**
### Protótipos de Triggers (LLD)
**Regra: Descoberta de Serviços Exchange**
- [HIGH] **🚨 Serviço Parado: {#SERVICE.DISPLAYNAME}**
**Regra: Descoberta de Bancos de Dados**
- [AVERAGE] **⚠️ Exchange: Page Faults Elevados em {#INSTANCE}**
- [AVERAGE] **🐢 Exchange: Escrita de Logs Travada em {#INSTANCE}**
- [WARNING] **🐢 Exchange: Latência de Leitura Alta em {#INSTANCE}**
- [WARNING] **🐢 Exchange: Latência de Escrita Alta em {#INSTANCE}**
- [WARNING] **⚠️ Exchange: Banco de Dados Grande em {#INSTANCE}**

View File

@ -257,6 +257,76 @@ def validate_nested_structure(content):
return errors
def validate_zabbix_7_compliance(content):
"""
Check for Zabbix 7.0 specific issues and common YAML gotchas.
- Boolean keys (no: 1 -> False: 1)
- REGEXP vs MATCHES_REGEX
- Forbidden tags (e.g. 'no' in http steps)
- Hostname mismatches in triggers
"""
errors = []
warnings = []
# 1. Get Template Name for Hostname Mismatch Check
template_name = None
if isinstance(content, dict) and 'zabbix_export' in content:
if 'templates' in content and isinstance(content['templates'], list):
if len(content['templates']) > 0:
template_name = content['templates'][0].get('name')
def check_node(node, path="root"):
if isinstance(node, dict):
# Check 1: Boolean Keys
# PyYAML loads unquoted 'no', 'on', 'off' as booleans if unsafe, safe_load handles standard yaml 1.1 booleans
# But we are checking the KEYS of the dict.
# Convert keys to list to avoid runtime error during iteration
for k, v in node.items():
if isinstance(k, bool):
errors.append(f"[INVALID YAML KEY] Found boolean key '{k}' at {path}. Quote it! (e.g. 'no': 1)")
# Check 3: Forbidden tags in httptests
if path.endswith("steps") and isinstance(node, list) is False: # We are inside a step dict
if k == 'no':
errors.append(f"[FORBIDDEN TAG] Tag 'no' found at {path}. Zabbix 7.0 determines order by list position.")
# Check 2: Deprecated Operators
if k == 'operator' and v == 'REGEXP':
errors.append(f"[DEPRECATED CONSTANT] 'REGEXP' at {path}. Use 'MATCHES_REGEX' for Zabbix 7.0+.")
# Check 4: Hostname Mismatch in Triggers
if k == 'expression' and isinstance(v, str) and template_name:
# Look for {HOST.HOST} usage or literal matches
# Only flagged if we find a LITERAL string that looks like a hostname but isn't the current template name
# AND it's not a standard macro
# Regex to find /Host Name/Key
# Matches: /Hostname/key
matches = re.findall(r'\/([^\/]+)\/', v)
for match in matches:
if match != template_name and match != '{HOST.HOST}' and '$' not in match:
# Heuristic: If it looks like "Microsoft Exchange..." but isn't exact match
if "Microsoft" in match or "Exchange" in match or "Windows" in match:
warnings.append(f"[HOSTNAME MISMATCH] Trigger at {path} references '{match}' but template is '{template_name}'.")
# Check 5: Empty parameters in functions (e.g. forecast(...,,15m))
if ',,' in v:
warnings.append(f"[POTENTIAL SYNTAX ERROR] Found empty parameter ',,' in expression at {path}. Zabbix sometimes rejects this. Use explicit '0' or '0s'.")
if 'avg(' in v and re.search(r'avg\(.*,\d+[smhdw],\d+[smhdw]\)', v):
warnings.append(f"[POTENTIAL SYNTAX ERROR] Found 'avg' with comma separation (1h,1d) at {path}. Zabbix 7.0 uses colon (1h:now-1d).")
for k, v in node.items():
check_node(v, f"{path}.{k}")
elif isinstance(node, list):
for i, item in enumerate(node):
check_node(item, f"{path}[{i}]")
check_node(content)
return errors, warnings
def check_duplicate_yaml_keys(file_path):
"""
Check for duplicate YAML keys at the same level (e.g., two 'macros:' sections).
@ -427,6 +497,19 @@ def validate_yaml(file_path):
else:
print(" ✅ Structure nesting looks correct")
# ========== 7. Zabbix 7.0 Compliance Check ==========
print("\n[7/7] Checking for Zabbix 7.0 Compliance & Common Errors...")
z7_errors, z7_warnings = validate_zabbix_7_compliance(content)
if z7_errors:
all_errors.extend(z7_errors)
print(f" ❌ Found {len(z7_errors)} compliance errors")
else:
print(" ✅ Zabbix 7.0 compliance looks good")
if z7_warnings:
warnings.extend(z7_warnings)
# ========== Summary ==========
print("\n" + "=" * 60)