[PATCH 3/5] device: add statistics interface



Add statistics interface to all device instances. When active, the
properties of this interface are refreshed whenever there is network
activity for the device.

Activation is performed by changing RefreshRateMs property. If set to
zero, the interface is deactivated. If set to other value, the rest of
the interface properties are refreshed whenever the related network
metric changes, being RefreshRateMs the minimum time between property
changes, in milliseconds.
---
 introspection/Makefile.am              |   6 +-
 introspection/nm-device-statistics.xml |  37 +++++++++++
 libnm-core/nm-dbus-interface.h         |   1 +
 src/Makefile.am                        |   2 +
 src/devices/nm-device-private.h        |   3 +
 src/devices/nm-device-statistics.c     |  99 ++++++++++++++++++++++++++++
 src/devices/nm-device-statistics.h     |  31 +++++++++
 src/devices/nm-device.c                | 117 +++++++++++++++++++++++++++++++++
 src/devices/nm-device.h                |   4 ++
 9 files changed, 299 insertions(+), 1 deletion(-)
 create mode 100644 introspection/nm-device-statistics.xml
 create mode 100644 src/devices/nm-device-statistics.c
 create mode 100644 src/devices/nm-device-statistics.h

diff --git a/introspection/Makefile.am b/introspection/Makefile.am
index 3a62793..f72703d 100644
--- a/introspection/Makefile.am
+++ b/introspection/Makefile.am
@@ -41,6 +41,8 @@ nodist_libnmdbus_la_SOURCES = \
        nmdbus-device-modem.h \
        nmdbus-device-olpc-mesh.c \
        nmdbus-device-olpc-mesh.h \
+       nmdbus-device-statistics.c \
+       nmdbus-device-statistics.h \
        nmdbus-device-team.c \
        nmdbus-device-team.h \
        nmdbus-device-tun.c \
@@ -111,7 +113,8 @@ DBUS_INTERFACE_DOCS = \
        nmdbus-device-veth-org.freedesktop.NetworkManager.Device.Veth.xml \
        nmdbus-settings-org.freedesktop.NetworkManager.Settings.xml \
        nmdbus-device-ethernet-org.freedesktop.NetworkManager.Device.Wired.xml \
-       nmdbus-ip4-config-org.freedesktop.NetworkManager.IP4Config.xml
+       nmdbus-ip4-config-org.freedesktop.NetworkManager.IP4Config.xml \
+       nmdbus-device-statistics-org.freedesktop.NetworkManager.Device.Statistics.xml
 
 define _make_nmdbus_rule
 $(1): $(patsubst nmdbus-%.c,nm-%.xml,$(1))
@@ -150,6 +153,7 @@ EXTRA_DIST = \
        nm-device-macvlan.xml \
        nm-device-modem.xml \
        nm-device-olpc-mesh.xml \
+       nm-device-statistics.xml \
        nm-device-team.xml \
        nm-device-tun.xml \
        nm-device-veth.xml \
diff --git a/introspection/nm-device-statistics.xml b/introspection/nm-device-statistics.xml
new file mode 100644
index 0000000..5d23bf3
--- /dev/null
+++ b/introspection/nm-device-statistics.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/">
+  <interface name="org.freedesktop.NetworkManager.Device.Statistics">
+
+    <!--
+        RefreshRateMs:
+
+        Rate of change of the rest of properties of this interface. If zero, the
+        properties do not change. Otherwise, the properties are refreshed each
+        RefreshRateMs milliseconds in case the underlying counter has changed
+        too.
+    -->
+    <property name="RefreshRateMs" type="u" access="readwrite"/>
+
+    <!--
+        TxBytes:
+
+        Number of transmitted bytes
+    -->
+    <property name="TxBytes" type="t" access="read"/>
+
+    <!--
+        RxBytes:
+
+        Number of received bytes
+    -->
+    <property name="RxBytes" type="t" access="read"/>
+
+    <!--
+        PropertiesChanged:
+        @properties: A dictionary mapping property names to variant boxed values
+    -->
+    <signal name="PropertiesChanged">
+        <arg name="properties" type="a{sv}"/>
+    </signal>
+  </interface>
+</node>
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h
index 1e0fbe6..20eccb8 100644
--- a/libnm-core/nm-dbus-interface.h
+++ b/libnm-core/nm-dbus-interface.h
@@ -68,6 +68,7 @@
 #define NM_DBUS_INTERFACE_DEVICE_VXLAN      NM_DBUS_INTERFACE_DEVICE ".Vxlan"
 #define NM_DBUS_INTERFACE_DEVICE_GRE        NM_DBUS_INTERFACE_DEVICE ".Gre"
 #define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL  NM_DBUS_INTERFACE_DEVICE ".IPTunnel"
