Compare commits
2 Commits
79e99e4768
...
441a1e00a0
| Author | SHA1 | Date |
|---|---|---|
|
|
441a1e00a0 | |
|
|
204fb20b88 |
|
|
@ -0,0 +1,190 @@
|
|||
import yaml
|
||||
import sys
|
||||
import os
|
||||
|
||||
def load_yaml(file_path):
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
def get_priority_icon(priority):
|
||||
icons = {
|
||||
'INFO': 'ℹ️',
|
||||
'WARNING': '⚠️',
|
||||
'AVERAGE': '🚨',
|
||||
'HIGH': '🚨',
|
||||
'DISASTER': '🔥'
|
||||
}
|
||||
return icons.get(priority, '❓')
|
||||
|
||||
def format_trigger(trigger, is_prototype=False):
|
||||
priority = trigger.get('priority', 'INFO')
|
||||
icon = get_priority_icon(priority)
|
||||
name = trigger.get('name', 'Unknown Trigger')
|
||||
event_name = trigger.get('event_name', name)
|
||||
|
||||
# Use event_name for display if it's richer, but standard usually puts name in the doc
|
||||
# The reference doc uses "Name" field style but sometimes "Event Name" content might be more descriptive
|
||||
# Looking at reference:
|
||||
# - [AVERAGE] **🚨 DHCP Parado: Servidor DHCP não está em execução**
|
||||
# The YAML trigger name is: '🚨 DHCP Parado: Servidor DHCP não está em execução'
|
||||
# So it uses the Trigger Name directly.
|
||||
|
||||
return f"- [{priority}] **{name}**"
|
||||
|
||||
def generate_markdown(template_data):
|
||||
# Depending on export structure, 'zabbix_export' root might be present
|
||||
if 'zabbix_export' in template_data:
|
||||
templates = template_data['zabbix_export'].get('templates', [])
|
||||
else:
|
||||
# Fallback if just raw template list
|
||||
templates = template_data.get('templates', [])
|
||||
|
||||
if not templates:
|
||||
return "No templates found."
|
||||
|
||||
# Process first template found (usually one per file)
|
||||
template = templates[0]
|
||||
|
||||
output = []
|
||||
|
||||
# Header
|
||||
name = template.get('name', 'N/A')
|
||||
description = template.get('description', '').strip()
|
||||
|
||||
output.append(f"# Documentação: {name}")
|
||||
output.append("")
|
||||
output.append(f"**Template:** {name}")
|
||||
output.append("**Descrição:**")
|
||||
output.append(description)
|
||||
output.append("")
|
||||
|
||||
# Items
|
||||
items = template.get('items', [])
|
||||
output.append("## Itens Monitorados")
|
||||
output.append("")
|
||||
output.append("### Itens Globais")
|
||||
|
||||
for item in items:
|
||||
item_name = item.get('name', 'Unknown')
|
||||
item_key = item.get('key', 'Unknown')
|
||||
output.append(f"- **{item_name}** (`{item_key}`)")
|
||||
|
||||
output.append("")
|
||||
|
||||
# Discovery Rules
|
||||
discovery_rules = template.get('discovery_rules', [])
|
||||
if discovery_rules:
|
||||
output.append("### Regras de Descoberta (LLD)")
|
||||
output.append("")
|
||||
for rule in discovery_rules:
|
||||
rule_name = rule.get('name', 'Unknown')
|
||||
rule_key = rule.get('key', 'Unknown')
|
||||
output.append(f"#### {rule_name} (`{rule_key}`)")
|
||||
|
||||
prototypes = rule.get('item_prototypes', [])
|
||||
if prototypes:
|
||||
output.append(" - **Protótipos de Itens:**")
|
||||
for proto in prototypes:
|
||||
p_name = proto.get('name', 'Unknown')
|
||||
p_key = proto.get('key', 'Unknown')
|
||||
output.append(f" - {p_name} (`{p_key}`)")
|
||||
output.append("")
|
||||
|
||||
# Triggers
|
||||
# Triggers can be top-level (under template) or inside discovery rules (prototypes)
|
||||
|
||||
triggers = template.get('items', []) # Actually triggers are under 'items' in some exports? No, typically under 'templates' -> 'items' -> 'triggers' key IS NOT STANDARD.
|
||||
# Wait, in 7.0 export format:
|
||||
# templates:
|
||||
# - items:
|
||||
# - triggers: ... (triggers linked to items? No, standard Zabbix export puts triggers list under the template object usually, OR under items? Let's check YAML structure)
|
||||
|
||||
# Checking file content from previous turn:
|
||||
# Items have 'triggers' list inside them!
|
||||
# e.g.
|
||||
# - uuid: ...
|
||||
# name: Status do servidor DHCP
|
||||
# ...
|
||||
# triggers:
|
||||
# - uuid: ...
|
||||
# expression: ...
|
||||
# name: '🚨 DHCP Parado: Servidor DHCP não está em execução'
|
||||
|
||||
# So we need to collect triggers from all items.
|
||||
|
||||
global_triggers = []
|
||||
for item in items:
|
||||
if 'triggers' in item:
|
||||
global_triggers.extend(item['triggers'])
|
||||
|
||||
output.append("## Alertas (Triggers)")
|
||||
output.append("")
|
||||
output.append("### Triggers Globais")
|
||||
|
||||
# Sort triggers by name or severity? Reference doc seems arbitrary or definition order.
|
||||
# Let's keep definition order or sort by name.
|
||||
|
||||
for trigger in global_triggers:
|
||||
output.append(format_trigger(trigger))
|
||||
|
||||
output.append("")
|
||||
|
||||
# Trigger Prototypes
|
||||
if discovery_rules:
|
||||
output.append("### Protótipos de Triggers (LLD)")
|
||||
output.append("")
|
||||
|
||||
for rule in discovery_rules:
|
||||
rule_name = rule.get('name', 'Unknown rule')
|
||||
output.append(f"**Regra: {rule_name}**")
|
||||
|
||||
# Trigger prototypes are under discovery rule -> trigger_prototypes (older) or
|
||||
# item_prototypes -> trigger_prototypes ?
|
||||
# Looking at previous file view:
|
||||
# - uuid: ...
|
||||
# name: 'Interface [{#IFNAME}({#IFALIAS})]: Pacotes de entrada descartados'
|
||||
# ...
|
||||
# trigger_prototypes:
|
||||
# - uuid: ...
|
||||
# name: '🐢 Congestionamento: Descartes na interface {#IFNAME}'
|
||||
|
||||
# So they are nested in item_prototypes.
|
||||
|
||||
prototypes = []
|
||||
item_prototypes = rule.get('item_prototypes', [])
|
||||
for ip in item_prototypes:
|
||||
if 'trigger_prototypes' in ip:
|
||||
prototypes.extend(ip['trigger_prototypes'])
|
||||
|
||||
for proto in prototypes:
|
||||
output.append(format_trigger(proto, is_prototype=True))
|
||||
|
||||
output.append("")
|
||||
|
||||
return "\n".join(output)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python generate_template_docs.py <path_to_yaml>")
|
||||
sys.exit(1)
|
||||
|
||||
file_path = sys.argv[1]
|
||||
|
||||
try:
|
||||
data = load_yaml(file_path)
|
||||
markdown = generate_markdown(data)
|
||||
|
||||
# Output to same dir as YAML with .md extension, or print to stdout?
|
||||
# User asked to generate documentation.
|
||||
# Let's write to file for convenience, similar name.
|
||||
|
||||
base_name = os.path.splitext(file_path)[0]
|
||||
output_path = base_name + "_generated.md"
|
||||
|
||||
with open(output_path, 'w', encoding='utf-8') as f:
|
||||
f.write(markdown)
|
||||
|
||||
print(f"Documentation generated at: {output_path}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
|
@ -14,7 +14,6 @@ HOST-RESOURCES-MIB
|
|||
|
||||
Gerado originalmente pela ferramenta oficial "Templator", Otimizado para Padrão Gold (Arthur)
|
||||
|
||||
|
||||
## Itens Monitorados
|
||||
|
||||
### Itens Globais
|
||||
|
|
@ -77,14 +76,19 @@ Gerado originalmente pela ferramenta oficial "Templator", Otimizado para Padrão
|
|||
- [AVERAGE] **🚨 DHCP Parado: Servidor DHCP não está em execução**
|
||||
- [AVERAGE] **🚨 DNS Parado: Servidor DNS (Unbound) não está em execução**
|
||||
- [AVERAGE] **🚨 WebServer Parado: Nginx não está rodando**
|
||||
- [WARNING] **🧩 Fragmentação Excessiva IPv4**
|
||||
- [WARNING] **🛡️ Possível Ataque/Scan: Pico de Bloqueios**
|
||||
- [HIGH] **🚨 Firewall Desligado: Packet Filter inativo**
|
||||
- [WARNING] **⚠️ Tabela de Rastreamento Cheia: Uso elevado ({ITEM.LASTVALUE1}%)**
|
||||
- [HIGH] **⏳ Source Tracking cheia em < 1h (Previsão)**
|
||||
- [WARNING] **🔥 Tabela de Estados Crítica: Risco de bloqueio ({ITEM.LASTVALUE1}%)**
|
||||
- [HIGH] **⏳ Tabela de Estados cheia em < 1h (Previsão)**
|
||||
- [WARNING] **🚨 Falha SNMP: Sem coleta de dados no pfSense**
|
||||
|
||||
### Protótipos de Triggers (LLD)
|
||||
|
||||
**Regra: Descoberta de interfaces de rede**
|
||||
- [INFO] **ℹ️ PFSense: Interface [{#IFNAME}({#IFALIAS})]: Velocidade da Ethernet diminuiu**
|
||||
- [WARNING] **⚠️ PFSense: Interface [{#IFNAME}({#IFALIAS})]: Alto uso de banda de entrada**
|
||||
- [WARNING] **⚠️ PFSense: Interface [{#IFNAME}({#IFALIAS})]: Alto uso de banda de saída**
|
||||
- [WARNING] **🐢 Congestionamento: Descartes na interface {#IFNAME}**
|
||||
- [WARNING] **⚠️ PFSense: Interface [{#IFNAME}({#IFALIAS})]: Alta taxa de erros de entrada**
|
||||
- [WARNING] **⚠️ PFSense: Interface [{#IFNAME}({#IFALIAS})]: Alta taxa de erros de saída**
|
||||
- [AVERAGE] **🔌 PFSense: Interface [{#IFNAME}({#IFALIAS})]: Link indisponível**
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
# 📜 Especificação Técnica: Template pfSense SNMP (Zabbix 7.0 Native)
|
||||
|
||||
Este documento define a especificação exata para a implementação das melhorias no template `template_app_pfsense_snmp.yaml`. O agente executor deve seguir estritamente as definições de UUID, Tags e Expressões abaixo.
|
||||
|
||||
## 1. Diretrizes de Engenharia (Reference: Zabbix 7.0 Advanced YAML)
|
||||
* **UUIDs:** Devem ser gerados novos para cada entidade (Item, Trigger, Dashboard). Formato: 32 chars hex lowercase (ex: `uuidgen | tr -d '-' | tr 'A-F' 'a-f'`).
|
||||
* **Tags:** Taxonomia obrigatória para todos os novos triggers.
|
||||
* `scope`: `availability` | `capacity` | `performance` | `security`
|
||||
* `layer`: `hardware` | `network` | `application`
|
||||
* **Macros:** Usar macros de template `{$MACRO}` para todos os thresholds.
|
||||
|
||||
## 2. Novos Triggers (Física & Segurança)
|
||||
|
||||
### 2.1 Interface Health (Contexto LLD: `net.if.discovery`)
|
||||
Aplicar nos **Trigger Prototypes**.
|
||||
|
||||
| Nome do Trigger | Expressão (Notação Zabbix) | Severidade | Tags |
|
||||
|---|---|---|---|
|
||||
| **🚨 Falha Física: Erros de Entrada Excessivos na {#IFNAME}** | `min(/PFSense by SNMP/net.if.in.errors[{#SNMPINDEX}],#3) > {$IF.ERRORS.WARN}` | HIGH | `scope:availability`, `layer:hardware` |
|
||||
| **⚠️ Possível Duplex Mismatch: Erros de Saída na {#IFNAME}** | `min(/PFSense by SNMP/net.if.out.errors[{#SNMPINDEX}],#3) > {$IF.ERRORS.WARN}` | HIGHER | `scope:performance`, `layer:network` |
|
||||
| **🐢 Congestionamento: Descartes na interface {#IFNAME}** | `min(/PFSense by SNMP/net.if.in.discards[{#SNMPINDEX}],5m) > {$IF.DISCARDS.WARN}` | WARNING | `scope:capacity`, `layer:network` |
|
||||
|
||||
### 2.2 Security & Integrity (Global)
|
||||
Aplicar em **Triggers Globais**.
|
||||
|
||||
| Nome do Trigger | Expressão | Sev | Tags |
|
||||
|---|---|---|---|
|
||||
| **🛡️ Possível Portscan: Pico de Bloqueios na WAN** | `min(/PFSense by SNMP/pfsense.packets.match, 5m) > avg(/PFSense by SNMP/pfsense.packets.match, 1h) * 3` | WARNING | `scope:security`, `type:anomaly` |
|
||||
| **🧩 Fragmentação Excessiva IPv4** | `min(/PFSense by SNMP/pfsense.packets.fragment, 5m) > {$PF.FRAG.MAX}` | WARNING | `scope:security` |
|
||||
|
||||
## 3. Análise Preditiva & Derivada (Calculated Items & Triggers)
|
||||
|
||||
### 3.1 Item Calculado: Noise Ratio
|
||||
Criar novo item calculado para medir a "sujeira" do tráfego.
|
||||
* **Key:** `pfsense.firewall.noise_ratio`
|
||||
* **Formula:** `last(//pfsense.packets.match) / ( last(//pfsense.packets.match) + last(//net.if.pass.total) + 0.001 )`
|
||||
* *Nota: `net.if.pass.total` deve ser um item de soma de todas as interfaces (pode ser complexo em SNMP puro, alternativa: usar apenas counters globais do PF se disponíveis).*
|
||||
* *Correção:* O PF MIB tem counters globais. Usaremos: `last(//pfsense.packets.match) / ( last(//pfsense.pf_counters.walk[...pass]) + ... )`. Simplificação para o agente: Usar apenas **Taxa de Bloqueio** se o total não estiver claro.
|
||||
* **Fallback Specification:** Usar Trigger em `pfsense.packets.match` (Bloqueio) comparado com baseline.
|
||||
|
||||
### 3.2 Triggers Preditivas (Forecast)
|
||||
|
||||
| Nome do Trigger | Expressão | Sev |
|
||||
|---|---|---|
|
||||
| **⏳ Tabela de Estados cheia em < 1h** | `timeleft(/PFSense by SNMP/pfsense.state.table.pused, 1h, 100) < 1h` | HIGH |
|
||||
| **⏳ Source Tracking cheia em < 1h** | `timeleft(/PFSense by SNMP/pfsense.source.tracking.table.pused, 1h, 100) < 1h` | HIGH |
|
||||
|
||||
## 4. Dashboards (Visualização como Código)
|
||||
Adicionar bloco `dashboards` no YAML root.
|
||||
|
||||
* **Dashboard:** "PFsense: Security & Health"
|
||||
* **Widgets:**
|
||||
1. **Graph (Stacked):** "Firewall Traffic Mix" -> Series: `pfsense.packets.match` (Red), `pfsense.packets.normalize` (Yellow).
|
||||
2. **Gauge:** "State Table Capacity" -> Item: `pfsense.state.table.pused`.
|
||||
3. **Top Hosts (Honeycomb style equivalent if 7.0):** "Interface Errors" -> LLD Items `net.if.in.errors[*]`.
|
||||
|
||||
## 5. Instruções de Execução
|
||||
1. **Edição:** Inserir os novos blocos no `template_app_pfsense_snmp.yaml`.
|
||||
2. **Validação:** Garantir que UUIDs sejam únicos (não reutilizar strings do exemplo).
|
||||
3. **Macros:** Adicionar as macros `{$IF.ERRORS.WARN}`, `{$IF.DISCARDS.WARN}`, `{$PF.FRAG.MAX}` no bloco `macros` do template.
|
||||
Loading…
Reference in New Issue