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.
This commit is contained in:
Alfonso Sanchez-Beato 2016-08-10 11:54:31 +02:00 committed by Thomas Haller
parent 85834a6675
commit 24b193ab64
9 changed files with 299 additions and 1 deletions

View file

@ -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 \

View file

@ -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>

View file

@ -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"

View file

@ -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 \

View file

@ -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; \
{ \

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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);
}

View file

@ -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 ())