wip
This commit is contained in:
parent
82d76f776b
commit
71bc767533
17 changed files with 303 additions and 14 deletions
|
@ -1,9 +1,11 @@
|
||||||
assert node.has_bundle('redis')
|
assert node.has_bundle('redis')
|
||||||
assert node.has_bundle('postgresql')
|
assert node.has_bundle('postgresql')
|
||||||
|
|
||||||
|
from mako.template import Template
|
||||||
from shlex import quote
|
from shlex import quote
|
||||||
|
from copy import deepcopy
|
||||||
import yaml
|
import yaml
|
||||||
|
import json
|
||||||
|
|
||||||
svc_systemd['grafana-server'] = {
|
svc_systemd['grafana-server'] = {
|
||||||
'needs': [
|
'needs': [
|
||||||
|
@ -29,6 +31,11 @@ directories = {
|
||||||
'/etc/grafana/provisioning/datasources': {
|
'/etc/grafana/provisioning/datasources': {
|
||||||
'purge': True,
|
'purge': True,
|
||||||
},
|
},
|
||||||
|
'/etc/grafana/provisioning/dashboards': {
|
||||||
|
'purge': True,
|
||||||
|
},
|
||||||
|
'/var/lib/grafana': {},
|
||||||
|
'/var/lib/grafana/dashboards': {},
|
||||||
}
|
}
|
||||||
|
|
||||||
files = {
|
files = {
|
||||||
|
@ -47,4 +54,61 @@ files = {
|
||||||
'svc_systemd:grafana-server:restart',
|
'svc_systemd:grafana-server:restart',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
'/etc/grafana/provisioning/dashboards/managed.yaml': {
|
||||||
|
'content': yaml.dump({
|
||||||
|
'apiVersion': 1,
|
||||||
|
'providers': [{
|
||||||
|
'name': 'Default',
|
||||||
|
'folder': 'Generated',
|
||||||
|
'type': 'file',
|
||||||
|
'options': {
|
||||||
|
'path': '/var/lib/grafana/dashboards',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
}),
|
||||||
|
'triggers': [
|
||||||
|
'svc_systemd:grafana-server:restart',
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# DASHBOARDS
|
||||||
|
|
||||||
|
with open(repo.path.join([f'data/grafana/dashboard.py'])) as file:
|
||||||
|
dashboard_template = eval(file.read())
|
||||||
|
with open(repo.path.join([f'data/grafana/panel.py'])) as file:
|
||||||
|
panel_template = eval(file.read())
|
||||||
|
with open(repo.path.join([f'data/grafana/flux.mako'])) as file:
|
||||||
|
flux_template = Template(file.read())
|
||||||
|
|
||||||
|
bucket = repo.get_node(node.metadata.get('grafana/influxdb_node')).metadata.get('influxdb/bucket')
|
||||||
|
|
||||||
|
for dashboard_id, (node_name, panels) in enumerate(node.metadata.get('grafana/dashboards').items(), start=1):
|
||||||
|
dashboard = deepcopy(dashboard_template)
|
||||||
|
dashboard['id'] = dashboard_id
|
||||||
|
|
||||||
|
for panel_id, (panel_name, panel_config) in enumerate(panels.items(), start=1):
|
||||||
|
panel = deepcopy(panel_template)
|
||||||
|
panel['id'] = panel_id
|
||||||
|
panel['title'] = panel_name
|
||||||
|
|
||||||
|
for target_name, target_config in panel_config.items():
|
||||||
|
panel['targets'].append({
|
||||||
|
'refId': target_name,
|
||||||
|
'query': flux_template.render(
|
||||||
|
bucket=bucket,
|
||||||
|
host=node_name,
|
||||||
|
field=target_name,
|
||||||
|
filters=target_config,
|
||||||
|
).strip()
|
||||||
|
})
|
||||||
|
|
||||||
|
dashboard['panels'].append(panel)
|
||||||
|
|
||||||
|
files[f'/var/lib/grafana/dashboards/{node_name}.json'] = {
|
||||||
|
'content': json.dumps(dashboard, sort_keys=True, indent=4),
|
||||||
|
'triggers': [
|
||||||
|
'svc_systemd:grafana-server:restart',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from mako.template import Template
|
||||||
|
|
||||||
postgres_password = repo.vault.password_for(f'{node.name} postgres role grafana')
|
postgres_password = repo.vault.password_for(f'{node.name} postgres role grafana')
|
||||||
|
|
||||||
defaults = {
|
defaults = {
|
||||||
|
@ -29,16 +31,8 @@ defaults = {
|
||||||
'allow_signup': False,
|
'allow_signup': False,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'panels': {
|
'dashboards': {},
|
||||||
'CPU': {
|
'datasources': {},
|
||||||
'usage_user': {
|
|
||||||
'filter': {
|
|
||||||
'_measurement': 'cpu',
|
|
||||||
'cpu': 'cpu-total',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
'postgresql': {
|
'postgresql': {
|
||||||
'databases': {
|
'databases': {
|
||||||
|
@ -62,6 +56,26 @@ defaults = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@metadata_reactor.provides(
|
||||||
|
'grafana/dashboards',
|
||||||
|
)
|
||||||
|
def dashboards(metadata):
|
||||||
|
dashboards = {}
|
||||||
|
|
||||||
|
for monitored_node in repo.nodes:
|
||||||
|
if monitored_node.metadata.get('telegraf/influxdb_node', None) == metadata.get('grafana/influxdb_node'):
|
||||||
|
for telegraf_input in monitored_node.metadata.get('telegraf/config/inputs'):
|
||||||
|
with open(repo.path.join([f'data/grafana/panels/{telegraf_input}.py'])) as file:
|
||||||
|
dashboards.setdefault(monitored_node.name, {})[telegraf_input] = \
|
||||||
|
eval(Template(file.read()).render(metadata=monitored_node.metadata))
|
||||||
|
|
||||||
|
return {
|
||||||
|
'grafana': {
|
||||||
|
'dashboards': dashboards,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@metadata_reactor.provides(
|
@metadata_reactor.provides(
|
||||||
'grafana/datasources',
|
'grafana/datasources',
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,14 +18,14 @@ defaults = {
|
||||||
'metric_batch_size': 1000,
|
'metric_batch_size': 1000,
|
||||||
'metric_buffer_limit': 10000,
|
'metric_buffer_limit': 10000,
|
||||||
'omit_hostname': False,
|
'omit_hostname': False,
|
||||||
'round_interval': True
|
'round_interval': True,
|
||||||
},
|
},
|
||||||
'inputs': {
|
'inputs': {
|
||||||
'cpu': [{
|
'cpu': [{
|
||||||
'collect_cpu_time': False,
|
'collect_cpu_time': False,
|
||||||
'percpu': True,
|
'percpu': True,
|
||||||
'report_active': False,
|
'report_active': False,
|
||||||
'totalcpu': True
|
'totalcpu': True,
|
||||||
}],
|
}],
|
||||||
'disk': [{
|
'disk': [{
|
||||||
'ignore_fs': [
|
'ignore_fs': [
|
||||||
|
@ -35,7 +35,7 @@ defaults = {
|
||||||
'iso9660',
|
'iso9660',
|
||||||
'overlay',
|
'overlay',
|
||||||
'aufs',
|
'aufs',
|
||||||
'squashfs'
|
'squashfs',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
'diskio': [{}],
|
'diskio': [{}],
|
||||||
|
|
|
@ -38,6 +38,13 @@ defaults = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'telegraf': {
|
||||||
|
'config': {
|
||||||
|
'inputs': {
|
||||||
|
'zfs': [{}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
'zfs': {
|
'zfs': {
|
||||||
'datasets': {},
|
'datasets': {},
|
||||||
'pools': {},
|
'pools': {},
|
||||||
|
|
64
data/grafana/dashboard.py
Normal file
64
data/grafana/dashboard.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
# "id": 1,
|
||||||
|
"annotations": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"builtIn": 1,
|
||||||
|
"datasource": "-- Grafana --",
|
||||||
|
"enable": True,
|
||||||
|
"hide": True,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"type": "dashboard"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"editable": True,
|
||||||
|
"gnetId": None,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"iteration": 1625410820978,
|
||||||
|
"links": [],
|
||||||
|
"panels": [],
|
||||||
|
"refresh": False,
|
||||||
|
"schemaVersion": 30,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": [],
|
||||||
|
"templating": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"allValue": None,
|
||||||
|
"current": {
|
||||||
|
"isNone": True,
|
||||||
|
"selected": False,
|
||||||
|
"text": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"datasource": None,
|
||||||
|
"definition": "",
|
||||||
|
"description": None,
|
||||||
|
"error": None,
|
||||||
|
"hide": 0,
|
||||||
|
"includeAll": False,
|
||||||
|
"label": None,
|
||||||
|
"multi": False,
|
||||||
|
"name": "query0",
|
||||||
|
"options": [],
|
||||||
|
"query": "",
|
||||||
|
"refresh": 1,
|
||||||
|
"regex": "",
|
||||||
|
"skipUrlSync": False,
|
||||||
|
"sort": 0,
|
||||||
|
"type": "query"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "2021-07-01T19:00:00.201Z",
|
||||||
|
"to": "2021-07-01T19:03:10.018Z"
|
||||||
|
},
|
||||||
|
"timepicker": {},
|
||||||
|
"timezone": "",
|
||||||
|
"title": "New dashboard Copy",
|
||||||
|
"uid": "IBPgYBznk",
|
||||||
|
"version": 15
|
||||||
|
}
|
8
data/grafana/flux.mako
Normal file
8
data/grafana/flux.mako
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from(bucket: "${bucket}")
|
||||||
|
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|
||||||
|
|> filter(fn: (r) => r["host"] == "${host}")
|
||||||
|
% for key, value in filters.items():
|
||||||
|
|> filter(fn: (r) => r["_field"] == "${field}")
|
||||||
|
% endfor
|
||||||
|
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
|
||||||
|
|> yield(name: "mean")
|
75
data/grafana/panel.py
Normal file
75
data/grafana/panel.py
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
{
|
||||||
|
# 'id': 1,
|
||||||
|
# 'title': 'TBD',
|
||||||
|
'type': 'timeseries',
|
||||||
|
'transformations': [],
|
||||||
|
'description': '',
|
||||||
|
'fieldConfig': {
|
||||||
|
'defaults': {
|
||||||
|
'custom': {
|
||||||
|
'drawStyle': 'line',
|
||||||
|
'lineInterpolation': 'smooth',
|
||||||
|
'barAlignment': 0,
|
||||||
|
'lineWidth': 1,
|
||||||
|
'fillOpacity': 40,
|
||||||
|
'gradientMode': 'opacity',
|
||||||
|
'spanNulls': False,
|
||||||
|
'showPoints': 'never',
|
||||||
|
'pointSize': 5,
|
||||||
|
'stacking': {
|
||||||
|
'mode': 'normal',
|
||||||
|
'group': 'A'
|
||||||
|
},
|
||||||
|
'axisPlacement': 'hidden',
|
||||||
|
'axisLabel': '',
|
||||||
|
'scaleDistribution': {
|
||||||
|
'type': 'linear'
|
||||||
|
},
|
||||||
|
'hideFrom': {
|
||||||
|
'tooltip': False,
|
||||||
|
'viz': False,
|
||||||
|
'legend': False
|
||||||
|
},
|
||||||
|
'thresholdsStyle': {
|
||||||
|
'mode': 'off',
|
||||||
|
},
|
||||||
|
'lineStyle': {
|
||||||
|
'fill': 'solid',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'color': {
|
||||||
|
'mode': 'palette-classic',
|
||||||
|
},
|
||||||
|
'thresholds': {
|
||||||
|
'mode': 'percentage',
|
||||||
|
'steps': [
|
||||||
|
{
|
||||||
|
'color': 'green',
|
||||||
|
'value': None,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'color': 'dark-red',
|
||||||
|
'value': 80,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'mappings': [],
|
||||||
|
'displayName': '${__field.name}',
|
||||||
|
'unit': 'percent',
|
||||||
|
},
|
||||||
|
'overrides': [],
|
||||||
|
},
|
||||||
|
'options': {
|
||||||
|
'tooltip': {
|
||||||
|
'mode': 'none',
|
||||||
|
},
|
||||||
|
'legend': {
|
||||||
|
'displayMode': 'list',
|
||||||
|
'placement': 'bottom',
|
||||||
|
'calcs': [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'targets': [],
|
||||||
|
'transparent': True,
|
||||||
|
'datasource': None,
|
||||||
|
}
|
20
data/grafana/panels/cpu.py
Normal file
20
data/grafana/panels/cpu.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
'usage_system': {
|
||||||
|
'filter': {
|
||||||
|
'_measurement': 'cpu',
|
||||||
|
'cpu': 'cpu-total',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'usage_iowait': {
|
||||||
|
'filter': {
|
||||||
|
'_measurement': 'cpu',
|
||||||
|
'cpu': 'cpu-total',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'usage_user': {
|
||||||
|
'filter': {
|
||||||
|
'_measurement': 'cpu',
|
||||||
|
'cpu': 'cpu-total',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
1
data/grafana/panels/disk.py
Normal file
1
data/grafana/panels/disk.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
data/grafana/panels/diskio.py
Normal file
1
data/grafana/panels/diskio.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
data/grafana/panels/kernel.py
Normal file
1
data/grafana/panels/kernel.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
data/grafana/panels/mem.py
Normal file
1
data/grafana/panels/mem.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
data/grafana/panels/processes.py
Normal file
1
data/grafana/panels/processes.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
data/grafana/panels/swap.py
Normal file
1
data/grafana/panels/swap.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
data/grafana/panels/system.py
Normal file
1
data/grafana/panels/system.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
data/grafana/panels/zfs.py
Normal file
1
data/grafana/panels/zfs.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
29
libs/grafana.py
Normal file
29
libs/grafana.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
from mako.template import Template
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
|
||||||
|
def generate_flux(bucket, host, field, data):
|
||||||
|
return Template(flux_template).render(
|
||||||
|
bucket=bucket,
|
||||||
|
host=host,
|
||||||
|
field=field,
|
||||||
|
data=data
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_panel(bucket, host, title, targets, min=None, max=None):
|
||||||
|
panel = deepcopy(panel_template)
|
||||||
|
panel['title'] = title
|
||||||
|
|
||||||
|
if min:
|
||||||
|
panel['fieldConfig']['defaults']['min'] = min
|
||||||
|
if max:
|
||||||
|
panel['fieldConfig']['defaults']['max'] = max
|
||||||
|
|
||||||
|
panel['targets'] = [
|
||||||
|
{
|
||||||
|
'hide': False,
|
||||||
|
'refId': field,
|
||||||
|
'query': generate_flux(bucket, host, field, data),
|
||||||
|
} for field, data in targets.items()
|
||||||
|
]
|
Loading…
Reference in a new issue