#include <errno.h>
#include <fcntl.h>
#include <linux/hdreg.h>
+#include <mntent.h>
#include <oping.h>
#include <sensors/error.h>
#include <sensors/sensors.h>
return list;
}
+static int _collecty_mountpoint_is_virtual(const struct mntent* mp) {
+ // Ignore all ramdisks
+ if (mp->mnt_fsname[0] != '/')
+ return 1;
+
+ // Ignore network mounts
+ if (hasmntopt(mp, "_netdev") != NULL)
+ return 1;
+
+ return 0;
+}
+
+static PyObject* _collecty_get_mountpoints() {
+ FILE* fp = setmntent(_PATH_MOUNTED, "r");
+ if (!fp)
+ return NULL;
+
+ PyObject* list = PyList_New(0);
+ int r = 0;
+
+ struct mntent* mountpoint = getmntent(fp);
+ while (mountpoint) {
+ if (!_collecty_mountpoint_is_virtual(mountpoint)) {
+ // Create a tuple with the information of the mountpoint
+ PyObject* mp = PyTuple_New(4);
+ PyTuple_SET_ITEM(mp, 0, PyUnicode_FromString(mountpoint->mnt_fsname));
+ PyTuple_SET_ITEM(mp, 1, PyUnicode_FromString(mountpoint->mnt_dir));
+ PyTuple_SET_ITEM(mp, 2, PyUnicode_FromString(mountpoint->mnt_type));
+ PyTuple_SET_ITEM(mp, 3, PyUnicode_FromString(mountpoint->mnt_opts));
+
+ // Append the tuple to the list
+ r = PyList_Append(list, mp);
+ if (r)
+ break;
+ }
+
+ // Move on to the next mountpoint
+ mountpoint = getmntent(fp);
+ }
+
+ endmntent(fp);
+
+ if (r) {
+ Py_DECREF(list);
+ return NULL;
+ }
+
+ return list;
+}
+
static PyMethodDef collecty_module_methods[] = {
{"get_detected_sensors", (PyCFunction)_collecty_get_detected_sensors, METH_VARARGS, NULL},
+ {"get_mountpoints", (PyCFunction)_collecty_get_mountpoints, METH_NOARGS, NULL},
{"sensors_cleanup", (PyCFunction)_collecty_sensors_cleanup, METH_NOARGS, NULL},
{"sensors_init", (PyCFunction)_collecty_sensors_init, METH_NOARGS, NULL},
{NULL},
--- /dev/null
+#!/usr/bin/python3
+###############################################################################
+# #
+# collecty - A system statistics collection daemon for IPFire #
+# Copyright (C) 2012 IPFire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+from collecty import _collecty
+import os
+
+from ..constants import *
+from .. import util
+from . import base
+
+from ..i18n import _
+
+class GraphTemplateDiskUsage(base.GraphTemplate):
+ name = "disk-usage"
+ lower_limit = 0
+
+ @property
+ def rrd_graph(self):
+ _ = self.locale.translate
+
+ return [
+ "DEF:used=%(file)s:used:AVERAGE",
+ "VDEF:used_cur=used,LAST",
+ "VDEF:used_min=used,MINIMUM",
+ "VDEF:used_max=used,MAXIMUM",
+
+ "DEF:free=%(file)s:free:AVERAGE",
+ "VDEF:free_cur=free,LAST",
+ "VDEF:free_min=free,MINIMUM",
+ "VDEF:free_max=free,MAXIMUM",
+
+ # Calculate the percentage of the currently used
+ # space since this is helps the user very much to
+ # judge
+ "CDEF:percentage_used=100,used,*,used,free,+,/",
+ "VDEF:percentage_used_now=percentage_used,LAST",
+ "CDEF:percentage_left=100,percentage_used,-",
+ "VDEF:percentage_left_now=percentage_left,LAST",
+
+ # Area for the used space
+ "AREA:used%s:%s" % (util.lighten(LIGHT_RED, .66), _("Used")),
+ "GPRINT:percentage_used_now: (%6.2lf%%)",
+ "GPRINT:used_cur:%12s\:" % _("Current") + " %9.2lf%s",
+ "GPRINT:used_min:%12s\:" % _("Minimum") + " %9.2lf%s",
+ "GPRINT:used_max:%12s\:" % _("Maximum") + " %9.2lf%s\\n",
+
+ # Stacked area of unused space
+ "AREA:free%s:%s:STACK" % (util.lighten(LIGHT_GREEN, .66), _("Free")),
+ "GPRINT:percentage_left_now: (%6.2lf%%)",
+ "GPRINT:free_cur:%12s\:" % _("Current") + " %9.2lf%s",
+ "GPRINT:free_min:%12s\:" % _("Minimum") + " %9.2lf%s",
+ "GPRINT:free_max:%12s\:" % _("Maximum") + " %9.2lf%s\\n",
+
+ # Add contour lines for the areas
+ "LINE:used%s" % LIGHT_RED,
+ "LINE:free%s::STACK" % LIGHT_GREEN,
+ ]
+
+ @property
+ def graph_title(self):
+ _ = self.locale.translate
+ return _("Disk Usage of %s") % self.object.mountpoint
+
+ @property
+ def graph_vertical_label(self):
+ _ = self.locale.translate
+ return _("Bytes")
+
+
+class GraphTemplateInodeUsage(base.GraphTemplate):
+ name = "inode-usage"
+ lower_limit = 0
+
+ @property
+ def rrd_graph(self):
+ _ = self.locale.translate
+
+ rrd_graph = [
+ "DEF:used=%(file)s:inodes_used:AVERAGE",
+ "VDEF:used_cur=used,LAST",
+ "VDEF:used_min=used,MINIMUM",
+ "VDEF:used_max=used,MAXIMUM",
+
+ "DEF:free=%(file)s:inodes_free:AVERAGE",
+ "VDEF:free_cur=free,LAST",
+ "VDEF:free_min=free,MINIMUM",
+ "VDEF:free_max=free,MAXIMUM",
+
+ # Calculate the percentage of the currently used
+ # inodes since this is helps the user very much to
+ # judge
+ "CDEF:percentage_used=100,used,*,used,free,+,/",
+ "VDEF:percentage_used_now=percentage_used,LAST",
+ "CDEF:percentage_left=100,percentage_used,-",
+ "VDEF:percentage_left_now=percentage_left,LAST",
+
+ # Area for the used inodes
+ "AREA:used%s:%s" % (util.lighten(LIGHT_RED, .66), _("Used")),
+ "GPRINT:percentage_used_now: (%6.2lf%%)",
+ "GPRINT:used_cur:%12s\:" % _("Current") + " %9.2lf%s",
+ "GPRINT:used_min:%12s\:" % _("Minimum") + " %9.2lf%s",
+ "GPRINT:used_max:%12s\:" % _("Maximum") + " %9.2lf%s\\n",
+
+ # Stacked area of unused inodes
+ "AREA:free%s:%s:STACK" % (util.lighten(LIGHT_GREEN, .66), _("Free")),
+ "GPRINT:percentage_left_now: (%6.2lf%%)",
+ "GPRINT:free_cur:%12s\:" % _("Current") + " %9.2lf%s",
+ "GPRINT:free_min:%12s\:" % _("Minimum") + " %9.2lf%s",
+ "GPRINT:free_max:%12s\:" % _("Maximum") + " %9.2lf%s\\n",
+
+ # Add contour lines for the areas
+ "LINE:used%s" % LIGHT_RED,
+ "LINE:free%s::STACK" % LIGHT_GREEN,
+ ]
+
+ return rrd_graph
+
+ rrd_graph_args = [
+ "--base", "1000", # inodes
+ ]
+
+ @property
+ def graph_title(self):
+ _ = self.locale.translate
+ return _("Inode Usage of %s") % self.object.mountpoint
+
+ @property
+ def graph_vertical_label(self):
+ _ = self.locale.translate
+ return _("Inodes")
+
+
+class DiskUsageObject(base.Object):
+ rrd_schema = [
+ "DS:used:GAUGE:0:U",
+ "DS:free:GAUGE:0:U",
+ "DS:inodes_used:GAUGE:0:U",
+ "DS:inodes_free:GAUGE:0:U",
+ ]
+
+ def __repr__(self):
+ return "<%s %s>" % (self.__class__.__name__, self.mountpoint)
+
+ def init(self, mountpoint):
+ self.mountpoint = mountpoint
+
+ @property
+ def id(self):
+ mountpoint = self.mountpoint
+
+ if mountpoint.startswith("/"):
+ mountpoint = mountpoint[1:]
+
+ if not mountpoint:
+ return "root"
+
+ return mountpoint.replace("/", "-")
+
+ def collect(self):
+ stats = os.statvfs(self.mountpoint)
+
+ return (
+ # used
+ (stats.f_blocks * stats.f_frsize) - \
+ (stats.f_bfree * stats.f_bsize),
+ # free
+ stats.f_bfree * stats.f_bsize,
+ # inodes used
+ stats.f_files - stats.f_ffree,
+ # inodes free
+ stats.f_ffree,
+ )
+
+
+class DiskUsagePlugin(base.Plugin):
+ name = "df"
+ description = "Disk Usage Plugin"
+
+ templates = [
+ GraphTemplateDiskUsage,
+ GraphTemplateInodeUsage,
+ ]
+
+ @property
+ def objects(self):
+ for dev, mnt, fs, opts in _collecty.get_mountpoints():
+ yield DiskUsageObject(self, mnt)
--- /dev/null
+#!/usr/bin/python3
+###############################################################################
+# #
+# collecty - A system statistics collection daemon for IPFire #
+# Copyright (C) 2015 IPFire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+def __add_colour(colour, amount):
+ colour = colour.strip("#")
+
+ colour = (
+ int(colour[0:2], 16),
+ int(colour[2:4], 16),
+ int(colour[4:6], 16),
+ )
+
+ # Scale the colour
+ colour = (e + amount for e in colour)
+ colour = (max(e, 0) for e in colour)
+ colour = (min(e, 255) for e in colour)
+
+ return "#%02x%02x%02x" % tuple(colour)
+
+def lighten(colour, scale=0.1):
+ """
+ Takes a hexadecimal colour code
+ and brightens the colour.
+ """
+ return __add_colour(colour, 0xff * scale)
+
+def darken(colour, scale=0.1):
+ """
+ Takes a hexadecimal colour code
+ and darkens the colour.
+ """
+ return __add_colour(colour, 0xff * -scale)