diff --git a/all/files/vnstat-metrics.cgi b/all/files/vnstat-metrics.cgi new file mode 100644 index 0000000..7ca48fd --- /dev/null +++ b/all/files/vnstat-metrics.cgi @@ -0,0 +1,118 @@ +#!/usr/bin/perl -w + +# vnstat-metrics.cgi -- Prometheus compatible metrics endpoint output from vnStat data +# copyright (c) 2022 Teemu Toivola +# released under the GNU General Public License + +use strict; +use JSON::PP; + +# location of vnstat binary +my $vnstat_cmd = '/usr/bin/vnstat'; + + +################ + + +sub get_interface_alias +{ + my ($interface) = @_; + my $interface_alias = $interface->{'alias'}; + if (length($interface_alias) == 0) { + $interface_alias = $interface->{'name'}; + } + return $interface_alias; +} + +sub print_totals +{ + my ($data) = @_; + + print "\n# HELP vnstat_interface_total_received_bytes All time total received (rx) bytes\n"; + print "# TYPE vnstat_interface_total_received_bytes counter\n"; + + foreach my $interface ( @{ $data->{'interfaces'} } ) { + my $interface_alias = get_interface_alias($interface); + print "vnstat_interface_total_received_bytes{interface=\"$interface->{'name'}\",alias=\"$interface_alias\"} $interface->{'traffic'}{'total'}{'rx'} $interface->{'updated'}{'timestamp'}000\n"; + } + + print "\n# HELP vnstat_interface_total_transmitted_bytes All time total transmitted (tx) bytes\n"; + print "# TYPE vnstat_interface_total_transmitted_bytes counter\n"; + + foreach my $interface ( @{ $data->{'interfaces'} } ) { + my $interface_alias = get_interface_alias($interface); + print "vnstat_interface_total_transmitted_bytes{interface=\"$interface->{'name'}\",alias=\"$interface_alias\"} $interface->{'traffic'}{'total'}{'tx'} $interface->{'updated'}{'timestamp'}000\n"; + } +} + +sub print_data_resolution +{ + my ($resolution, $data) = @_; + my $output_count = 0; + + print "\n# HELP vnstat_interface_".$resolution."_received_bytes Received (rx) bytes for current $resolution\n"; + print "# TYPE vnstat_interface_".$resolution."_received_bytes gauge\n"; + + $output_count = 0; + foreach my $interface ( @{ $data->{'interfaces'} } ) { + my $interface_alias = get_interface_alias($interface); + if (defined $interface->{'traffic'}{$resolution}) { + print "vnstat_interface_".$resolution."_received_bytes{interface=\"$interface->{'name'}\",alias=\"$interface_alias\"} $interface->{'traffic'}{$resolution}[0]{'rx'} $interface->{'updated'}{'timestamp'}000\n"; + $output_count++; + } + } + if ($output_count == 0) { + print "# no data\n"; + } + + print "\n# HELP vnstat_interface_".$resolution."_transmitted_bytes Transmitted (tx) bytes for current $resolution\n"; + print "# TYPE vnstat_interface_".$resolution."_transmitted_bytes gauge\n"; + + $output_count = 0; + foreach my $interface ( @{ $data->{'interfaces'} } ) { + my $interface_alias = get_interface_alias($interface); + if (defined $interface->{'traffic'}{$resolution}) { + print "vnstat_interface_".$resolution."_transmitted_bytes{interface=\"$interface->{'name'}\",alias=\"$interface_alias\"} $interface->{'traffic'}{$resolution}[0]{'tx'} $interface->{'updated'}{'timestamp'}000\n"; + $output_count++; + } + } + if ($output_count == 0) { + print "# no data\n"; + } +} + +my @data_resolutions = ('fiveminute', 'hour', 'day', 'month', 'year'); + +print "Content-Type: text/plain\n\n"; + +my $json_data = `$vnstat_cmd --json s 1`; + +my $data = ""; +eval { $data = decode_json($json_data) }; +if ($@) { + print "# Error: Invalid command output: $json_data\n"; + exit 1; +} + +if (not defined $data->{'vnstatversion'}) { + print "# Error: Expected content from command output missing\n"; + exit 1; +} + +if (not defined $data->{'interfaces'}[0]) { + print "# Error: No interfaces found in command output\n"; + exit 1; +} + +if (not defined $data->{'interfaces'}[0]{'created'}{'timestamp'}) { + print "# Error: Incompatible vnStat version used\n"; + exit 1; +} + +print "# vnStat version: ".$data->{'vnstatversion'}."\n"; + +print_totals($data); + +foreach my $data_resolution ( @data_resolutions ) { + print_data_resolution($data_resolution, $data); +} diff --git a/all/playbook.yaml b/all/playbook.yaml index 93bba67..22362be 100644 --- a/all/playbook.yaml +++ b/all/playbook.yaml @@ -44,6 +44,7 @@ - apt-file - fail2ban - tree + - perl # Monitoring - htop - gdu @@ -202,6 +203,20 @@ validate: "/usr/sbin/sshd -T -f %s" notify: - Restart sshd + - name: Create www directory if doesn't exist already + ansible.builtin.file: + path: /var/lib/caddy/www + state: directory + mode: '0755' + owner: caddy + group: caddy + - name: "Instal VNStat Metrics CGI Script to WWW" + ansible.builtin.copy: + src: ./files/vnstat-metrics.cgi + dest: /var/lib/caddy/www/vnstat-metrics.cgi + mode: preserve + owner: caddy + group: caddy - name: "Remove useless passphrase line (runs after borgmatic role)" ansible.builtin.lineinfile: dest: "/etc/borgmatic/config.yaml" diff --git a/privfrontends/templates/Caddyfile.j2 b/privfrontends/templates/Caddyfile.j2 index f6cda77..a36e4c2 100644 --- a/privfrontends/templates/Caddyfile.j2 +++ b/privfrontends/templates/Caddyfile.j2 @@ -1,5 +1,6 @@ { order rate_limit before basicauth + order cgi before respond } (tor) { @@ -296,3 +297,7 @@ lace.{{ server_prefix }}.projectsegfau.lt lace.projectsegfau.lt l.psf.lt l.{{ se reverse_proxy :9029 import torloc lace } + +:8093 { + cgi /vnstat /var/lib/caddy/www/vnstat-metrics.cgi +}