This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "IPFire 3.x development tree".
The branch, master has been updated via 5fc517c0e5f99cfc46dbb9cc05927fe873017445 (commit) via 65c47a01078ff0a6e12e51b97d18f94387dc7da5 (commit) from bedef2dbb661281984553f2163deae2d1406adf6 (commit)
Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below.
- Log ----------------------------------------------------------------- commit 5fc517c0e5f99cfc46dbb9cc05927fe873017445 Author: Michael Tremer michael.tremer@ipfire.org Date: Tue Dec 15 14:46:25 2015 +0000
collecty: Update to version 004
Signed-off-by: Michael Tremer michael.tremer@ipfire.org
commit 65c47a01078ff0a6e12e51b97d18f94387dc7da5 Author: Michael Tremer michael.tremer@ipfire.org Date: Tue Dec 15 14:45:58 2015 +0000
python-rrdtool: Update to version 0.1.2
Signed-off-by: Michael Tremer michael.tremer@ipfire.org
-----------------------------------------------------------------------
Summary of changes: collecty/collecty.nm | 4 +- ...plugins-Automatically-replace-None-by-NaN.patch | 56 -- .../0002-latency-Rewrite-latency-module.patch | 995 --------------------- .../patches/python-rrdtool-fix-SEGV.patch | 47 + python-rrdtool/python-rrdtool.nm | 5 +- 5 files changed, 51 insertions(+), 1056 deletions(-) delete mode 100644 collecty/patches/0001-plugins-Automatically-replace-None-by-NaN.patch delete mode 100644 collecty/patches/0002-latency-Rewrite-latency-module.patch create mode 100644 python-rrdtool/patches/python-rrdtool-fix-SEGV.patch
Difference in files: diff --git a/collecty/collecty.nm b/collecty/collecty.nm index 72ed7b1..2b30bd5 100644 --- a/collecty/collecty.nm +++ b/collecty/collecty.nm @@ -4,8 +4,8 @@ ###############################################################################
name = collecty -version = 003 -release = 4 +version = 004 +release = 1
maintainer = Michael Tremer michael.tremer@ipfire.org groups = System/Monitoring diff --git a/collecty/patches/0001-plugins-Automatically-replace-None-by-NaN.patch b/collecty/patches/0001-plugins-Automatically-replace-None-by-NaN.patch deleted file mode 100644 index 772c65d..0000000 --- a/collecty/patches/0001-plugins-Automatically-replace-None-by-NaN.patch +++ /dev/null @@ -1,56 +0,0 @@ -From a9af411f0703eac939e0df5d5f75b46d35f531bc Mon Sep 17 00:00:00 2001 -From: Michael Tremer michael.tremer@ipfire.org -Date: Mon, 29 Jun 2015 20:44:18 +0000 -Subject: [PATCH 1/2] plugins: Automatically replace None by NaN - -rrdtool uses NaN to represent no value. Python uses None. -This patch automatically translates from None to NaN. - -Signed-off-by: Michael Tremer michael.tremer@ipfire.org ---- - src/collecty/plugins/base.py | 22 ++++++++++++++++++++-- - 1 file changed, 20 insertions(+), 2 deletions(-) - -diff --git a/src/collecty/plugins/base.py b/src/collecty/plugins/base.py -index bed461f..cf9c3b4 100644 ---- a/src/collecty/plugins/base.py -+++ b/src/collecty/plugins/base.py -@@ -147,8 +147,7 @@ class Plugin(object, metaclass=PluginRegistration): - try: - result = o.collect() - -- if isinstance(result, tuple) or isinstance(result, list): -- result = ":".join(("%s" % e for e in result)) -+ result = self._format_result(result) - except: - self.log.warning(_("Unhandled exception in %s.collect()") % o, exc_info=True) - continue -@@ -170,6 +169,25 @@ class Plugin(object, metaclass=PluginRegistration): - if delay >= 60: - self.log.warning(_("A worker thread was stalled for %.4fs") % delay) - -+ @staticmethod -+ def _format_result(result): -+ if not isinstance(result, tuple) and not isinstance(result, list): -+ return result -+ -+ # Replace all Nones by NaN -+ s = [] -+ -+ for e in result: -+ if e is None: -+ e = "NaN" -+ -+ # Format as string -+ e = "%s" % e -+ -+ s.append(e) -+ -+ return ":".join(s) -+ - def get_object(self, id): - for object in self.objects: - if not object.id == id: --- -1.8.1 - diff --git a/collecty/patches/0002-latency-Rewrite-latency-module.patch b/collecty/patches/0002-latency-Rewrite-latency-module.patch deleted file mode 100644 index 837502b..0000000 --- a/collecty/patches/0002-latency-Rewrite-latency-module.patch +++ /dev/null @@ -1,995 +0,0 @@ -From 63f9f8beed445a80dcb492570b105c5b50e65a59 Mon Sep 17 00:00:00 2001 -From: Michael Tremer michael.tremer@ipfire.org -Date: Mon, 29 Jun 2015 20:49:02 +0000 -Subject: [PATCH 2/2] latency: Rewrite latency module - -This patch replaces the builtin python implementation -that pinged hosts by a Python C module that uses liboping. - -liboping is able to ping IPv6 hosts as well and should -implement the ICMP protocol more appropriately. - -The graph has been extended so that hosts will have a -line for latency over IPv6 and IPv4 if available and -the packet loss is merged from both protocols, too. - -Signed-off-by: Michael Tremer michael.tremer@ipfire.org ---- - Makefile.am | 5 +- - configure.ac | 2 + - po/POTFILES.in | 1 - - src/_collectymodule.c | 338 ++++++++++++++++++++++++++++++++++++++++ - src/collecty/ping.py | 324 -------------------------------------- - src/collecty/plugins/latency.py | 151 +++++++++++------- - 6 files changed, 437 insertions(+), 384 deletions(-) - delete mode 100644 src/collecty/ping.py - -diff --git a/Makefile.am b/Makefile.am -index fe00da7..0b7e299 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -79,8 +79,7 @@ collecty_PYTHON = \ - src/collecty/daemon.py \ - src/collecty/errors.py \ - src/collecty/i18n.py \ -- src/collecty/logger.py \ -- src/collecty/ping.py -+ src/collecty/logger.py - - collectydir = $(pythondir)/collecty - -@@ -109,6 +108,7 @@ _collecty_la_SOURCES = \ - _collecty_la_CFLAGS = \ - $(AM_CFLAGS) \ - $(LIBATASMART_CFLAGS) \ -+ $(OPING_CFLAGS) \ - $(PYTHON_CFLAGS) - - _collecty_la_LDFLAGS = \ -@@ -119,6 +119,7 @@ _collecty_la_LDFLAGS = \ - - _collecty_la_LIBADD = \ - $(LIBATASMART_LIBS) \ -+ $(OPING_LIBS) \ - $(PYTHON_LIBS) \ - $(SENSORS_LIBS) - -diff --git a/configure.ac b/configure.ac -index 59250ca..9b540fd 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -61,6 +61,8 @@ AC_PROG_GCC_TRADITIONAL - - AC_PATH_PROG([XSLTPROC], [xsltproc]) - -+PKG_CHECK_MODULES([OPING], [liboping]) -+ - # Python - AM_PATH_PYTHON([3.2]) - PKG_CHECK_MODULES([PYTHON], [python-${PYTHON_VERSION}]) -diff --git a/po/POTFILES.in b/po/POTFILES.in -index f6aebb3..a96f7b2 100644 ---- a/po/POTFILES.in -+++ b/po/POTFILES.in -@@ -5,7 +5,6 @@ src/collecty/daemon.py - src/collecty/errors.py - src/collecty/i18n.py - src/collecty/__init__.py --src/collecty/ping.py - src/collecty/plugins/base.py - src/collecty/plugins/conntrack.py - src/collecty/plugins/cpu.py -diff --git a/src/_collectymodule.c b/src/_collectymodule.c -index 422c27d..c13ca69 100644 ---- a/src/_collectymodule.c -+++ b/src/_collectymodule.c -@@ -22,15 +22,21 @@ - #include <errno.h> - #include <fcntl.h> - #include <linux/hdreg.h> -+#include <oping.h> - #include <sensors/error.h> - #include <sensors/sensors.h> - #include <stdbool.h> - #include <string.h> - #include <sys/ioctl.h> -+#include <time.h> - - #define MODEL_SIZE 40 - #define SERIAL_SIZE 20 - -+#define PING_HISTORY_SIZE 1024 -+#define PING_DEFAULT_COUNT 10 -+#define PING_DEFAULT_TIMEOUT 8 -+ - typedef struct { - PyObject_HEAD - char* path; -@@ -313,6 +319,324 @@ static PyTypeObject BlockDeviceType = { - BlockDevice_new, /* tp_new */ - }; - -+static PyObject* PyExc_PingError; -+static PyObject* PyExc_PingAddHostError; -+ -+typedef struct { -+ PyObject_HEAD -+ pingobj_t* ping; -+ const char* host; -+ struct { -+ double history[PING_HISTORY_SIZE]; -+ size_t history_index; -+ size_t history_size; -+ size_t packets_sent; -+ size_t packets_rcvd; -+ double average; -+ double stddev; -+ double loss; -+ } stats; -+} PingObject; -+ -+static void Ping_dealloc(PingObject* self) { -+ if (self->ping) -+ ping_destroy(self->ping); -+ -+ Py_TYPE(self)->tp_free((PyObject*)self); -+} -+ -+static void Ping_init_stats(PingObject* self) { -+ self->stats.history_index = 0; -+ self->stats.history_size = 0; -+ self->stats.packets_sent = 0; -+ self->stats.packets_rcvd = 0; -+ -+ self->stats.average = 0.0; -+ self->stats.stddev = 0.0; -+ self->stats.loss = 0.0; -+} -+ -+static PyObject* Ping_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { -+ PingObject* self = (PingObject*)type->tp_alloc(type, 0); -+ -+ if (self) { -+ self->ping = NULL; -+ self->host = NULL; -+ -+ Ping_init_stats(self); -+ } -+ -+ return (PyObject*)self; -+} -+ -+static int Ping_init(PingObject* self, PyObject* args, PyObject* kwds) { -+ char* kwlist[] = {"host", "family", "timeout", "ttl", NULL}; -+ int family = PING_DEF_AF; -+ double timeout = PING_DEFAULT_TIMEOUT; -+ int ttl = PING_DEF_TTL; -+ -+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|idi", kwlist, &self->host, -+ &family, &timeout, &ttl)) -+ return -1; -+ -+ if (family != AF_UNSPEC && family != AF_INET6 && family != AF_INET) { -+ PyErr_Format(PyExc_ValueError, "Family must be AF_UNSPEC, AF_INET6, or AF_INET"); -+ return -1; -+ } -+ -+ if (timeout < 0) { -+ PyErr_Format(PyExc_ValueError, "Timeout must be greater than zero"); -+ return -1; -+ } -+ -+ if (ttl < 1 || ttl > 255) { -+ PyErr_Format(PyExc_ValueError, "TTL must be between 1 and 255"); -+ return -1; -+ } -+ -+ self->ping = ping_construct(); -+ if (!self->ping) { -+ return -1; -+ } -+ -+ // Set options -+ int r; -+ -+ r = ping_setopt(self->ping, PING_OPT_AF, &family); -+ if (r) { -+ PyErr_Format(PyExc_RuntimeError, "Could not set address family: %s", -+ ping_get_error(self->ping)); -+ return -1; -+ } -+ -+ if (timeout > 0) { -+ r = ping_setopt(self->ping, PING_OPT_TIMEOUT, &timeout); -+ -+ if (r) { -+ PyErr_Format(PyExc_RuntimeError, "Could not set timeout: %s", -+ ping_get_error(self->ping)); -+ return -1; -+ } -+ } -+ -+ r = ping_setopt(self->ping, PING_OPT_TTL, &ttl); -+ if (r) { -+ PyErr_Format(PyExc_RuntimeError, "Could not set TTL: %s", -+ ping_get_error(self->ping)); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static double Ping_compute_average(PingObject* self) { -+ assert(self->stats.packets_rcvd > 0); -+ -+ double total_latency = 0.0; -+ -+ for (int i = 0; i < self->stats.history_size; i++) { -+ if (self->stats.history[i] > 0) -+ total_latency += self->stats.history[i]; -+ } -+ -+ return total_latency / self->stats.packets_rcvd; -+} -+ -+static double Ping_compute_stddev(PingObject* self, double mean) { -+ assert(self->stats.packets_rcvd > 0); -+ -+ double deviation = 0.0; -+ -+ for (int i = 0; i < self->stats.history_size; i++) { -+ if (self->stats.history[i] > 0) { -+ deviation += pow(self->stats.history[i] - mean, 2); -+ } -+ } -+ -+ // Normalise -+ deviation /= self->stats.packets_rcvd; -+ -+ return sqrt(deviation); -+} -+ -+static void Ping_compute_stats(PingObject* self) { -+ // Compute the average latency -+ self->stats.average = Ping_compute_average(self); -+ -+ // Compute the standard deviation -+ self->stats.stddev = Ping_compute_stddev(self, self->stats.average); -+ -+ // Compute lost packets -+ self->stats.loss = 1.0; -+ self->stats.loss -= (double)self->stats.packets_rcvd \ -+ / (double)self->stats.packets_sent; -+} -+ -+static double time_elapsed(struct timeval* t0) { -+ struct timeval now; -+ gettimeofday(&now, NULL); -+ -+ double r = now.tv_sec - t0->tv_sec; -+ r += ((double)now.tv_usec / 1000000) - ((double)t0->tv_usec / 1000000); -+ -+ return r; -+} -+ -+static PyObject* Ping_ping(PingObject* self, PyObject* args, PyObject* kwds) { -+ char* kwlist[] = {"count", "deadline", NULL}; -+ size_t count = PING_DEFAULT_COUNT; -+ double deadline = 0; -+ -+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Id", kwlist, &count, &deadline)) -+ return NULL; -+ -+ int r = ping_host_add(self->ping, self->host); -+ if (r) { -+ PyErr_Format(PyExc_PingAddHostError, "Could not add host %s: %s", -+ self->host, ping_get_error(self->ping)); -+ return NULL; -+ } -+ -+ // Reset all collected statistics in case ping() is called more than once. -+ Ping_init_stats(self); -+ -+ // Save start time -+ struct timeval time_start; -+ r = gettimeofday(&time_start, NULL); -+ if (r) { -+ PyErr_Format(PyExc_RuntimeError, "Could not determine start time"); -+ return NULL; -+ } -+ -+ // Do the pinging -+ while (count--) { -+ self->stats.packets_sent++; -+ -+ Py_BEGIN_ALLOW_THREADS -+ r = ping_send(self->ping); -+ Py_END_ALLOW_THREADS -+ -+ // Count recieved packets -+ if (r >= 0) { -+ self->stats.packets_rcvd += r; -+ -+ // Raise any errors -+ } else { -+ PyErr_Format(PyExc_RuntimeError, "Error executing ping_send(): %s", -+ ping_get_error(self->ping)); -+ return NULL; -+ } -+ -+ // Extract all data -+ pingobj_iter_t* iter = ping_iterator_get(self->ping); -+ -+ double* latency = &self->stats.history[self->stats.history_index]; -+ size_t buffer_size = sizeof(latency); -+ ping_iterator_get_info(iter, PING_INFO_LATENCY, latency, &buffer_size); -+ -+ // Increase the history pointer -+ self->stats.history_index++; -+ self->stats.history_index %= sizeof(self->stats.history); -+ -+ // Increase the history size -+ if (self->stats.history_size < sizeof(self->stats.history)) -+ self->stats.history_size++; -+ -+ // Check if the deadline is due -+ if (deadline > 0) { -+ double elapsed_time = time_elapsed(&time_start); -+ -+ // If we have run longer than the deadline is, we end the main loop -+ if (elapsed_time >= deadline) -+ break; -+ } -+ } -+ -+ if (self->stats.packets_rcvd == 0) { -+ PyErr_Format(PyExc_PingError, "No replies received"); -+ return NULL; -+ } -+ -+ Ping_compute_stats(self); -+ -+ Py_RETURN_NONE; -+} -+ -+static PyObject* Ping_get_packets_sent(PingObject* self) { -+ return PyLong_FromUnsignedLong(self->stats.packets_sent); -+} -+ -+static PyObject* Ping_get_packets_rcvd(PingObject* self) { -+ return PyLong_FromUnsignedLong(self->stats.packets_rcvd); -+} -+ -+static PyObject* Ping_get_average(PingObject* self) { -+ return PyFloat_FromDouble(self->stats.average); -+} -+ -+static PyObject* Ping_get_stddev(PingObject* self) { -+ return PyFloat_FromDouble(self->stats.stddev); -+} -+ -+static PyObject* Ping_get_loss(PingObject* self) { -+ return PyFloat_FromDouble(self->stats.loss); -+} -+ -+static PyGetSetDef Ping_getsetters[] = { -+ {"average", (getter)Ping_get_average, NULL, NULL, NULL}, -+ {"loss", (getter)Ping_get_loss, NULL, NULL, NULL}, -+ {"stddev", (getter)Ping_get_stddev, NULL, NULL, NULL}, -+ {"packets_sent", (getter)Ping_get_packets_sent, NULL, NULL, NULL}, -+ {"packets_rcvd", (getter)Ping_get_packets_rcvd, NULL, NULL, NULL}, -+ {NULL} -+}; -+ -+static PyMethodDef Ping_methods[] = { -+ {"ping", (PyCFunction)Ping_ping, METH_VARARGS|METH_KEYWORDS, NULL}, -+ {NULL} -+}; -+ -+static PyTypeObject PingType = { -+ PyVarObject_HEAD_INIT(NULL, 0) -+ "_collecty.Ping", /*tp_name*/ -+ sizeof(PingObject), /*tp_basicsize*/ -+ 0, /*tp_itemsize*/ -+ (destructor)Ping_dealloc, /*tp_dealloc*/ -+ 0, /*tp_print*/ -+ 0, /*tp_getattr*/ -+ 0, /*tp_setattr*/ -+ 0, /*tp_compare*/ -+ 0, /*tp_repr*/ -+ 0, /*tp_as_number*/ -+ 0, /*tp_as_sequence*/ -+ 0, /*tp_as_mapping*/ -+ 0, /*tp_hash */ -+ 0, /*tp_call*/ -+ 0, /*tp_str*/ -+ 0, /*tp_getattro*/ -+ 0, /*tp_setattro*/ -+ 0, /*tp_as_buffer*/ -+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ -+ "Ping object", /* tp_doc */ -+ 0, /* tp_traverse */ -+ 0, /* tp_clear */ -+ 0, /* tp_richcompare */ -+ 0, /* tp_weaklistoffset */ -+ 0, /* tp_iter */ -+ 0, /* tp_iternext */ -+ Ping_methods, /* tp_methods */ -+ 0, /* tp_members */ -+ Ping_getsetters, /* tp_getset */ -+ 0, /* tp_base */ -+ 0, /* tp_dict */ -+ 0, /* tp_descr_get */ -+ 0, /* tp_descr_set */ -+ 0, /* tp_dictoffset */ -+ (initproc)Ping_init, /* tp_init */ -+ 0, /* tp_alloc */ -+ Ping_new, /* tp_new */ -+}; -+ - typedef struct { - PyObject_HEAD - const sensors_chip_name* chip; -@@ -743,6 +1067,9 @@ PyMODINIT_FUNC PyInit__collecty(void) { - if (PyType_Ready(&BlockDeviceType) < 0) - return NULL; - -+ if (PyType_Ready(&PingType) < 0) -+ return NULL; -+ - if (PyType_Ready(&SensorType) < 0) - return NULL; - -@@ -751,6 +1078,17 @@ PyMODINIT_FUNC PyInit__collecty(void) { - Py_INCREF(&BlockDeviceType); - PyModule_AddObject(m, "BlockDevice", (PyObject*)&BlockDeviceType); - -+ Py_INCREF(&PingType); -+ PyModule_AddObject(m, "Ping", (PyObject*)&PingType); -+ -+ PyExc_PingError = PyErr_NewException("_collecty.PingError", NULL, NULL); -+ Py_INCREF(PyExc_PingError); -+ PyModule_AddObject(m, "PingError", PyExc_PingError); -+ -+ PyExc_PingAddHostError = PyErr_NewException("_collecty.PingAddHostError", NULL, NULL); -+ Py_INCREF(PyExc_PingAddHostError); -+ PyModule_AddObject(m, "PingAddHostError", PyExc_PingAddHostError); -+ - Py_INCREF(&SensorType); - PyModule_AddObject(m, "Sensor", (PyObject*)&SensorType); - -diff --git a/src/collecty/ping.py b/src/collecty/ping.py -deleted file mode 100644 -index e2d7970..0000000 ---- a/src/collecty/ping.py -+++ /dev/null -@@ -1,324 +0,0 @@ --#!/usr/bin/python3 -- --import array --import math --import os --import random --import select --import socket --import struct --import sys --import time -- --ICMP_TYPE_ECHO_REPLY = 0 --ICMP_TYPE_ECHO_REQUEST = 8 --ICMP_MAX_RECV = 2048 -- --MAX_SLEEP = 1000 -- --class PingError(Exception): -- msg = None -- -- --class PingResolveError(PingError): -- msg = "Could not resolve hostname" -- -- --class Ping(object): -- def __init__(self, destination, timeout=1000, packet_size=56): -- self.destination = self._resolve(destination) -- self.timeout = timeout -- self.packet_size = packet_size -- -- self.id = os.getpid() & 0xffff # XXX ? Is this a good idea? -- -- self.seq_number = 0 -- -- # Number of sent packets. -- self.send_count = 0 -- -- # Save the delay of all responses. -- self.times = [] -- -- def run(self, count=None, deadline=None): -- while True: -- delay = self.do() -- -- self.seq_number += 1 -- -- if count and self.seq_number >= count: -- break -- -- if deadline and self.total_time >= deadline: -- break -- -- if delay == None: -- delay = 0 -- -- if MAX_SLEEP > delay: -- time.sleep((MAX_SLEEP - delay) / 1000) -- -- def do(self): -- s = None -- try: -- # Open a socket for ICMP communication. -- s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname("icmp")) -- -- # Send one packet. -- send_time = self.send_icmp_echo_request(s) -- -- # Increase number of sent packets (even if it could not be sent). -- self.send_count += 1 -- -- # If the packet could not be sent, we may stop here. -- if send_time is None: -- return -- -- # Wait for the reply. -- receive_time, packet_size, ip, ip_header, icmp_header = self.receive_icmp_echo_reply(s) -- -- finally: -- # Close the socket. -- if s: -- s.close() -- -- # If a packet has been received... -- if receive_time: -- delay = (receive_time - send_time) * 1000 -- self.times.append(delay) -- -- return delay -- -- def send_icmp_echo_request(self, s): -- # Header is type (8), code (8), checksum (16), id (16), sequence (16) -- checksum = 0 -- -- # Create a header with checksum == 0. -- header = struct.pack("!BBHHH", ICMP_TYPE_ECHO_REQUEST, 0, -- checksum, self.id, self.seq_number) -- -- # Get some bytes for padding. -- padding = os.urandom(self.packet_size) -- -- # Calculate the checksum for header + padding data. -- checksum = self._calculate_checksum(header + padding) -- -- # Rebuild the header with the new checksum. -- header = struct.pack("!BBHHH", ICMP_TYPE_ECHO_REQUEST, 0, -- checksum, self.id, self.seq_number) -- -- # Build the packet. -- packet = header + padding -- -- # Save the time when the packet has been sent. -- send_time = time.time() -- -- # Send the packet. -- try: -- s.sendto(packet, (self.destination, 0)) -- except socket.error as e: -- if e.errno == 1: # Operation not permitted -- # The packet could not be sent, probably because of -- # wrong firewall settings. -- return -- -- return send_time -- -- def receive_icmp_echo_reply(self, s): -- timeout = self.timeout / 1000.0 -- -- # Wait until the reply packet arrived or until we hit timeout. -- while True: -- select_start = time.time() -- -- inputready, outputready, exceptready = select.select([s], [], [], timeout) -- select_duration = (time.time() - select_start) -- -- if inputready == []: # Timeout -- return None, 0, 0, 0, 0 -- -- # Save the time when the packet has been received. -- receive_time = time.time() -- -- # Read the packet from the socket. -- packet_data, address = s.recvfrom(ICMP_MAX_RECV) -- -- # Parse the ICMP header. -- icmp_header = self._header2dict( -- ["type", "code", "checksum", "packet_id", "seq_number"], -- "!BBHHH", packet_data[20:28] -- ) -- -- # This is the reply to our packet if the ID matches. -- if icmp_header["packet_id"] == self.id: -- # Parse the IP header. -- ip_header = self._header2dict( -- ["version", "type", "length", "id", "flags", -- "ttl", "protocol", "checksum", "src_ip", "dst_ip"], -- "!BBHHHBBHII", packet_data[:20] -- ) -- -- packet_size = len(packet_data) - 28 -- ip = socket.inet_ntoa(struct.pack("!I", ip_header["src_ip"])) -- -- return receive_time, packet_size, ip, ip_header, icmp_header -- -- # Check if the timeout has already been hit. -- timeout = timeout - select_duration -- if timeout <= 0: -- return None, 0, 0, 0, 0 -- -- def _header2dict(self, names, struct_format, data): -- """ -- Unpack tghe raw received IP and ICMP header informations to a dict -- """ -- unpacked_data = struct.unpack(struct_format, data) -- return dict(list(zip(names, unpacked_data))) -- -- def _calculate_checksum(self, source_string): -- if len(source_string) % 2: -- source_string += "\x00" -- -- converted = array.array("H", source_string) -- if sys.byteorder == "big": -- converted.byteswap() -- -- val = sum(converted) -- -- # Truncate val to 32 bits (a variance from ping.c, which uses signed -- # integers, but overflow is unlinkely in ping). -- val &= 0xffffffff -- -- # Add high 16 bits to low 16 bits. -- val = (val >> 16) + (val & 0xffff) -- -- # Add carry from above (if any). -- val += (val >> 16) -- -- # Invert and truncate to 16 bits. -- answer = ~val & 0xffff -- -- return socket.htons(answer) -- -- def _resolve(self, host): -- """ -- Resolve host. -- """ -- if self._is_valid_ipv4_address(host): -- return host -- -- try: -- return socket.gethostbyname(host) -- except socket.gaierror as e: -- if e.errno == -3: -- raise PingResolveError -- -- raise -- -- def _is_valid_ipv4_address(self, addr): -- """ -- Check addr to be a valid IPv4 address. -- """ -- parts = addr.split(".") -- -- if not len(parts) == 4: -- return False -- -- for part in parts: -- try: -- number = int(part) -- except ValueError: -- return False -- -- if number > 255: -- return False -- -- return True -- -- @property -- def receive_count(self): -- """ -- The number of received packets. -- """ -- return len(self.times) -- -- @property -- def total_time(self): -- """ -- The total time of all roundtrips. -- """ -- try: -- return sum(self.times) -- except ValueError: -- return -- -- @property -- def min_time(self): -- """ -- The smallest roundtrip time. -- """ -- try: -- return min(self.times) -- except ValueError: -- return -- -- @property -- def max_time(self): -- """ -- The biggest roundtrip time. -- """ -- try: -- return max(self.times) -- except ValueError: -- return -- -- @property -- def avg_time(self): -- """ -- Calculate the average response time. -- """ -- try: -- return self.total_time / self.receive_count -- except ZeroDivisionError: -- return -- -- @property -- def variance(self): -- """ -- Calculate the variance of all roundtrips. -- """ -- if self.avg_time is None: -- return -- -- var = 0 -- -- for t in self.times: -- var += (t - self.avg_time) ** 2 -- -- var /= self.receive_count -- return var -- -- @property -- def stddev(self): -- """ -- Standard deviation of all roundtrips. -- """ -- return math.sqrt(self.variance) -- -- @property -- def loss(self): -- """ -- Outputs the percentage of dropped packets. -- """ -- dropped = self.send_count - self.receive_count -- -- return dropped / self.send_count -- -- --if __name__ == "__main__": -- p = Ping("ping.ipfire.org") -- p.run(count=5) -- -- print("Min/Avg/Max/Stddev: %.2f/%.2f/%.2f/%.2f" % \ -- (p.min_time, p.avg_time, p.max_time, p.stddev)) -- print("Sent/Recv/Loss: %d/%d/%.2f" % (p.send_count, p.receive_count, p.loss)) -diff --git a/src/collecty/plugins/latency.py b/src/collecty/plugins/latency.py -index df67102..a219240 100644 ---- a/src/collecty/plugins/latency.py -+++ b/src/collecty/plugins/latency.py -@@ -19,8 +19,9 @@ - # # - ############################################################################### - --import collecty.ping -+import socket - -+import collecty._collecty - from . import base - - from ..i18n import _ -@@ -37,86 +38,124 @@ class GraphTemplateLatency(base.GraphTemplate): - @property - def rrd_graph(self): - return [ -- "DEF:latency=%(file)s:latency:AVERAGE", -- "DEF:latency_loss=%(file)s:latency_loss:AVERAGE", -- "DEF:latency_stddev=%(file)s:latency_stddev:AVERAGE", -- -- # Compute loss in percentage. -- "CDEF:latency_ploss=latency_loss,100,*", -- -- # Compute standard deviation. -- "CDEF:stddev1=latency,latency_stddev,+", -- "CDEF:stddev2=latency,latency_stddev,-", -- -- "CDEF:l005=latency_ploss,0,5,LIMIT,UN,UNKN,INF,IF", -- "CDEF:l010=latency_ploss,5,10,LIMIT,UN,UNKN,INF,IF", -- "CDEF:l025=latency_ploss,10,25,LIMIT,UN,UNKN,INF,IF", -- "CDEF:l050=latency_ploss,25,50,LIMIT,UN,UNKN,INF,IF", -- "CDEF:l100=latency_ploss,50,100,LIMIT,UN,UNKN,INF,IF", -- -+ "DEF:latency6=%(file)s:latency6:AVERAGE", -+ "DEF:loss6=%(file)s:loss6:AVERAGE", -+ "DEF:stddev6=%(file)s:stddev6:AVERAGE", -+ -+ "DEF:latency4=%(file)s:latency4:AVERAGE", -+ "DEF:loss4=%(file)s:loss4:AVERAGE", -+ "DEF:stddev4=%(file)s:stddev4:AVERAGE", -+ -+ # Compute the biggest loss and convert into percentage -+ "CDEF:ploss=loss6,loss4,MAX,100,*", -+ -+ # Compute standard deviation -+ "CDEF:stddevarea6=stddev6,2,*", -+ "CDEF:spacer6=latency6,stddev6,-", -+ "CDEF:stddevarea4=stddev4,2,*", -+ "CDEF:spacer4=latency4,stddev4,-", -+ -+ "CDEF:l005=ploss,0,5,LIMIT,UN,UNKN,INF,IF", -+ "CDEF:l010=ploss,5,10,LIMIT,UN,UNKN,INF,IF", -+ "CDEF:l025=ploss,10,25,LIMIT,UN,UNKN,INF,IF", -+ "CDEF:l050=ploss,25,50,LIMIT,UN,UNKN,INF,IF", -+ "CDEF:l100=ploss,50,100,LIMIT,UN,UNKN,INF,IF", -+ -+ "VDEF:latency6min=latency6,MINIMUM", -+ "VDEF:latency6max=latency6,MAXIMUM", -+ "VDEF:latency6avg=latency6,AVERAGE", -+ "VDEF:latency4min=latency4,MINIMUM", -+ "VDEF:latency4max=latency4,MAXIMUM", -+ "VDEF:latency4avg=latency4,AVERAGE", -+ -+ "LINE1:latency6avg#00ff0066:%s" % _("Average latency (IPv6)"), -+ "LINE1:latency4avg#ff000066:%s\r" % _("Average latency (IPv4)"), -+ -+ "COMMENT:%s" % _("Packet Loss"), - "AREA:l005#ffffff:%s" % _("0-5%%"), -- "AREA:l010#000000:%s" % _("5-10%%"), -- "AREA:l025#ff0000:%s" % _("10-25%%"), -- "AREA:l050#00ff00:%s" % _("25-50%%"), -- "AREA:l100#0000ff:%s" % _("50-100%%") + "\n", -- -- "LINE1:stddev1#00660088", -- "LINE1:stddev2#00660088", -- -- "LINE3:latency#ff0000:%s" % _("Latency"), -- "VDEF:latencymin=latency,MINIMUM", -- "VDEF:latencymax=latency,MAXIMUM", -- "VDEF:latencyavg=latency,AVERAGE", -- "GPRINT:latencymax:%12s:" % _("Maximum") + " %6.2lf", -- "GPRINT:latencymin:%12s:" % _("Minimum") + " %6.2lf", -- "GPRINT:latencyavg:%12s:" % _("Average") + " %6.2lf\n", -- -- "LINE1:latencyavg#000000:%s" % _("Average latency"), -+ "AREA:l010#cccccc:%s" % _("5-10%%"), -+ "AREA:l025#999999:%s" % _("10-25%%"), -+ "AREA:l050#666666:%s" % _("25-50%%"), -+ "AREA:l100#333333:%s" % _("50-100%%") + "\r", -+ -+ "COMMENT: \n", # empty line -+ -+ "AREA:spacer4", -+ "AREA:stddevarea4#ff000033:STACK", -+ "LINE2:latency4#ff0000:%s" % _("Latency (IPv4)"), -+ "GPRINT:latency4max:%12s:" % _("Maximum") + " %6.2lf", -+ "GPRINT:latency4min:%12s:" % _("Minimum") + " %6.2lf", -+ "GPRINT:latency4avg:%12s:" % _("Average") + " %6.2lf\n", -+ -+ "AREA:spacer6", -+ "AREA:stddevarea6#00ff0033:STACK", -+ "LINE2:latency6#00ff00:%s" % _("Latency (IPv6)"), -+ "GPRINT:latency6max:%12s:" % _("Maximum") + " %6.2lf", -+ "GPRINT:latency6min:%12s:" % _("Minimum") + " %6.2lf", -+ "GPRINT:latency6avg:%12s:" % _("Average") + " %6.2lf\n", - ] - - @property - def graph_title(self): -- return _("Latency to %(host)s") -+ return _("Latency to %s") % self.object.hostname - - @property - def graph_vertical_label(self): - return _("Milliseconds") - -+ @property -+ def rrd_graph_args(self): -+ return [ -+ "--legend-direction=bottomup", -+ ] -+ - - class LatencyObject(base.Object): - rrd_schema = [ -- "DS:latency:GAUGE:0:U", -- "DS:latency_loss:GAUGE:0:100", -- "DS:latency_stddev:GAUGE:0:U", -+ "DS:latency6:GAUGE:0:U", -+ "DS:stddev6:GAUGE:0:U", -+ "DS:loss6:GAUGE:0:100", -+ "DS:latency4:GAUGE:0:U", -+ "DS:stddev4:GAUGE:0:U", -+ "DS:loss4:GAUGE:0:100", - ] - - def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, self.hostname) - -- def init(self, hostname, deadline=None): -+ def init(self, hostname): - self.hostname = hostname -- self.deadline = deadline - - @property - def id(self): - return self.hostname - - def collect(self): -- # Send up to five ICMP echo requests. -- try: -- ping = collecty.ping.Ping(destination=self.hostname, timeout=20000) -- ping.run(count=5, deadline=self.deadline) -+ result = [] -+ -+ for family in (socket.AF_INET6, socket.AF_INET): -+ try: -+ p = collecty._collecty.Ping(self.hostname, family=family) -+ p.ping(count=5, deadline=10) -+ -+ result += (p.average, p.stddev, p.loss) -+ -+ except collecty._collecty.PingAddHostError as e: -+ self.log.debug(_("Could not add host %(host)s for family %(family)s") \ -+ % { "host" : self.hostname, "family" : family }) - -- except collecty.ping.PingError as e: -- self.log.warning(_("Could not run latency check for %(host)s: %(msg)s") \ -- % { "host" : self.hostname, "msg" : e.msg }) -- return -+ # No data available -+ result += (None, None, None) -+ continue - -- return ( -- "%.10f" % ping.avg_time, -- "%.10f" % ping.loss, -- "%.10f" % ping.stddev, -- ) -+ except collecty._collecty.PingError as e: -+ self.log.warning(_("Could not run latency check for %(host)s: %(msg)s") \ -+ % { "host" : self.hostname, "msg" : e }) -+ -+ # A hundred percent loss -+ result += (None, None, 1) -+ -+ return result - - - class LatencyPlugin(base.Plugin): -@@ -127,7 +166,5 @@ class LatencyPlugin(base.Plugin): - - @property - def objects(self): -- deadline = self.interval / len(PING_HOSTS) -- - for hostname in PING_HOSTS: -- yield LatencyObject(self, hostname, deadline=deadline) -+ yield LatencyObject(self, hostname) --- -1.8.1 - diff --git a/python-rrdtool/patches/python-rrdtool-fix-SEGV.patch b/python-rrdtool/patches/python-rrdtool-fix-SEGV.patch new file mode 100644 index 0000000..c45316d --- /dev/null +++ b/python-rrdtool/patches/python-rrdtool-fix-SEGV.patch @@ -0,0 +1,47 @@ +From a45ed2ad6f92730a479522c46febb1b56b442aba Mon Sep 17 00:00:00 2001 +From: Michael Tremer michael.tremer@ipfire.org +Date: Mon, 23 Nov 2015 02:15:33 +0000 +Subject: [PATCH] Fix crash in lastupdate() method + +When a RRD database contains floating point values, the +PyRRD_Int_FromString function returns NULL, which will then be tried +to be put into a dictionary. +This operation fails as PyDict_SetItemString does not handle NULL +and the program crashes with SEGV. + +This patch parses the value as a floating point number +and will add that instead. If the value could not be parsed, +None will be added instead. + +Signed-off-by: Michael Tremer michael.tremer@ipfire.org +--- + rrdtoolmodule.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/rrdtoolmodule.c b/rrdtoolmodule.c +index dead4ff..4713850 100644 +--- a/rrdtoolmodule.c ++++ b/rrdtoolmodule.c +@@ -880,9 +880,19 @@ _rrdtool_lastupdate(PyObject *self, PyObject *args) + PyDict_SetItemString(ret, "ds", ds_dict); + + for (i = 0; i < ds_cnt; i++) { +- PyDict_SetItemString(ds_dict, +- ds_names[i], +- PyRRD_Int_FromString(last_ds[i], NULL, 10)); ++ PyObject* val = Py_None; ++ ++ double num; ++ if (sscanf(last_ds[i], "%lf", &num) == 1) { ++ val = PyFloat_FromDouble(num); ++ } ++ ++ if (!val) ++ return NULL; ++ ++ PyDict_SetItemString(ds_dict, ds_names[i], val); ++ Py_DECREF(val); ++ + free(last_ds[i]); + free(ds_names[i]); + } diff --git a/python-rrdtool/python-rrdtool.nm b/python-rrdtool/python-rrdtool.nm index 6d3d49a..7fa8b8b 100644 --- a/python-rrdtool/python-rrdtool.nm +++ b/python-rrdtool/python-rrdtool.nm @@ -4,9 +4,8 @@ ###############################################################################
name = python-rrdtool -version = 0.1.1 -release = 2 -thisapp = rrdtool-%{version} +version = 0.1.2 +release = 1
groups = System/Libraries url = https://github.com/commx/python-rrdtool
hooks/post-receive -- IPFire 3.x development tree