python拉取grafana监控图形
python拉取grafana监控图形
python通过grafana提供的api接口拉取grafana监控图形并保存至word文档生成日报发送邮件
前置条件:
1.grafana平台需要安装grafana-image-renderer 插件,用于生成静态图形
页面可以检查是否已安装


未安装会进入如下页面:

从API接口拉取图片会提示:

安装方式参考:
https://grafana.com/grafana/plugins/grafana-image-renderer/
该插件对内存大小有一定要求:

如果已安装该插件,点击Direct link rendered image后会显示一个静态监控图形页面
参考: https://cloud.tencent.com/document/product/1437/65674
2.在grafana页面生成api_keys,用于接口请求认证
不同版本获取方式不同 此示例为V9.2.6版,入口如下:



点击Add API key添加,可以指定有效期,弹出密钥的界面记得把密钥复制下来,否则关闭后就看不到了。
代码示例:
- 该示例是将Grafana获取的多张图形保存到本地的panels目录下(多线程执行),
- 通过python的Image模块进行图片拼接(拼接方式可自定义),
- 读取日志模版文档(模版中预留了等字符用于定位),
- 将拼好的图片插入到日志模版文档并替换文档中的日期信息,
- 保存生成新的文档,删除本地panels目录下的图片缓存,并发送邮件。
# -*- coding: UTF-8 -*-
import smtplib
import requests
import os
import shutil
import time
from PIL import Image
from docx import Document
from docx.shared import Inches
from datetime import datetime, timedelta
from concurrent.futures import ThreadPoolExecutor, as_completed
from email.header import Header
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import make_header
# 配置 Grafana API 参数
GRAFANA_HOST = "http://xxxxxxxxxxx"
# Grafana API 密钥具有时效性,如过期,请联系运维人员生成新的api_keys
API_TOKEN = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# 仪表板ID
DASHBOARD_UID = "xxxxxxxxxx"
# 图形ID
PANEL_ID_DATA = {"PANEL_1":[30,31,2,10,12,14,16,18,20,22,24,26,28],
"PANEL_2":[37, 39, 41, 43, 45, 47, 49, 51, 53],}
ORG_ID = "1"
#日志模版文件
document_path = "xxxxxxxx日志 - 模板.docx"
# 邮箱接收人
RECEIVERS = ["xxxxxx.com", "xxx.com"]
# 保存图片的文件名
folder = "panels"
#邮件发送
def send_mail(filename):
sender = 'xxxxxxx.com'
receiver = ','.join(RECEIVERS)
smtpserver = 'xxxxxx.com'
user = 'xxxxxx.com'
password = 'xxxxxxxx'
mail_title = filename.split('/')[1]
mail_title = mail_title.split('.')[0]
# 创建一个带附件的实例
message = MIMEMultipart()
message['From'] = sender
message['To'] = receiver
message['Subject'] = Header(mail_title, 'utf-8')
# 邮件正文内容
message.attach(MIMEText(f'hello,附件为 {mail_title},请查收,如有问题请与我联系。', 'plain', 'utf-8'))
# 构造附件
file_msg = MIMEText(open(filename, 'rb').read(), 'base64', 'UTF-8')
file_msg["Content-Type"] = 'application/octet-stream;name="%s"' % make_header([(filename, 'UTF-8')]).encode('UTF-8')
file_msg["Content-Disposition"] = 'attachment;filename= "%s"' % make_header([(filename, 'UTF-8')]).encode('UTF-8')
message.attach(file_msg)
try:
smtpObj = smtplib.SMTP(smtpserver)
smtpObj.starttls()
smtpObj.login(user, password)
smtpObj.sendmail(sender, receiver, message.as_string())
print("邮件发送成功!!!")
except Exception as e:
print(e)
print("邮件发送失败!!!")
finally:
smtpObj.quit()
def center_insert_img(doc, img,date_str):
"""插入图片"""
date_obj = datetime.strptime(date_str, '%Y-%m-%d')
# 将datetime对象格式化为指定的中文格式字符串
formatted_date = date_obj.strftime('%Y年%m月%d日')
for paragraph in doc.paragraphs:
# 根据文档中的占位符定位图片插入的位置
if '<datetime>' in paragraph.text:
paragraph.text = paragraph.text.replace('<datetime>', formatted_date)
elif '<img1>' in paragraph.text:
# 把占位符去掉
paragraph.text = paragraph.text.replace('<img1>', '')
# 添加一个文字块
run = paragraph.add_run('')
# 添加一个’回车换行效果‘
run.add_break()
# 添加图片并指定大小
run.add_picture(img[0], width=Inches(6.2))
elif '<img2>' in paragraph.text:
# 把占位符去掉
paragraph.text = paragraph.text.replace('<img2>', '')
# 添加一个文字块
run = paragraph.add_run('')
# 添加一个’回车换行效果‘
run.add_break()
# 添加图片并指定大小
run.add_picture(img[1], width=Inches(6.2))
def save_img_to_doc(img,day_time):
"""把图片保存到doc文件中的指定位置"""
tpl_doc = document_path
current_year = datetime.now().year
if not os.path.exists(f"{current_year}_doc"):
os.makedirs(f"{current_year}_doc")
res_doc = f'{current_year}_doc/日志报告_{day_time}.docx'
# 打开模板文件
document = Document(tpl_doc)
# 插入图片居中
center_insert_img(document, img,day_time)
# 保存结果文件
document.save(res_doc)
return res_doc
def download_image(render_url, output_path, headers):
"""下载单张图片的函数"""
try:
response = requests.get(render_url, headers=headers, stream=True)
if response.status_code == 200 and "image/png" in response.headers.get("Content-Type", ""):
with open(output_path, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
return output_path
else:
print(f"获取图形失败: {render_url}")
print(f"状态码: {response.status_code}, 返回内容: {response.text}")
return None
except Exception as e:
print(f"下载图片出错: {e}")
return None
def merge_images(image_paths, output_path, layout):
"""合并图片"""
images = [Image.open(path) for path in image_paths]
width, height = images[0].size
new_image = object
if layout == "grid_3xN":
num_rows = (len(images) + 2) // 3
new_image = Image.new('RGB', (3 * width, num_rows * height))
for idx, img in enumerate(images):
x, y = (idx % 3) * width, (idx // 3) * height
new_image.paste(img, (x, y))
elif layout == "grid_3x3":
new_image = Image.new('RGB', (3 * width, 3 * height))
for idx, img in enumerate(images[:9]):
x, y = (idx % 3) * width, (idx // 3) * height
new_image.paste(img, (x, y))
new_image.save(output_path)
new_image.close()
def download_grafana_panel():
# start = time.time()
time_tuple = generate_daily_timestamps_and_dates()
image_paths = []
save_img_to_doc_paths = []
headers = {"Authorization": f"Bearer {API_TOKEN}"}
tasks = []
# 创建下载目录
daily_folder = f"{folder}/{time_tuple[0]}"
os.makedirs(daily_folder, exist_ok=True)
# 使用线程池并行下载图片
# 此处的的URL仅供参考,I6xasdas要替换为你页面的实际值
# 也就是你点击Direct link rendered image后显示的静态监控图形页面的URL
with ThreadPoolExecutor(max_workers=5) as executor:
for PANEL_NAME, PANEL_ID_list in PANEL_ID_DATA.items():
for PANEL_ID in PANEL_ID_list:
render_url = f'{GRAFANA_HOST}/render/d-solo/I6xasdas/{DASHBOARD_UID}?orgId=1&from={time_tuple[1]}&to={time_tuple[2]}&panelId={PANEL_ID}&width=1000&height=500&tz=Asia%2FShanghai'
output_path = f"{daily_folder}/{DASHBOARD_UID}_panel_{PANEL_ID}.png"
tasks.append(executor.submit(download_image, render_url, output_path, headers))
# 收集任务结果
for future in as_completed(tasks):
result = future.result()
if result:
image_paths.append(result)
# 合并图片 (第一组 3xN)
if len(image_paths) > 0:
output_path1 = f"{daily_folder}/panel_01.jpg"
merge_images(image_paths[:13], output_path1, layout="grid_3xN")
save_img_to_doc_paths.append(output_path1)
# 合并图片 (第二组 3x3)
if len(image_paths) > 13:
output_path2 = f"{daily_folder}/panel_02.jpg"
merge_images(image_paths[12:21], output_path2, layout="grid_3x3")
save_img_to_doc_paths.append(output_path2)
# 插入文档保存
doc_file_path = save_img_to_doc(save_img_to_doc_paths, time_tuple[0])
# 删除缓存图片
remove_dir(f"{folder}/{time_tuple[0]}")
#发送邮件
send_mail(doc_file_path)
# end_time = time.time() - start
# print(f"巡检报告成功保存并删除图片缓存!耗时{round(end_time,2)}秒")
def generate_daily_timestamps_and_dates():
current_date_time = datetime.now()
# 提取日期部分并格式化为年-月-日的字符串形式
formatted_date = current_date_time.strftime('%Y-%m-%d')
# 将输入的日期字符串转换为datetime对象
start_date = datetime.strptime(formatted_date, '%Y-%m-%d')
last_day_start_date = start_date - timedelta(days=1)
# 获取当天的起始时间(将时间设置为00:00:00)
start_of_day = last_day_start_date.replace(hour=0, minute=0, second=0, microsecond=0)
# 将datetime对象转换为时间戳(单位:秒)
timestamp = int(start_of_day.timestamp()*1000)
date_str = start_of_day.strftime('%Y-%m-%d')
return (date_str, timestamp, timestamp + 86399*1000)
def remove_dir(directory):
try:
shutil.rmtree(directory)
except OSError as e:
print(f"删除目录时出错: {e}")
if e.errno == 32: # 文件正在被占用
print("文件被占用,等待释放后重试...")
time.sleep(0.1) # 等待 0.1 秒后再次尝试
try:
shutil.rmtree(directory)
print(f"第二次尝试删除成功: {directory}")
except Exception as e2:
print(f"仍然无法删除目录: {e2}")
if __name__ == "__main__":
download_grafana_panel()
python拉取grafana监控图形的更多相关文章
- 一步步教你用Prometheus搭建实时监控系统系列(二)——详细分析拉取和推送两种不同模式
前言 本系列着重介绍Prometheus以及如何用它和其周边的生态来搭建一套属于自己的实时监控告警平台. 本系列受众对象为初次接触Prometheus的用户,大神勿喷,偏重于操作和实战,但是重要的概念 ...
- python自动拉取备份压缩包并删除3天前的旧备份
业务场景,异地机房自动拉取已备份好的tar.gz数据库压缩包,并且只保留3天内的压缩包文件,用python实现 #!/usr/bin/env python import requests,time,o ...
- Python 一键拉取Git分支源码自动解析并执行SQL语句
基于Python实现自动拉取Git分支源码自动解析并执行SQL语句 by:授客 QQ:1033553122 1.代码用途 开发过程中,研发人员会提交SQL更新脚本到Git源码库,然后测试负责去拉取这些 ...
- 分布式监控系统Zabbix--使用Grafana进行图形展示
今天介绍一款高颜值监控绘图工具Grafana,在使用Zabbix监控环境中,通常我们会结合Grafana进行图形展示.Grafana默认没有zabbix作为数据源,需要手动给zabbix安装一个插 ...
- Centos 7安装Grafana 4及结合Zabbix3.2实现可视化监控图形
上一篇介绍了如何在Centos 7环境下安装zabbix监控,本章继续介绍在Centos 7环境下安装Grafana,并结合Zabbix实现可视化监控图形! 简介: Grafana 是 Graphit ...
- Python使用又拍云进行第三方文件拉取
在爬虫过程中,需要将图片或其他文件进行存储到云上,但在下载图片时,看官方文档,貌似需要先下载到后再上传又拍云,感觉效率很低下.经查找资料实现Python直接对第三方资源进行文件拉取操作,即不需要下载到 ...
- python 写了一个批量拉取文件进excel文档
路径如: C:\\Users\\huaqi\\Desktop\\信息收集 “信息收集”目录下有以下子目录:[技术,客服,运营,行政] “技术”目录下有以下子文件:[小白.txt,小红.txt,小黑.t ...
- Ubuntu上Grafana 监控 Docker的技巧
导读 Grafana 是一个有着丰富指标的开源控制面板.在可视化大规模测量数据的时候是非常有用的.根据不同的指标数据,它提供了一个强大.优雅的来创建.分享和浏览数据的方式. 它提供了丰富多样.灵活的图 ...
- kubernetes(k8s) Prometheus+grafana监控告警安装部署
主机数据收集 主机数据的采集是集群监控的基础:外部模块收集各个主机采集到的数据分析就能对整个集群完成监控和告警等功能.一般主机数据采集和对外提供数据使用cAdvisor 和node-exporter等 ...
- Prometheus Grafana监控全方位实践
这次就不用 docker 部署服务了,这样大家会更容易接受.欢迎阅读. 引言 Prometheus 是一个监控系统,也是一个时间序列数据库,用Go语言开发的,官方文档.通过从某些特定的目标如主机,My ...
随机推荐
- Figma 学习笔记 – Border
Figma 画 Border 其实还挺难的 4 边框 可以用 build-in 的 Stroke 实现 注: border 是在内部的, 和 HTML 一直 Border Top, Right, Bo ...
- QT疑难杂症之QML程序中如何使用文件系统模型QFileSystemModel?
简介 本文介绍了 QML程序中如何使用树状控件TreeView展示QT文件系统模型QFileSystemModel中的数据,并给出了两种实现模式. 目录 QML程序中使用文件系统模型的代码 树状控件自 ...
- 线段树 transformation——hdu 4578
问题描述: 给定一个数列,数列中所有元素都初始化为0,对其执行多种区间操作 操作1:add修改:对区间[L,R]内的所有数加c 操作2:multi修改:对区间[L,R]内所有数乘以c 操作3:chan ...
- 一篇文章讲清楚synchronized关键字的作用及原理
概述 在应用Sychronized关键字时需要把握如下注意点: 一把锁只能同时被一个线程获取,没有获得锁的线程只能等待: 每个实例都对应有自己的一把锁(this),不同实例之间互不影响:例外:锁对象是 ...
- laravel框架接口
下面是增删改查的接口,在使用过程中按自己需求对代码进行更改 控制器代码 <?php namespace App\Http\Controllers; use App\Models\Fang; us ...
- linux下UsbMon-WireShark之USB协议抓取分析
usbmon配置 使用usbmon抓包分的,是需要 内核开启CONFIG_USB_MON=m, 重新编译内核, 编译ko : make ARCH=arm64 CROSS_COMPILE=aarch64 ...
- /proc/pids/limits
cat /proc/39977/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Ma ...
- 将Vscode添加右键打开文件夹功能
1. wan + r 输入 regedit 打开注册表 注册表编辑 2. 找到 HKEY_CLASSES_ROOT\*\shell分支 3. 在shell下新建"VisualCode&quo ...
- 有封装过v-model相关的双向绑定组件吗?
有的,之前在做⼀个通⽤的上传组件的时候,⼀开始是通过传统的⽗⼦通信实现的,⽗传⼦实现的图⽚ 回显,⼦传⽗实现的图⽚url抛出传给给接⼝,后来使⽤v-model做了⼀点优化,简单了不少 追问: 具体是怎 ...
- 云原生周刊:Meshery v0.70 发布 | 2024.1.22
开源项目推荐 flux-cluster-template 该项目用于部署由 Flux.SOPS.GitHub Actions.Renovate.Cilium 等支持的 Kubernetes 集群,专注 ...