report-basic, networkd: add Version, KernelTimestamp, Address metrics (#42315)
This PR adds some more useful metrics:
- io.systemd.Network.Address
- io.systemd.Basic.KernelTimestamp.{Realtime,Monotonic}
- io.systemd.Basic.Version
This commit is contained in:
@@ -7,9 +7,11 @@
|
||||
#include "manager.h"
|
||||
#include "metrics.h"
|
||||
#include "service.h"
|
||||
#include "string-util.h"
|
||||
#include "unit-def.h"
|
||||
#include "unit.h"
|
||||
#include "varlink-metrics.h"
|
||||
#include "version.h"
|
||||
|
||||
static int active_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
@@ -84,6 +86,71 @@ static int inactive_exit_timestamp_build_json(const MetricFamily *mf, sd_varlink
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int version_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
|
||||
assert(mf && mf->name);
|
||||
assert(vl);
|
||||
|
||||
return metric_build_send_string(
|
||||
mf,
|
||||
vl,
|
||||
/* object= */ NULL,
|
||||
GIT_VERSION,
|
||||
/* fields= */ NULL);
|
||||
}
|
||||
|
||||
static int boot_timestamp_build_json(
|
||||
const MetricFamily *mf,
|
||||
sd_varlink *vl,
|
||||
const dual_timestamp *t,
|
||||
bool with_monotonic) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(mf && mf->name);
|
||||
assert(vl);
|
||||
assert(t);
|
||||
|
||||
if (timestamp_is_set(t->realtime)) {
|
||||
r = metric_build_send_unsigned(
|
||||
mf, /* the .Realtime metric family entry */
|
||||
vl,
|
||||
/* object= */ NULL,
|
||||
t->realtime,
|
||||
/* fields= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (with_monotonic && timestamp_is_set(t->monotonic)) {
|
||||
assert(endswith(mf[1].name, ".Monotonic"));
|
||||
r = metric_build_send_unsigned(
|
||||
mf + 1, /* the .Monotonic sibling is the next entry */
|
||||
vl,
|
||||
/* object= */ NULL,
|
||||
t->monotonic,
|
||||
/* fields= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kernel_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
return boot_timestamp_build_json(mf, vl, &manager->timestamps[MANAGER_TIMESTAMP_KERNEL], /* with_monotonic= */ false);
|
||||
}
|
||||
|
||||
static int userspace_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
return boot_timestamp_build_json(mf, vl, &manager->timestamps[MANAGER_TIMESTAMP_USERSPACE], /* with_monotonic= */ true);
|
||||
}
|
||||
|
||||
static int finish_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
return boot_timestamp_build_json(mf, vl, &manager->timestamps[MANAGER_TIMESTAMP_FINISH], /* with_monotonic= */ true);
|
||||
}
|
||||
|
||||
static int state_change_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
Unit *unit;
|
||||
@@ -390,6 +457,19 @@ static const MetricFamily metric_family_table[] = {
|
||||
.type = METRIC_FAMILY_TYPE_GAUGE,
|
||||
.generate = active_timestamp_build_json,
|
||||
},
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "FinishTimestamp.Realtime",
|
||||
.description = "CLOCK_REALTIME microseconds at which userspace finished booting",
|
||||
.type = METRIC_FAMILY_TYPE_GAUGE,
|
||||
.generate = finish_timestamp_build_json,
|
||||
},
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "FinishTimestamp.Monotonic",
|
||||
.description = "CLOCK_MONOTONIC microseconds at which userspace finished booting",
|
||||
.type = METRIC_FAMILY_TYPE_GAUGE,
|
||||
.generate = NULL,
|
||||
},
|
||||
/* Keep those ↑ in sync with finish_timestamp_build_json(). */
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "InactiveExitTimestamp",
|
||||
.description = "Per unit metric: timestamp when the unit last exited the inactive state in microseconds; 0 indicates the transition has not occurred",
|
||||
@@ -402,6 +482,12 @@ static const MetricFamily metric_family_table[] = {
|
||||
.type = METRIC_FAMILY_TYPE_GAUGE,
|
||||
.generate = jobs_queued_build_json,
|
||||
},
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "KernelTimestamp.Realtime",
|
||||
.description = "CLOCK_REALTIME microseconds at which the kernel started (CLOCK_MONOTONIC == 0)",
|
||||
.type = METRIC_FAMILY_TYPE_GAUGE,
|
||||
.generate = kernel_timestamp_build_json,
|
||||
},
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "NRestarts",
|
||||
.description = "Per unit metric: number of restarts",
|
||||
@@ -468,6 +554,25 @@ static const MetricFamily metric_family_table[] = {
|
||||
.type = METRIC_FAMILY_TYPE_GAUGE,
|
||||
.generate = units_total_build_json,
|
||||
},
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "UserspaceTimestamp.Realtime",
|
||||
.description = "CLOCK_REALTIME microseconds at which userspace was reached",
|
||||
.type = METRIC_FAMILY_TYPE_GAUGE,
|
||||
.generate = userspace_timestamp_build_json,
|
||||
},
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "UserspaceTimestamp.Monotonic",
|
||||
.description = "CLOCK_MONOTONIC microseconds at which userspace was reached",
|
||||
.type = METRIC_FAMILY_TYPE_GAUGE,
|
||||
.generate = NULL,
|
||||
},
|
||||
/* Keep those ↑ in sync with userspace_timestamp_build_json(). */
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "Version",
|
||||
.description = "Version of systemd",
|
||||
.type = METRIC_FAMILY_TYPE_STRING,
|
||||
.generate = version_build_json,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <net/if.h>
|
||||
|
||||
#include "sd-json.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "af-list.h"
|
||||
#include "alloc-util.h"
|
||||
#include "argv-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "metrics.h"
|
||||
#include "network-util.h"
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-route-util.h"
|
||||
#include "networkd-varlink-metrics.h"
|
||||
#include "set.h"
|
||||
|
||||
#define METRIC_IO_SYSTEMD_NETWORK_PREFIX "io.systemd.Network."
|
||||
|
||||
@@ -65,6 +72,52 @@ static const char* link_get_oper_state(const Link *l) {
|
||||
return link_operstate_to_string(ASSERT_PTR(l)->operstate);
|
||||
}
|
||||
|
||||
static int link_addresses_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
Link *link;
|
||||
int r;
|
||||
|
||||
assert(mf && mf->name);
|
||||
assert(vl);
|
||||
|
||||
HASHMAP_FOREACH(link, manager->links_by_index) {
|
||||
Address *a;
|
||||
|
||||
SET_FOREACH(a, link->addresses) {
|
||||
if (!address_is_ready(a))
|
||||
continue;
|
||||
|
||||
/* Remove localhost address (127.0.0.1 and ::1) */
|
||||
if (link->flags & IFF_LOOPBACK && in_addr_is_localhost_one(a->family, &a->in_addr) > 0)
|
||||
continue;
|
||||
|
||||
_cleanup_free_ char *scope = NULL;
|
||||
r = route_scope_to_string_alloc(a->scope, &scope);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fields = NULL;
|
||||
r = sd_json_buildo(
|
||||
&fields,
|
||||
SD_JSON_BUILD_PAIR_STRING("family", af_to_ipv4_ipv6(a->family)),
|
||||
SD_JSON_BUILD_PAIR_STRING("scope", scope));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = metric_build_send_string(
|
||||
mf,
|
||||
vl,
|
||||
link->ifname,
|
||||
IN_ADDR_PREFIX_TO_STRING(a->family, &a->in_addr, a->prefixlen),
|
||||
fields);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int link_address_state_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) {
|
||||
return link_metric_build_json(mf, vl, link_get_address_state, userdata);
|
||||
}
|
||||
@@ -159,6 +212,12 @@ static int required_for_online_build_json(const MetricFamily *mf, sd_varlink *vl
|
||||
|
||||
/* Keep metrics ordered alphabetically */
|
||||
static const MetricFamily network_metric_family_table[] = {
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_NETWORK_PREFIX "Address",
|
||||
.description = "Per interface metric: configured IP address in CIDR notation",
|
||||
.type = METRIC_FAMILY_TYPE_STRING,
|
||||
.generate = link_addresses_build_json,
|
||||
},
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_NETWORK_PREFIX "AddressState",
|
||||
.description = "Per interface metric: address state",
|
||||
|
||||
@@ -50,6 +50,17 @@ varlinkctl list-methods /run/systemd/report/io.systemd.Network
|
||||
varlinkctl --more call /run/systemd/report/io.systemd.Network io.systemd.Metrics.List {}
|
||||
varlinkctl --more call /run/systemd/report/io.systemd.Network io.systemd.Metrics.Describe {}
|
||||
|
||||
varlinkctl --more --json=short call /run/systemd/report/io.systemd.Network io.systemd.Metrics.Describe {} | grep '"name":"io.systemd.Network.Address"' >/dev/null
|
||||
# we do not send "lo"
|
||||
net_metrics="$(varlinkctl call --more --json=short /run/systemd/report/io.systemd.Network io.systemd.Metrics.List {})"
|
||||
(! echo "$net_metrics" | grep '"name":"io.systemd.Network.Address"' | grep '"object":"lo"' >/dev/null)
|
||||
# add a scratch address and check that it shows up.
|
||||
ip address add 192.0.2.1/32 dev lo
|
||||
timeout 30 bash -c 'until varlinkctl call --more --json=short /run/systemd/report/io.systemd.Network io.systemd.Metrics.List {} | grep -F "192.0.2.1/32" >/dev/null; do sleep .5; done'
|
||||
net_metrics="$(varlinkctl call --more --json=short /run/systemd/report/io.systemd.Network io.systemd.Metrics.List {})"
|
||||
echo "$net_metrics" | grep '"name":"io.systemd.Network.Address"' | grep '"object":"lo"' | grep '"value":"192.0.2.1/32"' | grep '"family":"ipv4"' >/dev/null
|
||||
ip address del 192.0.2.1/32 dev lo
|
||||
|
||||
# test io.systemd.Basic Metrics
|
||||
# ensure the socket is running, as some distros don't enable it by default
|
||||
systemctl start systemd-report-basic.socket
|
||||
@@ -80,6 +91,24 @@ swap_reported="$(basic_value io.systemd.Basic.SwapBytes)"
|
||||
swap_expected=$(( $(awk '/^SwapTotal:/ { print $2; found=1 } END { if (!found) print 0 }' /proc/meminfo) * 1024 ))
|
||||
[ "$swap_reported" = "$swap_expected" ]
|
||||
|
||||
# io.systemd.Manager.Version should be non-empty and match what `systemctl --version` reports
|
||||
metrics_version="$(varlinkctl call --more /run/systemd/report/io.systemd.Manager io.systemd.Metrics.List {} | jq --seq -r 'select(.name == "io.systemd.Manager.Version") | .value')"
|
||||
[ -n "$metrics_version" ]
|
||||
systemctl --version | grep -F "($metrics_version)" >/dev/null
|
||||
|
||||
# Boot timeline timestamps. The kernel CLOCK_MONOTONIC is 0 by definition, so only its realtime is
|
||||
# reported; userspace reports both realtime and monotonic. We don't check FinishTimestamp here:
|
||||
# the test runs as a service, so manager_check_finished() typically hasn't fired yet and the
|
||||
# timestamp is unset (the metric is then suppressed). Likewise KernelTimestamp is cleared when
|
||||
# systemd runs inside a container (see main.c), so the metric is suppressed there too.
|
||||
manager_metrics="$(varlinkctl call --more /run/systemd/report/io.systemd.Manager io.systemd.Metrics.List {})"
|
||||
metric_value() { echo "$manager_metrics" | jq --seq -r "select(.name == \"io.systemd.Manager.$1\") | .value | tostring"; }
|
||||
if ! systemd-detect-virt --quiet --container; then
|
||||
[ "$(metric_value KernelTimestamp.Realtime)" -gt 0 ]
|
||||
fi
|
||||
[ "$(metric_value UserspaceTimestamp.Realtime)" -gt 0 ]
|
||||
[ "$(metric_value UserspaceTimestamp.Monotonic)" -gt 0 ]
|
||||
|
||||
# test io.systemd.Basic.MachineInfo.* metrics, sourced from /etc/machine-info
|
||||
if [ -e /etc/machine-info ]; then
|
||||
MACHINE_INFO_BACKUP="$(mktemp)"
|
||||
|
||||
Reference in New Issue
Block a user