1#!/usr/local/bin/perl -w
2
3# libstatgrab
4# https://libstatgrab.org
5# Copyright (C) 2003-2004 Peter Saunders
6# Copyright (C) 2003-2019 Tim Bishop
7# Copyright (C) 2003-2013 Adam Sampson
8# Copyright (C) 2012-2019 Jens Rehsack
9#
10# This program is free software; you can redistribute it and/or
11# modify it under the terms of the GNU General Public License
12# as published by the Free Software Foundation; either version 2
13# of the License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program; if not, write to the Free Software
22# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23# 02110-1301, USA.
24
25use strict;
26use Getopt::Long;
27
28my $progname = "statgrab-make-mrtg-config";
29my $statgrab = "statgrab";
30my $workdir = undef;
31
32my $kib = 1024;
33my $mib = $kib * $kib;
34
35# Type 0 is plain integers.
36my $KIBIBYTES = 1;
37my $PERCENT = 2;
38my $FLOAT = 3;
39
40# Print an entry in the MRTG config file.
41sub entry ($$$$$$$$$$) {
42	my ($title, $vali, $valo, $max, $ylegend, $yunit, $legendi, $legendo, $gauge, $type) = @_;
43	my $name = $vali;
44	my $options = "";
45	$options .= " noo" unless defined $valo;
46	$options .= " gauge" if $gauge;
47	my $sgoptions = "";
48	$sgoptions .= " -o -p" if $type == $PERCENT;
49	$sgoptions .= " -f 1000" if $type == $FLOAT;
50
51	print "\n";
52	print "Title[$name]: $title\n";
53	print "PageTop[$name]: $title\n";
54	print "MaxBytes[$name]: $max\n";
55	print "YLegend[$name]: $ylegend\n";
56	print "ShortLegend[$name]: $yunit\n";
57	print "LegendI[$name]: $legendi\n";
58	print "LegendO[$name]: $legendo\n" if defined $valo;
59	if ($type == $KIBIBYTES) {
60		print "kMG[$name]: Ki,Mi,Gi,Ti\n";
61		$sgoptions .= " -K";
62	}
63	$valo = "const.0" unless defined $valo;
64	print "Options[$name]:$options\n" if $options ne "";
65	print "Target[$name]: `$statgrab$sgoptions -m $vali $valo`\n";
66}
67
68my $package_version = '@PACKAGE_VERSION@';
69my $package_bugreport = '@PACKAGE_BUGREPORT@';
70my $help_text = <<EOF;
71Usage: $progname [OPTION]...
72Generate MRTG configuration from statgrab output and write it to stdout.
73
74--no-header                  Don't print MRTG global options; useful if you
75                             want to include the output of this script in
76                             another MRTG config file
77--workdir PATH               Use PATH for MRTG's WorkDir option
78--statgrab PATH              Specify location of statgrab binary
79                             (default "statgrab")
80--help                       Display this help and exit
81
82Version $package_version - report bugs to $package_bugreport.
83EOF
84
85sub fatal ($) {
86	my ($message) = @_;
87	die "$progname: $message\n";
88}
89
90sub main () {
91	GetOptions('statgrab=s' => \$statgrab,
92	           'workdir=s' => \$workdir,
93	           'no-header' => \my $no_header,
94	           'help' => \my $help) or die $help_text;
95	if ($help) {
96		print "$help_text";
97		exit 0;
98	}
99
100	unless ($no_header or defined $workdir) {
101		fatal "must specify --workdir or --no-header"
102	}
103
104	my %stats = ();
105	my %toplevel = ();
106	my %disks = ();
107	my %fss = ();
108	my %nets = ();
109	open STATGRAB, "$statgrab|" or fatal "can't run statgrab";
110	while (<STATGRAB>) {
111		chomp;
112		/^([^=]*) = (.*)$/ or fatal "bad line in statgrab output";
113		$stats{$1} = $2;
114
115		my @parts = split /\./, $1;
116		$toplevel{$parts[0]} = 1;
117		$disks{$parts[1]} = 1 if $parts[0] eq "disk";
118		$fss{$parts[1]} = 1 if $parts[0] eq "fs";
119		$nets{$parts[1]} = 1 if $parts[0] eq "net";
120	}
121	close STATGRAB;
122
123	unless ($no_header) {
124		print "WorkDir: $workdir\n";
125		print "Options[^]: growright\n";
126		print "WriteExpires: Yes\n";
127	}
128
129	if (exists $toplevel{"cpu"}) {
130		entry("CPU idle", "cpu.idle", undef, "100", "Idle", "%", "idle", undef, 1, $PERCENT);
131		entry("CPU iowait", "cpu.iowait", undef, "100", "iowait", "%", "iowait", undef, 1, $PERCENT);
132		entry("CPU kernel", "cpu.kernel", undef, "100", "Kernel", "%", "kernel", undef, 1, $PERCENT);
133		entry("CPU nice", "cpu.nice", undef, "100", "Nice", "%", "nice", undef, 1, $PERCENT);
134		entry("CPU swap", "cpu.swap", undef, "100", "Swap", "%", "swap", undef, 1, $PERCENT);
135		entry("CPU user", "cpu.user", undef, "100", "User", "%", "user", undef, 1, $PERCENT);
136	}
137
138	foreach my $disk (sort keys %disks) {
139		my $name = $stats{"disk.$disk.disk_name"};
140		entry("Disk $name IO", "disk.$disk.read_bytes", "disk.$disk.write_bytes", 100*$mib, "IO rate", "KiB/s", "read", "write", 0, $KIBIBYTES);
141	}
142
143	foreach my $fs (sort keys %fss) {
144		my $name = $stats{"fs.$fs.mnt_point"};
145		my $size = $stats{"fs.$fs.size"};
146		my $inodes = $stats{"fs.$fs.total_inodes"};
147		entry("Filesystem $name space usage", "fs.$fs.used", undef, $size, "Space used", "KiB", "used", undef, 1, $KIBIBYTES);
148		entry("Filesystem $name inode usage", "fs.$fs.used_inodes", undef, $inodes, "Inodes used", "inodes", "used", undef, 1, 0);
149	}
150
151	if (exists $toplevel{"load"}) {
152		entry("Load average over 1 minute", "load.min1", undef, 100, "Load average", "running * 1000", "load", undef, 1, $FLOAT);
153		entry("Load average over 5 minutes", "load.min5", undef, 100, "Load average", "running * 1000", "load", undef, 1, $FLOAT);
154		entry("Load average over 15 minutes", "load.min15", undef, 100, "Load average", "running * 1000", "load", undef, 1, $FLOAT);
155	}
156
157	if (exists $toplevel{"mem"}) {
158		my $total = $stats{"mem.total"};
159		entry("Memory usage", "mem.used", "mem.cache", $total, "Memory usage", "KiB", "total", "cache", 1, $KIBIBYTES);
160	}
161
162	foreach my $net (sort keys %nets) {
163		my $name = $stats{"net.$net.interface_name"};
164		my $speed = int($stats{"net.$net.speed"});
165		$speed = 100 if $speed == 0;
166
167		# The speed is reported in Mbit/s; we want KiB/s.
168		$speed = int(($speed * 1000000) / (8 * $kib));
169
170		entry("Network interface $name IO", "net.$net.rx", "net.$net.tx", $speed, "Network IO", "KiB/s", "rx", "tx", 0, $KIBIBYTES);
171	}
172
173	if (exists $toplevel{"page"}) {
174		# FIXME what's a sensible maximum?
175		entry("Paging IO", "page.in", "page.out", 1000, "Paging IO", "pages", "in", "out", 0, 0);
176	}
177
178	if (exists $toplevel{"proc"}) {
179		# FIXME mildly silly assumption
180		my $maxproc = 65536;
181		entry("Processes running", "proc.running", undef, $maxproc, "Running", "procs", "running", undef, 1, 0);
182		entry("Processes sleeping", "proc.sleeping", undef, $maxproc, "Sleeping", "procs", "running", undef, 1, 0);
183		entry("Processes stopped", "proc.stopped", undef, $maxproc, "Stopped", "procs", "running", undef, 1, 0);
184		entry("Processes", "proc.total", undef, $maxproc, "Total", "procs", "running", undef, 1, 0);
185		entry("Processes zombie", "proc.zombie", undef, $maxproc, "Zombie", "procs", "running", undef, 1, 0);
186	}
187
188	if (exists $toplevel{"swap"}) {
189		my $swapsize = $stats{"swap.total"};
190		if ($swapsize ne "0") {
191			entry("Swap usage", "swap.used", undef, $swapsize, "Swap usage", "KiB", "used", undef, 1, $KIBIBYTES);
192		}
193	}
194
195	if (exists $toplevel{"user"}) {
196		entry("Users", "user.num", undef, 1000, "Users", "users", "users", undef, 1, 0);
197	}
198}
199
200main();
201