Escalando o Monitoramento: Adicionando painéis no Grafana via API

Escalando o Monitoramento: Adicionando painéis no Grafana via API

Automatizando dashboards no Grafana usando a API

Em ambientes com muitos dashboards, pequenas mudanças podem rapidamente se transformar em trabalho repetitivo. Adicionar um painel novo em dez dashboards diferentes, por exemplo, significa abrir cada um, editar, salvar e repetir o processo diversas vezes.

Quando essa tarefa começa a se repetir, a API do Grafana passa a ser uma alternativa mais eficiente e agradavel. Com poucas linhas de código Python, é possível automatizar alterações e reduzir bastante o tempo gasto em atividades operacionais.

Por que usar a API em vez da interface?

A interface do Grafana é excelente para criar visualizações e fazer ajustes rápidos. Porém, quando a mesma alteração precisa ser aplicada em vários dashboards, o processo manual deixa de escalar.

Com a API é possível automatizar tarefas como:

  • adicionar painéis em múltiplos dashboards simultaneamente;
  • padronizar configurações visuais, unidades e thresholds;
  • exportar dashboards para backup;
  • restaurar configurações rapidamente em caso de erro;
  • integrar mudanças com versionamento em Git.

Mesmo com conhecimentos básicos de Python, já é possível automatizar boa parte dessas tarefas sem precisar desenvolver ferramentas complexas.

Quando vale a pena usar a API?

A API costuma fazer mais sentido em cenários como:

  • ambientes com vários dashboards semelhantes;
  • inclusão de painéis de resumo executivo em diferentes áreas;
  • criação de backups antes de mudanças importantes;
  • provisionamento automático para novos clientes ou ambientes;
  • tarefas repetitivas com maior chance de erro manual.

Para ajustes pontuais em um único dashboard, a interface continua sendo mais rápida. Já quando a mesma alteração precisa ser aplicada diversas vezes, automatizar passa a compensar rapidamente.

Mão na massa: um exemplo prático

Imagine um dashboard de servidores Linux. Agora suponha que seja necessário incluir dois novos painéis:

  • status de ICMP Ping, indicando se o servidor responde na rede;
  • quantidade de usuários logados naquele momento.

Esses indicadores costumam aparecer em painéis operacionais porque ajudam a identificar rapidamente problemas básicos de disponibilidade e acesso.

Pré-requisitos

  • Grafana em execução (localmente ou via Docker);
  • Python 3 instalado;
  • biblioteca requests.
pip install requests

Subindo um Grafana local para testes

docker run -d -p 3000:3000 --name grafana grafana/grafana

Após iniciar o container, acesse:

http://localhost:3000

Usuário: admin
Senha: admin

Passo 1: Buscar o dashboard pela API

O primeiro passo é obter o JSON do dashboard que será alterado.

O script abaixo busca dashboards pelo título usando a API de pesquisa do Grafana. Não é necessário conhecer o UID previamente.

import requests
import json

# Endereço do seu Grafana.
# Troque pelo IP ou domínio do ambiente onde está instalado.
GRAFANA = 'http://localhost:3000'

# Usuário e senha do Grafana.
# Em produção, o ideal é criar um Service Account com permissão de Editor:
# Configuration > Service Accounts > Add service account
AUTH = ('admin', 'admin')

# Busca dashboards pelo título.
# Mude o valor de "query" para o nome do dashboard que você quer editar.
# O parâmetro "type=dash-db" garante que só retorna dashboards reais,
# sem incluir pastas na listagem.
search = requests.get(
    f'{GRAFANA}/api/search?query=Servidores Linux&type=dash-db',
    auth=AUTH
)

# Para cada resultado encontrado, mostra o UID e o título.
# Se o nome for muito genérico, podem aparecer vários dashboards.
# Use o título exato para filtrar melhor.
dashboards = search.json()
for d in dashboards:
    print(f"uid={d['uid']} | titulo={d['title']}")

Saída esperada:

uid=abc123 | titulo=Servidores Linux

Com o UID identificado, podemos buscar o JSON completo:

# Cole aqui o uid que apareceu na busca acima.
# Se já souber o uid de cabeça (localizou na URL), pode colocar direto.
uid = 'abc123'

# Busca o JSON completo do dashboard, incluindo todos os painéis,
# variáveis, configurações de layout e metadados da pasta.
resp = requests.get(f'{GRAFANA}/api/dashboards/uid/{uid}', auth=AUTH)
data = resp.json()

