Skip to content

Commit c844244

Browse files
author
Óscar Nájera
committed
Update dashboard directly on Grafana database
1 parent fd72ea7 commit c844244

File tree

4 files changed

+101
-73
lines changed

4 files changed

+101
-73
lines changed

src/DataSource.ts

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ import {
77
DataSourceInstanceSettings,
88
MutableDataFrame,
99
FieldType,
10-
SelectableValue,
1110
} from '@grafana/data';
1211
import { getBackendSrv } from '@grafana/runtime';
1312

14-
import { buildRequestBody, combinedDesc, extractSingleInfos, graphDefinitionRequest } from './graphspecs';
13+
import { buildRequestBody, combinedDesc, graphDefinitionRequest } from './graphspecs';
1514
import { MyQuery, defaultQuery } from './types';
1615

1716
export const buildUrlWithParams = (url: string, params: any) => url + '?' + new URLSearchParams(params).toString();
@@ -52,24 +51,10 @@ export class DataSource extends DataSourceApi<MyQuery> {
5251
return Promise.all(promises).then((data) => ({ data }));
5352
}
5453

55-
async graphRecipesQuery({ refId, context }: MyQuery): Promise<Array<SelectableValue<number>>> {
56-
const template = buildRequestBody({
57-
specification: ['template', extractSingleInfos(context || {})],
58-
});
59-
const response = await this.doRequest({
60-
refId: refId,
61-
params: { action: 'get_graph_recipes' },
62-
data: template,
63-
context,
64-
});
65-
return response.data.result.map((graph: any, index: number) => ({ label: graph.title, value: index }));
66-
}
67-
6854
async getGraphQuery(range: number[], query: MyQuery) {
69-
if (isEmpty(query.context) || !(query.params.graph || query.params.metric || query.params.graph_name)) {
55+
if (isEmpty(query.context) || !query.params.graph_name) {
7056
return new MutableDataFrame();
7157
}
72-
7358
const editionMode = get(this, 'instanceSettings.jsonData.edition', 'CEE');
7459
const response = await this.doRequest({
7560
...query,

src/components/fields.tsx

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import { AsyncSelect, InlineField, Select } from '@grafana/ui';
33
import { SelectableValue } from '@grafana/data';
44
import { AutoCompleteEditorProps, EditorProps } from './types';
5-
import { get, update, isEmpty } from 'lodash';
5+
import { get, update } from 'lodash';
66
import { DataSource } from '../DataSource';
77
import { combinedDesc } from 'graphspecs';
88

@@ -40,20 +40,6 @@ export const AsyncAutocomplete = ({
4040
if (contextPath === 'params.metric' || contextPath === 'params.graph') {
4141
contextKey += contextPath;
4242
}
43-
// recover graph_name
44-
if (contextPath === 'params.graph_name' && typeof query.graph === 'number' && query.params.graphMode === 'template') {
45-
autocompleter('').then((res) => {
46-
console.log('auto qu', res, query);
47-
const graph_index = query.graph;
48-
delete query.graph;
49-
if (res.length > graph_index) {
50-
onSelection(res[graph_index]);
51-
const col = get(query, 'params.selections.' + contextPath, {});
52-
console.log('up', col, isEmpty(col));
53-
}
54-
});
55-
}
56-
5743
return (
5844
<AsyncSelect
5945
onChange={onSelection}

src/components/site.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ export const HostTagsFilter = (props: EditorProps) => {
271271
return (
272272
<>
273273
{Array.from({ length: 3 }).map((_, idx) => (
274-
<HostTagsItemFilter index={idx} {...props} />
274+
<HostTagsItemFilter key={idx} index={idx} {...props} />
275275
))}
276276
</>
277277
);

utils/converter.py

Lines changed: 97 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#!/usr/bin/env python3
2+
23
import argparse
34
import json
45
import sqlite3
6+
import urllib.request as rq
57

68

79
def cli_options():
810
parser = argparse.ArgumentParser(description="Grafana JSON Model updater")
911

10-
parser.add_argument("json_file", help="File exported from Grafana Dashboard")
12+
parser.add_argument("-db", "--db-file", help="Grafana sqlite database")
1113
parser.add_argument(
1214
"-o", "--datasource-old", help="Name of the OLD Grafana Connector Version 1.x"
1315
)
@@ -28,12 +30,12 @@ def nested_set(target, keys, value):
2830
target[keys[-1]] = value
2931

3032

31-
def config_set(target, keys, value):
33+
def config_set(target, keys, value, label=""):
3234
nested_set(target, keys, value)
3335
nested_set(
3436
target,
3537
["params", "selections"] + keys,
36-
{"value": value, "label": value, "isDisabled": False},
38+
{"value": value, "label": label or value, "isDisabled": False},
3739
)
3840

3941

@@ -47,18 +49,103 @@ def update_host_tags(target):
4749
if value := target.pop(key):
4850
new_key = f"host_tag_{nr}_{host_tag_obj[obj]}"
4951
config_set(target, ["context", "host_tags", new_key], value)
52+
if all(x.endswith("op") for x in target["context"]["host_tags"].keys()):
53+
del target["context"]["host_tags"]
54+
del target["params"]["selections"]["context"]["host_tags"]
55+
56+
57+
def update_context(target):
58+
update_host_tags(target)
59+
target.pop("format", None)
60+
target.pop("usehostregex", None)
61+
if host := target.pop("host", None):
62+
config_set(target, ["context", "host", "host"], host)
63+
if host := target.pop("hostregex", None):
64+
nested_set(target, ["context", "hostregex", "host_regex"], host)
65+
66+
if service := target.pop("service", None):
67+
config_set(target, ["context", "service", "service"], service)
68+
if service := target.pop("serviceregex", None):
69+
nested_set(target, ["context", "serviceregex", "service_regex"], service)
70+
71+
if site := target.pop("site", None):
72+
config_set(target, ["context", "siteopt", "site"], site)
73+
74+
75+
def extract_single_info(context):
76+
return {
77+
"site": context.get("siteopt", {}).get("site", ""),
78+
"host_name": context.get("host", {}).get("host", ""),
79+
"service_description": context.get("service", {}).get("service", ""),
80+
}
81+
82+
83+
def update_graph(query_graph, target):
84+
mode = target.pop("mode", "")
85+
metric_id = [int(x) for x in target.pop("metric", "").split(".") if x]
86+
graph_idx = target.pop("graph", 0)
87+
presentation = target.pop("presentiation", "lines")
88+
if combined_graph_name := target.pop("combinedgraph", None):
89+
config_set(target, ["params", "graph_name"], combined_graph_name)
90+
target["params"]["presentation"] = presentation
91+
return
92+
93+
graph = query_graph(target["context"])
94+
if mode == "graph" and graph_idx < len(graph):
95+
graph_name = graph[graph_idx]["specification"][1]["graph_id"]
96+
graph_title = graph[graph_idx]["title"]
97+
config_set(target, ["params", "graph_name"], graph_name, graph_title)
98+
elif mode == "metric" and len(metric_id) == 2:
99+
graph_idx, metric_idx = metric_id
100+
if graph_idx < len(graph) and metric_idx < len(graph[graph_idx]["metrics"]):
101+
metric = graph[graph_idx]["metrics"][metric_idx]
102+
expression = metric["expression"]
103+
if expression[0] == "rrd":
104+
nested_set(target, ["params", "graphMode"], "metric")
105+
config_set(
106+
target, ["params", "graph_name"], expression[4], metric["title"]
107+
)
108+
else:
109+
config_set(target, ["params", "graph_name"], "", "Not available")
110+
111+
112+
def get_datasource_configs(cursor):
113+
for ds, name, json_data in cursor.execute(
114+
"select type, name, json_data from data_source"
115+
):
116+
if ds in ["checkmk-datasource", "tribe-29-grafana-checkmk-datasource"]:
117+
yield name, {**json.loads(json_data), "name": name, "ds": ds}
118+
119+
120+
def query(conf):
121+
def _query(context):
122+
req = rq.Request(
123+
conf["url"]
124+
+ "/check_mk/webapi.py?_username=%s&_secret=%s&action=get_graph_recipes"
125+
% (conf["username"], conf["secret"])
126+
)
127+
spec = {"specification": ["template", extract_single_info(context)]}
128+
response = rq.urlopen(req, ("request=%s" % json.dumps(spec)).encode("utf-8"))
129+
if response.status == 200:
130+
return json.loads(response.read())["result"]
131+
return []
132+
133+
return _query
50134

51135

52136
def main():
53137
args = cli_options().parse_args()
54-
con = sqlite3.connect(args.json_file)
138+
con = sqlite3.connect(args.db_file)
55139
cur = con.cursor()
140+
datasource_config = dict(get_datasource_configs(cur))
141+
query_graph = query(datasource_config[args.datasource_old])
142+
56143
row = list(cur.execute("select data from dashboard where slug = 'cmk'"))
57144
dash = json.loads(row[0][0])
58145

59-
# if title := args.new_dashboard_title:
60-
# dash["title"] = title
61-
# else:
146+
# # if title := args.new_dashboard_title:
147+
# # dash["title"] = title
148+
# # else:
62149
dash["title"] += " NEW"
63150
dash["uid"] += "1"
64151

@@ -67,46 +154,16 @@ def main():
67154
panel["datasource"] = args.datasource_new
68155

69156
for target in panel["targets"]:
70-
update_host_tags(target)
71-
72-
if host := target.pop("host", None):
73-
config_set(target, ["context", "host", "host"], host)
74-
if host := target.pop("hostregex", None):
75-
nested_set(target, ["context", "hostregex", "host_regex"], host)
76-
77-
if service := target.pop("service", None):
78-
config_set(target, ["context", "service", "service"], service)
79-
if service := target.pop("serviceregex", None):
80-
nested_set(
81-
target, ["context", "serviceregex", "service_regex"], service
82-
)
83-
84-
if site := target.pop("site", None):
85-
config_set(target, ["context", "siteopt", "site"], site)
86-
87-
mode = target.pop("mode", "")
88-
metric_id = target.pop("metric", "")
89-
graph_id = str(target.pop("graph", "0"))
90-
combined_graph_name = target.pop("combinedgraph", None)
91-
if mode == "metric":
92-
nested_set(target, ["params", "graphMode"], "metric")
93-
nested_set(target, ["params", "graph_name"], metric_id)
94-
elif mode == "graph":
95-
nested_set(target, ["params", "graph_name"], graph_id)
96-
elif combined_graph_name:
97-
config_set(target, ["params", "graph_name"], combined_graph_name)
98-
99-
if presentation := target.pop("presentiation", None):
100-
target["params"]["presentation"] = presentation
157+
update_context(target)
158+
update_graph(query_graph, target)
101159

160+
print(json.dumps(dash))
102161
cur.execute(
103162
"update dashboard set data = '%s' where slug = 'cmk-new'" % json.dumps(dash)
104163
)
105164
con.commit()
106165
con.close()
107166

108-
print(json.dumps(dash))
109-
110167

111168
if __name__ == "__main__":
112169
main()

0 commit comments

Comments
 (0)