diff options
| -rw-r--r-- | kerbana/settings.py | 26 | ||||
| -rw-r--r-- | rrd/models.py | 29 | ||||
| -rw-r--r-- | rrd/mqtt.py | 5 | ||||
| -rw-r--r-- | rrd/tests/test_graphs.py | 39 | 
4 files changed, 95 insertions, 4 deletions
diff --git a/kerbana/settings.py b/kerbana/settings.py index 5d65b47..383d86f 100644 --- a/kerbana/settings.py +++ b/kerbana/settings.py @@ -154,4 +154,30 @@ RRA:MAX:0.5:288:365  RRA:MIN:0.5:288:365  """ +RRD_GRAPH_CONFIG = """ +--imgformat +  PNG +--width +  800 +--height +  200 +--start +  -86400 +--end +  -1 +--vertical-label +  {ds_names[0]} +--title +  {title} +--right-axis +  1:0 +--alt-autoscale +DEF:{ds_names[0]}={ds_paths[0]}:{ds_names[0]}:AVERAGE +VDEF:max={ds_names[0]},MAXIMUM +VDEF:min={ds_names[0]},MINIMUM +LINE2:{ds_names[0]}#0000FF +LINE1:max#FF0000 +LINE1:min#FF0000 +""" +  from .config import *  # noqa diff --git a/rrd/models.py b/rrd/models.py index 8d995c8..1d1e5e9 100644 --- a/rrd/models.py +++ b/rrd/models.py @@ -80,6 +80,9 @@ class DataSource(ModelWithPerms):          except ValueError as e:              log.warning("Could not update ds: %s", e) +        for graph in self.graph_set.all(): +            graph.update() +  class Graph(ModelWithPerms):      title = models.CharField(max_length=64) @@ -89,11 +92,35 @@ class Graph(ModelWithPerms):          recursive=True,          max_length=512,      ) -    rrd_config = models.TextField() +    rrd_config = models.TextField( +        default=settings.RRD_GRAPH_CONFIG +    )      def __str__(self):          return self.title +    def update(self): +        graph_path = os.path.join(settings.RRD_GRAPH_PATH, self.path) +        os.makedirs(os.path.dirname(graph_path), exist_ok=True) +        rrd_paths = [] +        rrd_topics = [] +        rrd_ds_names = [] +        for ds in self.data_sources.all(): +            rrd_paths.append(os.path.join(settings.RRD_DB_PATH, ds.path)) +            rrd_topics.append(ds.topic) +            rrd_ds_names.append(ds.topic.split("/")[-1]) +        opts = self.rrd_config.format( +            topics=rrd_topics, +            ds_names=rrd_ds_names, +            ds_paths=rrd_paths, +            title=self.title, +        ).strip().split('\n') +        opts = [o.strip() for o in opts] +        rrdtool.graph( +            graph_path, +            * opts +        ) +  class Dashboard(ModelWithPerms):      title = models.CharField(max_length=64) diff --git a/rrd/mqtt.py b/rrd/mqtt.py index 09589c3..94bd772 100644 --- a/rrd/mqtt.py +++ b/rrd/mqtt.py @@ -66,10 +66,9 @@ class MQTTClient:          topic = msg.topic.removeprefix(settings.MQTT_TOPIC)          dss = models.DataSource.objects.filter(topic=topic)          if not dss: -            dss = [models.DataSource.objects.create( +            dss = (models.DataSource.objects.create(                  topic=topic,                  path=slugify.slugify(topic) + ".rrd", -            )] -            dss[0].save() +            ),)          for ds in dss:              ds.update(ts, msg.payload.decode()) diff --git a/rrd/tests/test_graphs.py b/rrd/tests/test_graphs.py new file mode 100644 index 0000000..f2a4948 --- /dev/null +++ b/rrd/tests/test_graphs.py @@ -0,0 +1,39 @@ +import datetime +import os + +from django.conf import settings +from django.test import TestCase + +from .. import models + + +class TestGraphs(TestCase): +    def setUp(self): +        self.rrd_file = "test.rrd" +        try: +            os.remove(os.path.join(settings.RRD_DB_PATH, self.rrd_file)) +        except FileNotFoundError: +            pass + +    def tearDown(self): +        try: +            os.remove(os.path.join(settings.RRD_DB_PATH, self.rrd_file)) +        except FileNotFoundError: +            pass + +    def test_generate_at_ds_update(self): +        ds = models.DataSource.objects.create( +            topic="test", +            path=self.rrd_file, +        ) +        graph = models.Graph.objects.create( +            title="Test Graph", +            path="test/test.png", +        ) +        graph.data_sources.add(ds) +        graph.save() +        now = datetime.datetime.now() +        ts = int(now.timestamp()) +        ds.update(ts, 10) +        stat = os.stat(os.path.join(settings.RRD_GRAPH_PATH, "test/test.png")) +        self.assertGreaterEqual(stat.st_mtime, now.timestamp())  | 
