Automatizando a criação de Dashboards no Grafana com Python3

Thiago Soares
Observability and visualizations
4 min readFeb 12, 2021

Pipelines simples, padronizáveis e de simples execução para os desenvolvedores sempre foram sonhos, mas algumas aplicações possuem fluxos próprios e de difícil replicação.

Recentemente eu me deparei, com esse problema e resolvi começar com uma POC de automatização e padronização de pipeline para os micro serviços no Gitlab (RPA) da minha empresa.

Escrevi alguns scripts em Python3 para poder pegar os dados das aplicações e fazer todas as "steps do pipeline", das aplicações; irei escrever um outro artigo focando nessas outras etapas e na aprendizagem.

Uma das etapas que a equipe, até então fazia de forma manual, era a criação da monitoração das aplicações no Grafana; geralmente copiando um dashboard já existente e alterando os dados.

Quem não conhece o Grafana, é uma ótima ferramenta de observabilidade e recomendo a utilização, a documentação, é bem completa e eficiente.

https://grafana.com/docs/

Como eu estava com a POC, e o poder criador em mãos, queria melhorar esse fluxo de forma eficiente e rápida, ainda no pipeline de criação da aplicação.

Uma das formas era a partir de templates dos dashboards; que eu já tinha de outras aplicações e queria replicar para o Gitlab.

Porém precisava fazer replaces nos dados e não fazia muita idéia de como poderia fazer de uma forma simples e não custosa. A resposta desse Stackoverflow, foi muito importante para me ajudar a amadurecer esse pensamento:

https://stackoverflow.com/questions/52802086/what-is-the-best-way-to-replace-text-in-json

A partir disso, comecei a ver a documentação do Grafana, da API para poder fazer o POST dos dados:

https://grafana.com/docs/grafana/latest/http_api/dashboard/

Para fazer o POST, é necessário a criação do TOKEN da API:

https://grafana.com/docs/grafana/latest/http_api/create-api-tokens-for-org/

Sanado o POST, porém me faltava a questão do replace no JSON de template, e o mesmo estava com as suas 2344 linhas, sendo bastante custoso o processamento com as libs normais do Python para JSON.

Seguindo a linha de pensamento do Stackoverflow, comecei a fazer a alteração; Utilizarei o exemplo da própria documentação do Grafana acima, pois não posso revelar códigos internos.

Exemplo de JSON:

{
"dashboard": {
"id": null,
"uid": null,
"title": "replace_production_Overview",
"tags": [ "templated" ],
"timezone": "browser",
"schemaVersion": 16,
"version": 0,
"refresh": "25s"
},
"folderId": 0,
"overwrite": false
}

No nó de title com o valor de replace_production_Overview , esse valor é um caracter "coringa" que irei substituir pelo valor de desejado.

Usando o seguinte comando de Perl, dentro do script de Python3:

perl -p -e ‘s/replace_production_overview/’{application}’/g’

Sendo o valor do caracter “coringa” o que ele irá buscar dentro do JSON e o valor de {application} o valor a ser substituído.

Para executar o comando, utilizei o subprocess no Python3:

https://docs.python.org/3/library/subprocess.html

Exemplo de código:

import sys, subprocess

replace_application = "perl -p -e ‘s/replace_production_overview/’{application}’/g’".format(application=self.app_aplication)

cmd = "cat dashboard.json" + replace_application ">> replace_dashboard.json”

p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding=’utf-8')

output, error = p.communicate()

O cmd, é a execução do comando do perl, citado acima, dentro do template dashboard.json retornando a substituição no replace_dashboard.json

Dessa forma, eu consegui percorrer um JSON de 2344 linhas de uma forma simples e eficiente.

Como os Dashboards no Grafana, são separados por folders dos produtos/times, ainda tive que primeiro fazer um GET, para pegar o ID da folder que iria fazer o POST.

conn.request(“GET”, “/api/folders?limit=10”, payload, headers)

A documentação poderá tira maiores dúvidas:

https://grafana.com/docs/grafana/latest/http_api/folder_dashboard_search/

O retorno do GET, é uma lista de dicionários, da qual tenho que percorrer para pegar o ID do folder, de acordo com o nome do produto/time.

Exemplo de código:

next((item[“id”] for i, item in enumerate(folder_id) if item[“title”] == self.app_product), None)

O valor de self.app_product, é o nome do produto/time a ser pesquisado.

Esse Stackoverflow, ajuda a entender a lógica usada:

https://stackoverflow.com/questions/8653516/python-list-of-dictionaries-search

O JSON final ficou da seguinte forma, sendo o replace_production_Overview e folderID valores que serão substituídos antes de fazer o POST.

{
"dashboard": {
"id": null,
"uid": null,
"title": "replace_production_Overview",
"tags": [ "templated" ],
"timezone": "browser",
"schemaVersion": 16,
"version": 0,
"refresh": "25s"
},
“overwrite”: true,
“folderId”: folderID,
“version”: 1
}

O resultado foi a criação de Grafana de forma automática no pipeline do Gitlab.

Está com os valores de "No data", pois não habilitei os datasources do dashboard, somente para demonstração.

Ao ir no settings > versions, o campo de update by fica em branco, demonstrando que não foi alterado por um usuário do Grafana.

--

--