From: Michael Tremer Date: Sun, 18 Oct 2015 12:01:09 +0000 (+0200) Subject: Add handler that serves graphs from collecty X-Git-Url: http://git.ipfire.org/?p=people%2Fms%2Fwestferry.git;a=commitdiff_plain;h=21a66327533dbf721880537e7da5cb3545915e1f Add handler that serves graphs from collecty This handler connects to collecty and generates graphs of the system in various formats and resolutions. Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 8fa2863..6e90876 100644 --- a/Makefile.am +++ b/Makefile.am @@ -96,12 +96,18 @@ CLEANFILES += \ src/westferry/constants.py westferry_backend_PYTHON = \ - src/westferry/backend/__version__.py + src/westferry/backend/__init__.py \ + src/westferry/backend/__version__.py \ + src/westferry/backend/base.py \ + src/westferry/backend/graphs.py \ + src/westferry/backend/main.py \ + src/westferry/backend/system.py westferry_backenddir = $(pythondir)/westferry/backend westferry_handlers_PYTHON = \ src/westferry/handlers/__init__.py \ + src/westferry/handlers/analytics.py \ src/westferry/handlers/base.py \ src/westferry/handlers/index.py diff --git a/po/POTFILES.in b/po/POTFILES.in index 77f3c22..292abac 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,3 +1,4 @@ src/westferry/backend/__version__.py +src/westferry/handlers/analytics.py src/westferry/handlers/base.py src/westferry/__init__.py diff --git a/src/westferry/application.py b/src/westferry/application.py index 06acd8c..a402848 100644 --- a/src/westferry/application.py +++ b/src/westferry/application.py @@ -21,6 +21,7 @@ import tornado.web +from . import backend from . import handlers from . import ui @@ -54,3 +55,10 @@ class WebApplication(tornado.web.Application): # Initialise the web application tornado.web.Application.__init__(self, handlers=h, **settings) + + @property + def backend(self): + if not hasattr(self, "_backend"): + self._backend = backend.Backend() + + return self._backend diff --git a/src/westferry/backend/__init__.py b/src/westferry/backend/__init__.py new file mode 100644 index 0000000..cad0eb3 --- /dev/null +++ b/src/westferry/backend/__init__.py @@ -0,0 +1,22 @@ +#!/usr/bin/python3 +############################################################################### +# # +# Westferry - The IPFire web user interface # +# 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 . # +# # +############################################################################### + +from .main import Backend diff --git a/src/westferry/backend/base.py b/src/westferry/backend/base.py new file mode 100644 index 0000000..3ebf2d9 --- /dev/null +++ b/src/westferry/backend/base.py @@ -0,0 +1,24 @@ +#!/usr/bin/python3 +############################################################################### +# # +# Westferry - The IPFire web user interface # +# 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 . # +# # +############################################################################### + +class BaseBackend(object): + def __init__(self, backend): + self.backend = backend diff --git a/src/westferry/backend/graphs.py b/src/westferry/backend/graphs.py new file mode 100644 index 0000000..1e24aee --- /dev/null +++ b/src/westferry/backend/graphs.py @@ -0,0 +1,55 @@ +#!/usr/bin/python3 +############################################################################### +# # +# Westferry - The IPFire web user interface # +# 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 . # +# # +############################################################################### + +import collecty.client + +from . import base + +class GraphsBackend(base.BaseBackend): + @property + def collecty(self): + """ + Returns a CollectyClient object which is created + when it is needed. + """ + if not hasattr(self, "_collecty"): + self._collecty = collecty.client.CollectyClient() + + return self._collecty + + def generate_graph(self, template_name, **kwargs): + assert self.template_exists(template_name) + + # Remove all keys with value None + # dbus cannot marshall None and when None is set, we assume + # that the default is selected, so we can simply remove the + # setting. + for key in kwargs.copy(): + if kwargs[key] is None: + del kwargs[key] + + return self.collecty.generate_graph(template_name, **kwargs) + + def template_exists(self, template_name): + """ + Returns True if a template with the given name exists + """ + return template_name in self.collecty.list_templates() diff --git a/src/westferry/backend/main.py b/src/westferry/backend/main.py new file mode 100644 index 0000000..fa05a13 --- /dev/null +++ b/src/westferry/backend/main.py @@ -0,0 +1,32 @@ +#!/usr/bin/python3 +############################################################################### +# # +# Westferry - The IPFire web user interface # +# 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 . # +# # +############################################################################### + +from .__version__ import VERSION + +from . import graphs +from . import system + +class Backend(object): + version = VERSION + + def __init__(self): + self.graphs = graphs.GraphsBackend(self) + self.system = system.SystemBackend(self) diff --git a/src/westferry/backend/system.py b/src/westferry/backend/system.py new file mode 100644 index 0000000..085c359 --- /dev/null +++ b/src/westferry/backend/system.py @@ -0,0 +1,32 @@ +#!/usr/bin/python3 +############################################################################### +# # +# Westferry - The IPFire web user interface # +# 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 . # +# # +############################################################################### + +import socket + +from . import base + +class SystemBackend(base.BaseBackend): + @property + def hostname(self): + """ + Returns the hostname of this system + """ + return socket.gethostname() diff --git a/src/westferry/handlers/__init__.py b/src/westferry/handlers/__init__.py index a934bbd..bd3ac0b 100644 --- a/src/westferry/handlers/__init__.py +++ b/src/westferry/handlers/__init__.py @@ -20,6 +20,7 @@ ############################################################################### from . import base +from . import analytics from . import index def get_handlers(): diff --git a/src/westferry/handlers/analytics.py b/src/westferry/handlers/analytics.py new file mode 100644 index 0000000..488d0ac --- /dev/null +++ b/src/westferry/handlers/analytics.py @@ -0,0 +1,72 @@ +#!/usr/bin/python3 +############################################################################### +# # +# Westferry - The IPFire web user interface # +# 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 . # +# # +############################################################################### + +import datetime +import collecty.client +import mimetypes +import tornado.web + +from . import base + +class GraphExportHandler(base.BaseHandler): + VALID_INTERVALS = ("hour", "day", "month", "week", "year") + DEFAULT_INTERVAL = "day" + + SUPPORTED_FORMATS = ("pdf", "png", "svg") + + url = r"/graph/([\w\-]+)(?:/([\w\d]+))?.(%s)" % "|".join(SUPPORTED_FORMATS) + + def get(self, template_name, object_id, format): + # Get the requested dimensions of the image + height = self.get_argument_int("height", None) + width = self.get_argument_int("width", None) + + # Get the requested interval + interval = self.get_argument("interval", self.DEFAULT_INTERVAL) + if interval and not interval in self.VALID_INTERVALS: + raise tornado.web.HTTPError(400, _("Invalid interval: %s") % interval) + + # Generate the graph image + g = self.backend.graphs.generate_graph(template_name, object_id=object_id, + format=format.upper(), height=height, width=width, interval=interval) + + # Set the HTTP headers + self._make_headers(format, template_name, object_id) + + # Deliver the content + self.finish(g) + + def _make_headers(self, extension, template_name, object_id): + # Determine the mime type + mimetype = mimetypes.types_map.get(".%s" % extension, "application/octet-stream") + self.set_header("Content-Type", mimetype) + + # Add the timestamp when this graph was generated + now = datetime.datetime.now() + + # Put together the filename (for downloads) + filename = [self.backend.system.hostname, template_name, object_id, now.isoformat()] + filename = "%s.%s" % ("-".join((e for e in filename if e)), extension) + + if extension == "pdf": + self.set_header("Content-Disposition", "attachment; filename=%s" % filename) + else: + self.set_header("Content-Disposition", "inline; filename=%s" % filename) diff --git a/src/westferry/handlers/base.py b/src/westferry/handlers/base.py index e3d2461..9027bf4 100644 --- a/src/westferry/handlers/base.py +++ b/src/westferry/handlers/base.py @@ -44,3 +44,22 @@ class HandlerRegistration(type): class BaseHandler(tornado.web.RequestHandler, metaclass=HandlerRegistration): url = None + + @property + def backend(self): + """ + Shortcut to access the backend + """ + return self.application.backend + + def get_argument_int(self, name, *args, **kwargs): + val = self.get_argument(name, *args, **kwargs) + + if val is None: + return + + try: + return int(val) + except ValueError: + raise tornado.web.HTTPError(400, + _("Invalid type for '%s', expected integer") % name)