diff --git a/apt_updates/tasks/main.yml b/apt_updates/tasks/main.yml index 7611b1a..3053b2a 100644 --- a/apt_updates/tasks/main.yml +++ b/apt_updates/tasks/main.yml @@ -11,6 +11,7 @@ src: apt-updates-metrics.j2 dest: /usr/local/sbin/apt-updates-metrics mode: "0755" + register: apt_updates_script - name: Create the apt timers' drop-in directories ansible.builtin.file: @@ -39,3 +40,41 @@ when: apt_updates_dropins.changed # Check mode does not write the drop-ins. ignore_errors: "{{ ansible_check_mode }}" + +# Debian's prometheus-node-exporter-apt collector writes apt.prom with the same metric families as this role under +# other HELP texts; node_exporter drops the rows of both files then. This role owns the apt metrics, so the +# collector stays off and its file, also a stale leftover of a removed collector, goes away. +- name: Look for the Debian apt metrics collector + ansible.builtin.stat: + path: /lib/systemd/system/prometheus-node-exporter-apt.timer + register: apt_updates_debian_collector + +- name: Stop and disable the Debian apt metrics collector timer + ansible.builtin.systemd: + name: prometheus-node-exporter-apt.timer + enabled: false + state: stopped + when: apt_updates_debian_collector.stat.exists + +- name: Stop the Debian apt metrics collector service + ansible.builtin.systemd: + name: prometheus-node-exporter-apt.service + state: stopped + when: apt_updates_debian_collector.stat.exists + +- name: Remove the Debian apt metrics collector output + ansible.builtin.file: + path: "{{ apt_updates_textfile_dir }}/apt.prom" + state: absent + +# The timers' drop-ins write the metrics after the next apt run; this gives a new host and a changed script +# current metrics at once. +- name: Look for the apt metrics file + ansible.builtin.stat: + path: "{{ apt_updates_textfile_dir }}/apt_updates.prom" + register: apt_updates_prom + +- name: Write the apt metrics + ansible.builtin.command: /usr/local/sbin/apt-updates-metrics + when: apt_updates_script.changed or not apt_updates_prom.stat.exists + changed_when: true diff --git a/apt_updates/templates/apt-updates-metrics.j2 b/apt_updates/templates/apt-updates-metrics.j2 index 1c686c1..373b9cd 100644 --- a/apt_updates/templates/apt-updates-metrics.j2 +++ b/apt_updates/templates/apt-updates-metrics.j2 @@ -9,11 +9,14 @@ out=$textfile_dir/apt_updates.prom tmp=$(mktemp "$textfile_dir/.apt_updates.XXXXXX") trap 'rm -f "$tmp"' EXIT -arch=$(dpkg --print-architecture) +# Architectures a pending package can have: the native and foreign ones, plus "all" for architecture-independent +# packages. Each gets a row per kind even at 0, so a resolved backlog reads as 0 and not as a vanished series. +archs="$(dpkg --print-architecture) $(dpkg --print-foreign-architectures | tr '\n' ' ') all" -# The simulated upgrade lists "Inst [] ( [])" per package. -# An origin containing "security" counts as security, everything else as regular. -pending=$(apt-get -s upgrade | awk -v host="$host" -v arch="$arch" ' +# The simulated upgrade lists "Inst [] ( [])" per package. A version in +# several suites lists them all, comma separated. A package counts as security when any origin contains "security", +# everything else as regular. +pending=$(apt-get -s upgrade | awk -v host="$host" -v archs="$archs" ' /^Inst / { line = $0 sub(/^[^(]*\(/, "", line) @@ -21,13 +24,16 @@ pending=$(apt-get -s upgrade | awk -v host="$host" -v arch="$arch" ' n = split(line, f, " ") pkg_arch = f[n] gsub(/\[|\]/, "", pkg_arch) - origin = (n >= 3) ? f[2] : "" - kind = (tolower(origin) ~ /security/) ? "security" : "regular" + sub(/^[^ ]+ /, "", line) + kind = (tolower(line) ~ /security/) ? "security" : "regular" count[kind, pkg_arch]++ } END { - if (!(("security", arch) in count)) count["security", arch] = 0 - if (!(("regular", arch) in count)) count["regular", arch] = 0 + split(archs, a, " ") + for (i in a) { + if (!(("security", a[i]) in count)) count["security", a[i]] = 0 + if (!(("regular", a[i]) in count)) count["regular", a[i]] = 0 + } for (key in count) { split(key, k, SUBSEP) printf "apt_upgrades_pending{host=\"%s\",origin=\"%s\",arch=\"%s\"} %d\n", host, k[1], k[2], count[key] @@ -41,11 +47,15 @@ if [[ -e /run/reboot-required ]]; then reboot=1 fi -# Debian writes update-stamp, Ubuntu update-success-stamp; both are touched by a successful update. +# Debian writes update-stamp, Ubuntu update-success-stamp; both are touched by a successful update. Both are missing +# until apt.systemd.daily has run an update, then the package cache stands in. stamp=/var/lib/apt/periodic/update-success-stamp if [[ ! -e $stamp ]]; then stamp=/var/lib/apt/periodic/update-stamp fi +if [[ ! -e $stamp ]]; then + stamp=/var/cache/apt/pkgcache.bin +fi cache=$(stat -c %Y "$stamp") {