+#define NM_DBUS_INTERFACE_DEVICE_STATISTICS NM_DBUS_INTERFACE_DEVICE ".Statistics"
 
 #define NM_DBUS_INTERFACE_SETTINGS        "org.freedesktop.NetworkManager.Settings"
 #define NM_DBUS_PATH_SETTINGS             "/org/freedesktop/NetworkManager/Settings"
diff --git a/src/Makefile.am b/src/Makefile.am
index 04b8b69..7deee32 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -289,6 +289,8 @@ libNetworkManager_la_SOURCES = \
        devices/nm-device-generic.h \
        devices/nm-device-logging.h \
        devices/nm-device-private.h \
+       devices/nm-device-statistics.c \
+       devices/nm-device-statistics.h \
        \
        dhcp-manager/nm-dhcp-client.c \
        dhcp-manager/nm-dhcp-client.h \
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index 7381fd9..58216e1 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -114,6 +114,9 @@ void nm_device_ip_method_failed (NMDevice *self, int family, NMDeviceStateReason
 
 gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value);
 
+void nm_device_set_tx_bytes (NMDevice *self, guint64 tx_bytes);
+void nm_device_set_rx_bytes (NMDevice *self, guint64 rx_bytes);
+
 #define NM_DEVICE_CLASS_DECLARE_TYPES(klass, conn_type, ...) \
        NM_DEVICE_CLASS (klass)->connection_type = conn_type; \
        { \
diff --git a/src/devices/nm-device-statistics.c b/src/devices/nm-device-statistics.c
new file mode 100644
index 0000000..b5d4197
--- /dev/null
+++ b/src/devices/nm-device-statistics.c
@@ -0,0 +1,99 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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 2, 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016 Canonical Ltd
+ *
+ */
+
+#include "nm-default.h"
+
+#include <inttypes.h>
+
+#include "nm-device-statistics.h"
+#include "nm-device-private.h"
+#include "nm-utils.h"
+#include "nm-platform.h"
+
+#define _NMLOG_DOMAIN        LOGD_DEVICE
+#define _NMLOG(level, ...) \
+    nm_log_obj ((level), _NMLOG_DOMAIN, (self->device), "device-stats", \
+                "(%s): " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                nm_device_get_iface (self->device) ?: "(none)" \
+                _NM_UTILS_MACRO_REST(__VA_ARGS__))
+
+struct _NMDeviceStatistics {
+       NMDevice *device;
+       guint stats_update_id;
+};
+
+static gboolean
+update_stats (gpointer user_data)
+{
+       NMDeviceStatistics *self = user_data;
+       guint64 rx_packets;
+       guint64 rx_bytes;
+       guint64 tx_packets;
+       guint64 tx_bytes;
+       int ifindex;
+
+       ifindex = nm_device_get_ip_ifindex (self->device);
+
+       if (nm_platform_link_get_stats (NM_PLATFORM_GET, ifindex,
+                                           &rx_packets, &rx_bytes,
+                                           &tx_packets, &tx_bytes)) {
+               _LOGT ("{RX} %"PRIu64" packets %"PRIu64" bytes {TX} %"PRIu64" packets %"PRIu64" bytes",
+                      rx_packets, rx_bytes, tx_packets, tx_bytes);
+
+               nm_device_set_tx_bytes (self->device, tx_bytes);
+               nm_device_set_rx_bytes (self->device, rx_bytes);
+       } else {
+               _LOGE ("error no stats available");
+       }
+
+       /* Keep polling */
+       nm_platform_link_refresh (NM_PLATFORM_GET, ifindex);
+
+       return TRUE;
+}
+
+/********************************************/
+
+NMDeviceStatistics *
+nm_device_statistics_new (NMDevice *device, unsigned rate_ms)
+{
+       NMDeviceStatistics *self;
+
+       self = g_malloc0 (sizeof (*self));
+       self->device = device;
+
+       self->stats_update_id = g_timeout_add (rate_ms, update_stats, self);
+
+       return self;
+}
+
+void
+nm_device_statistics_unref (NMDeviceStatistics *self)
+{
+       g_source_remove (self->stats_update_id);
+       g_free (self);
+}
+
+void
+nm_device_statistics_change_rate (NMDeviceStatistics *self, unsigned rate_ms)
+{
+       g_source_remove (self->stats_update_id);
+
+       self->stats_update_id = g_timeout_add (rate_ms, update_stats, self);
+}
diff --git a/src/devices/nm-device-statistics.h b/src/devices/nm-device-statistics.h
new file mode 100644
index 0000000..17ff19f
--- /dev/null
+++ b/src/devices/nm-device-statistics.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* 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 2, 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016 Canonical Ltd
+ */
+
+#ifndef __NETWORKMANAGER_DEVICE_STATISTICS_H__
+#define __NETWORKMANAGER_DEVICE_STATISTICS_H__
+
+typedef struct _NMDeviceStatistics NMDeviceStatistics;
+
+NMDeviceStatistics *
+nm_device_statistics_new (NMDevice *device, unsigned rate_ms);
+
+void nm_device_statistics_unref (NMDeviceStatistics *self);
+
+void nm_device_statistics_change_rate (NMDeviceStatistics *self, unsigned rate_ms);
+
+#endif /* __NETWORKMANAGER_DEVICE_STATISTICS_H__ */
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 177080d..c8370d7 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -66,11 +66,13 @@
 #include "sd-ipv4ll.h"
 #include "nm-audit-manager.h"
 #include "nm-arping-manager.h"
+#include "nm-device-statistics.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF (NMDevice);
 
 #include "nmdbus-device.h"
+#include "nmdbus-device-statistics.h"
 
 G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_EXPORTED_OBJECT)
 
@@ -138,6 +140,9 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice,
        PROP_LLDP_NEIGHBORS,
        PROP_REAL,
        PROP_SLAVES,
+       PROP_REFRESH_RATE_MS,
+       PROP_TX_BYTES,
+       PROP_RX_BYTES,
 );
 
 #define DEFAULT_AUTOCONNECT TRUE
@@ -407,6 +412,13 @@ typedef struct _NMDevicePrivate {
        NMLldpListener *lldp_listener;
 
        guint check_delete_unrealized_id;
+
+       guint refresh_rate_ms;
+       guint64 tx_bytes;
+       guint64 rx_bytes;
+
+       NMDeviceStatistics *statistics;
+
 } NMDevicePrivate;
 
 static gboolean nm_device_set_ip4_config (NMDevice *self,
@@ -769,6 +781,36 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
        g_free (old_ip_iface);
 }
 
+void
+nm_device_set_tx_bytes (NMDevice *self, guint64 tx_bytes)
+{
+       NMDevicePrivate *priv;
+
+       g_return_if_fail (NM_IS_DEVICE (self));
+
+       priv = NM_DEVICE_GET_PRIVATE (self);
+       if (tx_bytes == priv->tx_bytes)
+               return;
+
+       priv->tx_bytes = tx_bytes;
+       _notify (self, PROP_TX_BYTES);
+}
+
+void
+nm_device_set_rx_bytes (NMDevice *self, guint64 rx_bytes)
+{
+       NMDevicePrivate *priv;
+
+       g_return_if_fail (NM_IS_DEVICE (self));
+
+       priv = NM_DEVICE_GET_PRIVATE (self);
+       if (rx_bytes == priv->rx_bytes)
+               return;
+
+       priv->rx_bytes = rx_bytes;
+       _notify (self, PROP_RX_BYTES);
+}
+
 static gboolean
 get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid)
 {
@@ -2193,6 +2235,11 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
                priv->carrier = TRUE;
        }
 
+       if (priv->refresh_rate_ms && !priv->statistics) {
+               priv->statistics = nm_device_statistics_new (self,
+                                                                                                        
priv->refresh_rate_ms);
+       }
+
        klass->realize_start_notify (self, plink);
 
        /* Do not manage externally created software devices until they are IFF_UP
@@ -2364,6 +2411,14 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
                g_clear_pointer (&priv->physical_port_id, g_free);
                _notify (self, PROP_PHYSICAL_PORT_ID);
        }
+       if (priv->statistics) {
+               nm_device_statistics_unref (priv->statistics);
+               priv->statistics = NULL;
+               priv->tx_bytes = 0;
+               priv->tx_bytes = 0;
+               _notify (self, PROP_TX_BYTES);
+               _notify (self, PROP_RX_BYTES);
+       }
 
        priv->hw_addr_type = HW_ADDR_TYPE_UNSET;
        g_clear_pointer (&priv->hw_addr_perm, g_free);
@@ -11957,6 +12012,11 @@ nm_device_init (NMDevice *self)
 
        priv->v4_commit_first_time = TRUE;
        priv->v6_commit_first_time = TRUE;
+
+       priv->refresh_rate_ms = 0;
+       priv->tx_bytes = 0;
+       priv->rx_bytes = 0;
+       priv->statistics = NULL;
 }
 
 static GObject*
@@ -12105,6 +12165,11 @@ dispose (GObject *object)
                g_clear_object (&priv->lldp_listener);
        }
 
+       if (priv->statistics) {
+               nm_device_statistics_unref (priv->statistics);
+               priv->statistics = NULL;
+       }
+
        G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
 
        if (nm_clear_g_source (&priv->queued_state.id)) {
@@ -12237,6 +12302,28 @@ set_property (GObject *object, guint prop_id,
                /* construct only */
                priv->hw_addr_perm = g_value_dup_string (value);
                break;
