From 24b193ab641f63f3f8de770a40f0c7ec30ea04b5 Mon Sep 17 00:00:00 2001 From: Alfonso Sanchez-Beato Date: Wed, 10 Aug 2016 11:54:31 +0200 Subject: [PATCH] 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 d4762637a..27b54e5d0 100644 --- a/introspection/Makefile.am +++ b/introspection/Makefile.am @@ -43,6 +43,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 \ @@ -114,7 +116,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)) @@ -154,6 +157,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 000000000..5d23bf306 --- /dev/null +++ b/introspection/nm-device-statistics.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index c7ce11002..e5b1b3d6b 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 c460caff6..35bcf93ff 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -331,6 +331,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 7381fd930..58216e175 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 000000000..b5d4197cf --- /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 + +#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 000000000..17ff19f48 --- /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 9c7319f03..72ed5e911 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) { @@ -2199,6 +2241,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 @@ -2370,6 +2417,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); @@ -11963,6 +12018,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* @@ -12111,6 +12171,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)) { @@ -12243,6 +12308,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; @@ -12405,6 +12492,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; @@ -12655,6 +12751,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 */ @@ -12725,4 +12838,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 6a4f22a5f..433448565 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 ())