使用Python向Azure托管Grafana实例创建仪表盘报401错误如何解决
问题说明
使用如下Python函数将grafanalib生成的仪表盘上传至Azure托管Grafana实例时,已为账号分配User Access Administrator角色,HTTPS协议调用接口返回401错误,HTTP协议调用返回连接错误。
def upload_to_grafana(json, server, api_key, verify=True): ''' upload_to_grafana tries to upload dashboard to grafana and prints response :param json - dashboard json generated by grafanalib :param server - grafana server name :param api_key - grafana api key with read and write privileges ''' headers = {'Authorization': f"Bearer <apikey>","Accept": "application/json", 'Content-Type': 'application/json'} r = requests.post(f"<endpoint url of the Azure managed grafana instance >", data=json, headers=headers, verify=False) # TODO: add error handling print(f"{r.status_code} - {r.content}")
排查解决步骤
- HTTP连接错误无需额外排查:Azure托管Grafana默认强制HTTPS访问,不开放HTTP端口,所有HTTP请求会直接被拒绝,直接弃用HTTP调用即可。
- 修复代码硬编码问题:当前代码鉴权头中使用了占位符
<apikey>,未实际传入函数入参的api_key变量,是触发401的最常见原因,直接替换为实际传入的密钥变量即可。 - 校验API密钥有效性:
- 你分配的User Access Administrator是Azure资源平面的RBAC角色,仅用于管理Azure侧的资源权限,不授予Grafana应用内部的操作权限,不能直接用于Grafana API鉴权。
- 需要登录Grafana实例Web控制台,在服务账号管理页面创建权限为
Editor或Admin的服务账号,再为该账号生成API密钥,Viewer权限的密钥无仪表盘写入权限。
- 修正请求配置:
- 仪表盘上传接口的正确路径为Grafana实例地址下的
/api/dashboards/db,不能直接调用实例根地址。 - 接口要求的载荷格式不是grafanalib输出的裸仪表盘JSON,需要包装为
{"dashboard": <仪表盘JSON>, "overwrite": <是否覆盖同名仪表盘>, "folderId": <目标文件夹ID>}的结构。 - 发送请求时使用requests库的
json参数传递载荷,不要用data参数传递序列化字符串,避免编码异常触发鉴权或格式错误。
- 仪表盘上传接口的正确路径为Grafana实例地址下的
- 检查网络访问规则:确认Grafana实例的IP访问限制策略没有拦截当前调用方的出口IP。
修正后可直接使用的代码
import requests import urllib3 # 测试环境关闭SSL校验时,屏蔽对应的警告信息,生产环境建议删除该段、开启SSL校验 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) def upload_to_grafana(dashboard_json, server, api_key, verify=True, folder_id=0, overwrite=True): ''' 上传仪表盘到Azure托管Grafana实例 :param dashboard_json: grafanalib生成的仪表盘字典对象 :param server: Grafana实例访问地址,格式为https://<你的实例域名> :param api_key: Grafana控制台生成的服务账号API密钥 :param verify: 是否校验SSL证书,生产环境设为True :param folder_id: 仪表盘存放的目标文件夹ID,默认0为根目录 :param overwrite: 存在同名仪表盘时是否覆盖 ''' headers = { 'Authorization': f"Bearer {api_key}", "Accept": "application/json" } upload_endpoint = f"{server.rstrip('/')}/api/dashboards/db" payload = { "dashboard": dashboard_json, "overwrite": overwrite, "folderId": folder_id } resp = requests.post(upload_endpoint, json=payload, headers=headers, verify=verify) print(f"响应状态码:{resp.status_code},响应内容:{resp.text}") resp.raise_for_status() return resp.json()
内容的提问来源于stack exchange,提问作者firdous。
相关产品推荐
相关产品推荐

