feat(templates): Resolve cross-template collisions and validation
- exchange: Replaced 'service.discovery' and 'service.info' with WMI to avoid conflict with Windows template. - exchange: Added JS preprocessing for Service State mapping. - windows: Fixed missing 'ZABBIX_ACTIVE' type on RDP, Disk Queue, and Security Audit items. - tools: Updated 'validate_zabbix_template.py' to detect reserved keys and Active Agent compliance. - docs: Regenerated documentation for both templates.
This commit is contained in:
parent
0bc4984f89
commit
b74b96473c
|
|
@ -277,33 +277,35 @@ zabbix_export:
|
|||
- uuid: c3c2d45be1f4439292678d07d36eb042
|
||||
name: Descoberta de Serviços Exchange
|
||||
type: ZABBIX_ACTIVE
|
||||
key: service.discovery
|
||||
key: wmi.getall[root\cimv2,"SELECT Name, DisplayName, State FROM Win32_Service WHERE Name LIKE 'MSExchange%' AND StartMode='Auto'"]
|
||||
delay: 1h
|
||||
filter:
|
||||
evaltype: AND
|
||||
conditions:
|
||||
- macro: '{#SERVICE.NAME}'
|
||||
value: ^(?!MSExchangeTransport$|MSExchangeSubmission$)MSExchange.*
|
||||
operator: MATCHES_REGEX
|
||||
- macro: '{#SERVICE.STARTUPNAME}'
|
||||
value: automatic
|
||||
operator: MATCHES_REGEX
|
||||
description: Descobre serviços do Exchange que iniciam automaticamente.
|
||||
description: Descobre serviços do Exchange que iniciam automaticamente via WMI (evita conflito com Template Windows).
|
||||
lld_macro_paths:
|
||||
- lld_macro: '{#SERVICE.NAME}'
|
||||
path: $.Name
|
||||
- lld_macro: '{#SERVICE.DISPLAYNAME}'
|
||||
path: $.DisplayName
|
||||
item_prototypes:
|
||||
- uuid: dd3c489ce5f94540b50b6f2aa1aac65e
|
||||
name: 'Serviço: Status do {#SERVICE.DISPLAYNAME}'
|
||||
type: ZABBIX_ACTIVE
|
||||
key: service.info["{#SERVICE.NAME}",state]
|
||||
key: wmi.get[root\cimv2,"SELECT State FROM Win32_Service WHERE Name='{#SERVICE.NAME}'"]
|
||||
delay: 3m
|
||||
description: Status atual do serviço {#SERVICE.DISPLAYNAME}.
|
||||
description: Status atual do serviço {#SERVICE.DISPLAYNAME} via WMI.
|
||||
valuemap:
|
||||
name: Service state
|
||||
preprocessing:
|
||||
- type: JAVASCRIPT
|
||||
parameters:
|
||||
- |
|
||||
var states = {'Running': 0, 'Paused': 1, 'Start Pending': 2, 'Pause Pending': 3, 'Continue Pending': 4, 'Stop Pending': 5, 'Stopped': 6};
|
||||
return states[value] !== undefined ? states[value] : 7;
|
||||
tags:
|
||||
- tag: component
|
||||
value: service
|
||||
trigger_prototypes:
|
||||
- uuid: 38cefb16c51448d69e05b322e08a495a
|
||||
expression: last(/Microsoft Exchange Server 2016 by Zabbix agent active/service.info["{#SERVICE.NAME}",state])<>0
|
||||
expression: last(/Microsoft Exchange Server 2016 by Zabbix agent active/wmi.get[root\cimv2,"SELECT State FROM Win32_Service WHERE Name='{#SERVICE.NAME}'"])<>0
|
||||
name: '🚨 Serviço Parado: {#SERVICE.DISPLAYNAME}'
|
||||
priority: HIGH
|
||||
description: O serviço {#SERVICE.DISPLAYNAME} deveria estar rodando (Auto) mas está parado.
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ Desenvolvido por Arthur "O Farol" Mendes.
|
|||
|
||||
### Regras de Descoberta (LLD)
|
||||
|
||||
#### Descoberta de Serviços Exchange (`service.discovery`)
|
||||
#### Descoberta de Serviços Exchange (`wmi.getall[root\cimv2,"SELECT Name, DisplayName, State FROM Win32_Service WHERE Name LIKE 'MSExchange%' AND StartMode='Auto'"]`)
|
||||
- **Protótipos de Itens:**
|
||||
- Serviço: Status do {#SERVICE.DISPLAYNAME} (`service.info["{#SERVICE.NAME}",state]`)
|
||||
- Serviço: Status do {#SERVICE.DISPLAYNAME} (`wmi.get[root\cimv2,"SELECT State FROM Win32_Service WHERE Name='{#SERVICE.NAME}'"]`)
|
||||
#### 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"]`)
|
||||
|
|
|
|||
|
|
@ -545,6 +545,7 @@ zabbix_export:
|
|||
- uuid: ab5cc8d3c6734c7c9456c02dfecffd5d
|
||||
name: 'RDP: Sessões Ativas (Total)'
|
||||
key: perf_counter_en["\Terminal Services\Total Sessions"]
|
||||
type: ZABBIX_ACTIVE
|
||||
delay: 1m
|
||||
value_type: FLOAT
|
||||
units: ''
|
||||
|
|
@ -563,6 +564,7 @@ zabbix_export:
|
|||
description: Existem muitas sessões de terminal abertas. Isso pode consumir recursos ou indicar sessões "penduradas".
|
||||
- uuid: 44cf725b9c03464cac04df1103f51092
|
||||
name: 'Disco: Tamanho da Fila (Queue Length)'
|
||||
type: ZABBIX_ACTIVE
|
||||
key: perf_counter_en["\PhysicalDisk(_Total)\Current Disk Queue Length"]
|
||||
delay: 1m
|
||||
value_type: FLOAT
|
||||
|
|
@ -581,6 +583,7 @@ zabbix_export:
|
|||
description: A fila de disco está constantemente alta. O armazenamento não está dando conta das requisições.
|
||||
- uuid: 141cc10cfa0b40c0a00e4737a965ef87
|
||||
name: 'Segurança: Falhas de Login (Audit Failure)'
|
||||
type: ZABBIX_ACTIVE
|
||||
key: eventlog[Security,,,,4625]
|
||||
delay: 1m
|
||||
value_type: LOG
|
||||
|
|
|
|||
|
|
@ -322,10 +322,88 @@ def validate_zabbix_7_compliance(content):
|
|||
for i, item in enumerate(node):
|
||||
check_node(item, f"{path}[{i}]")
|
||||
|
||||
check_node(content)
|
||||
check_node(content)
|
||||
return errors, warnings
|
||||
|
||||
|
||||
def validate_reserved_keys(content):
|
||||
"""
|
||||
Check for keys that are known to cause collisions or are reserved/monopolistic.
|
||||
e.g. service.discovery, service.info with generic params.
|
||||
"""
|
||||
warnings = []
|
||||
|
||||
def check_keys(node, path="root"):
|
||||
if isinstance(node, dict):
|
||||
if 'key' in node:
|
||||
key = node['key']
|
||||
# Check for service.discovery (without unique params, it collides generally)
|
||||
if key == 'service.discovery':
|
||||
warnings.append(f"[POTENTIAL COLLISION] Key 'service.discovery' found at {path}. This often conflicts with Windows OS templates. Consider 'wmi.getall'.")
|
||||
|
||||
if str(key).startswith('service.info'):
|
||||
warnings.append(f"[POTENTIAL COLLISION] Key '{key}' found at {path}. Ensure this does not conflict with standard Windows templates. Consider 'wmi.get'.")
|
||||
|
||||
for k, v in node.items():
|
||||
check_keys(v, f"{path}.{k}")
|
||||
elif isinstance(node, list):
|
||||
for i, item in enumerate(node):
|
||||
check_keys(item, f"{path}[{i}]")
|
||||
|
||||
check_keys(content)
|
||||
return warnings
|
||||
|
||||
|
||||
def validate_active_agent_compliance(content):
|
||||
"""
|
||||
If template name suggests 'Active', ensure items have type: ZABBIX_ACTIVE.
|
||||
"""
|
||||
errors = []
|
||||
template_name = ""
|
||||
|
||||
# Extract template name
|
||||
if isinstance(content, dict) and 'zabbix_export' in content:
|
||||
if 'templates' in content and isinstance(content['templates'], list) and len(content['templates']) > 0:
|
||||
template_name = content['templates'][0].get('name', '')
|
||||
|
||||
# Check case-insensitive 'active' in template name
|
||||
if 'active' not in template_name.lower():
|
||||
return errors # Not an active agent template candidate, skip check
|
||||
|
||||
def check_items(node, path="root"):
|
||||
if isinstance(node, dict):
|
||||
# Check if it looks like an item/prototype (has 'key' and 'name' and 'uuid')
|
||||
# Avoiding false positives on non-item dicts
|
||||
if 'key' in node and 'name' in node and 'type' in node:
|
||||
item_type = node.get('type')
|
||||
key = node['key']
|
||||
|
||||
# List of types that are NOT Agent checks (so don't need ZABBIX_ACTIVE)
|
||||
# ZABBIX_PASSIVE is omitted because that's exactly what we want to FLAG if found.
|
||||
allowed_non_agent_types = [
|
||||
'ZABBIX_ACTIVE',
|
||||
'SIMPLE', 'SNMP', 'INTERNAL', 'TRAP', 'EXTERNAL',
|
||||
'DB_MONITOR', 'HTTP_AGENT', 'SSH', 'TELNET', 'CALCULATED', 'JMX', 'DEPENDENT', 'SCRIPT', 'BROWSER'
|
||||
]
|
||||
|
||||
# If type is invalid or missing (None), or explicitly Passive (ZABBIX_PASSIVE/0)
|
||||
if item_type not in allowed_non_agent_types:
|
||||
# If it turns out to be Passive (which is default export if missing, or explicit)
|
||||
errors.append(f"[ACTIVE AGENT MISMATCH] Item '{node['name']}' ({key}) at {path} is not ZABBIX_ACTIVE (found: '{item_type}'). Template '{template_name}' is marked Active.")
|
||||
|
||||
# Recurse
|
||||
for k, v in node.items():
|
||||
check_items(v, f"{path}.{k}")
|
||||
|
||||
elif isinstance(node, list):
|
||||
for i, item in enumerate(node):
|
||||
check_items(item, f"{path}[{i}]")
|
||||
|
||||
check_items(content)
|
||||
return errors
|
||||
|
||||
|
||||
|
||||
def check_duplicate_yaml_keys(file_path):
|
||||
"""
|
||||
|
|
@ -510,6 +588,24 @@ def validate_yaml(file_path):
|
|||
if z7_warnings:
|
||||
warnings.extend(z7_warnings)
|
||||
|
||||
# ========== 8. Reserved Keys/Collision Check ==========
|
||||
print("\n[8/9] Checking for Collision-Prone Keys...")
|
||||
collision_warnings = validate_reserved_keys(content)
|
||||
if collision_warnings:
|
||||
warnings.extend(collision_warnings)
|
||||
print(f" ⚠️ Found {len(collision_warnings)} potential key collisions")
|
||||
else:
|
||||
print(" ✅ Key safety check passed")
|
||||
|
||||
# ========== 9. Active Agent Compliance ==========
|
||||
print("\n[9/9] Checking Active Agent Compliance...")
|
||||
active_errors = validate_active_agent_compliance(content)
|
||||
if active_errors:
|
||||
all_errors.extend(active_errors)
|
||||
print(f" ❌ Found {len(active_errors)} items missing ZABBIX_ACTIVE type")
|
||||
else:
|
||||
print(" ✅ Active Agent compliance check passed")
|
||||
|
||||
# ========== Summary ==========
|
||||
print("\n" + "=" * 60)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue