report-basic: expose os-release fields as metrics

Add io.systemd.Basic.OSRelease metric families that reports select fields
from os-release.

$ cat /etc/os-release
NAME="Fedora Linux"
VERSION="45 (Cloud Edition Prerelease)"
RELEASE_TYPE=development
ID=fedora
VERSION_ID=45
VERSION_CODENAME=""
PRETTY_NAME="Fedora Linux 45 (Cloud Edition Prerelease)"
ANSI_COLOR="0;38;2;60;110;180"
LOGO=fedora-logo-icon
CPE_NAME="cpe:/o:fedoraproject:fedora:45"
HOME_URL="https://fedoraproject.org/"
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/rawhide/"
SUPPORT_URL="https://ask.fedoraproject.org/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Fedora"
REDHAT_BUGZILLA_PRODUCT_VERSION=rawhide
REDHAT_SUPPORT_PRODUCT="Fedora"
REDHAT_SUPPORT_PRODUCT_VERSION=rawhide
SUPPORT_END=2027-11-24
VARIANT="Cloud Edition"
VARIANT_ID=cloud

$ varlinkctl call --more ./build/systemd-report-basic io.systemd.Metrics.List {} | jq --seq -c
...
{"name":"io.systemd.Basic.OSRelease.NAME","value":"Fedora Linux 44 (Workstation Edition)"}
{"name":"io.systemd.Basic.OSRelease.ID","value":"fedora"}
{"name":"io.systemd.Basic.OSRelease.CPE_NAME","value":"cpe:/o:fedoraproject:fedora:44"}
{"name":"io.systemd.Basic.OSRelease.VARIANT_ID","value":"workstation"}
{"name":"io.systemd.Basic.OSRelease.VERSION_ID","value":"44"}
{"name":"io.systemd.Basic.OSRelease.SUPPORT_END","value":"2027-05-19"}

I picked the fields that contain useful information about the specific
version/image/variant/experiment/flavour of the system. Also, either
NAME or PRETTY_NAME is included. This one is intended for human readers
to be able to identify the OS version easily.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-05-12 22:56:06 +02:00
parent 5cba9704ad
commit e27c382bf0
2 changed files with 97 additions and 0 deletions
+93
View File
@@ -9,7 +9,9 @@
#include "alloc-util.h"
#include "architecture.h"
#include "hostname-setup.h"
#include "log.h"
#include "metrics.h"
#include "os-util.h"
#include "report-basic.h"
#include "virt.h"
@@ -98,6 +100,71 @@ static int machine_id_generate(const MetricFamily *mf, sd_varlink *link, void *u
/* fields= */ NULL);
}
enum {
FIELD_PRETTY_NAME,
FIELD_NAME,
FIELD_ID,
FIELD_CPE_NAME,
FIELD_VARIANT_ID,
FIELD_VERSION_ID,
FIELD_BUILD_ID,
FIELD_IMAGE_VERSION,
FIELD_IMAGE_ID,
FIELD_SUPPORT_END,
FIELD_EXPERIMENT,
FIELD_SYSEXT_LEVEL,
FIELD_CONFEXT_LEVEL,
_FIELD_MAX,
};
static int os_release_generate(const MetricFamily mf[static _FIELD_MAX - 1], sd_varlink *link, void *userdata) {
char* values[_FIELD_MAX] = {};
CLEANUP_ELEMENTS(values, free_many_charp);
int r;
assert(mf && mf->name);
assert(link);
r = parse_os_release(NULL,
"PRETTY_NAME", &values[FIELD_PRETTY_NAME],
"NAME", &values[FIELD_NAME],
"ID", &values[FIELD_ID],
"CPE_NAME", &values[FIELD_CPE_NAME],
"VARIANT_ID", &values[FIELD_VARIANT_ID],
"VERSION_ID", &values[FIELD_VERSION_ID],
"BUILD_ID", &values[FIELD_BUILD_ID],
"IMAGE_VERSION", &values[FIELD_IMAGE_VERSION],
"IMAGE_ID", &values[FIELD_IMAGE_ID],
"SUPPORT_END", &values[FIELD_SUPPORT_END],
"EXPERIMENT", &values[FIELD_EXPERIMENT],
"SYSEXT_LEVEL", &values[FIELD_SYSEXT_LEVEL],
"CONFEXT_LEVEL", &values[FIELD_CONFEXT_LEVEL]);
if (r < 0) {
log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
"Failed to read os-release file, ignoring: %m");
return 0;
}
for (size_t i = 1; i < _FIELD_MAX; i++) {
const char *v = values[i];
if (i == FIELD_NAME && values[FIELD_PRETTY_NAME])
v = values[FIELD_PRETTY_NAME]; /* Prefer PRETTY_NAME to NAME */
if (v) {
r = metric_build_send_string(
mf + i - 1,
link,
/* object= */ NULL,
v,
/* fields= */ NULL);
if (r < 0)
return r;
}
}
return 0;
}
static int virtualization_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) {
assert(mf && mf->name);
assert(link);
@@ -114,6 +181,14 @@ static int virtualization_generate(const MetricFamily *mf, sd_varlink *link, voi
/* fields= */ NULL);
}
#define OS_RELEASE_STANDARD_FIELD(name) \
{ \
METRIC_IO_SYSTEMD_BASIC_PREFIX "OSRelease." name, \
"Operating system identification (" name "= field from os-release)", \
METRIC_FAMILY_TYPE_STRING, \
.generate = NULL, \
}
static const MetricFamily metric_family_table[] = {
/* Keep entries ordered alphabetically */
{
@@ -146,6 +221,24 @@ static const MetricFamily metric_family_table[] = {
METRIC_FAMILY_TYPE_STRING,
.generate = machine_id_generate,
},
{
METRIC_IO_SYSTEMD_BASIC_PREFIX "OSRelease.NAME",
"Operating system human-readable name (PRETTY_NAME= or NAME= field from os-release)",
METRIC_FAMILY_TYPE_STRING,
.generate = os_release_generate,
},
OS_RELEASE_STANDARD_FIELD("ID"),
OS_RELEASE_STANDARD_FIELD("CPE_NAME"),
OS_RELEASE_STANDARD_FIELD("VARIANT_ID"),
OS_RELEASE_STANDARD_FIELD("VERSION_ID"),
OS_RELEASE_STANDARD_FIELD("BUILD_ID"),
OS_RELEASE_STANDARD_FIELD("IMAGE_VERSION"),
OS_RELEASE_STANDARD_FIELD("IMAGE_ID"),
OS_RELEASE_STANDARD_FIELD("SUPPORT_END"),
OS_RELEASE_STANDARD_FIELD("EXPERIMENT"),
OS_RELEASE_STANDARD_FIELD("SYSEXT_LEVEL"),
OS_RELEASE_STANDARD_FIELD("CONFEXT_LEVEL"),
/* Keep those ↑ in sync with os_release_generate(). */
{
METRIC_IO_SYSTEMD_BASIC_PREFIX "Virtualization",
"Virtualization type",
+4
View File
@@ -57,6 +57,10 @@ varlinkctl list-methods /run/systemd/report/io.systemd.Basic
varlinkctl --more call /run/systemd/report/io.systemd.Basic io.systemd.Metrics.List {}
varlinkctl --more call /run/systemd/report/io.systemd.Basic io.systemd.Metrics.Describe {}
id1="$(varlinkctl call --more /run/systemd/report/io.systemd.Basic io.systemd.Metrics.List {} | jq --seq -r 'select(.name == "io.systemd.Basic.OSRelease.ID") | .value')"
id2="$(. /etc/os-release; echo "$ID")"
[ "$id1" = "$id2" ]
# Test HTTP upload (plain http)
FAKE_SERVER=/usr/lib/systemd/tests/integration-tests/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/fake-report-server.py
CERTDIR=$(mktemp -d)