diff options
Diffstat (limited to 'rrd/models.py')
-rw-r--r-- | rrd/models.py | 68 |
1 files changed, 49 insertions, 19 deletions
diff --git a/rrd/models.py b/rrd/models.py index 3cace0c..2dc629a 100644 --- a/rrd/models.py +++ b/rrd/models.py @@ -1,5 +1,6 @@ import logging import os +import pathlib import django.contrib.auth.models as amodels import rrdtool @@ -9,6 +10,16 @@ from django.db import models log = logging.getLogger(__name__) +def _sanitize_path(path): + # changing all separators to a _ should result in an ugly filename + # that will not cause issues. + # an additional _ is added at the beginning to prevent hidden files + for sep in os.sep, os.altsep: + if sep: + path = path.replace(sep, "_") + return "_" + path + + class ModelWithPerms(models.Model): users_read = models.ManyToManyField( amodels.User, @@ -35,9 +46,7 @@ class DataSource(ModelWithPerms): # an mqtt topic can be as long as 65,535 bytes when UTF-8 encoded, # which is probably too much for a sensible db topic = models.CharField(max_length=512) - path = models.FilePathField( - path=settings.RRD_DB_PATH.as_posix(), - recursive=True, + path = models.CharField( max_length=512, ) rrd_config = models.TextField( @@ -51,26 +60,36 @@ class DataSource(ModelWithPerms): return self.topic @property + def rrd_path(self): + path = os.path.abspath(os.path.join( + settings.RRD_DB_PATH, self.path + )) + base_path = os.path.abspath(settings.RRD_DB_PATH) + if pathlib.Path(base_path) not in pathlib.Path(path).parents: + return os.path.abspath(os.path.join( + settings.RRD_DB_PATH, + _sanitize_path(self.path) + )) + return path + + @property + def ds_name(self): + return self.topic.split("/")[-1] + + @property def lastupdate(self): try: - last = rrdtool.lastupdate(os.path.join( - settings.RRD_DB_PATH, self.path - )) + last = rrdtool.lastupdate(self.rrd_path) except rrdtool.OperationalError as e: log.warning("Failure reading from ds: %s", e) return (None, None) else: return last["date"], last["ds"][self.ds_name] - @property - def ds_name(self): - return self.topic.split("/")[-1] - def update(self, ts, value): - rrd_path = os.path.join(settings.RRD_DB_PATH, self.path) - if not os.path.isfile(rrd_path): + if not os.path.isfile(self.rrd_path): rrdtool.create( - os.path.join(settings.RRD_DB_PATH, self.path), + self.rrd_path, "--no-overwrite", self.rrd_config.format( ds_name=self.ds_name @@ -78,7 +97,7 @@ class DataSource(ModelWithPerms): ) try: rrdtool.update( - os.path.join(settings.RRD_DB_PATH, self.path), + self.rrd_path, str(ts) + ":" + str(value) ) except ValueError as e: @@ -91,9 +110,7 @@ class DataSource(ModelWithPerms): class Graph(ModelWithPerms): title = models.CharField(max_length=64) data_sources = models.ManyToManyField(DataSource) - path = models.FilePathField( - path=settings.RRD_GRAPH_PATH.as_posix(), - recursive=True, + path = models.CharField( max_length=512, ) rrd_config = models.TextField( @@ -103,14 +120,27 @@ class Graph(ModelWithPerms): def __str__(self): return self.title + @property + def graph_path(self): + path = os.path.abspath(os.path.join( + settings.RRD_GRAPH_PATH, self.path + )) + base_path = os.path.abspath(settings.RRD_GRAPH_PATH) + if pathlib.Path(base_path) not in pathlib.Path(path).parents: + return os.path.abspath(os.path.join( + settings.RRD_GRAPH_PATH, + _sanitize_path(self.path) + )) + return path + def update(self): - graph_path = os.path.join(settings.RRD_GRAPH_PATH, self.path) + graph_path = self.graph_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_paths.append(ds.rrd_path) rrd_topics.append(ds.topic) rrd_ds_names.append(ds.ds_name) opts = self.rrd_config.format( |