+       case PROP_REFRESH_RATE_MS: {
+               guint refresh_rate_ms;
+
+               refresh_rate_ms = g_value_get_uint (value);
+               if (priv->refresh_rate_ms == refresh_rate_ms)
+                       break;
+
+               priv->refresh_rate_ms = refresh_rate_ms;
+               _LOGI (LOGD_DEVICE, "statistics refresh rate set to %u ms", priv->refresh_rate_ms);
+
+               if (priv->refresh_rate_ms) {
+                       if (priv->statistics)
+                               nm_device_statistics_change_rate (priv->statistics, priv->refresh_rate_ms);
+                       else
+                               priv->statistics =
+                                   nm_device_statistics_new (self, priv->refresh_rate_ms);
+               } else {
+                       nm_device_statistics_unref (priv->statistics);
+                       priv->statistics = NULL;
+               }
+               break;
+       }
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
@@ -12399,6 +12486,15 @@ get_property (GObject *object, guint prop_id,
                g_value_take_boxed (value, slave_list);
                break;
        }
+       case PROP_REFRESH_RATE_MS:
+               g_value_set_uint (value, priv->refresh_rate_ms);
+               break;
+       case PROP_TX_BYTES:
+               g_value_set_uint64 (value, priv->tx_bytes);
+               break;
+       case PROP_RX_BYTES:
+               g_value_set_uint64 (value, priv->rx_bytes);
+               break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
@@ -12649,6 +12745,23 @@ nm_device_class_init (NMDeviceClass *klass)
                                G_PARAM_READABLE |
                                G_PARAM_STATIC_STRINGS);
 
+       /* Statistics */
+       obj_properties[PROP_REFRESH_RATE_MS] =
+           g_param_spec_uint (NM_DEVICE_STATISTICS_REFRESH_RATE_MS, "", "",
+                              0, UINT32_MAX, 0,
+                              G_PARAM_READWRITE |
+                              G_PARAM_STATIC_STRINGS);
+       obj_properties[PROP_TX_BYTES] =
+           g_param_spec_uint64 (NM_DEVICE_STATISTICS_TX_BYTES, "", "",
+                                0, UINT64_MAX, 0,
+                                G_PARAM_READABLE |
+                                G_PARAM_STATIC_STRINGS);
+       obj_properties[PROP_RX_BYTES] =
+           g_param_spec_uint64 (NM_DEVICE_STATISTICS_RX_BYTES, "", "",
+                                0, UINT64_MAX, 0,
+                                G_PARAM_READABLE |
+                                G_PARAM_STATIC_STRINGS);
+
        g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
        /* Signals */
@@ -12719,4 +12832,8 @@ nm_device_class_init (NMDeviceClass *klass)
                                                "Disconnect", impl_device_disconnect,
                                                "Delete", impl_device_delete,
                                                NULL);
+
+       nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+                                               NMDBUS_TYPE_DEVICE_STATISTICS_SKELETON,
+                                               NULL);
 }
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 6a4f22a..4334485 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -88,6 +88,10 @@
 #define NM_DEVICE_STATE_CHANGED         "state-changed"
 #define NM_DEVICE_LINK_INITIALIZED      "link-initialized"
 
+#define NM_DEVICE_STATISTICS_REFRESH_RATE_MS "refresh-rate-ms"
+#define NM_DEVICE_STATISTICS_TX_BYTES        "tx-bytes"
+#define NM_DEVICE_STATISTICS_RX_BYTES        "rx-bytes"
+
 G_BEGIN_DECLS
 
 #define NM_TYPE_DEVICE            (nm_device_get_type ())
-- 
2.7.4



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]