metrics.pl 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #!/usr/bin/perl -w
  2. # vnstat-metrics.cgi -- Prometheus compatible metrics endpoint output from vnStat data
  3. # copyright (c) 2022 Teemu Toivola <tst at iki dot fi>
  4. # released under the GNU General Public License
  5. use strict;
  6. use JSON::PP;
  7. use HTTP::Daemon;
  8. use HTTP::Status;
  9. # location of vnstat binary
  10. my $vnstat_cmd = '/usr/bin/vnstat';
  11. my @data_resolutions = ('fiveminute', 'hour', 'day', 'month', 'year');
  12. # Create an HTTP::Daemon instance listening on port 9955
  13. my $d = HTTP::Daemon->new(
  14. LocalAddr => 'localhost',
  15. LocalPort => 9955,
  16. ) || die "Cannot create HTTP::Daemon instance: $!";
  17. print "HTTP server listening on ", $d->url, "\n";
  18. # Enter an event loop to handle incoming requests
  19. while (my $c = $d->accept) {
  20. while (my $r = $c->get_request) {
  21. if ($r->method eq 'GET') {
  22. my $response_text = generate_response();
  23. $c->send_response(HTTP::Response->new(RC_OK, undef, undef, $response_text));
  24. } else {
  25. $c->send_error(RC_FORBIDDEN);
  26. }
  27. }
  28. $c->close;
  29. undef($c);
  30. }
  31. sub generate_response {
  32. my $response = "Content-Type: text/plain\n\n";
  33. my $json_data = `$vnstat_cmd --json s 1`;
  34. my $data = "";
  35. eval { $data = decode_json($json_data) };
  36. if ($@) {
  37. $response .= "# Error: Invalid command output: $json_data\n";
  38. return $response;
  39. }
  40. if (not defined $data->{'vnstatversion'}) {
  41. $response .= "# Error: Expected content from command output missing\n";
  42. return $response;
  43. }
  44. if (not defined $data->{'interfaces'}[0]) {
  45. $response .= "# Error: No interfaces found in command output\n";
  46. return $response;
  47. }
  48. if (not defined $data->{'interfaces'}[0]{'created'}{'timestamp'}) {
  49. $response .= "# Error: Incompatible vnStat version used\n";
  50. return $response;
  51. }
  52. $response .= "# vnStat version: ".$data->{'vnstatversion'}."\n";
  53. $response .= print_totals($data);
  54. foreach my $data_resolution ( @data_resolutions ) {
  55. $response .= print_data_resolution($data_resolution, $data);
  56. }
  57. return $response;
  58. }
  59. ################
  60. sub get_interface_alias
  61. {
  62. my ($interface) = @_;
  63. my $interface_alias = $interface->{'alias'};
  64. if (length($interface_alias) == 0) {
  65. $interface_alias = $interface->{'name'};
  66. }
  67. return $interface_alias;
  68. }
  69. sub print_totals
  70. {
  71. my ($data) = @_;
  72. print "\n# HELP vnstat_interface_total_received_bytes All time total received (rx) bytes\n";
  73. print "# TYPE vnstat_interface_total_received_bytes counter\n";
  74. foreach my $interface ( @{ $data->{'interfaces'} } ) {
  75. my $interface_alias = get_interface_alias($interface);
  76. print "vnstat_interface_total_received_bytes{interface=\"$interface->{'name'}\",alias=\"$interface_alias\"} $interface->{'traffic'}{'total'}{'rx'} $interface->{'updated'}{'timestamp'}000\n";
  77. }
  78. print "\n# HELP vnstat_interface_total_transmitted_bytes All time total transmitted (tx) bytes\n";
  79. print "# TYPE vnstat_interface_total_transmitted_bytes counter\n";
  80. foreach my $interface ( @{ $data->{'interfaces'} } ) {
  81. my $interface_alias = get_interface_alias($interface);
  82. print "vnstat_interface_total_transmitted_bytes{interface=\"$interface->{'name'}\",alias=\"$interface_alias\"} $interface->{'traffic'}{'total'}{'tx'} $interface->{'updated'}{'timestamp'}000\n";
  83. }
  84. }
  85. sub print_data_resolution
  86. {
  87. my ($resolution, $data) = @_;
  88. my $output_count = 0;
  89. print "\n# HELP vnstat_interface_".$resolution."_received_bytes Received (rx) bytes for current $resolution\n";
  90. print "# TYPE vnstat_interface_".$resolution."_received_bytes gauge\n";
  91. $output_count = 0;
  92. foreach my $interface ( @{ $data->{'interfaces'} } ) {
  93. my $interface_alias = get_interface_alias($interface);
  94. if (defined $interface->{'traffic'}{$resolution}) {
  95. print "vnstat_interface_".$resolution."_received_bytes{interface=\"$interface->{'name'}\",alias=\"$interface_alias\"} $interface->{'traffic'}{$resolution}[0]{'rx'} $interface->{'updated'}{'timestamp'}000\n";
  96. $output_count++;
  97. }
  98. }
  99. if ($output_count == 0) {
  100. print "# no data\n";
  101. }
  102. print "\n# HELP vnstat_interface_".$resolution."_transmitted_bytes Transmitted (tx) bytes for current $resolution\n";
  103. print "# TYPE vnstat_interface_".$resolution."_transmitted_bytes gauge\n";
  104. $output_count = 0;
  105. foreach my $interface ( @{ $data->{'interfaces'} } ) {
  106. my $interface_alias = get_interface_alias($interface);
  107. if (defined $interface->{'traffic'}{$resolution}) {
  108. print "vnstat_interface_".$resolution."_transmitted_bytes{interface=\"$interface->{'name'}\",alias=\"$interface_alias\"} $interface->{'traffic'}{$resolution}[0]{'tx'} $interface->{'updated'}{'timestamp'}000\n";
  109. $output_count++;
  110. }
  111. }
  112. if ($output_count == 0) {
  113. print "# no data\n";
  114. }
  115. }
  116. print "Content-Type: text/plain\n\n";
  117. my $json_data = `$vnstat_cmd --json s 1`;
  118. my $data = "";
  119. eval { $data = decode_json($json_data) };
  120. if ($@) {
  121. print "# Error: Invalid command output: $json_data\n";
  122. exit 1;
  123. }
  124. if (not defined $data->{'vnstatversion'}) {
  125. print "# Error: Expected content from command output missing\n";
  126. exit 1;
  127. }
  128. if (not defined $data->{'interfaces'}[0]) {
  129. print "# Error: No interfaces found in command output\n";
  130. exit 1;
  131. }
  132. if (not defined $data->{'interfaces'}[0]{'created'}{'timestamp'}) {
  133. print "# Error: Incompatible vnStat version used\n";
  134. exit 1;
  135. }
  136. print "# vnStat version: ".$data->{'vnstatversion'}."\n";
  137. print_totals($data);
  138. foreach my $data_resolution ( @data_resolutions ) {
  139. print_data_resolution($data_resolution, $data);
  140. }