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. # Create an HTTP::Daemon instance listening on port 9955
  12. my $d = HTTP::Daemon->new(
  13. LocalAddr => 'localhost',
  14. LocalPort => 9955,
  15. ) || die "Cannot create HTTP::Daemon instance: $!";
  16. print "HTTP server listening on ", $d->url, "\n";
  17. # Enter an event loop to handle incoming requests
  18. while (my $c = $d->accept) {
  19. while (my $r = $c->get_request) {
  20. if ($r->method eq 'GET') {
  21. my $response_text = generate_response();
  22. $c->send_response(HTTP::Response->new(RC_OK, undef, undef, $response_text));
  23. } else {
  24. $c->send_error(RC_FORBIDDEN);
  25. }
  26. }
  27. $c->close;
  28. undef($c);
  29. }
  30. sub generate_response {
  31. my $response = "Content-Type: text/plain\n\n";
  32. my $json_data = `$vnstat_cmd --json s 1`;
  33. my $data = "";
  34. eval { $data = decode_json($json_data) };
  35. if ($@) {
  36. $response .= "# Error: Invalid command output: $json_data\n";
  37. return $response;
  38. }
  39. if (not defined $data->{'vnstatversion'}) {
  40. $response .= "# Error: Expected content from command output missing\n";
  41. return $response;
  42. }
  43. if (not defined $data->{'interfaces'}[0]) {
  44. $response .= "# Error: No interfaces found in command output\n";
  45. return $response;
  46. }
  47. if (not defined $data->{'interfaces'}[0]{'created'}{'timestamp'}) {
  48. $response .= "# Error: Incompatible vnStat version used\n";
  49. return $response;
  50. }
  51. $response .= "# vnStat version: ".$data->{'vnstatversion'}."\n";
  52. $response .= print_totals($data);
  53. foreach my $data_resolution ( @data_resolutions ) {
  54. $response .= print_data_resolution($data_resolution, $data);
  55. }
  56. return $response;
  57. }
  58. ################
  59. sub get_interface_alias
  60. {
  61. my ($interface) = @_;
  62. my $interface_alias = $interface->{'alias'};
  63. if (length($interface_alias) == 0) {
  64. $interface_alias = $interface->{'name'};
  65. }
  66. return $interface_alias;
  67. }
  68. sub print_totals
  69. {
  70. my ($data) = @_;
  71. print "\n# HELP vnstat_interface_total_received_bytes All time total received (rx) bytes\n";
  72. print "# TYPE vnstat_interface_total_received_bytes counter\n";
  73. foreach my $interface ( @{ $data->{'interfaces'} } ) {
  74. my $interface_alias = get_interface_alias($interface);
  75. print "vnstat_interface_total_received_bytes{interface=\"$interface->{'name'}\",alias=\"$interface_alias\"} $interface->{'traffic'}{'total'}{'rx'} $interface->{'updated'}{'timestamp'}000\n";
  76. }
  77. print "\n# HELP vnstat_interface_total_transmitted_bytes All time total transmitted (tx) bytes\n";
  78. print "# TYPE vnstat_interface_total_transmitted_bytes counter\n";
  79. foreach my $interface ( @{ $data->{'interfaces'} } ) {
  80. my $interface_alias = get_interface_alias($interface);
  81. print "vnstat_interface_total_transmitted_bytes{interface=\"$interface->{'name'}\",alias=\"$interface_alias\"} $interface->{'traffic'}{'total'}{'tx'} $interface->{'updated'}{'timestamp'}000\n";
  82. }
  83. }
  84. sub print_data_resolution
  85. {
  86. my ($resolution, $data) = @_;
  87. my $output_count = 0;
  88. print "\n# HELP vnstat_interface_".$resolution."_received_bytes Received (rx) bytes for current $resolution\n";
  89. print "# TYPE vnstat_interface_".$resolution."_received_bytes gauge\n";
  90. $output_count = 0;
  91. foreach my $interface ( @{ $data->{'interfaces'} } ) {
  92. my $interface_alias = get_interface_alias($interface);
  93. if (defined $interface->{'traffic'}{$resolution}) {
  94. print "vnstat_interface_".$resolution."_received_bytes{interface=\"$interface->{'name'}\",alias=\"$interface_alias\"} $interface->{'traffic'}{$resolution}[0]{'rx'} $interface->{'updated'}{'timestamp'}000\n";
  95. $output_count++;
  96. }
  97. }
  98. if ($output_count == 0) {
  99. print "# no data\n";
  100. }
  101. print "\n# HELP vnstat_interface_".$resolution."_transmitted_bytes Transmitted (tx) bytes for current $resolution\n";
  102. print "# TYPE vnstat_interface_".$resolution."_transmitted_bytes gauge\n";
  103. $output_count = 0;
  104. foreach my $interface ( @{ $data->{'interfaces'} } ) {
  105. my $interface_alias = get_interface_alias($interface);
  106. if (defined $interface->{'traffic'}{$resolution}) {
  107. print "vnstat_interface_".$resolution."_transmitted_bytes{interface=\"$interface->{'name'}\",alias=\"$interface_alias\"} $interface->{'traffic'}{$resolution}[0]{'tx'} $interface->{'updated'}{'timestamp'}000\n";
  108. $output_count++;
  109. }
  110. }
  111. if ($output_count == 0) {
  112. print "# no data\n";
  113. }
  114. }
  115. my @data_resolutions = ('fiveminute', 'hour', 'day', 'month', 'year');
  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. }