Initial commit
This commit is contained in:
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
venv/
|
||||
.env
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
flask.log
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Project Specific
|
||||
hosts.json
|
||||
implementation_plan.md
|
||||
task.md
|
||||
walkthrough.md
|
||||
.DS_Store
|
||||
|
||||
# Build & Dist
|
||||
dist/
|
||||
verification/
|
||||
*.key
|
||||
*.rkey
|
||||
6
.pyarmor/config
Normal file
6
.pyarmor/config
Normal file
@@ -0,0 +1,6 @@
|
||||
[project]
|
||||
src = /home/halbebruno/Projetos/ZabbixAPI
|
||||
|
||||
[builder]
|
||||
outer_keyname = license.key
|
||||
|
||||
61
README.md
Normal file
61
README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# IPv0 OLT API (Middleware Zabbix)
|
||||
|
||||
API RESTful intermediária desenvolvida em Python (Flask) para facilitar o monitoramento de OLTs (Nokia, Fiberhome, etc.) no Zabbix via Low Level Discovery (LLD).
|
||||
|
||||
## 🎯 Objetivo
|
||||
Transformar comandos complexos de CLI (Telnet/SSH) e estruturas SNMP proprietárias em JSON limpo e otimizado para o Zabbix, com cache inteligente para proteger a OLT de sobrecarga.
|
||||
|
||||
## 🚀 Recursos Principais
|
||||
- **Multi-Vendor**: Arquitetura baseada em drivers. Suporta Nokia (ISAM/FW específicos) com suporte futuro para Fiberhome, Huawei e ZTE.
|
||||
- **Smart Cache**: Cache em memória evita múltiplas conexões para a mesma coleta de dados.
|
||||
|
||||
## 📂 Estrutura do Projeto
|
||||
```
|
||||
/
|
||||
├── app.py # Aplicação Principal (Flask)
|
||||
├── drivers/ # Camada de abstração (Factory Pattern)
|
||||
│ ├── __init__.py # Contrato (Interface OltDriver)
|
||||
│ ├── nokia.py # Driver concreto Nokia (Implementa connect/get_pon_stats)
|
||||
│ └── fiberhome.py # Placeholder para expansão futura
|
||||
├── utils/ # Cache, SNMP, Helpers
|
||||
├── tools/ # Scripts de Manutenção (Build, Clean, License)
|
||||
└── doc/ # Documentação detalhada
|
||||
```
|
||||
|
||||
## 📦 Instalação (Usuário Final)
|
||||
|
||||
Para um guia detalhado, consulte: [doc/install_dist.md](doc/install_dist.md).
|
||||
|
||||
1. Transfira o arquivo `ipv0-olt-api.zip` para o servidor (ex: `/opt`).
|
||||
2. Descompacte o arquivo e entre na pasta:
|
||||
```bash
|
||||
unzip ipv0-olt-api.zip -d ipv0-olt-api
|
||||
cd ipv0-olt-api
|
||||
```
|
||||
3. Execute o script de instalação como root:
|
||||
```bash
|
||||
sudo ./install.sh
|
||||
```
|
||||
*Este script instalará as dependências e configurará o serviço.*
|
||||
|
||||
> ⚠️ **Atenção:** Ao final, ele exibirá o **Machine ID**. Copie este código para solicitar sua licença.
|
||||
|
||||
## 🔌 Expansão (Novos Drivers/Recursos)
|
||||
A API foi desenhada para ser estendida e suportar outros fabricantes.
|
||||
Consulte o guia de desenvolvimento: [doc/expansion.md](doc/expansion.md).
|
||||
|
||||
## 📊 Integrando no Zabbix
|
||||
Para um guia passo-a-passo detalhado de como criar Master Items, regras LLD e protótipos de itens, consulte: [doc/zabbix_integration.md](doc/zabbix_integration.md).
|
||||
|
||||
## 📚 Guia do Desenvolvedor
|
||||
Documentação técnica para manutenção e evolução do projeto:
|
||||
|
||||
* **Instalação (Ambiente Dev)**: [doc/install.md](doc/install.md) - *Como rodar o código fonte sem compilar.*
|
||||
* **Expansão**: [doc/expansion.md](doc/expansion.md) - *Como criar novos drivers.*
|
||||
* **Integração Zabbix (Guia Técnico)**: [doc/zabbix_integration.md](doc/zabbix_integration.md) - *Como criar templates e regras LLD.*
|
||||
* **Build e Distribuição**: [doc/create_dist.md](doc/create_dist.md) - *Como gerar novas versões e licenças.*
|
||||
* **Templates Oficiais**:
|
||||
* **Nokia**: [doc/templates/template-nokia-api.xml](doc/templates/template-nokia-api.xml)
|
||||
|
||||
---
|
||||
Desenvolvido com ❤️ e Python.
|
||||
71
app.py
Normal file
71
app.py
Normal file
@@ -0,0 +1,71 @@
|
||||
from flask import Flask, jsonify, request
|
||||
from drivers.nokia import NokiaDriver
|
||||
from drivers.fiberhome import FiberhomeDriver
|
||||
from config import Config
|
||||
from utils.cache import cache_response
|
||||
import time
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Carregar inventário de hosts
|
||||
HOSTS_CONFIG = {}
|
||||
if os.path.exists('hosts.json'):
|
||||
with open('hosts.json', 'r') as f:
|
||||
HOSTS_CONFIG = json.load(f)
|
||||
|
||||
# Mapeamento de drivers
|
||||
DRIVERS = {
|
||||
'nokia': NokiaDriver,
|
||||
'fiberhome': FiberhomeDriver
|
||||
}
|
||||
|
||||
@app.route('/api/v1/olt_stats', methods=['GET'])
|
||||
@cache_response # Cacheia a resposta baseado nos argumentos da request
|
||||
def get_olt_stats():
|
||||
host_ip = request.args.get('host')
|
||||
driver_name_param = request.args.get('driver')
|
||||
|
||||
if not host_ip:
|
||||
return jsonify({'error': 'Missing host parameter'}), 400
|
||||
|
||||
# Determinar configurações (hosts.json ou defaults)
|
||||
host_config = HOSTS_CONFIG.get(host_ip, {})
|
||||
|
||||
# Parâmetros: Preference para query param > hosts.json > default config
|
||||
driver_name = driver_name_param or host_config.get('driver')
|
||||
username = host_config.get('username') or Config.OLT_USERNAME
|
||||
password = host_config.get('password') or Config.OLT_PASSWORD
|
||||
|
||||
# Opções extras para o driver (ex: ssh_options, snmp_community)
|
||||
# Remove chaves padrão para não duplicar e deixa o resto como opção
|
||||
driver_options = {k: v for k, v in host_config.items() if k not in ['username', 'password', 'driver']}
|
||||
|
||||
if not driver_name:
|
||||
return jsonify({'error': 'Driver not specified (param or hosts.json)'}), 400
|
||||
|
||||
if driver_name not in DRIVERS:
|
||||
return jsonify({'error': f'Driver {driver_name} not supported'}), 400
|
||||
|
||||
try:
|
||||
# Instancia o driver com opções extras
|
||||
driver_class = DRIVERS[driver_name]
|
||||
driver = driver_class(host_ip, username, password, **driver_options)
|
||||
|
||||
# Coleta estatísticas completas (Cards > PONs > ONTs)
|
||||
stats = driver.get_olt_stats()
|
||||
|
||||
# Estrutura flexível, o driver define o retorno
|
||||
return jsonify(stats)
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/health', methods=['GET'])
|
||||
def health_check():
|
||||
return jsonify({'status': 'ok', 'service': 'IPv0 OLT API', 'version': '3.1'}), 200
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5050)
|
||||
16
config.py
Normal file
16
config.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
class Config:
|
||||
# Credenciais padrão (podem ser sobrescritas por variáveis de ambiente)
|
||||
OLT_USERNAME = os.getenv('OLT_USERNAME', 'admin')
|
||||
OLT_PASSWORD = os.getenv('OLT_PASSWORD', 'admin')
|
||||
|
||||
# Timeout do Netmiko em segundos
|
||||
NETMIKO_TIMEOUT = int(os.getenv('NETMIKO_TIMEOUT', 10))
|
||||
|
||||
# TTL do Cache em segundos (padrão 5 minutos)
|
||||
CACHE_TTL = int(os.getenv('CACHE_TTL', 300))
|
||||
CACHE_MAX_SIZE = int(os.getenv('CACHE_MAX_SIZE', 100))
|
||||
51
debug_snmp.py
Normal file
51
debug_snmp.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import asyncio
|
||||
from utils.snmp import _snmp_walk_async, SnmpEngine, CommunityData, UdpTransportTarget, ContextData, ObjectType, ObjectIdentity, get_cmd
|
||||
|
||||
async def _snmp_get_async(host, community, oid):
|
||||
snmp_engine = SnmpEngine()
|
||||
community_data = CommunityData(community, mpModel=1)
|
||||
# Using larger timeout/retries just in case
|
||||
transport = await UdpTransportTarget.create((host, 161), timeout=2, retries=1)
|
||||
context = ContextData()
|
||||
|
||||
iterator = get_cmd(
|
||||
snmp_engine,
|
||||
community_data,
|
||||
transport,
|
||||
context,
|
||||
ObjectType(ObjectIdentity(oid))
|
||||
)
|
||||
|
||||
errorIndication, errorStatus, errorIndex, varBinds = await iterator
|
||||
snmp_engine.closeDispatcher()
|
||||
|
||||
if errorIndication or errorStatus:
|
||||
return None
|
||||
return str(varBinds[0][1])
|
||||
|
||||
async def main():
|
||||
host = "10.186.203.14"
|
||||
community = "public"
|
||||
|
||||
# Hypothesis: Slot 6, Port 1
|
||||
# 0x06A00000 = 111149056
|
||||
|
||||
targets = [
|
||||
(5, 1, 94371840), # Validated
|
||||
(6, 1, 111149056), # To Evaluate
|
||||
(6, 2, 111149056 + 65536)
|
||||
]
|
||||
|
||||
base_oid_prefix = "1.3.6.1.2.1.2.2.1.2" # ifDescr
|
||||
|
||||
for slot, port, if_index in targets:
|
||||
oid = f"{base_oid_prefix}.{if_index}"
|
||||
print(f"Checking Slot {slot} Port {port} (ID={if_index})...")
|
||||
val = await _snmp_get_async(host, community, oid)
|
||||
if val and "No Such" not in val:
|
||||
print(f" MATCH: {val}")
|
||||
else:
|
||||
print(f" FAIL: {val}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
35
doc/create_dist.md
Normal file
35
doc/create_dist.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Manual de Criação de Distribuição (Developer Guide)
|
||||
|
||||
Este documento destina-se a geração de novas versões de distribuição da **API** e emissão delicenças.
|
||||
|
||||
## 1. Gerando o Pacote de Distribuição
|
||||
Para criar um pacote pronto para o cliente (binário protegido), execute:
|
||||
|
||||
```bash
|
||||
# Executar da raiz
|
||||
./tools/build.sh
|
||||
```
|
||||
|
||||
**Saída:**
|
||||
* **Staging:** `dist/ipv0-olt-api/` (Arquivos soltos para conferência)
|
||||
* **Release:** `dist/release/ipv0-olt-api.zip` (Arquivo final para o cliente)
|
||||
|
||||
## 2. Gerenciamento de Licenças
|
||||
A aplicação não rodará sem uma licença válida. Utilize o script automatizado para gerar e organizar licenças:
|
||||
|
||||
```bash
|
||||
./tools/gen_license.sh
|
||||
```
|
||||
|
||||
O script solicitará:
|
||||
1. **Nome do Cliente** (Cria pasta em `dist/licenses/CLIENTE`).
|
||||
2. **Tipo de Licença** (Data ou Hardware).
|
||||
3. **Dados** (Vencimento ou Machine ID).
|
||||
|
||||
A licença gerada será salva em: `dist/licenses/<CLIENTE>/license.key`.
|
||||
|
||||
## 3. Atualizando a Versão
|
||||
Ao modificar o código fonte:
|
||||
1. Edite `config.py` ou features.
|
||||
2. Rode `./build.sh`.
|
||||
3. Envie o novo `release_production.zip` para o cliente.
|
||||
112
doc/expansion.md
Normal file
112
doc/expansion.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Guia de Expansão e Desenvolvimento
|
||||
|
||||
Este documento explica como adicionar novos recursos (métodos) e suportar novos fabricantes (drivers) na API Zabbix OLT.
|
||||
|
||||
## Arquitetura
|
||||
A API segue um padrão de **Factory** simplificado:
|
||||
1. **Driver Base (`drivers/__init__.py`)**: Define a interface comum (`OltDriver`).
|
||||
2. **Implementações (`drivers/nokia.py`, etc)**: Classes concretas que herdam de `OltDriver`.
|
||||
3. **API (`app.py`)**: Roteia as requisições para o método correto do driver instanciado.
|
||||
|
||||
---
|
||||
|
||||
## 1. Adicionar Novo Tipo de Monitoramento (Ex: Interfaces)
|
||||
|
||||
Para adicionar uma nova coleta de dados (ex: estatísticas de interface, temperatura, CPU):
|
||||
|
||||
### Passo 1: Atualizar o Contrato
|
||||
Edite `drivers/__init__.py` e adicione o método abstrato na classe `OltDriver`.
|
||||
|
||||
```python
|
||||
@abstractmethod
|
||||
def get_interface_stats(self):
|
||||
"""Retorna estatísticas de interfaces Ethernet/Uplink/PON."""
|
||||
pass
|
||||
```
|
||||
|
||||
### Passo 2: Implementar nos Drivers
|
||||
Atualize **todas** as classes que herdam de `OltDriver` (ex: `drivers/nokia.py`).
|
||||
|
||||
```python
|
||||
def get_interface_stats(self):
|
||||
connection = self.connect()
|
||||
output = connection.send_command_timing("show interface ...")
|
||||
|
||||
# Implementar lógica de parsing (regex)
|
||||
stats = self._parse_interfaces(output)
|
||||
|
||||
connection.disconnect()
|
||||
return stats
|
||||
```
|
||||
|
||||
### Passo 3: Criar Rota na API
|
||||
Edite `app.py` para expor o novo método.
|
||||
|
||||
```python
|
||||
@app.route('/api/v1/interface_stats', methods=['GET'])
|
||||
@cache_response
|
||||
def get_interface_stats():
|
||||
# ... (Copiar lógica de instanciação do driver de get_pon_stats) ...
|
||||
|
||||
try:
|
||||
stats = driver.get_interface_stats()
|
||||
return jsonify({"data": stats, "meta": ...})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Adicionar Novo Fabricante (Vendor)
|
||||
|
||||
Para suportar uma nova OLT (ex: Huawei, ZTE):
|
||||
|
||||
### Passo 1: Criar o Driver
|
||||
Crie um arquivo em `drivers/` (ex: `drivers/huawei.py`).
|
||||
|
||||
```python
|
||||
from drivers import OltDriver
|
||||
import re
|
||||
|
||||
class HuaweiDriver(OltDriver):
|
||||
def connect(self):
|
||||
# Configurar conexão Netmiko (device_type='huawei')
|
||||
return ConnectHandler(...)
|
||||
|
||||
def get_pon_stats(self):
|
||||
connection = self.connect()
|
||||
output = connection.send_command("display ont info ...")
|
||||
connection.disconnect()
|
||||
return self._parse_output(output)
|
||||
```
|
||||
|
||||
### Passo 2: Registrar o Driver
|
||||
Edite `app.py` e importe a nova classe.
|
||||
|
||||
```python
|
||||
from drivers.huawei import HuaweiDriver
|
||||
|
||||
DRIVERS = {
|
||||
'nokia': NokiaDriver,
|
||||
'fiberhome': FiberhomeDriver,
|
||||
'huawei': HuaweiDriver # <--- Novo registro
|
||||
}
|
||||
```
|
||||
|
||||
### Passo 3: Configurar Host
|
||||
No `hosts.json`, defina o driver para o IP correspondente.
|
||||
|
||||
```json
|
||||
"10.10.10.1": {
|
||||
"driver": "huawei",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dicas de Desenvolvimento
|
||||
|
||||
- **Netmiko Timing**: Use `send_command_timing` se o equipamento tiver prompts complexos ou lentidão.
|
||||
- **Regex**: Teste suas expressões regulares com outputs reais variados para garantir robustez.
|
||||
- **Cache**: O decorador `@cache_response` em `utils/cache.py` já trata cache por URL (host distinto = cache distinto).
|
||||
96
doc/install.md
Normal file
96
doc/install.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Guia de Instalação: API Zabbix OLT
|
||||
|
||||
Este documento descreve os passos para instalação e configuração da API em ambiente Linux.
|
||||
|
||||
## Pré-requisitos
|
||||
- Sistema Operacional Linux (Debian/Ubuntu/CentOS)
|
||||
- Python 3.9 ou superior
|
||||
- Git
|
||||
|
||||
## Passos de Instalação
|
||||
|
||||
### 1. Clonar o Repositório
|
||||
O diretório padrão de instalação será `/opt/zbx-ipv0`.
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /opt/zbx-ipv0
|
||||
sudo chown $USER:$USER /opt/zbx-ipv0
|
||||
git clone https://git.ipv0.com.br/halbebruno/zabbix-api /opt/zbx-ipv0
|
||||
cd /opt/zbx-ipv0
|
||||
```
|
||||
|
||||
### 2. Configurar Ambiente Virtual (venv)
|
||||
É recomendado usar um ambiente virtual para isolar as dependências.
|
||||
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
### 3. Instalar Dependências
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
# Para produção, instale também o Gunicorn
|
||||
pip install gunicorn
|
||||
```
|
||||
|
||||
### 4. Configuração
|
||||
|
||||
#### 4.1 Inventário de Hosts
|
||||
Crie ou edite o arquivo `hosts.json` na raiz do projeto com as credenciais das OLTs.
|
||||
|
||||
```json
|
||||
{
|
||||
"10.186.203.14": {
|
||||
"username": "admin",
|
||||
"password": "senha_segura",
|
||||
"driver": "nokia",
|
||||
"port": 22,
|
||||
"ssh_options": {
|
||||
"disabled_algorithms": {
|
||||
"pubkeys": ["rsa-sha2-256", "rsa-sha2-512"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2 Variáveis de Ambiente (Opcional)
|
||||
Você pode sobrescrever configurações padrão via variáveis de ambiente ou editando `config.py`.
|
||||
|
||||
### 5. Execução como Serviço (Systemd)
|
||||
Para garantir que a API inicie automaticamente e rode em background, crie um serviço systemd.
|
||||
|
||||
Crie o arquivo `/etc/systemd/system/zabbix-api.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Zabbix OLT API
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory=/opt/zbx-ipv0
|
||||
Environment="PATH=/opt/zbx-ipv0/venv/bin"
|
||||
ExecStart=/opt/zbx-ipv0/venv/bin/gunicorn --workers 4 --bind 0.0.0.0:5000 app:app
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Ative e inicie o serviço:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable zabbix-api
|
||||
sudo systemctl start zabbix-api
|
||||
sudo systemctl status zabbix-api
|
||||
```
|
||||
|
||||
## Teste
|
||||
Verifique se a API está rodando:
|
||||
```bash
|
||||
curl http://127.0.0.1:5000/api/v1/pon_stats?host=SEU_IP_DA_OLT
|
||||
```
|
||||
86
doc/install_dist.md
Normal file
86
doc/install_dist.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Manual de Instalação (Cliente)
|
||||
|
||||
Este guia descreve como instalar a **IPv0 OLT API** em seu servidor Linux.
|
||||
|
||||
## Pré-requisitos
|
||||
* Linux (Ubuntu 20.04+, Debian 11+, CentOS 8+)
|
||||
* Python 3.8 ou superior
|
||||
* Acesso root/sudo
|
||||
|
||||
## 1. Instalação Automática
|
||||
|
||||
1. Transfira o arquivo `ipv0-olt-api.zip` para o servidor (ex: `/opt`).
|
||||
2. Descompacte o arquivo:
|
||||
```bash
|
||||
unzip ipv0-olt-api.zip -d ipv0-olt-api
|
||||
cd ipv0-olt-api
|
||||
```
|
||||
3. Execute o script de instalação como root:
|
||||
```bash
|
||||
sudo ./install.sh
|
||||
```
|
||||
* Este script instalará as dependências e configurará o serviço.
|
||||
* **Atenção:** Ao final, ele exibirá o **Machine ID**. Copie este código para solicitar sua licença.
|
||||
|
||||
## 2. Ativação da Licença e Início do Serviço
|
||||
|
||||
A aplicação não iniciará sem uma licença válida.
|
||||
|
||||
1. Envie o **Machine ID** para o suporte.
|
||||
2. Você receberá um arquivo de licença chamado `license.key`.
|
||||
3. Copie este arquivo para a pasta da aplicação e INICIE o serviço:
|
||||
```bash
|
||||
# Copiar a licença
|
||||
sudo cp license.key /opt/ipv0-olt-api/
|
||||
sudo chown root:root /opt/ipv0-olt-api/license.key
|
||||
|
||||
# Habilitar e Iniciar o serviço agora:
|
||||
sudo systemctl enable --now ipv0-olt-api
|
||||
```
|
||||
|
||||
## 3. Configuração (hosts.json)
|
||||
|
||||
Edite o arquivo `hosts.json` para adicionar suas OLTs.
|
||||
Caminho: `/opt/ipv0-olt-api/hosts.json`
|
||||
|
||||
**Exemplo Completo:**
|
||||
```json
|
||||
{
|
||||
"10.155.156.2": {
|
||||
"username": "zabbix",
|
||||
"password": "p4ssw0rd",
|
||||
"driver": "nokia",
|
||||
"snmp_community": "public",
|
||||
"port": 22,
|
||||
"ssh_options": {
|
||||
"disabled_algorithms": {
|
||||
"pubkeys": [
|
||||
"rsa-sha2-256",
|
||||
"rsa-sha2-512"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"192.168.1.10": {
|
||||
"username": "admin",
|
||||
"password": "simple_password",
|
||||
"driver": "fiberhome",
|
||||
"port": 23
|
||||
}
|
||||
}
|
||||
```
|
||||
Após editar, reinicie o serviço.
|
||||
|
||||
## 4. Diagnóstico e Debug
|
||||
|
||||
A API inclui uma ferramenta de diagnóstico. Para verificar status, licença e conexão:
|
||||
|
||||
```bash
|
||||
cd /opt/ipv0-olt-api
|
||||
sudo ./venv/bin/python3 debug.py
|
||||
```
|
||||
|
||||
### Comandos Úteis
|
||||
* **Ver logs:** `sudo journalctl -u ipv0-olt-api -f`
|
||||
* ** Status do Serviço:** `sudo systemctl status ipv0-olt-api`
|
||||
* **Testar API:** `curl http://localhost:5050/health`
|
||||
945
doc/templates/template-nokia-api.xml
vendored
Normal file
945
doc/templates/template-nokia-api.xml
vendored
Normal file
@@ -0,0 +1,945 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<zabbix_export>
|
||||
<version>7.2</version>
|
||||
<template_groups>
|
||||
<template_group>
|
||||
<uuid>b01bb96a1ed547e1a8770677a646aa3c</uuid>
|
||||
<name>Templates IPv0</name>
|
||||
</template_group>
|
||||
<template_group>
|
||||
<uuid>1a62bf6492d64062a2b5f58e30234244</uuid>
|
||||
<name>Templates Nokia</name>
|
||||
</template_group>
|
||||
</template_groups>
|
||||
<templates>
|
||||
<template>
|
||||
<uuid>41049df2762c47218e8282386e938ff7</uuid>
|
||||
<template>IPv0 - Template Olt Nokia API</template>
|
||||
<name>IPv0 - Template Olt Nokia API</name>
|
||||
<groups>
|
||||
<group>
|
||||
<name>Templates IPv0</name>
|
||||
</group>
|
||||
<group>
|
||||
<name>Templates Nokia</name>
|
||||
</group>
|
||||
</groups>
|
||||
<items>
|
||||
<item>
|
||||
<uuid>2e17d6504d804ef098c6d35614ac0626</uuid>
|
||||
<name>Get OLT Stats</name>
|
||||
<type>HTTP_AGENT</type>
|
||||
<key>olt.api.stats</key>
|
||||
<delay>3m</delay>
|
||||
<history>1h</history>
|
||||
<value_type>TEXT</value_type>
|
||||
<timeout>90s</timeout>
|
||||
<url>http://127.0.0.1:5050/api/v1/olt_stats</url>
|
||||
<query_fields>
|
||||
<query_field>
|
||||
<name>host</name>
|
||||
<value>{HOST.IP}</value>
|
||||
</query_field>
|
||||
<query_field>
|
||||
<name>driver</name>
|
||||
<value>nokia</value>
|
||||
</query_field>
|
||||
</query_fields>
|
||||
</item>
|
||||
</items>
|
||||
<discovery_rules>
|
||||
<discovery_rule>
|
||||
<uuid>7a978a056189440f8bc3252788ce5034</uuid>
|
||||
<name>Discovery Card de Gerencia</name>
|
||||
<type>DEPENDENT</type>
|
||||
<key>olt.mgmt.card.discovery</key>
|
||||
<item_prototypes>
|
||||
<item_prototype>
|
||||
<uuid>69b6137ee1f1498eb495ca9c14e0c9f1</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Utilização de Memória %</name>
|
||||
<type>CALCULATED</type>
|
||||
<key>mng.avgUsage[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<value_type>FLOAT</value_type>
|
||||
<units>%</units>
|
||||
<params>(last(//mng.memAbsoluteUsage[{#CARD_INDEX}])*100)/last(//mng.totalMemSize[{#CARD_INDEX}])</params>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>MGMT</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>b460b66d4d8c47959388655797c47abb</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Utilização de CPU %</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.9.29.1.1.4.{#CARD_INDEX}</snmp_oid>
|
||||
<key>mng.cpuLoadAverage[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<value_type>FLOAT</value_type>
|
||||
<units>%</units>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>MGMT</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>8f8f575a9ca24fd88519fe42a6e436b8</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Status Administrativo</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.23.3.1.5.{#CARD_INDEX}</snmp_oid>
|
||||
<key>mng.eqptBoardAdminStatus[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<valuemap>
|
||||
<name>CARD: Status Administrativo</name>
|
||||
</valuemap>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>MGMT</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>6effd55c130a47a2bf9ff51e1aaf7f37</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Código PBA</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.23.3.1.15.{#CARD_INDEX}</snmp_oid>
|
||||
<key>mng.eqptBoardInventoryPBACode[{#CARD_INDEX}]</key>
|
||||
<delay>1d</delay>
|
||||
<value_type>CHAR</value_type>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>application</tag>
|
||||
<value>inventory</value>
|
||||
</tag>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>MGMT</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>538be380bc604f44b4e25f8a2eb2e7ba</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: SN do Card</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.23.3.1.19.{#CARD_INDEX}</snmp_oid>
|
||||
<key>mng.eqptBoardInventorySerialNumber[{#CARD_INDEX}]</key>
|
||||
<delay>1d</delay>
|
||||
<value_type>CHAR</value_type>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>application</tag>
|
||||
<value>inventory</value>
|
||||
</tag>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>MGMT</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>8a2b29a22fce4f27817e3b39b741c973</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Status Operacional do Card</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.23.3.1.6.{#CARD_INDEX}</snmp_oid>
|
||||
<key>mng.eqptBoardOperStatus[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<valuemap>
|
||||
<name>CARD: Status Operacional</name>
|
||||
</valuemap>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>MGMT</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>3bb2deae12174e56ab4a91f9620028bb</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME} / Sensor {#SENSOR_INDEX}: Temperatura Atual</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.23.10.1.2.{#CARD_INDEX}.{#SENSOR_INDEX}</snmp_oid>
|
||||
<key>mng.eqptBoardThermalSensorActualTemperature[{#CARD_INDEX}.{#SENSOR_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<units>°C</units>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>MGMT</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>4f7a5fecd1554ea0baa33ee04ba9ef39</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Status Energético do Card</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.23.3.1.4.{#CARD_INDEX}</snmp_oid>
|
||||
<key>mng.eqptSlotPowerStatus[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<valuemap>
|
||||
<name>CARD: Power Status</name>
|
||||
</valuemap>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>MGMT</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>279d9f528e6e4c22a2947530ec4df837</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Utilização de Memória</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.9.29.2.1.2.{#CARD_INDEX}</snmp_oid>
|
||||
<key>mng.memAbsoluteUsage[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<units>!MB</units>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>MGMT</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>79604b14350941cf9cc896cc22648ef1</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Status de Operação da CPU</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.9.29.1.1.5.{#CARD_INDEX}</snmp_oid>
|
||||
<key>mng.operateStatus[{#CARD_INDEX}]</key>
|
||||
<delay>10m</delay>
|
||||
<valuemap>
|
||||
<name>CARD: Status CPU</name>
|
||||
</valuemap>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>MGMT</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>0bc920aa3f95405a86f6bfd5fb2b2ee8</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Total de Memória</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.9.29.2.1.1.{#CARD_INDEX}</snmp_oid>
|
||||
<key>mng.totalMemSize[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<units>!MB</units>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>MGMT</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
</item_prototypes>
|
||||
<master_item>
|
||||
<key>olt.api.stats</key>
|
||||
</master_item>
|
||||
<preprocessing>
|
||||
<step>
|
||||
<type>JAVASCRIPT</type>
|
||||
<parameters>
|
||||
<parameter>var data = JSON.parse(value);
|
||||
var output = [];
|
||||
|
||||
if (data.FANT) {
|
||||
data.FANT.forEach(function (card) {
|
||||
output.push({
|
||||
"{#CARD_INDEX}": card.cardIndex,
|
||||
"{#CARD_NAME}": card.cardName,
|
||||
"{#CARD_NUMBER}": card.cardNumber,
|
||||
"{#CARD_TYPE}": card.cardType,
|
||||
"{#CARD_CLASS}": card.cardClass
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return JSON.stringify(output);</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
</preprocessing>
|
||||
</discovery_rule>
|
||||
<discovery_rule>
|
||||
<uuid>846ecdc97b7e45a9a62142aed73703d9</uuid>
|
||||
<name>Discovery PONs</name>
|
||||
<type>DEPENDENT</type>
|
||||
<key>olt.pon.discovery</key>
|
||||
<item_prototypes>
|
||||
<item_prototype>
|
||||
<uuid>a2702a688be54c88b8a3c3dc057b312f</uuid>
|
||||
<name>{#PON_NAME} Status Operacional</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.2.1.2.2.1.8.{#PON_CODE}</snmp_oid>
|
||||
<key>ifOperStatus[{#PON_CODE}]</key>
|
||||
<value_type>CHAR</value_type>
|
||||
<valuemap>
|
||||
<name>PON: Status Operacional</name>
|
||||
</valuemap>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>GPON Port</tag>
|
||||
<value>{#PON_NAME}</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>0b8b225f12c142a3b4001e5e48522b45</uuid>
|
||||
<name>{#PON_NAME} ONTs Offline</name>
|
||||
<type>DEPENDENT</type>
|
||||
<key>olt.pon.onts.offline[{#PON_CODE}]</key>
|
||||
<preprocessing>
|
||||
<step>
|
||||
<type>JSONPATH</type>
|
||||
<parameters>
|
||||
<parameter>$.FGLT[*].pons[?(@.ponCode=='{#PON_CODE}')].onuStats.down.first()</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
</preprocessing>
|
||||
<master_item>
|
||||
<key>olt.api.stats</key>
|
||||
</master_item>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>GPON Port</tag>
|
||||
<value>{#PON_NAME}</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>d665247611d44c0d99320118b0a3788f</uuid>
|
||||
<name>{#PON_NAME} ONTs Online</name>
|
||||
<type>DEPENDENT</type>
|
||||
<key>olt.pon.onts.online[{#PON_CODE}]</key>
|
||||
<preprocessing>
|
||||
<step>
|
||||
<type>JSONPATH</type>
|
||||
<parameters>
|
||||
<parameter>$.FGLT[*].pons[?(@.ponCode=='{#PON_CODE}')].onuStats.up.first()</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
</preprocessing>
|
||||
<master_item>
|
||||
<key>olt.api.stats</key>
|
||||
</master_item>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>GPON Port</tag>
|
||||
<value>{#PON_NAME}</value>
|
||||
</tag>
|
||||
</tags>
|
||||
<trigger_prototypes>
|
||||
<trigger_prototype>
|
||||
<uuid>ddc136525d274ef4b88d7e45f5c1cac7</uuid>
|
||||
<expression>((last(/IPv0 - Template Olt Nokia API/olt.pon.onts.online[{#PON_CODE}],#2)+0.1)
|
||||
- last(/IPv0 - Template Olt Nokia API/olt.pon.onts.online[{#PON_CODE}]))
|
||||
/
|
||||
(last(/IPv0 - Template Olt Nokia API/olt.pon.onts.online[{#PON_CODE}],#2)+0.1)*100>={$LIMITONTOFF}
|
||||
and
|
||||
last(/IPv0 - Template Olt Nokia API/olt.pon.onts.online[{#PON_CODE}])<>0</expression>
|
||||
<recovery_mode>RECOVERY_EXPRESSION</recovery_mode>
|
||||
<recovery_expression>((last(/IPv0 - Template Olt Nokia API/olt.pon.onts.online[{#PON_CODE}],#2)+0.1)
|
||||
- last(/IPv0 - Template Olt Nokia API/olt.pon.onts.online[{#PON_CODE}]))
|
||||
/
|
||||
(last(/IPv0 - Template Olt Nokia API/olt.pon.onts.online[{#PON_CODE}],#2)+0.1)*100<={$LIMITONTOFF}
|
||||
and
|
||||
last(/IPv0 - Template Olt Nokia API/olt.pon.onts.online[{#PON_CODE}])>=
|
||||
avg(/IPv0 - Template Olt Nokia API/olt.pon.onts.online[{#PON_CODE}],12h)</recovery_expression>
|
||||
<name>Queda Massiva de ONUs {#PON_INDEX}</name>
|
||||
<priority>HIGH</priority>
|
||||
<manual_close>YES</manual_close>
|
||||
</trigger_prototype>
|
||||
</trigger_prototypes>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>7362cd3b3c814d6992928cd2e971fb6b</uuid>
|
||||
<name>{#PON_NAME} ONTs Total</name>
|
||||
<type>DEPENDENT</type>
|
||||
<key>olt.pon.onts.total[{#PON_CODE}]</key>
|
||||
<preprocessing>
|
||||
<step>
|
||||
<type>JSONPATH</type>
|
||||
<parameters>
|
||||
<parameter>$.FGLT..[?(@.ponCode=='{#PON_CODE}')].onuStats.total.first()</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
</preprocessing>
|
||||
<master_item>
|
||||
<key>olt.api.stats</key>
|
||||
</master_item>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>GPON Port</tag>
|
||||
<value>{#PON_NAME}</value>
|
||||
</tag>
|
||||
</tags>
|
||||
<trigger_prototypes>
|
||||
<trigger_prototype>
|
||||
<uuid>d0c4dee94181467685df65cfcc3a77a5</uuid>
|
||||
<expression>last(/IPv0 - Template Olt Nokia API/olt.pon.onts.total[{#PON_CODE}])>{$ALERTLIMMITONTS}</expression>
|
||||
<recovery_mode>RECOVERY_EXPRESSION</recovery_mode>
|
||||
<recovery_expression>last(/IPv0 - Template Olt Nokia API/olt.pon.onts.total[{#PON_CODE}])<{$ALERTLIMMITONTS}</recovery_expression>
|
||||
<name>Limite de ONTs</name>
|
||||
<priority>AVERAGE</priority>
|
||||
<manual_close>YES</manual_close>
|
||||
</trigger_prototype>
|
||||
</trigger_prototypes>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>5148cc790e694fc183aec93c3e2ab2bd</uuid>
|
||||
<name>Interface GPON {#PON_INDEX} Operational status</name>
|
||||
<type>DEPENDENT</type>
|
||||
<key>olt.pon.status.oper[{#PON_INDEX}]</key>
|
||||
<value_type>TEXT</value_type>
|
||||
<status>DISABLED</status>
|
||||
<discover>NO_DISCOVER</discover>
|
||||
<preprocessing>
|
||||
<step>
|
||||
<type>JSONPATH</type>
|
||||
<parameters>
|
||||
<parameter>$.data[?(@.pon_index=='{#PON_INDEX}')].oper_status.first()</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
</preprocessing>
|
||||
<master_item>
|
||||
<key>olt.api.stats</key>
|
||||
</master_item>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>GPON Port</tag>
|
||||
<value>{#PON_NAME}</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>929d97d69e7d42618bfb063368f2937b</uuid>
|
||||
<name>{#PON_NAME}: Voltagem</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.56.5.1.9.{#CARD_INDEX}.{#PON_INDEX}</snmp_oid>
|
||||
<key>sfpDiagSupplyVoltage[{#CARD_INDEX}.{#PON_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<value_type>FLOAT</value_type>
|
||||
<units>V</units>
|
||||
<preprocessing>
|
||||
<step>
|
||||
<type>TRIM</type>
|
||||
<parameters>
|
||||
<parameter>"</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
<step>
|
||||
<type>TRIM</type>
|
||||
<parameters>
|
||||
<parameter>VDC</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
</preprocessing>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>GPON Port</tag>
|
||||
<value>{#PON_NAME}</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>83148e945c884ce99713c43806af6283</uuid>
|
||||
<name>{#PON_NAME}: Temperatura</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.56.5.1.10.{#CARD_INDEX}.{#PON_INDEX}</snmp_oid>
|
||||
<key>sfpDiagTemperature[{#CARD_INDEX}.{#PON_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<value_type>FLOAT</value_type>
|
||||
<units>ºC</units>
|
||||
<preprocessing>
|
||||
<step>
|
||||
<type>TRIM</type>
|
||||
<parameters>
|
||||
<parameter>"</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
<step>
|
||||
<type>TRIM</type>
|
||||
<parameters>
|
||||
<parameter>degrees Celsius</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
</preprocessing>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>GPON Port</tag>
|
||||
<value>{#PON_NAME}</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>256a6c2404ef4d759c75c1d5f4104b59</uuid>
|
||||
<name>{#PON_NAME}: Corrente Elétrica</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.56.5.1.8.{#CARD_INDEX}.{#PON_INDEX}</snmp_oid>
|
||||
<key>sfpDiagTxBiasCurrent[{#CARD_INDEX}.{#PON_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<value_type>FLOAT</value_type>
|
||||
<units>!mA</units>
|
||||
<preprocessing>
|
||||
<step>
|
||||
<type>TRIM</type>
|
||||
<parameters>
|
||||
<parameter>"</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
<step>
|
||||
<type>TRIM</type>
|
||||
<parameters>
|
||||
<parameter>mA</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
</preprocessing>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>GPON Port</tag>
|
||||
<value>{#PON_NAME}</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>3da4a5d55323446ea0b6ada7676c5597</uuid>
|
||||
<name>{#PON_NAME}: Tx Power</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.56.5.1.6.{#CARD_INDEX}.{#PON_INDEX}</snmp_oid>
|
||||
<key>sfpDiagTxPower[{#CARD_INDEX}.{#PON_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<value_type>FLOAT</value_type>
|
||||
<units>dBm</units>
|
||||
<preprocessing>
|
||||
<step>
|
||||
<type>TRIM</type>
|
||||
<parameters>
|
||||
<parameter>"</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
<step>
|
||||
<type>TRIM</type>
|
||||
<parameters>
|
||||
<parameter>dBm</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
</preprocessing>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>GPON Port</tag>
|
||||
<value>{#PON_NAME}</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>70e2452a22da41d6b4229d6c575c1999</uuid>
|
||||
<name>{#PON_NAME}: Tipo do SFP</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.56.6.1.13.{#CARD_INDEX}.{#PON_INDEX}</snmp_oid>
|
||||
<key>sfpInvSpecificalType[{#CARD_INDEX}.{#PON_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<valuemap>
|
||||
<name>PON: SFP Type</name>
|
||||
</valuemap>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>GPON Port</tag>
|
||||
<value>{#PON_NAME}</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
</item_prototypes>
|
||||
<master_item>
|
||||
<key>olt.api.stats</key>
|
||||
</master_item>
|
||||
<preprocessing>
|
||||
<step>
|
||||
<type>JAVASCRIPT</type>
|
||||
<parameters>
|
||||
<parameter>var data = JSON.parse(value);
|
||||
var output = [];
|
||||
if (data.FGLT) {
|
||||
data.FGLT.forEach(function(card) {
|
||||
if (card.pons) {
|
||||
card.pons.forEach(function(pon) {
|
||||
output.push({
|
||||
"{#CARD_INDEX}": card.cardIndex,
|
||||
"{#PON_INDEX}": pon.ponIndex,
|
||||
"{#PON_CODE}": pon.ponCode,
|
||||
"{#PON_NAME}": pon.ponName
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return JSON.stringify(output);</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
</preprocessing>
|
||||
</discovery_rule>
|
||||
<discovery_rule>
|
||||
<uuid>de352b4307a24dc3b7a7ba228224663d</uuid>
|
||||
<name>Discovery Card de Serviço</name>
|
||||
<type>DEPENDENT</type>
|
||||
<key>olt.service.card.discovery</key>
|
||||
<item_prototypes>
|
||||
<item_prototype>
|
||||
<uuid>bb009bd92e18473ebbcc4daea67577c6</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Utilização de Memória %</name>
|
||||
<type>CALCULATED</type>
|
||||
<key>srv.avgUsage[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<value_type>FLOAT</value_type>
|
||||
<units>%</units>
|
||||
<params>(last(//srv.memAbsoluteUsage[{#CARD_INDEX}])*100)/last(//srv.totalMemSize[{#CARD_INDEX}])</params>
|
||||
<preprocessing>
|
||||
<step>
|
||||
<type>JAVASCRIPT</type>
|
||||
<parameters>
|
||||
<parameter>return Math.round(value * 100)/100;</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
</preprocessing>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>Service</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>bf0fc61688d24828a22a23437f0baf26</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Utilização de CPU %</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.9.29.1.1.4.{#CARD_INDEX}</snmp_oid>
|
||||
<key>srv.cpuLoadAverage[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<units>%</units>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>Service</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>b4ffa90bd79744669c4b1acbeefb8329</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Status Administrativo</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.23.3.1.5.{#CARD_INDEX}</snmp_oid>
|
||||
<key>srv.eqptBoardAdminStatus[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<valuemap>
|
||||
<name>CARD: Status Administrativo</name>
|
||||
</valuemap>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>Service</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>4aa8a84284a4439b996183274d01f8b8</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Código PBA</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.23.3.1.15.{#CARD_INDEX}</snmp_oid>
|
||||
<key>srv.eqptBoardInventoryPBACode[{#CARD_INDEX}]</key>
|
||||
<delay>1d</delay>
|
||||
<value_type>CHAR</value_type>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>application</tag>
|
||||
<value>inventory</value>
|
||||
</tag>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>Service</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>c0f2e77e0c594edebd4720ae608b8311</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: SN do Card</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.23.3.1.19.{#CARD_INDEX}</snmp_oid>
|
||||
<key>srv.eqptBoardInventorySerialNumber[{#CARD_INDEX}]</key>
|
||||
<delay>1d</delay>
|
||||
<value_type>CHAR</value_type>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>application</tag>
|
||||
<value>inventory</value>
|
||||
</tag>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>Service</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>0acd1bc1e82441bfa195ec8b97d0c034</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Status Operacional do Card</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.23.3.1.6.{#CARD_INDEX}</snmp_oid>
|
||||
<key>srv.eqptBoardOperStatus[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<valuemap>
|
||||
<name>CARD: Status Operacional</name>
|
||||
</valuemap>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>Service</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>d955f5a1d1324e7baf380f63e4a1b6b3</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME} / Sensor {#SENSOR_INDEX}: Temperatura Atual</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.23.10.1.2.{#CARD_INDEX}.{#SENSOR_INDEX}</snmp_oid>
|
||||
<key>srv.eqptBoardThermalSensorActualTemperature[{#CARD_INDEX}.{#SENSOR_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<value_type>FLOAT</value_type>
|
||||
<units>ºC</units>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>Service</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>372b8ece709443f99d076ccd1b6927cf</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Status Energético do Card</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.23.3.1.4.{#CARD_INDEX}</snmp_oid>
|
||||
<key>srv.eqptSlotPowerStatus[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<valuemap>
|
||||
<name>CARD: Power Status</name>
|
||||
</valuemap>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>Service</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>039582f21ece4b5b9ede608fdcaf769e</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Utilização de Memória</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.9.29.2.1.2.{#CARD_INDEX}</snmp_oid>
|
||||
<key>srv.memAbsoluteUsage[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<value_type>FLOAT</value_type>
|
||||
<units>!MB</units>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>Service</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>2a206cc0b25e451285a37057a606b348</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Status de Operação da CPU</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.9.29.1.1.5.{#CARD_INDEX}</snmp_oid>
|
||||
<key>srv.operateStatus[{#CARD_INDEX}]</key>
|
||||
<delay>10m</delay>
|
||||
<value_type>FLOAT</value_type>
|
||||
<valuemap>
|
||||
<name>CARD: Status CPU</name>
|
||||
</valuemap>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>Service</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
<item_prototype>
|
||||
<uuid>76599aec4242436685dfc13209792312</uuid>
|
||||
<name>{#CARD_TYPE}-{#CARD_CLASS} / {#CARD_NAME}: Total de Memória</name>
|
||||
<type>SNMP_AGENT</type>
|
||||
<snmp_oid>1.3.6.1.4.1.637.61.1.9.29.2.1.1.{#CARD_INDEX}</snmp_oid>
|
||||
<key>srv.totalMemSize[{#CARD_INDEX}]</key>
|
||||
<delay>2m</delay>
|
||||
<value_type>FLOAT</value_type>
|
||||
<units>!MB</units>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>Card</tag>
|
||||
<value>Service</value>
|
||||
</tag>
|
||||
</tags>
|
||||
</item_prototype>
|
||||
</item_prototypes>
|
||||
<master_item>
|
||||
<key>olt.api.stats</key>
|
||||
</master_item>
|
||||
<preprocessing>
|
||||
<step>
|
||||
<type>JAVASCRIPT</type>
|
||||
<parameters>
|
||||
<parameter>var data = JSON.parse(value);
|
||||
var output = [];
|
||||
if (data.FGLT) {
|
||||
data.FGLT.forEach(function (card) {
|
||||
output.push({
|
||||
"{#CARD_CLASS}": card.cardClass,
|
||||
"{#CARD_INDEX}": card.cardIndex,
|
||||
"{#CARD_NAME}": card.cardName,
|
||||
"{#CARD_NUMBER}": card.cardNumber,
|
||||
"{#CARD_TYPE}": card.cardType
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return JSON.stringify(output);</parameter>
|
||||
</parameters>
|
||||
</step>
|
||||
</preprocessing>
|
||||
</discovery_rule>
|
||||
</discovery_rules>
|
||||
<tags>
|
||||
<tag>
|
||||
<tag>ipv0</tag>
|
||||
</tag>
|
||||
<tag>
|
||||
<tag>target</tag>
|
||||
<value>nokia</value>
|
||||
</tag>
|
||||
</tags>
|
||||
<macros>
|
||||
<macro>
|
||||
<macro>{$ALERTLIMMITONTS}</macro>
|
||||
<value>120</value>
|
||||
</macro>
|
||||
<macro>
|
||||
<macro>{$LIMITONTOFF}</macro>
|
||||
<value>20</value>
|
||||
<description>Valor em %</description>
|
||||
</macro>
|
||||
</macros>
|
||||
<valuemaps>
|
||||
<valuemap>
|
||||
<uuid>f3ba4a6f09994b2fa4753ca09cfec605</uuid>
|
||||
<name>CARD: Power Status</name>
|
||||
<mappings>
|
||||
<mapping>
|
||||
<value>1</value>
|
||||
<newvalue>powerUp</newvalue>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<value>2</value>
|
||||
<newvalue>powerDown</newvalue>
|
||||
</mapping>
|
||||
</mappings>
|
||||
</valuemap>
|
||||
<valuemap>
|
||||
<uuid>ca194f4197714aa58de43a4088308a30</uuid>
|
||||
<name>CARD: Status Administrativo</name>
|
||||
<mappings>
|
||||
<mapping>
|
||||
<value>1</value>
|
||||
<newvalue>Unlock</newvalue>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<value>2</value>
|
||||
<newvalue>Lock</newvalue>
|
||||
</mapping>
|
||||
</mappings>
|
||||
</valuemap>
|
||||
<valuemap>
|
||||
<uuid>f36bdc78e7f341c8a3421d8a00e930f8</uuid>
|
||||
<name>CARD: Status CPU</name>
|
||||
<mappings>
|
||||
<mapping>
|
||||
<value>1</value>
|
||||
<newvalue>start</newvalue>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<value>2</value>
|
||||
<newvalue>stop</newvalue>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<value>3</value>
|
||||
<newvalue>proceeding</newvalue>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<value>4</value>
|
||||
<newvalue>idle</newvalue>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<value>5</value>
|
||||
<newvalue>not-operational</newvalue>
|
||||
</mapping>
|
||||
</mappings>
|
||||
</valuemap>
|
||||
<valuemap>
|
||||
<uuid>9cd3f98309994cd1be946cd7d2819d4a</uuid>
|
||||
<name>CARD: Status Operacional</name>
|
||||
<mappings>
|
||||
<mapping>
|
||||
<value>1</value>
|
||||
<newvalue>Enabled</newvalue>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<value>2</value>
|
||||
<newvalue>Disabled</newvalue>
|
||||
</mapping>
|
||||
</mappings>
|
||||
</valuemap>
|
||||
<valuemap>
|
||||
<uuid>987074d3178f4941ad79d06d4cff5e3f</uuid>
|
||||
<name>PON: SFP Type</name>
|
||||
<mappings>
|
||||
<mapping>
|
||||
<value>0</value>
|
||||
<newvalue>Desconhecido</newvalue>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<value>7</value>
|
||||
<newvalue>B</newvalue>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<value>8</value>
|
||||
<newvalue>B+</newvalue>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<value>9</value>
|
||||
<newvalue>C+</newvalue>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<value>10</value>
|
||||
<newvalue>C++</newvalue>
|
||||
</mapping>
|
||||
</mappings>
|
||||
</valuemap>
|
||||
<valuemap>
|
||||
<uuid>2546518a57394c75a2cc34f2b4f18517</uuid>
|
||||
<name>PON: Status Operacional</name>
|
||||
<mappings>
|
||||
<mapping>
|
||||
<value>1</value>
|
||||
<newvalue>Up</newvalue>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<value>2</value>
|
||||
<newvalue>Down</newvalue>
|
||||
</mapping>
|
||||
</mappings>
|
||||
</valuemap>
|
||||
</valuemaps>
|
||||
</template>
|
||||
</templates>
|
||||
</zabbix_export>
|
||||
120
doc/zabbix_integration.md
Normal file
120
doc/zabbix_integration.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Guia Técnico de Integração Zabbix
|
||||
|
||||
Este documento descreve como construir templates Zabbix para consumir a **IPv0 OLT API**. Ele detalha os Headers, Regras de Descoberta (LLD), Pré-processamentos Java Script e estrutura dos itens.
|
||||
|
||||
## 1. Visão Geral (Master Item)
|
||||
|
||||
Toda a coleta de dados de uma OLT deve ser centralizada em um único **Master Item (HTTP Agent)**. Isso aproveita o cache da API e evita sobrecarga de conexões.
|
||||
|
||||
### Configuração do Master Item
|
||||
* **Nome:** `OLT API Stats`
|
||||
* **Type:** `HTTP agent`
|
||||
* **Key:** `olt.api.stats`
|
||||
* **URL:** `http://{$API_IP}:{$API_PORT}/api/v1/olt_stats`
|
||||
* **Query Fields:**
|
||||
* `host`: `{HOST.CONN}` ou `{HOST.IP}`
|
||||
* **Headers:**
|
||||
* `Content-Type`: `application/json`
|
||||
* **Timeout:** `30s` (ou mais, dependendo do tamanho da OLT)
|
||||
* **History:** `1h` (NÃO guarde histórico longo do JSON bruto se for muito grande)
|
||||
* **Trends:** `0` (Texto não tem trend)
|
||||
|
||||
---
|
||||
|
||||
## 2. Padrão de Descoberta (Low Level Discovery - LLD)
|
||||
|
||||
A API retorna um JSON estruturado hierarquicamente (`NGFC`, `FGLT`, etc). Para criar itens no Zabbix, precisamos "achatar" essa estrutura usando **LLD Dependent Rules**.
|
||||
|
||||
### 2.1 LLD de Cards (Placas)
|
||||
Para descobrir as placas (FGLT, FANT, etc):
|
||||
|
||||
* **Type:** `Dependent item`
|
||||
* **Master Item:** `OLT API Stats`
|
||||
* **Key:** `olt.card.discovery`
|
||||
* **Preprocessing:**
|
||||
1. **JavaScript**:
|
||||
```javascript
|
||||
var data = JSON.parse(value);
|
||||
var output = [];
|
||||
// Itera sobre tipos de cards conhecidos
|
||||
['FGLT', 'FANT', 'NGFC'].forEach(function(type) {
|
||||
if (data[type]) {
|
||||
data[type].forEach(function(card) {
|
||||
output.push({
|
||||
"{#CARD_INDEX}": card.cardIndex,
|
||||
"{#CARD_NAME}": card.cardName,
|
||||
"{#CARD_TYPE}": card.cardType,
|
||||
"{#CARD_SLOT}": card.cardSlot || card.cardNumber // Ajuste conforme driver
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
return JSON.stringify(output);
|
||||
```
|
||||
|
||||
### 2.2 LLD de PONs (Portas)
|
||||
Para descobrir as portas PON e criar métricas de suporte (Total/Online/Offline):
|
||||
|
||||
* **Type:** `Dependent item`
|
||||
* **Master Item:** `OLT API Stats`
|
||||
* **Key:** `olt.pon.discovery`
|
||||
* **Preprocessing:**
|
||||
1. **JavaScript**:
|
||||
```javascript
|
||||
var data = JSON.parse(value);
|
||||
var output = [];
|
||||
if (data.FGLT) {
|
||||
data.FGLT.forEach(function(card) {
|
||||
if (card.pons) {
|
||||
card.pons.forEach(function(pon) {
|
||||
output.push({
|
||||
"{#PON_NAME}": pon.ponName,
|
||||
"{#PON_INDEX}": pon.ponIndex,
|
||||
"{#PON_CODE}": pon.ponCode, // Importante para SNMP
|
||||
"{#CARD_INDEX}": card.cardIndex
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return JSON.stringify(output);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Protótipos de Itens (Item Prototypes)
|
||||
|
||||
### 3.1 Métricas via JSON (Dependentes)
|
||||
Métricas como "ONTs Online" vêm direto do JSON. Não use SNMP ou HTTP novo. Use **Dependent Item**.
|
||||
|
||||
* **Name:** `PON {#PON_NAME}: ONTs Online`
|
||||
* **Type:** `Dependent item`
|
||||
* **Master Item:** `OLT API Stats`
|
||||
* **Key:** `pon.online[{#PON_INDEX}]`
|
||||
* **Preprocessing:**
|
||||
1. **JSONPath**:
|
||||
```
|
||||
$.FGLT[?(@.cardIndex=='{#CARD_INDEX}')].pons[?(@.ponIndex=='{#PON_INDEX}')].onuStats.up.first()
|
||||
```
|
||||
|
||||
### 3.2 Métricas via SNMP (SNMP Agent)
|
||||
Métricas físicas (Temperatura, Voltagem, Status Operacional) devem ser coletadas via SNMP direto da OLT, usando os índices descobertos.
|
||||
|
||||
* **Name:** `PON {#PON_NAME}: Temperatura`
|
||||
* **Type:** `SNMP agent`
|
||||
* **Key:** `pon.temp[{#PON_INDEX}]`
|
||||
* **SNMP OID:**
|
||||
* Exemplo Nokia: `1.3.6.1.4.1.637.61.1.56.5.1.10.{#CARD_INDEX}.{#PON_INDEX}`
|
||||
* Exemplo Interface Genérica: `1.3.6.1.2.1.2.2.1.8.{#PON_CODE}`
|
||||
|
||||
---
|
||||
|
||||
## 4. Templates Disponíveis
|
||||
|
||||
Abaixo listamos os templates XML prontos para importação que seguem este padrão:
|
||||
|
||||
* **OLT Nokia**: [doc/templates/template-nokia-api.xml](doc/templates/template-nokia-api.xml)
|
||||
* *Suporta: 7360 ISAM, Placas FGLT/FANT, Métricas de ONTs, Status Físico (SFP).*
|
||||
|
||||
---
|
||||
**Nota:** Mantenha os templates versionados junto com o código da API para garantir compatibilidade com as estruturas JSON retornadas.
|
||||
37
drivers/__init__.py
Normal file
37
drivers/__init__.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class OltDriver(ABC):
|
||||
"""
|
||||
Classe base abstrata para drivers de OLT.
|
||||
Todos os drivers específicos devem herdar desta classe e implementar
|
||||
seus métodos abstratos.
|
||||
"""
|
||||
def __init__(self, host, username, password, **kwargs):
|
||||
self.host = host
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.options = kwargs
|
||||
|
||||
@abstractmethod
|
||||
def connect(self):
|
||||
"""
|
||||
Estabelece a conexão com a OLT (SSH/Telnet).
|
||||
Deve retornar o objeto de conexão (ex: Netmiko connection handler).
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_olt_stats(self):
|
||||
"""
|
||||
Coleta estatísticas completas da OLT (Cards > PONs > ONTs).
|
||||
Deve retornar um dicionário hierárquico:
|
||||
{
|
||||
"FGLT": [
|
||||
{
|
||||
"cardIndex": "...",
|
||||
"pons": [...]
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
pass
|
||||
8
drivers/fiberhome.py
Normal file
8
drivers/fiberhome.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from drivers import OltDriver
|
||||
|
||||
class FiberhomeDriver(OltDriver):
|
||||
def connect(self):
|
||||
raise NotImplementedError("Driver Fiberhome ainda não implementado.")
|
||||
|
||||
def get_olt_stats(self):
|
||||
raise NotImplementedError("Driver Fiberhome ainda não implementado (v2.0).")
|
||||
326
drivers/nokia.py
Normal file
326
drivers/nokia.py
Normal file
@@ -0,0 +1,326 @@
|
||||
from netmiko import ConnectHandler
|
||||
from drivers import OltDriver
|
||||
from utils.snmp import snmp_walk
|
||||
from cachetools import TTLCache, cached
|
||||
import re
|
||||
import time
|
||||
|
||||
# Cache de estrutura (Cards/PONs) persistente por 1 hora
|
||||
# Chave: IP do host
|
||||
_structure_cache = TTLCache(maxsize=100, ttl=3600)
|
||||
|
||||
class NokiaDriver(OltDriver):
|
||||
def connect(self):
|
||||
device = {
|
||||
'device_type': 'nokia_sros',
|
||||
'host': self.host,
|
||||
'username': self.username,
|
||||
'password': self.password,
|
||||
}
|
||||
|
||||
# Merge options (ssh_options, etc)
|
||||
if self.options:
|
||||
# Tratamento especial para ssh_options
|
||||
if 'ssh_options' in self.options:
|
||||
device.update(self.options['ssh_options'])
|
||||
# Adiciona o restante das opções direto no device, excluindo snmp_community
|
||||
device.update({k: v for k, v in self.options.items() if k not in ['ssh_options', 'snmp_community']})
|
||||
|
||||
device.setdefault('global_delay_factor', 2)
|
||||
|
||||
return ConnectHandler(**device)
|
||||
|
||||
def get_olt_stats(self):
|
||||
start_time = time.time()
|
||||
print(f"[{self.host}] Starting collection...", flush=True)
|
||||
|
||||
# 1. Obter Estatísticas via CLI
|
||||
cli_stats = self._get_cli_stats()
|
||||
print(f"[{self.host}] CLI stats collected in {time.time() - start_time:.2f}s", flush=True)
|
||||
|
||||
t_snmp = time.time()
|
||||
# 2. Obter Estrutura via SNMP (Descoberta de Hardware) - Agora com Cache de 1h
|
||||
structure = self._get_snmp_structure()
|
||||
|
||||
if structure is None:
|
||||
print(f"[{self.host}] CRITICAL: SNMP structure is None!", flush=True)
|
||||
structure = {}
|
||||
|
||||
print(f"[{self.host}] SNMP structure keys: {list(structure.keys())} (Time: {time.time() - t_snmp:.2f}s)", flush=True)
|
||||
|
||||
# 3. Merge dos dados
|
||||
merged = self._merge_data(structure, cli_stats)
|
||||
|
||||
if merged is None:
|
||||
print(f"[{self.host}] CRITICAL: Merged data is None!", flush=True)
|
||||
merged = {}
|
||||
|
||||
print(f"[{self.host}] Total execution time: {time.time() - start_time:.2f}s", flush=True)
|
||||
return merged
|
||||
|
||||
def _get_cli_stats(self):
|
||||
try:
|
||||
# O comando 'match' falhou (invalid token).
|
||||
# Revertendo para o método '?' que sabemos que funciona (bypassa paginação).
|
||||
|
||||
device = {
|
||||
'device_type': 'nokia_sros',
|
||||
'host': self.host,
|
||||
'username': self.username,
|
||||
'password': self.password,
|
||||
}
|
||||
if self.options and 'ssh_options' in self.options:
|
||||
device.update(self.options['ssh_options'])
|
||||
|
||||
# Recriando conexão para garantir estado limpo (send_command_timing exige cuidado)
|
||||
conn = ConnectHandler(**device)
|
||||
|
||||
cmd = "show interface port ?"
|
||||
print(f"[{self.host}] Sending CLI cmd: {cmd}", flush=True)
|
||||
|
||||
output = conn.send_command_timing(cmd, last_read=2.0)
|
||||
conn.disconnect()
|
||||
|
||||
print(f"[{self.host}] CLI output length: {len(output)} chars", flush=True)
|
||||
parsed = self._parse_cli_output(output)
|
||||
print(f"[{self.host}] Parsed clean ONTs count: {sum(len(v['onuStats']) for v in ([],) if False) or sum(len(x) for x in parsed.values()) if parsed else 0}", flush=True)
|
||||
|
||||
return parsed
|
||||
except Exception as e:
|
||||
print(f"CLI Error: {e}. Falling back...", flush=True)
|
||||
return self._fallback_cli_stats()
|
||||
|
||||
def _fallback_cli_stats(self):
|
||||
try:
|
||||
device = {
|
||||
'device_type': 'nokia_sros',
|
||||
'host': self.host,
|
||||
'username': self.username,
|
||||
'password': self.password,
|
||||
}
|
||||
if self.options and 'ssh_options' in self.options:
|
||||
device.update(self.options['ssh_options'])
|
||||
|
||||
conn = ConnectHandler(**device)
|
||||
output = conn.send_command_timing("show interface port", last_read=2.0)
|
||||
conn.disconnect()
|
||||
return self._parse_cli_output(output)
|
||||
except Exception as ex:
|
||||
print(f"Fallback CLI Error: {ex}")
|
||||
return {}
|
||||
|
||||
def _parse_cli_output(self, output):
|
||||
stats = {}
|
||||
# Regex ajustado para pegar apenas ONTs
|
||||
regex_ont = re.compile(r'(ont:\d+/\d+/\d+/\d+/\d+)\s+(\S+)\s+(\S+)')
|
||||
|
||||
lines = output.splitlines()
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
|
||||
match_ont = regex_ont.search(line)
|
||||
if match_ont:
|
||||
ont_full = match_ont.group(1)
|
||||
ont_id_full = ont_full.replace("ont:", "")
|
||||
parts = ont_id_full.split("/")
|
||||
if len(parts) > 1:
|
||||
pon_index = "/".join(parts[:-1])
|
||||
if pon_index not in stats:
|
||||
stats[pon_index] = {"online_onts": 0, "offline_onts": 0, "total_onts": 0}
|
||||
|
||||
stats[pon_index]["total_onts"] += 1
|
||||
oper = match_ont.group(3).lower()
|
||||
if "up" in oper:
|
||||
stats[pon_index]["online_onts"] += 1
|
||||
else:
|
||||
stats[pon_index]["offline_onts"] += 1
|
||||
return stats
|
||||
|
||||
@cached(_structure_cache, key=lambda self: self.host)
|
||||
def _get_snmp_structure(self):
|
||||
community = self.options.get('snmp_community', 'public')
|
||||
host = self.host
|
||||
cards_oid = '1.3.6.1.4.1.637.61.1.23.3.1.3'
|
||||
|
||||
try:
|
||||
card_results = snmp_walk(host, community, cards_oid)
|
||||
except Exception as e:
|
||||
print(f"SNMP Error (Cards): {e}")
|
||||
return {}
|
||||
|
||||
structure = {}
|
||||
seq_counters = {}
|
||||
|
||||
for oid, value in card_results:
|
||||
card_index = oid.split('.')[-1]
|
||||
raw_type = value.replace('"', '').strip()
|
||||
|
||||
if raw_type == 'EMPTY' or not raw_type:
|
||||
continue
|
||||
|
||||
if '-' in raw_type:
|
||||
card_type, card_class = raw_type.split('-', 1)
|
||||
else:
|
||||
card_type, card_class = raw_type, ''
|
||||
|
||||
if card_type not in seq_counters:
|
||||
seq_counters[card_type] = 0
|
||||
seq_counters[card_type] += 1
|
||||
card_seq = seq_counters[card_type]
|
||||
|
||||
# Construção do objeto Card mantendo a ordem desejada das chaves
|
||||
card_obj = {
|
||||
"cardClass": card_class,
|
||||
"cardIndex": card_index,
|
||||
"cardName": f"CARD {card_seq}",
|
||||
"cardNumber": card_seq,
|
||||
"cardType": card_type,
|
||||
"pons": []
|
||||
}
|
||||
|
||||
if card_type == 'FGLT':
|
||||
# Pega PONs (estrutura apenas)
|
||||
pons = self._get_snmp_pons(host, community, card_index, card_seq)
|
||||
card_obj['pons'] = pons
|
||||
|
||||
if card_type not in structure:
|
||||
structure[card_type] = []
|
||||
structure[card_type].append(card_obj)
|
||||
|
||||
return structure
|
||||
|
||||
def _get_snmp_pons(self, host, community, card_index, card_seq):
|
||||
base_oid = f'1.3.6.1.4.1.637.61.1.56.5.1.3.{card_index}'
|
||||
try:
|
||||
pon_results = snmp_walk(host, community, base_oid)
|
||||
except:
|
||||
return []
|
||||
|
||||
pons = []
|
||||
for oid, value in pon_results:
|
||||
pon_index = oid.split('.')[-1]
|
||||
pon_code = value
|
||||
pon_name = f"1/1/{card_seq}/{pon_index}"
|
||||
|
||||
# Objeto PON provisório (será recriado no merge para ordem final)
|
||||
pons.append({
|
||||
"cardIndex": card_index, # Necessário para lógica interna, será removido no final
|
||||
"ponIndex": pon_index,
|
||||
"ponName": pon_name,
|
||||
"ponCode": pon_code,
|
||||
"onuStats": {
|
||||
"up": "0", "down": "0", "total": "0"
|
||||
}
|
||||
})
|
||||
|
||||
return pons
|
||||
|
||||
def _merge_data(self, structure, cli_stats):
|
||||
if not structure:
|
||||
return structure
|
||||
|
||||
cli_slots_map = {}
|
||||
for k, v in cli_stats.items():
|
||||
parts = k.split('/')
|
||||
if len(parts) >= 4:
|
||||
slot = parts[2]
|
||||
port = parts[3]
|
||||
if slot not in cli_slots_map:
|
||||
cli_slots_map[slot] = {}
|
||||
cli_slots_map[slot][port] = v
|
||||
|
||||
sorted_cli_slots = sorted(cli_slots_map.keys(), key=lambda x: int(x) if x.isdigit() else x)
|
||||
|
||||
# 1. Ordenação Top-Level: NGFC, FANT, FGLT
|
||||
ordered_structure = {}
|
||||
for type_key in ["NGFC", "FANT", "FGLT"]:
|
||||
if type_key in structure:
|
||||
ordered_structure[type_key] = structure[type_key]
|
||||
# Adiciona quaisquer outros tipos que não estejam na lista prioritária
|
||||
for k, v in structure.items():
|
||||
if k not in ordered_structure:
|
||||
ordered_structure[k] = v
|
||||
|
||||
if 'FGLT' in ordered_structure:
|
||||
fglt_cards = ordered_structure['FGLT']
|
||||
fglt_cards.sort(key=lambda x: int(x['cardIndex']) if x['cardIndex'].isdigit() else x['cardIndex'])
|
||||
|
||||
print(f"DEBUG: Processing {len(fglt_cards)} FGLT cards. CLI Slots: {sorted_cli_slots}")
|
||||
|
||||
for i, card in enumerate(fglt_cards):
|
||||
real_slot = "0"
|
||||
slot_stats = {}
|
||||
|
||||
if i < len(sorted_cli_slots):
|
||||
real_slot = sorted_cli_slots[i]
|
||||
if real_slot in cli_slots_map:
|
||||
slot_stats = cli_slots_map[real_slot]
|
||||
else:
|
||||
# Fallback para cards sem CLI stats
|
||||
# Empírico: Cards começam no slot 5 (ou 1?)
|
||||
# card['cardNumber'] 1 -> Slot 5?
|
||||
real_slot = str(card['cardNumber'] + 4)
|
||||
|
||||
print(f"DEBUG: Card {card['cardName']} (seq {card['cardNumber']}) mapped to Slot {real_slot}")
|
||||
|
||||
# Iterar sobre PONs
|
||||
for j, pon in enumerate(card['pons']):
|
||||
p_idx = pon['ponIndex']
|
||||
|
||||
# Valores default
|
||||
up, down, total = "0", "0", "0"
|
||||
|
||||
if p_idx in slot_stats:
|
||||
s = slot_stats[p_idx]
|
||||
up = str(s['online_onts'])
|
||||
down = str(s['offline_onts'])
|
||||
total = str(s['total_onts'])
|
||||
|
||||
# CALCULAR PON CODE (IF-INDEX)
|
||||
# Se real_slot for válido (não "0"), calculamos.
|
||||
# Se for "0" (impossível com fallback acima), mantemos original.
|
||||
|
||||
final_code = pon['ponCode']
|
||||
if real_slot != "0":
|
||||
# PATCH: Correção para Slot 6 (FGLT) que mapeia para 0x07...
|
||||
# Parece haver um offset de +1 a partir do Slot 6 (ou específico dele)
|
||||
calc_slot = real_slot
|
||||
if str(real_slot) == "6":
|
||||
calc_slot = "7"
|
||||
print(f"DEBUG: Slot 6 detected. Applying correction -> 7 for ifIndex calc.", flush=True)
|
||||
|
||||
final_code = self._calculate_if_index(calc_slot, p_idx)
|
||||
|
||||
# Debug para comparar valor SNMP original vs Calculado
|
||||
if pon['ponCode'] and pon['ponCode'] != final_code:
|
||||
print(f"DEBUG: Code Mismatch for {pon['ponName']}! SNMP={pon['ponCode']} Calc={final_code}", flush=True)
|
||||
|
||||
new_pon_obj = {
|
||||
"ponCode": final_code,
|
||||
"ponIndex": p_idx,
|
||||
"ponName": f"PON 1/1/{real_slot}/{p_idx}",
|
||||
"onuStats": {
|
||||
"down": down,
|
||||
"total": total,
|
||||
"up": up
|
||||
}
|
||||
}
|
||||
card['pons'][j] = new_pon_obj
|
||||
|
||||
return ordered_structure
|
||||
|
||||
def _calculate_if_index(self, slot, port):
|
||||
"""
|
||||
Calcula o if-index baseado no Slot e Porta.
|
||||
Engenharia reversa:
|
||||
Slot 5, Port 1 -> 94371840 (0x05A00000)
|
||||
Slot 6, Port 1 -> 127926272 (0x07A00000) -> Fix: Usar Slot 7 calc
|
||||
Fórmula: (Slot << 24) + ((159 + Port) << 16)
|
||||
"""
|
||||
try:
|
||||
s = int(slot)
|
||||
p = int(port)
|
||||
return str((s << 24) + ((159 + p) << 16))
|
||||
except:
|
||||
return "0"
|
||||
13
hosts.example.json
Normal file
13
hosts.example.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"10.0.0.1": {
|
||||
"username": "admin",
|
||||
"password": "change_me",
|
||||
"driver": "nokia",
|
||||
"port": 22,
|
||||
"ssh_options": {
|
||||
"disabled_algorithms": {
|
||||
"pubkeys": ["rsa-sha2-256", "rsa-sha2-512"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Flask==3.0.3
|
||||
netmiko==4.3.0
|
||||
cachetools==5.3.3
|
||||
python-dotenv==1.0.1
|
||||
pysnmp
|
||||
pyarmor
|
||||
42
tools/build.sh
Executable file
42
tools/build.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
# Definir diretórios (Relativos à raiz do projeto, assumindo execução via ./tools/build.sh)
|
||||
# Mas vamos garantir que o script rode da raiz
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
echo "[BUILD] Limpando builds anteriores..."
|
||||
rm -rf dist build release_production.zip
|
||||
|
||||
# Definir diretórios
|
||||
STAGE_DIR="dist/ipv0-olt-api"
|
||||
RELEASE_DIR="dist/release"
|
||||
mkdir -p $STAGE_DIR
|
||||
mkdir -p $RELEASE_DIR
|
||||
|
||||
echo "[BUILD] Iniciando Obfuscação Pyarmor (Modo Full)..."
|
||||
# Usando Pyarmor para proteger todo o código fonte
|
||||
# O comando gen irá usar a configuração existente em .pyarmor se houver, ou criar uma nova.
|
||||
# --outer: Permite usar chave de licença externa (license.key via outer_keyname=license.key)
|
||||
./venv/bin/pyarmor gen --outer -O $STAGE_DIR app.py drivers/ utils/ config.py
|
||||
|
||||
|
||||
echo "[BUILD] Copiando arquivos estáticos..."
|
||||
cp hosts.json $STAGE_DIR/
|
||||
cp requirements.txt $STAGE_DIR/
|
||||
cp README.md $STAGE_DIR/
|
||||
cp tools/debug.py $STAGE_DIR/
|
||||
cp tools/service/install.sh $STAGE_DIR/
|
||||
cp tools/service/ipv0-olt-api.service $STAGE_DIR/
|
||||
chmod +x $STAGE_DIR/install.sh
|
||||
|
||||
echo "[BUILD] Criando arquivo ZIP (via Python)..."
|
||||
# Usar Python para zipar o CONTEÚDO do diretório de staging para dentro do zip
|
||||
# O zip final ficará em dist/release/ipv0-olt-api.zip
|
||||
# Estando em dist/ipv0-olt-api, ../release aponta para dist/release
|
||||
cd $STAGE_DIR
|
||||
../../venv/bin/python3 -c "import shutil; shutil.make_archive('../release/ipv0-olt-api', 'zip', '.')"
|
||||
cd ../..
|
||||
|
||||
echo "✅ Build Process Complete!"
|
||||
echo "Artifacts:"
|
||||
echo " - Staging: $STAGE_DIR"
|
||||
echo " - Release: $RELEASE_DIR/ipv0-olt-api.zip"
|
||||
ls -F $RELEASE_DIR/
|
||||
51
tools/debug.py
Normal file
51
tools/debug.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import time
|
||||
import urllib.request
|
||||
import json
|
||||
|
||||
def print_header(msg):
|
||||
print(f"\n{'='*40}\n {msg}\n{'='*40}")
|
||||
|
||||
import glob
|
||||
|
||||
def check_pyarmor_binding():
|
||||
print_header("Hardware & License (Machine ID)")
|
||||
|
||||
try:
|
||||
# Usar ferramenta oficial do Pyarmor CLI para HD Info
|
||||
subprocess.run([sys.executable, "-m", "pyarmor.cli.hdinfo"], check=False)
|
||||
except Exception as e:
|
||||
print(f"Erro ao consultar HD Info: {e}")
|
||||
|
||||
def check_service_status():
|
||||
print_header("Service Status (systemd)")
|
||||
ret = subprocess.run(["systemctl", "status", "ipv0-olt-api", "--no-pager"], capture_output=False)
|
||||
if ret.returncode != 0:
|
||||
print("⚠️ Serviço parece estar parado ou com erro.")
|
||||
|
||||
def check_api_health():
|
||||
print_header("API Health Check")
|
||||
url = "http://localhost:5050/health"
|
||||
try:
|
||||
print(f"Connecting to {url}...")
|
||||
with urllib.request.urlopen(url, timeout=5) as response:
|
||||
if response.status == 200:
|
||||
data = json.loads(response.read().decode())
|
||||
print(f"✅ API Online! Status: {data}")
|
||||
else:
|
||||
print(f"❌ API retornou status code: {response.status}")
|
||||
except Exception as e:
|
||||
print(f"❌ Falha na conexão com API: {e}")
|
||||
print("Verifique se o serviço está rodando e se a licença é válida.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("IPv0 OLT API - Debug Tool (v3.1)")
|
||||
|
||||
check_service_status()
|
||||
check_pyarmor_binding()
|
||||
check_api_health()
|
||||
|
||||
print("\nLogs recentes:")
|
||||
subprocess.run(["journalctl", "-u", "ipv0-olt-api", "-n", "10", "--no-pager"], check=False)
|
||||
87
tools/gen_license.sh
Executable file
87
tools/gen_license.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Ensure we are in project root
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# Configuration
|
||||
OUTPUT_BASE="dist/licenses"
|
||||
PYARMOR="./venv/bin/pyarmor"
|
||||
|
||||
# Colors (only if terminal supports it, otherwise empty)
|
||||
if [ -t 1 ]; then
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
else
|
||||
RED=''
|
||||
GREEN=''
|
||||
BLUE=''
|
||||
NC=''
|
||||
fi
|
||||
|
||||
echo "${BLUE}=== Gerador de Licenças IPv0 OLT API ===${NC}"
|
||||
|
||||
# 1. Solicitar Nome do Cliente
|
||||
echo "Nome do Cliente (sem espaços, ex: ProvedorX): "
|
||||
read CLIENT_NAME
|
||||
if [ -z "$CLIENT_NAME" ]; then
|
||||
echo "${RED}Erro: Nome do cliente é obrigatório.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEST_DIR="$OUTPUT_BASE/$CLIENT_NAME"
|
||||
mkdir -p "$DEST_DIR"
|
||||
|
||||
# 2. Escolher Tipo de Licença
|
||||
echo ""
|
||||
echo "Tipo de Licença:"
|
||||
echo "1) Data de Expiração (Trial/PoC)"
|
||||
echo "2) Hardware (Produção/Machine ID)"
|
||||
echo "Opção [1/2]: "
|
||||
read OPTION
|
||||
|
||||
if [ "$OPTION" = "1" ]; then
|
||||
# Licença por Data
|
||||
echo "Data de Vencimento (YYYY-MM-DD): "
|
||||
read EXPIRE_DATE
|
||||
# Validação simples de formato YYYY-MM-DD
|
||||
if ! echo "$EXPIRE_DATE" | grep -qE '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'; then
|
||||
echo "${RED}Erro: Formato de data inválido.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Gerando licença Trial para ${GREEN}$CLIENT_NAME${NC} até ${GREEN}$EXPIRE_DATE${NC}..."
|
||||
$PYARMOR gen key -e "$EXPIRE_DATE"
|
||||
|
||||
elif [ "$OPTION" = "2" ]; then
|
||||
# Licença por Hardware
|
||||
echo "Machine ID do Cliente: "
|
||||
read MACHINE_ID
|
||||
if [ -z "$MACHINE_ID" ]; then
|
||||
echo "${RED}Erro: Machine ID é obrigatório.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Gerando licença Permanente para ${GREEN}$CLIENT_NAME${NC} (ID: $MACHINE_ID)..."
|
||||
$PYARMOR gen key --bind-device "$MACHINE_ID"
|
||||
|
||||
else
|
||||
echo "${RED}Opção inválida.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 3. Mover e Verifica
|
||||
# Por padrão, o pyarmor gera em dist/license.key (conforme config outer_keyname)
|
||||
GENERATED_FILE="dist/license.key"
|
||||
|
||||
if [ -f "$GENERATED_FILE" ]; then
|
||||
mv "$GENERATED_FILE" "$DEST_DIR/"
|
||||
echo ""
|
||||
echo "${GREEN}✅ Licença gerada com sucesso!${NC}"
|
||||
echo "Arquivo: ${BLUE}$DEST_DIR/license.key${NC}"
|
||||
echo "Envie este arquivo para o cliente."
|
||||
else
|
||||
echo "${RED}❌ Erro: O arquivo de licença não foi gerado.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
60
tools/service/install.sh
Normal file
60
tools/service/install.sh
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
|
||||
# IPv0 OLT API Installer
|
||||
# Usage: sudo ./install.sh
|
||||
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Please run as root (sudo ./install.sh)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEST_DIR="/opt/ipv0-olt-api"
|
||||
|
||||
echo "[INSTALL] Installing dependencies..."
|
||||
apt-get update
|
||||
apt-get install -y python3-venv python3-pip unzip
|
||||
|
||||
echo "[INSTALL] Setting up directory..."
|
||||
mkdir -p $DEST_DIR
|
||||
# Copiar arquivos do diretório atual para o destino (se não estiver lá)
|
||||
if [ "$PWD" != "$DEST_DIR" ]; then
|
||||
cp -r * $DEST_DIR/
|
||||
fi
|
||||
|
||||
cd $DEST_DIR
|
||||
|
||||
echo "[INSTALL] Creating virtual environment..."
|
||||
python3 -m venv venv
|
||||
./venv/bin/pip install -r requirements.txt
|
||||
|
||||
echo "[INSTALL] Configuring Service..."
|
||||
cp ipv0-olt-api.service /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
# Não habilitar/iniciar automaticamente, pois falta a licença
|
||||
# systemctl enable ipv0-olt-api
|
||||
# systemctl start ipv0-olt-api
|
||||
|
||||
echo ""
|
||||
echo "[INSTALL] Dependencies installed!"
|
||||
|
||||
# Obter Machine ID de forma limpa
|
||||
MACHINE_ID=$(./venv/bin/python3 -m pyarmor.cli.hdinfo 2>/dev/null | grep "Machine ID" | cut -d: -f2 | xargs)
|
||||
|
||||
# Cores
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo ""
|
||||
echo "Serviço 'ipv0-olt-api' configurado mas não iniciado - ${RED}PRECISA SER LICENCIADO${NC}."
|
||||
echo ""
|
||||
echo -e "Machine ID: ${YELLOW}${MACHINE_ID}${NC}"
|
||||
echo ""
|
||||
|
||||
echo "NEXT STEPS:"
|
||||
echo "1. Solicite sua licença."
|
||||
echo "2. Copie a licença 'license.key' para /opt/ipv0-olt-api/"
|
||||
echo "3. Inicialize o serviço: sudo systemctl enable --now ipv0-olt-api"
|
||||
echo "4. Após licenciar e inicializar o serviço, verifique se a api está funcionando com:"
|
||||
echo " curl http://localhost:5050/health"
|
||||
14
tools/service/ipv0-olt-api.service
Normal file
14
tools/service/ipv0-olt-api.service
Normal file
@@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=IPv0 OLT API (Zabbix Integration)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory=/opt/ipv0-olt-api
|
||||
ExecStart=/opt/ipv0-olt-api/venv/bin/python3 app.py
|
||||
Restart=always
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
27
utils/cache.py
Normal file
27
utils/cache.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from cachetools import TTLCache, cached
|
||||
from config import Config
|
||||
from flask import request
|
||||
|
||||
# Criação do cache com TTL (Time To Live)
|
||||
# O tamanho máximo e o tempo de vida são configurados via config.py
|
||||
ttl_cache = TTLCache(maxsize=Config.CACHE_MAX_SIZE, ttl=Config.CACHE_TTL)
|
||||
|
||||
def get_cache_key(*args, **kwargs):
|
||||
"""
|
||||
Gera uma chave única para o cache baseada na URL completa da requisição.
|
||||
Isso garante que query parameters diferentes (host, driver) gerem entradas diferentes.
|
||||
"""
|
||||
if request:
|
||||
return request.url
|
||||
return str(args) + str(kwargs)
|
||||
|
||||
def cache_response(func):
|
||||
"""
|
||||
Decorador wrapper para aplicar cache nas chamadas de função.
|
||||
A chave do cache será baseada na URL da request.
|
||||
"""
|
||||
# A função wrapper precisa aceitar args/kwargs, mas a chave é gerada por get_cache_key
|
||||
@cached(cache=ttl_cache, key=get_cache_key)
|
||||
def wrapper(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
41
utils/snmp.py
Normal file
41
utils/snmp.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import asyncio
|
||||
from pysnmp.hlapi.v3arch.asyncio import *
|
||||
|
||||
async def _snmp_walk_async(host, community, oid):
|
||||
results = []
|
||||
|
||||
# Configuração do Engine e dos dados de conexão
|
||||
snmp_engine = SnmpEngine()
|
||||
community_data = CommunityData(community, mpModel=1) # mpModel=1 para SNMPv2c
|
||||
transport = await UdpTransportTarget.create((host, 161), timeout=2, retries=1)
|
||||
context = ContextData()
|
||||
|
||||
# Realiza o Walk
|
||||
iterator = walk_cmd(
|
||||
snmp_engine,
|
||||
community_data,
|
||||
transport,
|
||||
context,
|
||||
ObjectType(ObjectIdentity(oid)),
|
||||
lexicographicMode=False
|
||||
)
|
||||
|
||||
async for errorIndication, errorStatus, errorIndex, varBinds in iterator:
|
||||
if errorIndication:
|
||||
print(f"SNMP Error: {errorIndication}")
|
||||
break
|
||||
elif errorStatus:
|
||||
print(f"SNMP Error: {errorStatus.prettyPrint()}")
|
||||
break
|
||||
else:
|
||||
for varBind in varBinds:
|
||||
results.append((str(varBind[0]), str(varBind[1])))
|
||||
|
||||
snmp_engine.closeDispatcher()
|
||||
return results
|
||||
|
||||
def snmp_walk(host, community, oid):
|
||||
"""
|
||||
Wrapper síncrono para o walk assíncrono.
|
||||
"""
|
||||
return asyncio.run(_snmp_walk_async(host, community, oid))
|
||||
Reference in New Issue
Block a user