aboutsummaryrefslogtreecommitdiff
path: root/rrd/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'rrd/models.py')
-rw-r--r--rrd/models.py68
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(