# O retorno tem duas partes:
# - data['dashboard']: o conteúdo do dashboard em si (painéis, variáveis etc.)
# - data['meta']: informações como pasta, quem criou, última modificação
dashboard = data['dashboard']
folder_uid = data['meta']['folderUid']  # precisamos disso para salvar no lugar certo depois

# Só para conferir: mostra quantos painéis o dashboard tem agora.
print(f"Painéis atuais: {len(dashboard['panels'])}")

Dica: se quiser ver o JSON completo para entender a estrutura antes de editar, adicione `print(json.dumps(dashboard, indent=2, ensure_ascii=False))` e leia a saída. Fica grande, mas é muito útil para entender como cada painel é construído.

Exemplo:

print(json.dumps(
    dashboard,
    indent=2,
    ensure_ascii=False
))

O JSON costuma ser extenso, mas é a melhor forma de entender como cada painel é representado internamente.

Passo 2: Criar os novos painéis

Neste exemplo serão criados dois painéis do tipo Stat, adequados para exibir indicadores rápidos.

Os exemplos utilizam o datasource do Zabbix, mas a mesma lógica se aplica a Prometheus, InfluxDB e outros.

# uid do seu datasource Zabbix (encontre em Configuration > Data sources)
DS = {"type": "alexanderzobnin-zabbix-datasource", "uid": "SEU_UID_DATASOURCE"}

# Painel 1: Status de ICMP ping
# Mostra 1 (verde) se o host responde, 0 (vermelho) se não responde
painel_ping = {
    "id": 900,
    "type": "stat",
    "title": "ICMP Ping",
    "gridPos": {"h": 8, "w": 12, "x": 0, "y": 100},
    "datasource": DS,
    "targets": [
        {
            "group": {"filter": "Servidores Linux"},
            "host": {"filter": "servidor-lab-01"},  # nome do host no Zabbix
            "item": {"filter": "ICMP ping"},
            "queryType": "0",
            "refId": "A",
            "options": {
                "legendFormat": "Ping",
                "skipEmptyValues": False
            }
        }
    ],
    "options": {
        "colorMode": "background",
        "graphMode": "none",
        "reduceOptions": {
            "calcs": ["lastNotNull"],  # pega o último valor recebido
            "fields": "",
            "values": False
        }
    },
    "fieldConfig": {
        "defaults": {
            "mappings": [
                {"type": "value", "options": {"1": {"text": "Online",  "color": "green"}}},
                {"type": "value", "options": {"0": {"text": "Offline", "color": "red"}}}
            ],
            "color": {"mode": "thresholds"},
            "thresholds": {
                "mode": "absolute",
                "steps": [
                    {"color": "red",   "value": 0},
                    {"color": "green", "value": 1}
                ]
            },
            "noValue": "Sem dados"
        },
        "overrides": []
    }
}

# Painel 2: Usuários logados no servidor
# O item "Number of logged in users" é coletado pelo agente Zabbix
painel_usuarios = {
    "id": 901,
    "type": "stat",
    "title": "Usuários Logados",
    "gridPos": {"h": 8, "w": 12, "x": 12, "y": 100},
    "datasource": DS,
    "targets": [
        {
            "group": {"filter": "Servidores Linux"},
            "host": {"filter": "servidor-lab-01"},
            "item": {"filter": "Number of logged in users"},
            "queryType": "0",
            "refId": "A",
            "options": {
                "legendFormat": "Usuários",
                "skipEmptyValues": False
            }
        }
    ],
    "options": {
        "colorMode": "background",
        "graphMode": "area",   # mostra mini gráfico de tendência no fundo
        "reduceOptions": {
            "calcs": ["lastNotNull"],
            "fields": "",
            "values": False
        }
    },
    "fieldConfig": {
        "defaults": {
            "color": {"mode": "thresholds"},
            "thresholds": {
                "mode": "absolute",
                "steps": [
                    {"color": "green",  "value": 0},
                    {"color": "yellow", "value": 5},
                    {"color": "red",    "value": 10}
                ]
            },
            "unit": "none",
            "noValue": "0"
        },
        "overrides": []
    }
}

Painel: ICMP Ping

No painel de ping, o valor retornado pelo Zabbix é convertido diretamente para Online ou Offline, sem necessidade de processamento adicional. Usa mapeamento de valor: transforma o `1` em “Online” verde e o `0` em “Offline” vermelho, sem precisar de nenhum dado extra.

Painel: Usuários Logados

O painel de usuários utiliza thresholds para destacar situações que merecem atenção.

Por exemplo:

  • até 5 usuários: verde;
  • entre 5 e 10: amarelo;
  • acima de 10: vermelho.

Essa abordagem fornece feedback visual imediato em ambientes operacionais.

Passo 3: Adicionar os painéis ao dashboard

Uma prática útil é criar uma row para organizar visualmente os novos painéis:

# Row separador (opcional, mas organiza visualmente)
novo_row = {
    "id": 899,
    "type": "row",
    "title": "Visão Rápida do Servidor",
    "collapsed": False,
    "gridPos": {"h": 1, "w": 24, "x": 0, "y": 99}
}

# Adiciona no final, sem mexer nos painéis existentes
dashboard['panels'].append(novo_row)
dashboard['panels'].append(painel_ping)
dashboard['panels'].append(painel_usuarios)

# Incrementa a versão para o Grafana aceitar a atualização
dashboard['version'] = dashboard.get('version', 1) + 1

O uso de append() evita modificar painéis existentes e reduz o risco de alterações indesejadas.

Passo 4: Salvar novamente via API

payload = {
    "dashboard": dashboard,
    "folderUid": folder_uid,
    "overwrite": True,
    "message": "Adicionando status servidores"
}

resp = requests.post(f'{GRAFANA}/api/dashboards/db', auth=AUTH, json=payload)
resultado = resp.json()

if resp.status_code == 200:
    print(f"Dashboard atualizado: {resultado['url']}")
else:
    print(f"Erro: {resultado}")

Bônus: backup de todos os dashboards

Antes de qualquer alteração, vale a pena exportar os dashboards existentes.

import os

os.makedirs('backup_dashboards', exist_ok=True)

todos = requests.get(f'{GRAFANA}/api/search?type=dash-db&limit=100', auth=AUTH).json()

for d in todos:
    uid = d['uid']
    titulo = d['title'].replace('/', '-').replace(' ', '_')
    
    dados = requests.get(f'{GRAFANA}/api/dashboards/uid/{uid}', auth=AUTH).json()
    
    with open(f"backup_dashboards/{titulo}.json", 'w', encoding='utf-8') as f:
        json.dump(dados, f, indent=2, ensure_ascii=False)
    
    print(f"Backup salvo: {titulo}.json")

Para restaurar qualquer dashboard basta ler o JSON e fazer o POST na API novamente.

Algumas lições aprendidas na prática

Depois de aplicar essa abordagem em ambientes com diversos dashboards, alguns cuidados se mostraram úteis:

Evite alterar painéis existentes diretamente

Sempre que possível, utilize append() para adicionar novos painéis. Quando for necessário alterar um painel já existente, localize-o pelo ID e modifique apenas os campos necessários.

Faça backup antes de qualquer mudança

Exportar todos os dashboards leva poucos segundos e pode evitar horas de retrabalho em caso de erro.

Considere ambientes heterogêneos

Nem todos os hosts possuem as mesmas métricas disponíveis. Em cenários mistos, uma expressão como:

/agent.ping|ICMP ping/

permite cobrir diferentes tipos de monitoramento em uma única consulta.

skipEmptyValues: False é seu amigo

Quando um host está offline e retorna 0, algumas consultas podem ignorar essa série. Para painéis de contagem ou disponibilidade, manter essa opção como False ajuda a representar corretamente o estado do ambiente.

Conclusão

A API do Grafana não é uma ferramenta exclusiva para especialistas. É uma forma prática de ganhar velocidade e consistência em qualquer ambiente, desde pequenos laboratórios até ambientes maiores com vários dashboards para cuidar.

Se você ainda está fazendo mudanças na mão, abrindo dashboard por dashboard, vale a pena testar esse caminho. O investimento inicial de escrever um script é pequeno comparado com o tempo que você vai economizar nas próximas atualizações.

O código mostrado aqui pode ser adaptado para qualquer datasource que o Grafana suporte. Zabbix, Prometheus, InfluxDB, não importa. A lógica de buscar, modificar e salvar via API é sempre a mesma.

Anterior Jev: o hype faz sentido ou estamos apenas colocando um nome novo em um problema antigo?

About author

Deborah Melo
Deborah Melo 16 posts

Analista de Infraestrutura | Construtora na 4Linux | LPIC 1 | Graduada em Eng. Elétrica | Apaixonada por tecnologia e ideologias Open Source.

View all posts by this author →

Você pode gostar também