Syed Jahanzaib – Personal Blog to Share Knowledge !

December 1, 2016

rrdtool: MRTG next level graphing

Filed under: Linux Related, Mikrotik Related — Tags: , , , , , , , — Syed Jahanzaib / Pinochio~:) @ 10:56 AM

DRAFT VERSION: This is incomplete Post ! Some points may be missing, I will update them later ..

rrdtool

We all know what is MRTG. You can graph so many information including temperature humidity, speed, voltage, uptime, routers, active numbers of users,  number of printouts and sometimes out of the box graphing/zaib.

In this post, I am just posting reference material on how to move plain mrtg graphs to RRD. The advantage of RRD over MRTG is that using RRD you can generate the graphs on the fly, as opposed as MRTG that generates the graphs all the time, no matter that nobody sees the generated HTML pages or not. we can use RRD to make graphs with one minute interval, whereas in plain MRTG, we have to use 5 minutes interval which is not good to monitor jerks or sensitive devices.

For initial / basic level of MRTG installation on Ubuntu Server , refer to the inernet or my previous post at

https://aacable.wordpress.com/tag/mrtg-installation-on-ubuntu/


Sample CFG Files:

main MRTG.CFG file

cat /etc/mrtg.cfg


#########################
# START OF /etc/mrtg.cfg#
#########################
# Author: Syed Jahanzaib
# Email : aacable@hotmail.com
# Web : https://aacable.wordpress.com

WorkDir: /var/www/mrtg
Options[_]: growright,nobanner,pngdate,noinfo,nobanner

XSize[_]: 600
YSize[_]: 200
EnableIPv6: no
RunAsDaemon: no
Interval: 1
Logformat: rrdtool
#Use MIBS as per your local config, make sure you download the mibs as mentioned here
# https://aacable.wordpress.com/tag/mrtg-installation-on-ubuntu/
LoadMIBs: /cfg/mibs/HOST-RESOURCES-MIB /cfg/mibs/IF-MIB /cfg/mibs/UCD-SNMP-MIB
PageFoot[^]: Page managed by SYED JAHANZAIB
AddHead[_]:
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%20type%3D%22text%2Fcss%22%3E%20%3C!.%20a%20%7B%20color%3A%20%23263F66%3B%20text-decoration%3A%20none%3B%20%7D%20a%3Ahover%20%7B%20color%3A%20%23785B41%3B%20text-decoration%3A%20none%3B%20%7D%20body%20%7B%20color%3A%20black%3B%20font%3A%208pt%20Verdana%2C%20Geneva%2C%20Arial%2C%20Helvetica%2C%20sans-serif%3B%20%7D%20h1%20%7B%20font%3A%20bold%2016pt%20Verdana%2C%20Geneva%2C%20Arial%2C%20Helvetica%2C%20sans-serif%3B%20color%3A%20%23342A21%3B%20%7D%20h2%20%7B%20color%3A%20%23666666%3B%20font%3A%20bold%2012pt%20Verdana%2C%20Geneva%2C%20Arial%2C%20Helvetica%2C%20sans-serif%3B%20%7D%20h3%20%7B%20color%3A%20black%3B%20font%3A%20bold%209pt%20Verdana%2C%20Geneva%2C%20Arial%2C%20Helvetica%2C%20sans-serif%3B%20%7D%20table%20%7B%20border%3A%200%3B%20%7D%20td%20%7B%20background-color%3A%20%23E7DDD3%3B%20border%3A%200px%20solid%20%23FFFFFF%3B%20color%3A%20Black%3B%20font%3A%208pt%20Verdana%2C%20Geneva%2C%20Arial%2C%20Helvetica%2C%20sans-serif%3B%20vertical-align%3A%20top%3B%20%7D%20th%20%7B%20background-color%3A%20%23735A4A%3B%20color%3A%20White%3B%20font%3A%2011px%20Verdana%2C%20arial%2C%20geneva%2C%20helvetica%2C%20sans-serif%3B%20border%3A%200%3B%20font-weight%3A%20bold%3B%20text-align%3A%20left%3B%20%7D%20.%3E%20%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />

Background[_]: #F6F1EE

# Change it as required , it means mrtg will read following file and will create graphs based on its contents
# ADD remove them as required, they are added for reference purposes only
#Include: /cfg/temp.cfg
Include: /cfg/kesc.cfg
Include: /cfg/lanwan.cfg
Include: /cfg/radius.cfg
Include: /cfg/mt.cfg
Include: /cfg/ping.cfg
#Include: /cfg/vlan.cfg

##################
# END OF /etc/mrtg.cfg #
##################


radius.cfg


# Created by
# /usr/bin/cfgmaker public@10.0.0.1

# or for NT
WorkDir: /var/www/mrtg

### Interface 2 >> Descr: 'eth0' | Name: 'eth0' | Ip: '1' | Eth: '00' ###

Target[10.0.0.1_eth0]: #eth0:public@10.0.0.1:
SetEnv[10.0.0.1_eth0]: MRTG_INT_IP="10.0.0.1" MRTG_INT_DESCR="eth0"
MaxBytes[10.0.0.1_eth0]: 125000000
Title[10.0.0.1_eth0]: Traffic Analysis for eth0 -- BILLING
PageTop[10.0.0.1_eth0]:
<h1>Traffic Analysis for eth0 -- BILLING</h1>
<div id="sysdetails">
<table>
<tr>
<td>System:</td>
<td>in "BILLING"</td>
</tr>
<tr>
<td>Maintainer:</td>
<td>aacable at hotmail dot com</td>
</tr>
<tr>
<td>Description:</td>
<td>eth0</td>
</tr>
<tr>
<td>ifType:</td>
<td>ethernetCsmacd (6)</td>
</tr>
<tr>
<td>ifName:</td>
<td>eth0</td>
</tr>
<tr>
<td>Max Speed:</td>
<td>125.0 MBytes/s</td>
</tr>
<tr>
<td>Ip:</td>
<td>10.0.0.1 (click.onmypc.net)</td>
</tr>
</table>
</div>
#Percent of memory used
Target[radius_server_mem_ram]: ( hrStorageUsed.1&hrStorageUsed.1:public@10.0.0.1) * 100 / ( hrStorageSize.1&hrStorageSize.1:public@10.0.0.1 ) / 10
Title[radius_server_mem_ram]: Memory usage for Radius Server
PageTop[radius_server_mem_ram]:
<H1> Memory usage for Radius Server </H1>

MaxBytes[radius_server_mem_ram]: 100
AbsMax[radius_server_mem_ram]: 100
ShortLegend[radius_server_mem_ram]: %
YLegend[radius_server_mem_ram]: % of Memory
Legend1[radius_server_mem_ram]: Used Memory
LegendI[radius_server_mem_ram]: Used :
LegendO[radius_server_mem_ram]:
Options[radius_server_mem_ram]: growright,nopercent,gauge,integer,nobanner,printrouter,pngdate,noo
#Unscaled[radius_server_mem_ram]: ymwd

Target[linux_server_dis_usage]: ( hrStorageUsed.31&hrStorageUsed.31:public@10.0.0.1 ) * 100 / ( hrStorageSize.31&hrStorageSize.31:public@10.0.0.1 ) / 8
Title[linux_server_dis_usage]: Disk usage for Linux Server
PageTop[linux_server_dis_usage]:
<H1> Disk usage for Linux Server </H1>

MaxBytes[linux_server_dis_usage]: 100
AbsMax[linux_server_dis_usage]: 100
ShortLegend[linux_server_dis_usage]: %
YLegend[linux_server_dis_usage]: % of RM_DISK
Legend1[linux_server_dis_usage]: Used RM_DISK
LegendI[linux_server_dis_usage]: Used :
LegendO[linux_server_dis_usage]:
Options[linux_server_dis_usage]: growright,gauge,integer,nobanner,printrouter,pngdate,noo,nopercent,logscale
#Unscaled[linux_server_dis_usage]: ymwd


Mikrotik Sample CFG File


workdir: /var/www/mrtg/
#Options[_]: growright,nopercent

# Z_CCR_MIKROTIK CCR_1036 PPPoE ACTIVE Users
Target[mtPPPoEusers]: 1.3.6.1.4.1.9.9.150.1.1.1.0&1.3.6.1.4.1.9.9.150.1.1.1.0:public@10.0.0.1 / 8
Title[mtPPPoEusers]: Active PPPoE Users on Z_CCR_MIKROTIK CCR_1036
PageTop[mtPPPoEusers]:
<H1>Active PPPoE Users on Z_CCR_MIKROTIK CCR_1036</H1>

MaxBytes[mtPPPoEusers]: 2000
Colours[mtPPPoEusers]: B#8888ff,B#8888ff,B#5398ff,B#5398ff
Options[mtPPPoEusers]: growright,nopercent,gauge,integer,nobanner,printrouter,pngdate,noo
LegendI[mtPPPoEusers]: Active PPPoE Users on MT
LegendO[mtPPPoEusers]:
YLegend[mtPPPoEusers]: Active PPPoE Users on MT
Legend1[mtPPPoEusers]: Active PPPoE Users on MT
Legend2[mtPPPoEusers]:
#Unscaled[mtPPPoEusers]: dwmy

### Z_CCR_MIKROTIK CCR_1036 192.168.100.2 CPU load ###
Target[192.168.100.2_cpu]: 1.3.6.1.2.1.25.3.3.1.2.1&1.3.6.1.2.1.25.3.3.1.2.1:public@10.0.0.1 / 8
AbsMax[192.168.100.2_cpu]: 100
MaxBytes[192.168.100.2_cpu]: 100
Title[192.168.100.2_cpu]: Z_CCR_MIKROTIK CCR_1036 CPU load
PageTop[192.168.100.2_cpu]:
<H1>Z_CCR_MIKROTIK CCR_1036 CPU load</H1>

Options[192.168.100.2_cpu]: growright,gauge,integer,nobanner,printrouter,pngdate,noo,nopercent
Legend1[192.168.100.2_cpu]: CPU load
YLegend[192.168.100.2_cpu]: CPU load
ShortLegend[192.168.100.2_cpu]: %
LegendI[192.168.100.2_cpu]: CPU load (percentage)
#Unscaled[192.168.100.2_cpu]: y

### MONITORING Z_CCR_MIKROTIK CCR_1036 CPU TEMP ###
Target[mt.cpu.temp]: 1.3.6.1.4.1.14988.1.1.3.11.0&1.3.6.1.4.1.14988.1.1.3.11.0:public@10.0.0.1 / 100
Options[mt.cpu.temp]: gauge, growright, nopercent, noinfo
MaxBytes[mt.cpu.temp]: 100
Colours[mt.cpu.temp]: RED#ff4f27,Y#fffb15,RED#ff4f27,RED#fffb15
#Unscaled[mt.cpu.temp]: dwmy
YLegend[mt.cpu.temp]: CPU Temprature
Title[mt.cpu.temp]: Z_CCR_MIKROTIK CCR_1036 RB CPU Temprature
PageTop[mt.cpu.temp]:
<H1>Z_CCR_MIKROTIK CCR_1036 RB CPU Temprature</H1>

ShortLegend[mt.cpu.temp]: c
LegendI[mt.cpu.temp]:
LegendO[mt.cpu.temp]: CPU Temp
Legend1[mt.cpu.temp]: CPU Temprature
Legend2[mt.cpu.temp]: CPU Temprature

### MONITORING Z_CCR_MIKROTIK CCR_1036 RB TEMP ###
Target[mt.rb.temp]: 1.3.6.1.4.1.14988.1.1.3.10.0&1.3.6.1.4.1.14988.1.1.3.10.0:public@10.0.0.1 / 100
Options[mt.rb.temp]: gauge, growright, nopercent, noinfo
MaxBytes[mt.rb.temp]: 100
Colours[mt.rb.temp]: RED#ff4f27,Y#fffb15,RED#ff4f27,RED#fffb15
#Unscaled[mt.rb.temp]: dwmy
YLegend[mt.rb.temp]: RB Temprature
Title[mt.rb.temp]: Z_CCR_MIKROTIK CCR_1036 Router Board Temprature
PageTop[mt.rb.temp]:
<H1>Z_CCR_MIKROTIK CCR_1036 Router Board Temprature</H1>

ShortLegend[mt.rb.temp]: c
LegendI[mt.rb.temp]:
LegendO[mt.rb.temp]: RB Temp
Legend1[mt.rb.temp]: RB Temprature
Legend2[mt.rb.temp]: RB Temprature

### MONITORING Z_CCR_MIKROTIK CCR_1036 VOLTAGE Monitor ###
Target[mt-voltage]: 1.3.6.1.4.1.14988.1.1.3.8.0&1.3.6.1.4.1.14988.1.1.3.8.0:public@10.0.0.1 / 8
Options[mt-voltage]: growright,nopercent,gauge,integer,nobanner,printrouter,pngdate
MaxBytes[mt-voltage]: 30
Colours[mt-voltage]: RED#ff4f27,Y#fffb15,RED#ff4f27,RED#fffb15
Unscaled[mt-voltage]: dwmy
YLegend[mt-voltage]: Voltaeg Monitor
Title[mt-voltage]: Z_CCR_MIKROTIK CCR_1036 Cloudcore RB VOLTAGE Monitor
PageTop[mt-voltage]:
<H1>Z_CCR_MIKROTIK CCR_1036 Cloudcore RB VOLTAGE Monitor</H1>

ShortLegend[mt-voltage]: v
LegendI[mt-voltage]:
LegendO[mt-voltage]: Voltage
Legend1[mt-voltage]: Voltage
Legend2[mt-voltage]: Voltage

### MONITORING Z_CCR_MIKROTIK CCR_1036 power.consumption Watt Usage Monitor ###
Target[mt-powerconsumption]: .1.3.6.1.4.1.14988.1.1.3.12.0&.1.3.6.1.4.1.14988.1.1.3.12.0:public@10.0.0.1 / 100
Options[mt-powerconsumption]: gauge, growright, nopercent, noinfo
MaxBytes[mt-powerconsumption]: 1000
Colours[mt-powerconsumption]: RED#ff4f27,Y#fffb15,RED#ff4f27,RED#fffb15
#Unscaled[mt-powerconsumption]: dwmy
YLegend[mt-powerconsumption]: Watts USAGE Monitor
Title[mt-powerconsumption]: Z_CCR_MIKROTIK CCR_1036 Cloudcore RB Watts Usage Monitor
PageTop[mt-powerconsumption]:
<H1>Z_CCR_MIKROTIK CCR_1036 Cloudcore RB Watts Usage Monitor</H1>

ShortLegend[mt-powerconsumption]: W
LegendI[mt-powerconsumption]:
LegendO[mt-powerconsumption]: power.consumption.wts
Legend1[mt-powerconsumption]: power.consumption.wts
Legend2[mt-powerconsumption]: power.consumption.wts

### MONITORING Z_CCR_MIKROTIK CCR_1036 FAN-2 Speed Monitor ###
Target[mt-FAN2-speed]: 1.3.6.1.4.1.14988.1.1.3.18.0&1.3.6.1.4.1.14988.1.1.3.18.0:public@10.0.0.1 / 8
Options[mt-FAN2-speed]: growright,nopercent,gauge,integer,nobanner,printrouter,pngdate,logscale,noi
MaxBytes[mt-FAN2-speed]: 10000
Colours[mt-FAN2-speed]: RED#ff4f27,Y#fffb15,RED#ff4f27,RED#fffb15
#Unscaled[mt-FAN2-speed]: dwmy
YLegend[mt-FAN2-speed]: FAN-2 RPM
Title[mt-FAN2-speed]: Z_CCR_MIKROTIK CCR_1036 Cloudcore RB FAN-2 Speed Monitor
PageTop[mt-FAN2-speed]:
<H1>Z_CCR_MIKROTIK CCR_1036 Cloudcore RB FAN-2 Speed Monitor</H1>

ShortLegend[mt-FAN2-speed]: RPM
LegendI[mt-FAN2-speed]:
LegendO[mt-FAN2-speed]: fan-2.rpm.speed
Legend1[mt-FAN2-speed]: fan-2.rpm.speed
Legend2[mt-FAN2-speed]: fan-2.rpm.speed

### MONITORING Z_CCR_MIKROTIK CCR_1036 AMP Monitor ###
Target[mt-amp-mon]: 1.3.6.1.4.1.14988.1.1.3.13.0&1.3.6.1.4.1.14988.1.1.3.13.0:public@10.0.0.1 / 10000
Options[mt-amp-mon]: gauge,growright,nopercent,pngdate,nobanner
MaxBytes[mt-amp-mon]: 30
Colours[mt-amp-mon]: RED#ff4f27,Y#fffb15,RED#ff4f27,RED#fffb15
#Unscaled[mt-amp-mon]: dwmy
YLegend[mt-amp-mon]: AMP USAGE Monitor
Title[mt-amp-mon]: Z_CCR_MIKROTIK CCR_1036 Cloudcore RB AMP Monitor
PageTop[mt-amp-mon]:
<H1>Z_CCR_MIKROTIK CCR_1036 Cloudcore RB AMP Monitor</H1>

ShortLegend[mt-amp-mon]: amp
LegendI[mt-amp-mon]:
LegendO[mt-amp-mon]: amps
Legend1[mt-amp-mon]: amps
Legend2[mt-amp-mon]: amps


ping.cfg


WorkDir: /var/www/mrtg
### ping.cfg
# 10.0.0.1 MIKROTIK NAS SERVERPING GRAPH
Title[mikrotik.ping]: MIKROTIK NAS SERVER PING RTT / Pkt LOSS Report
PageTop[mikrotik.ping]:
<H1>MIKROTIK NAS SERVER PING RTT / Pkt LOSS Report</H1>

Target[mikrotik.ping]: `/cfg/norping.sh 10.0.0.1`
Colours[mikrotik.ping]: R#f75712,G#04bf27,G#70ff53,R#70ff53
MaxBytes[mikrotik.ping]: 100
AbsMax[mikrotik.ping]: 100
Options[mikrotik.ping]: growright,gauge,pngdate,printrouter,nopercent,noinfo,logscale
#growright,nopercent,gauge
LegendI[mikrotik.ping]: Pkt LOSS
LegendO[mikrotik.ping]: Active Round Trip Time
YLegend[mikrotik.ping]: RTT
Legend1[mikrotik.ping]: Pkt LOSS
Legend2[mikrotik.ping]: Active RTT

# 8.8.8.8 GOOGLE SERVERPING GRAPH
Title[8.8.8.8.ping]: GOOGLE DNS PING RTT / Pkt LOSS Report
PageTop[8.8.8.8.ping]:
<H1>GOOGLE DNS PING RTT / Pkt LOSS Report</H1>

Target[8.8.8.8.ping]: `/cfg/norping.sh 8.8.8.8`
Colours[8.8.8.8.ping]: R#f75712,G#04bf27,G#70ff53,R#70ff53
MaxBytes[8.8.8.8.ping]: 100
AbsMax[8.8.8.8.ping]: 100
Options[8.8.8.8.ping]: growright,gauge,pngdate,printrouter,nopercent,noinfo,logscale
#growright,nopercent,gauge
LegendI[8.8.8.8.ping]: Pkt LOSS
LegendO[8.8.8.8.ping]: Active Round Trip Time
YLegend[8.8.8.8.ping]: RTT
Legend1[8.8.8.8.ping]: Pkt LOSS
Legend2[8.8.8.8.ping]: Active RTT


kesc.cfg


WorkDir: /var/www/mrtg

# 10.0.0.2 MIKROTIK AP FOR KESC RPING GRAPH
Title[kesc.ping]: K.E.S.C LIGHT FAILURE GRAPH / MRTG GRAPH
PageTop[kesc.ping]:
<H1>K.E.S.C LIGHT FAILURE / MRTG GRAPH Report ! z@iB</H1>

Target[kesc.ping]: `/cfg/kping.sh 10.0.0.2`
Colours[kesc.ping]: RED#ff4f27,GREEN#00eb0c,RED#ff4f27,RED#fffb15
MaxBytes[kesc.ping]: 100
AbsMax[kesc.ping]: 100
Options[kesc.ping]: growright,gauge,pngdate,printrouter,nopercent,noinfo,logscale
ShortLegend[kesc.ping]: %
LegendI[kesc.ping]: K.E Light Loss
LegendO[kesc.ping]: K.E Availability
YLegend[kesc.ping]: Red Bar Shows Failure (:)


norping.sh


#!/bin/bash
HOST=$1
ping -c 3 $HOST > /dev/null
DATA=`ping -c 3 $1 -q `
if [ $? -eq 0 ]
then
echo "0"
echo $DATA | awk -F/ '{print $6 }'

else
echo "100
0"
fi


APACHE / WEB SERVER Section

To enable cgi execution in /var/www/mrtg folder, edit the apache config file, and

nano /etc/apache2/sites-enabled/000-default

and paste following so that the last section of file should be like below …


<Directory /var/www/mrtg>
AddHandler cgi-script .cgi
Options +ExecCGI +Indexes
DirectoryIndex index.cgi
</Directory>
</VirtualHost>


mrtg-rrd.cgi or index.cgi to graph creation

Following CGI file will create MRTG graph on the fly (on demand)


#!/usr/bin/perl -w
# $Id: mrtg-rrd.cgi,v 1.35 2003/08/18 15:58:57 kas Exp $
# mrtg-rrd.cgi: The script for generating graphs for MRTG statistics.
# Loosely modelled after the Rainer.Bawidamann@informatik.uni-ulm.de's
# 14all.cgi
use strict;
use POSIX qw(strftime);
use Time::Local;
# The %.1f should work on 5.005+. There may be other problems, though.
# I've tested this on 5.8.0 only, so mind the gap!
require 5.005;
# Location of RRDs.pm, if it is not in @INC
# use lib '/usr/lib/perl5/5.00503/i386-linux';
use RRDs;
use vars qw(@config_files @all_config_files %targets $config_time
%directories $version $imagetype);

# EDIT THIS to reflect all your MRTG config files
BEGIN { @config_files = qw(/etc/mrtg.cfg); }

$version = '0.7';
# This depends on what image format your libgd (and rrdtool) uses
$imagetype = 'png'; # or make this 'gif';
# strftime(3) compatibility test
my $percent_h = '%-H';
$percent_h = '%H' if (strftime('%-H', gmtime(0)) !~ /^\d+$/);
sub handler ($)
{
my ($q) = @_;
try_read_config($q->url());
my $path = $q->path_info();
$path =~ s/^\///;
$path =~ s/\/$//;
if (defined $directories{$path}) {
if ($q->path_info() =~ /\/$/) {
print_dir($path);
} else {
print "Location: ", $q->url(-path_info=>1), "/\n\n";
}
return;
}

my ($dir, $stat, $ext) = ($q->path_info() =~
/^(.*)\/([^\/]+)(\.html|-(day|week|month|year)\.$imagetype)$/);

$dir =~ s/^\///;

print_error("Undefined statistics")
unless defined $targets{$stat};

print_error("Incorrect directory")
unless defined $targets{$stat}{directory} || $targets{$stat}{directory} eq $dir;

my $tgt = $targets{$stat};

common_args($stat, $tgt, $q);

# We may be running under mod_perl or something. Do not destroy
# the original settings of timezone.
my $oldtz;
if (defined $tgt->{timezone}) {
$oldtz = $ENV{TZ};
$ENV{TZ} = $tgt->{timezone};
}

if ($ext eq '.html') {
do_html($tgt);
} elsif ($ext eq '-day.' . $imagetype) {
do_image($tgt, 'day');
} elsif ($ext eq '-week.' . $imagetype) {
do_image($tgt, 'week');
} elsif ($ext eq '-month.' . $imagetype) {
do_image($tgt, 'month');
} elsif ($ext eq '-year.' . $imagetype) {
do_image($tgt, 'year');
} else {
print_error("Unknown extension");
}
$ENV{TZ} = $oldtz
if defined $oldtz;
}

sub do_html($)
{
my ($tgt) = @_;

my @day = do_image($tgt, 'day');
my @week = do_image($tgt, 'week');
my @month = do_image($tgt, 'month');
my @year = do_image($tgt, 'year');

http_headers('text/html', $tgt->{config});
print <<'EOF';
<HTML>
<HEAD>
<TITLE>
EOF
print $tgt->{title} if defined $tgt->{title};
print "</TITLE>\n";

html_comments($tgt, 'd', @{$day[0]}) if $#day != -1;
html_comments($tgt, 'w', @{$week[0]}) if $#week != -1;
html_comments($tgt, 'm', @{$month[0]}) if $#month != -1;
html_comments($tgt, 'y', @{$year[0]}) if $#year != -1;

print $tgt->{addhead} if defined $tgt->{addhead};

print "</HEAD>\n", $tgt->{bodytag}, "\n";

print $tgt->{pagetop} if defined $tgt->{pagetop};

unless (defined $tgt->{options}{noinfo}) {
my @st = stat $tgt->{rrd};

print "

<hr>

\nThe statistics were last updated ",
strftime("<B>%A, %d %B, %H:%M:%S %Z</B>\n",
localtime($st[9]));
}

my $dayavg = $tgt->{config}->{interval};

html_graph($tgt, 'day', 'Daily', $dayavg . ' Minute', \@day);
html_graph($tgt, 'week', 'Weekly', '30 Minute', \@week);
html_graph($tgt, 'month', 'Monthly', '2 Hour', \@month);
html_graph($tgt, 'year', 'Yearly', '1 Day', \@year);

unless (defined $tgt->{options}{nolegend}) {
print <<EOF;

<hr>

<table WIDTH=500 BORDER=0 CELLPADDING=4 CELLSPACING=0>
EOF
print <<EOF unless ($tgt->{options}{noi});
<tr>
<td ALIGN=RIGHT><font SIZE=-1 COLOR="$tgt->{col1}">
<b>$tgt->{colname1} ###</b></font></td>
<td><font SIZE=-1>$tgt->{legend1}</font></td>
</tr>
EOF
print <<EOF unless ($tgt->{options}{noo});
<tr>
<td ALIGN=RIGHT><font SIZE=-1 COLOR="$tgt->{col2}">
<b>$tgt->{colname2} ###</b></font></td>
<td><font SIZE=-1>$tgt->{legend2}</font></td>
</tr>
EOF
if ($tgt->{withpeak} ne '') {
print <<EOF unless ($tgt->{options}{noi});
<tr>
<td ALIGN=RIGHT><font SIZE=-1 COLOR="$tgt->{col3}">
<b>$tgt->{colname3} ###</b></font></td>
<td><font SIZE=-1>$tgt->{legend3}</font></td>
</tr>
EOF
print <<EOF unless ($tgt->{options}{noo});
<tr>
<td ALIGN=RIGHT><font SIZE=-1 COLOR="$tgt->{col4}">
<b>$tgt->{colname4} ###</b></font></td>
<td><font SIZE=-1>$tgt->{legend4}</font></td>
</tr>
EOF
}
print <<EOF;</table>
EOF
}

print_banner($tgt->{config})
unless defined $tgt->{options}{nobanner};

print $tgt->{pagefoot} if defined $tgt->{pagefoot};
print "\n", <<'EOF';
</body>
</html>
EOF

}

sub html_comments($$@)
{
my ($tgt, $letter, @val) = @_;

return if $#val == -1;

unless ($tgt->{options}{noi}) {
print "<!-- maxin $letter ", $val[1], " -->\n";
print "<!-- avin $letter ", $val[3], " -->\n";
print "<!-- cuin $letter ", $val[5], " -->\n";
}
unless ($tgt->{options}{noo}) {
print "<!-- maxout $letter ", $val[0], " -->\n";
print "<!-- avout $letter ", $val[2], " -->\n";
print "<!-- cuout $letter ", $val[4], " -->\n";
}
}
sub html_graph($$$$$)
{
my ($tgt, $ext, $freq, $period, $params) = @_;

return unless defined $tgt->{$ext};

my @values = @{$params->[0]};
my $x = $params->[1];
my $y = $params->[2];

$x *= $tgt->{xzoom} if defined $tgt->{xzoom};
$y *= $tgt->{yzoom} if defined $tgt->{yzoom};

my $kilo = $tgt->{kilo};
my @kmg = split(',', $tgt->{kmg});

my $fmt;
if (defined $tgt->{options}{integer}) {
$fmt = '%d';
} else {
$fmt = '%.1f';
}

my @percent = do_percent($tgt, \@values);
my @relpercent = do_relpercent($tgt, \@values);

my @nv;
for my $val (@values) {
if (@kmg == 0) { # kMG[target]: <empty>
push @nv, sprintf($fmt, $val);
next;
}
for my $si (@kmg) {
if ($val < 10000) {
push @nv, sprintf($fmt, $val) . " $si";
last;
}
$val /= $kilo;
}
}
@values = @nv;

print "

<hr>

\n<B>\`$freq\' Graph ($period Average)</B>
\n";

print '<IMG SRC="', $tgt->{url}, '-', $ext, '.' . $imagetype .
'" WIDTH=', $x, ' HEIGHT=', $y, ' ALT="', $freq,
' Graph" VSPACE=10 ALIGN=TOP>
', "\n";
print '
<TABLE CELLPADDING=0 CELLSPACING=0>';
print <<EOF if $tgt->{legendi} ne '' && !$tgt->{options}{noi};
<TR>
<TD ALIGN=RIGHT><SMALL>Max <FONT COLOR="$tgt->{col1}">$tgt->{legendi}</FONT></SMALL></TD>
<TD ALIGN=RIGHT><SMALL>&nbsp;$values[1]$tgt->{shortlegend}$percent[1]</SMALL></TD>
<TD WIDTH=5></TD>
<TD ALIGN=RIGHT><SMALL>Average <FONT COLOR="$tgt->{col1}">$tgt->{legendi}</FONT></SMALL></TD>
<TD ALIGN=RIGHT><SMALL>&nbsp;$values[3]$tgt->{shortlegend}$percent[3]</SMALL></TD>
<TD WIDTH=5></TD>
<TD ALIGN=RIGHT><SMALL>Current <FONT COLOR="$tgt->{col1}">$tgt->{legendi}</FONT></SMALL></TD>
<TD ALIGN=RIGHT><SMALL>&nbsp;$values[5]$tgt->{shortlegend}$percent[5]</SMALL></TD>
</TR>

EOF
print <<EOF if $tgt->{legendo} ne '' && !$tgt->{options}{noo};
<TR>
<TD ALIGN=RIGHT><SMALL>Max <FONT COLOR="$tgt->{col2}">$tgt->{legendo}</FONT></SMALL></TD>
<TD ALIGN=RIGHT><SMALL>&nbsp;$values[0]$tgt->{shortlegend}$percent[0]</SMALL></TD>
<TD WIDTH=5></TD>
<TD ALIGN=RIGHT><SMALL>Average <FONT COLOR="$tgt->{col2}">$tgt->{legendo}</FONT></SMALL></TD>
<TD ALIGN=RIGHT><SMALL>&nbsp;$values[2]$tgt->{shortlegend}$percent[2]</SMALL></TD>
<TD WIDTH=5></TD>
<TD ALIGN=RIGHT><SMALL>Current <FONT COLOR="$tgt->{col2}">$tgt->{legendo}</FONT></SMALL></TD>
<TD ALIGN=RIGHT><SMALL>&nbsp;$values[4]$tgt->{shortlegend}$percent[4]</SMALL></TD>

EOF
if (defined $tgt->{options}{dorelpercent}) {
print <<"EOF";
</TR>
<TR>
<TD ALIGN=RIGHT><SMALL>Max <FONT COLOR="$tgt->{col5}">&nbsp;Percentage:</FONT></SMALL></TD>
<TD ALIGN=RIGHT><SMALL>&nbsp;$relpercent[0]</SMALL></TD>
<TD WIDTH=5></TD>
<TD ALIGN=RIGHT><SMALL>Average <FONT COLOR="$tgt->{col5}">&nbsp;Percentage:</FONT></SMALL></TD>
<TD ALIGN=RIGHT><SMALL>&nbsp;$relpercent[1]</SMALL></TD>
<TD WIDTH=5></TD>
<TD ALIGN=RIGHT><SMALL>Current <FONT COLOR="$tgt->{col5}">&nbsp;Percentage:</FONT></SMALL></TD>
<TD ALIGN=RIGHT><SMALL>&nbsp;$relpercent[2]</SMALL></TD>

EOF
}
print <<'EOF';
</TR>
</TABLE>

EOF
}

sub do_percent($$)
{
my ($tgt, $values) = @_;

my @percent = ('', '', '', '', '', '');

return @percent if defined $tgt->{options}{nopercent};

for my $val (0..$#$values) {
my $mx = ($val % 2 == 1) ? $tgt->{maxbytes1} : $tgt->{maxbytes2};
next unless defined $mx;
my $p = sprintf("%.1f", $values->[$val]*100/$mx);
$percent[$val] = ' (' . $p . '%)';
}

@percent;
}

sub do_relpercent($$)
{
my ($tgt, $values) = @_;

my @percent = ('', '', '');

return @percent unless defined $tgt->{options}{dorelpercent};

for my $val (0..2) {
$percent[$val] = sprintf("%.1f",
$values->[2*$val+1] * 100 / $values->[2*$val])
if $values->[2*$val] > 0;
$percent[$val] ||= 0;
$percent[$val] .= ' %';
}

@percent;
}

sub http_headers($$)
{
my ($content_type, $cfg) = @_;

print <<"EOF"; Content-Type: $content_type Refresh: $cfg->{refresh}
Pragma: no-cache
EOF
# Expires header calculation stolen from CGI.pm
print strftime("Expires: %a, %d %b %Y %H:%M:%S GMT\n",
gmtime(time+60*$cfg->{interval}));

print "\n";
}

sub do_image($$)
{
my ($target, $ext) = @_;

my $file = $target->{$ext};

return unless defined $file;

# Now the vertical rule at the end of the day
my @t = localtime(time);
$t[0] = $t[1] = $t[2] = 0;

my $seconds;
my $oldsec;
my $back;
my $xgrid;

my $unscaled;
my $withpeak;

my $noi = 1 if $target->{options}{noi};
my $noo = 1 if $target->{options}{noo};

if ($ext eq 'day') {
$seconds = timelocal(@t);
$back = 30*3600; # 30 hours
$oldsec = $seconds - 86400;
$unscaled = 1 if $target->{unscaled} =~ /d/;
$withpeak = 1 if $target->{withpeak} =~ /d/;
# We need this only for day graph. The other ones
# are magically correct.
$xgrid = 'HOUR:1:HOUR:6:HOUR:2:0:' . $percent_h;
} elsif ($ext eq 'week') {
$seconds = timelocal(@t);
$t[6] = ($t[6]+6) % 7;
$seconds -= $t[6]*86400;
$back = 8*86400; # 8 days
$oldsec = $seconds - 7*86400;
$unscaled = 1 if $target->{unscaled} =~ /w/;
$withpeak = 1 if $target->{withpeak} =~ /w/;
} elsif ($ext eq 'month') {
$t[3] = 1;
$seconds = timelocal(@t);
$back = 36*86400; # 36 days
$oldsec = $seconds - 30*86400; # FIXME (the right # of days!!)
$unscaled = 1 if $target->{unscaled} =~ /m/;
$withpeak = 1 if $target->{withpeak} =~ /m/;
} elsif ($ext eq 'year') {
$t[3] = 1;
$t[4] = 0;
$seconds = timelocal(@t);
$back = 396*86400; # 365 + 31 days
$oldsec = $seconds - 365*86400; # FIXME (the right # of days!!)
$unscaled = 1 if $target->{unscaled} =~ /y/;
$withpeak = 1 if $target->{withpeak} =~ /y/;
} else {
print_error("Unknown file extension: $ext");
}

my @local_args;

if ($unscaled) {
@local_args = ('-u', $target->{maxbytes1});
push @local_args, '--rigid' unless defined $target->{absmax};
}

if ($xgrid) {
push @local_args, '-x', $xgrid;
}

my @local_args_end;

if ($withpeak) {
push @local_args_end, 'LINE1:maxin'.$target->{col3}.':MaxIn'
unless $noi;
push @local_args_end, 'LINE1:maxout'.$target->{col4}.':MaxOut'
unless $noo;
}

my @rv = RRDs::graph($file, '-s', "-$back", @local_args,
@{$target->{args}}, "VRULE:$oldsec#ff0000",
"VRULE:$seconds#ff0000", @local_args_end);

my $rrd_error = RRDs::error;
print_error("RRDs::graph failed, $rrd_error") if defined $rrd_error;

# In array context just return the values
if (wantarray) {
if (defined $target->{factor}) {
@{$rv[0]} = map { $_ * $target->{factor} } @{$rv[0]};
}
if ($noi) {
return ([$rv[0][0], 0, $rv[0][1], 0, $rv[0][2], 0],
$rv[1], $rv[2]);
} elsif ($noo) {
return ([0, $rv[0][0], 0, $rv[0][1], 0, $rv[0][2]],
$rv[1], $rv[2]);
} else {
return @rv;
}
}

# Not in array context ==> print out the PNG file.
open PNG, "<$file" or print_error("Can't open $file: $!"); binmode PNG; http_headers("image/$imagetype", $target->{config});

my $buf;
# could be sendfile in Linux 😉
while(sysread PNG, $buf, 8192) {
print $buf;
}
close PNG;
}

sub common_args($$$)
{
my ($name, $target, $q) = @_;

return @{$target->{args}} if defined @{$target->{args}};

my $noi = 1 if $target->{options}{noi};
my $noo = 1 if $target->{options}{noo};

$target->{name} = $name;

$target->{directory} = ''
unless defined $target->{directory};

my $tdir = $target->{directory};
$tdir .= '/'
unless $tdir eq '' || $tdir =~ /\/$/;

$target->{url} = $q->url . '/' . $tdir . $name;

my $cfg = $target->{config};

my $dir = $cfg->{workdir};
$dir = $cfg->{logdir}
if defined $cfg->{logdir};

$target->{rrd} = $dir . '/' . $tdir . $name . '.rrd';

%{$target->{options}} = ()
unless defined %{$target->{options}};

$dir = $cfg->{workdir};
$dir = $cfg->{imagedir}
if defined $cfg->{imagedir};

$target->{suppress} ||= '';

$target->{day} = $dir . '/' . $tdir . $name
. '-day.' . $imagetype unless $target->{suppress} =~ /d/;
$target->{week} = $dir . '/' . $tdir . $name
. '-week.' . $imagetype unless $target->{suppress} =~ /w/;
$target->{month} = $dir . '/' . $tdir . $name
. '-month.' . $imagetype unless $target->{suppress} =~ /m/;
$target->{year} = $dir . '/' . $tdir . $name
. '-year.' . $imagetype unless $target->{suppress} =~ /y/;

$target->{maxbytes1} = $target->{maxbytes}
if defined $target->{maxbytes} && !defined $target->{maxbytes1};

$target->{maxbytes2} = $target->{maxbytes1}
if defined $target->{maxbytes1} && !defined $target->{maxbytes2};

my @args = ();

push @args, '--lazy', '-c', 'FONT#000000', '-c',
'MGRID#000000', '-c', 'FRAME#000000',
'-g', '-l', '0';

$target->{background} = '#f5f5f5'
unless defined $target->{background};

push @args, '-c', 'BACK'. $target->{background};

push @args, '-c', 'SHADEA' . $target->{background},
'-c', 'SHADEB' . $target->{background}
if defined $target->{options}{noborder};

if (defined $target->{options}{noarrow}) {
push @args, '-c', 'ARROW' . $target->{background};
} else {
push @args, '-c', 'ARROW#000000';
}

push @args, '-b', $target->{kilo}
if defined $target->{kilo};

if (defined $target->{xsize}) {
if (defined $target->{xscale}) {
push @args, '-w', $target->{xsize}*$target->{xscale};
} else {
push @args, '-w', $target->{xsize};
}
}

if (defined $target->{ysize}) {
if (defined $target->{yscale}) {
push @args, '-h', $target->{ysize}*$target->{yscale};
} else {
push @args, '-h', $target->{ysize};
}
}

my $scale = 1;

if (defined $target->{options}->{perminute}) {
$scale *= 60;
} elsif (defined $target->{options}->{perhour}) {
$scale *= 3600;
}

if (defined $target->{options}->{bits}) {
$scale *= 8;
$target->{ylegend} = 'Bits per second'
unless defined $target->{ylegend};
$target->{legend1} = 'Incoming Traffic in Bits per Second'
unless defined $target->{legend1};
$target->{legend2} = 'Outgoing Traffic in Bits per Second'
unless defined $target->{legend2};
$target->{legend3} = 'Peak Incoming Traffic in Bits per Second'
unless defined $target->{legend3};
$target->{legend4} = 'Peak Outgoing Traffic in Bits per Second'
unless defined $target->{legend4};
$target->{shortlegend} = 'b/s'
unless defined $target->{shortlegend};
} else {
$target->{ylegend} = 'Bytes per second'
unless defined $target->{ylegend};
$target->{legend1} = 'Incoming Traffic in Bytes per Second'
unless defined $target->{legend1};
$target->{legend2} = 'Outgoing Traffic in Bytes per Second'
unless defined $target->{legend2};
$target->{legend3} = 'Peak Incoming Traffic in Bytes per Second'
unless defined $target->{legend3};
$target->{legend4} = 'Peak Outgoing Traffic in Bytes per Second'
unless defined $target->{legend4};
$target->{shortlegend} = 'B/s'
unless defined $target->{shortlegend};
}

if ($scale > 1) {
push @args, "DEF:in0=$target->{rrd}:ds0:AVERAGE",
"CDEF:in=in0,$scale,*",
"DEF:maxin0=$target->{rrd}:ds0:MAX",
"CDEF:maxin=maxin0,$scale,*"
unless $noi;
push @args, "DEF:out0=$target->{rrd}:ds1:AVERAGE",
"CDEF:out=out0,$scale,*",
"DEF:maxout0=$target->{rrd}:ds1:MAX",
"CDEF:maxout=maxout0,$scale,*"
unless $noo;
} else {
push @args, "DEF:in=$target->{rrd}:ds0:AVERAGE",
"DEF:maxin=$target->{rrd}:ds0:MAX"
unless $noi;
push @args, "DEF:out=$target->{rrd}:ds1:AVERAGE",
"DEF:maxout=$target->{rrd}:ds1:MAX"
unless $noo;
}

my $i=1;
for my $coltext (split(/,/, $target->{colours})) {
my ($text, $rgb) = ($coltext =~ /^([^#]+)(#[0-9a-fA-F]{6})$/);
$target->{'col'.$i} = $rgb;
$target->{'colname'.$i} = $text;
$i++;
}

push @args, '-v', $target->{ylegend};

push @args, 'AREA:in' . $target->{col1} . ':In',
unless $noi;
push @args, 'LINE2:out' . $target->{col2} . ':Out'
unless $noo;

push @args, 'PRINT:out:MAX:%.1lf' unless $noo;
push @args, 'PRINT:in:MAX:%.1lf' unless $noi;
push @args, 'PRINT:out:AVERAGE:%.1lf' unless $noo;
push @args, 'PRINT:in:AVERAGE:%.1lf' unless $noi;
push @args, 'PRINT:out:LAST:%.1lf' unless $noo;
push @args, 'PRINT:in:LAST:%.1lf' unless $noi;

if (defined $target->{maxbytes1}) {
$target->{maxbytes1} *= $scale;
push @args, 'HRULE:' . $target->{maxbytes1} . '#cc0000';
}

if (defined $target->{maxbytes2}) {
$target->{maxbytes2} *= $scale;
push @args, 'HRULE:' . $target->{maxbytes2} . '#cccc00'
if $target->{maxbytes2} != $target->{maxbytes1};
}

@{$target->{args}} = @args;

@args;
}

sub try_read_config($)
{
my ($prefix) = (@_);
$prefix =~ s/\/[^\/]*$//;

# Verify the version of RRDtool:
if (!defined $RRDs::VERSION || $RRDs::VERSION < 1.000331) {
print_error("Please install more up-to date RRDtool - need at least 1.000331");
}

my $read_cfg;
if (!defined $config_time) {
$read_cfg = 1;
} else {
for my $file (@all_config_files) {
my @stat = stat $file;
if ($config_time < $stat[9]) { $read_cfg = 1; last; } } } return unless $read_cfg; my %defaults = ( xsize => 400,
ysize => 100,
kmg => ',k,M,G,T,P',
kilo => 1000,
bodytag => "<BODY BGCOLOR=#ffffff>\n",
colours => 'GREEN#00cc00,BLUE#0000ff,DARK GREEN#006600,MAGENTA#ff00ff,AMBER#ef9f4f',
legendi => '&nbsp;In:',
legendo => '&nbsp;Out:',
unscaled => '',
withpeak => '',
directory => '',
);

%targets = ();

@all_config_files = @config_files;

my $order = 0;
for my $cfgfile (@config_files) {
%{$targets{_}} = %defaults;
%{$targets{'^'}} = ();
%{$targets{'$'}} = ();

my $cfgref = {
refresh => 300,
interval => 5,
icondir => $prefix
};

read_mrtg_config($cfgfile, \%defaults, $cfgref, \$order);
}

delete $targets{'^'};
delete $targets{_};
delete $targets{'$'};

parse_directories();

$config_time = time;
}

sub read_mrtg_config($$$$);

sub read_mrtg_config($$$$)
{
my ($file, $def, $cfgref, $order) = @_;

my %defaults = %$def;

my @lines;

open(CFG, "<$file") || print_error("Cannot open config file: $!");
while (<CFG>) {
chomp; # remove newline
s/\s+$//; # remove trailing space
s/\s+/ /g; # collapse white spaces to ' '
next if /^ *\#/; # skip comment lines
next if /^\s*$/; # skip empty lines
if (/^ \S/) { # multiline options
$lines[$#lines] .= $_;
} else {
push @lines, $_;
}
}
close CFG;

foreach (@lines) {
if (/^\s*([\w\d]+)\[(\S+)\]\s*:\s*(.*)$/) {
my ($tgt, $opt, $val) = (lc($2), lc($1), $3);
unless (exists $targets{$tgt}) {
# pre-set defaults constructed on all of ^, _, and $
for my $key (%{$targets{'^'}}) {
$targets{$tgt}{$key} = $targets{'^'}{$key};
}
for my $key (%{$targets{'$'}}) {
$targets{$tgt}{$key} .= $targets{'$'}{$key};
}
# WARNING: Tobi explicitly said that when all ^, _, and $
# options are set, the result should be just the value
# of the _ option (when the option itself is not explicitly
# defined. I do not agree with him here but I respect this
# and will try to be compatible with MRTG.
for my $key (%{$targets{'_'}}) {
$targets{$tgt}{$key} = $targets{'_'}{$key};
}

# anonymous hash ref - need copy, not ref
delete $targets{$tgt}{options};
# The same as above - we need to create this
# based on [^], [_], and [$] values
%{$targets{$tgt}{options}} = ();
%{$targets{$tgt}{options}} = %{$targets{'^'}{options}}
if defined $targets{'^'}{options};
%{$targets{$tgt}{options}} = (%{$targets{$tgt}{options}},
%{$targets{'_'}{options}})
if defined $targets{'_'}{options};
%{$targets{$tgt}{options}} = (%{$targets{$tgt}{options}},
%{$targets{'$'}{options}})
if defined $targets{'$'}{options};

$targets{$tgt}{order} = ++$$order;
$targets{$tgt}{config} = $cfgref;
}
if ($tgt eq '_' && $val eq '') {
if (defined $defaults{$opt}) {
$targets{_}{$opt} = $defaults{$opt};
} else {
delete $targets{_}{$opt};
}
} elsif (($tgt eq '^' || $tgt eq '$') && $val eq '') {
delete $targets{$tgt}{$opt};
} elsif ($opt eq 'options') {
# Do not forget defaults [^] and [$]
delete $targets{$tgt}{options};
%{$targets{$tgt}{options}} = %{$targets{'^'}{options}}
if defined $targets{'^'}{options};
$val = lc($val);
map { $targets{$tgt}{options}{$_} = 1 } ($val =~ m/([a-z]+)/g);
%{$targets{$tgt}{options}} = (%{$targets{$tgt}{options}},
%{$targets{'$'}{options}})
if defined $targets{'$'}{options};
} else {
my $pre = $targets{'^'}{$opt}
if defined $targets{'^'}{$opt};
$pre ||= '';
$targets{$tgt}{$opt} = $pre.$val;
$targets{$tgt}{$opt} .= $targets{'$'}{$opt}
if defined $targets{'$'}{$opt};
}
next;
} elsif (/^Include\s*:\s*(\S*)$/) {
push @all_config_files, $1;
read_mrtg_config($1, $def, $cfgref, $order);
next;
} elsif (/^([\w\d]+)\s*:\s*(\S.*)$/) {
my ($opt, $val) = (lc($1), $2);
$cfgref->{$opt} = $val;
next;
}
print_error("Parse error in $file near $_");
}

if (defined $cfgref->{pathadd}) {
$ENV{PATH} .= ':'.$cfgref->{pathadd};
}

# if (defined $cfgref->{libadd}) {
# use lib $cfgref->{libadd}
# }
}

sub parse_directories {
%directories = ();

# FIXME: the sort is expensive
for my $name (sort { $targets{$a}{order} <=> $targets{$b}{order} } keys %targets) {
my $dir = $targets{$name}{directory}
if defined $targets{$name}{directory};
$dir = '' unless defined $dir;

my $prefix = '';
for my $component (split /\/+/, $dir) {
unless (defined $directories{$prefix.$component}) {
push (@{$directories{$prefix}{subdir}},
$component);

# For the directory, get the global parameters
# from the # config of the first item of the
# directory:
$directories{$prefix}{config} =
$targets{$name}{config};
$directories{$prefix}{bodytag} =
$targets{$name}{bodytag};
}
$prefix .= $component . '/';
}
unless (defined $directories{$dir}) {
$directories{$dir}{config} =
$targets{$name}{config};
$directories{$dir}{bodytag} =
$targets{$name}{bodytag};
}

push (@{$directories{$dir}{target}}, $name);
}
}

sub print_dir($) {
my ($dir) = @_;

my $dir1 = $dir . '/';

http_headers('text/html', $directories{$dir}{config});

print <<EOF;
<HTML>
<HEAD>
<TITLE>MRTG: Directory $dir1</TITLE>
</HEAD>
EOF
print $directories{$dir}{bodytag};

my $subdirs_printed;
if (defined @{$directories{$dir}{subdir}}) {
$subdirs_printed = 1;
print <<EOF;
<H1>MRTG subdirectories in the directory $dir1</H1>
<UL>
EOF
for my $item (@{$directories{$dir}{subdir}}) {
print "
<LI><A HREF=\"$item/\">$item/</A>\n";
}

print "</UL>

\n";
}
if (defined @{$directories{$dir}{target}}) {
print "

<hr>

\n" if defined $subdirs_printed;
print <<EOF;
<H1>MRTG graphs in the directory $dir1</H1>
<TABLE BORDER=0 WIDTH=100%>
EOF
my $odd;
for my $item (@{$directories{$dir}{target}}) {
my $itemname = $item;
$itemname = $targets{$item}{title}
if defined $targets{$item}{title};
print "
<TR>\n" unless $odd;
print <<EOF;
<TD><A HREF="$item.html">$itemname
<IMG SRC="$item-day.$imagetype" BORDER=0 ALIGN=TOP VSPACE=10 ALT="$item">
</A><BR CLEAR=ALL>
</TD>

EOF
print " </TR>

\n" if $odd;
$odd = !$odd;
}
print " </TR>

\n</TABLE>

\n";
}

print_banner($directories{$dir}{config});
print "</BODY>\n</HTML>\n";
}

sub print_banner($) {
my $cfg = shift;

print <<EOF;

<hr>

<table BORDER=0 CELLSPACING=0 CELLPADDING=0>
<tr>
<td WIDTH=63><a ALT="MRTG" HREF="http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html"><img BORDER=0 SRC="$cfg->{icondir}/mrtg-l.$imagetype"></a></td>
<td WIDTH=25><a ALT="" HREF="http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html"><img BORDER=0 SRC="$cfg->{icondir}/mrtg-m.$imagetype"></a></td>
<td WIDTH=388><a ALT="" HREF="http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html"><img BORDER=0 SRC="$cfg->{icondir}/mrtg-r.$imagetype"></a></td>
</tr>
</table>
<spacer TYPE=VERTICAL SIZE=4>
<table BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH=476>
<tr VALIGN=top>
<td ALIGN=LEFT><font FACE="Arial,Helvetica" SIZE=2>
version 2.9.17</font></td>
<td ALIGN=RIGHT><font FACE="Arial,Helvetica" SIZE=2>
<a HREF="http://ee-staff.ethz.ch/~oetiker/">Tobias Oetiker</a>
<a HREF="mailto:oetiker\@ee.ethz.ch">&lt;oetiker\@ee.ethz.ch&gt;</a>
</font></td>
</tr>
<tr>
<td></td>
<td ALIGN=RIGHT><font FACE="Arial,Helvetica" SIZE=2>
and&nbsp;<a HREF="http://www.bungi.com">Dave&nbsp;Rand</a>&nbsp;<a HREF="mailto:dlr\@bungi.com">&lt;dlr\@bungi.com&gt;</a></font></td>
<tr VALIGN=top>
<td ALIGN=LEFT><font FACE="Arial,Helvetica" SIZE=2>
<A HREF=http://www.fi.muni.cz/~kas/mrtg-rrd/>mrtg-rrd.cgi version $version</A>
</font></td>
<td ALIGN=RIGHT><font FACE="Arial,Helvetica" SIZE=2>
<A HREF="http://www.fi.muni.cz/~kas/">Jan "Yenya" Kasprzak</A>
<A HREF="mailto:kas\@fi.muni.cz">&lt;kas\@fi.muni.cz&gt;</A>
</font></td>
</tr>
</table>
EOF
print '<!--$Id: mrtg-rrd.cgi,v 1.35 2003/08/18 15:58:57 kas Exp $-->', "\n";
}

sub dump_targets() {
for my $tgt (keys %targets) {
print "Target $tgt:\n";
for my $opt (keys %{$targets{$tgt}}) {
if ($opt eq 'options') {
print "\toptions: ";
for my $o1 (keys %{$targets{$tgt}{options}}) {
print $o1, ",";
}
print "\n";
next;
}
print "\t$opt: ", $targets{$tgt}{$opt}, "\n";
}
}
}

sub dump_directories {
print "Directories:\n";

for my $dir (keys %directories) {
print "Directory $dir:\n";
for my $item (@{$directories{$dir}}) {
print "\t$item\n";
}
}
}

sub print_error(@)
{
print "Content-Type: text/plain\n\nError: ", join(' ', @_), "\n";
exit 0;
}

#--BEGIN CGI--
#For CGI, use this:

use CGI;
my $q = new CGI;

# thttpd fix up by Akihiro Sagawa
if ($q->server_software() =~ m|^thttpd/|) {
my $path = $q->path_info();
$path .= '/' if ($q->script_name=~ m|/$|);
$q->path_info($path);
}

handler($q);

#--END CGI--
#--BEGIN FCGI--
# For FastCGI, uncomment this and comment out the above:
#-# use FCGI;
#-# use CGI;
#-#
#-# my $req = FCGI::Request();
#-#
#-# while ($req->Accept >= 0) {
#-# my $q = new CGI;
#-#
#-# # thttpd fix up by Akihiro Sagawa
#-# if ($q->server_software() =~ m|^thttpd/|) {
#-# my $path = $q->path_info();
#-# $path .= '/' if ($q->script_name=~ m|/$|);
#-# $q->path_info($path);
#-# }
#-#
#-# handler($q);
#-# }
#--END FCGI--

1;


Few Tips

Make sure you install rrdtool before using it 🙂

apt-get -y install rrdtool

To execute MRTG with command manually

env LANG=C mrtg /etc/mrtg.cfg

To assign apache www-data users full rights to /var/www/mrtg folder

chown -R www-data /var/www/mrtg/

SNMPWALK sample command to query any OID from snmp enabled target machine

snmpwalk -v1 -c gt 10.0.0.1  .1.3.6.1.2.1.25.2.3.1.6.65536

CFGMAKER command to create interface commands (this will support acquiring data larget then 120 M limit) as explained here. ( https://aacable.wordpress.com/tag/mrtg-120m-limit/ )

cfgmaker public@10.0.0.1:::::2 > temp.cfg

MRTG crontab sample (1 minute interval when using RRD)

*/1 * * * * env LANG=C mrtg /etc/mrtg.cfg –logging /var/log/mrtg.log

Enable CGI

sudo a2enmod cgi

Can’t locate RRDs.pm in @INC

SOLUTION:

sudo apt-get install librrds-perl


Some Sample Outputs !

WAN Usage: [Below …]

1-wan

Noc Room Temperature

1- noc room temp.PNG

Disk Space Used in Percent %

2- disk space of mail.PNG

Active PPP Users on CCR_1036

2-active-pppoe

CPU Usage of CCR_1036

3-mt-cpu-load

PING to Google DNS via CCR_1036

4-ping

Memory Usage of CCR_1036

5-memory

RB Temperature of CCR_1036

6-ccr-1036-rb-board-temperature

RB Voltage history of CCR_1036

7-rb-ccr-1036-voltage

SERVER UPTIME

3- server uptime.PNG

Memory Usage of Linux System

8-radius-memory


Regard’s
~ Syed Jahanza!B ~

October 31, 2013

Monitoring Mikrotik with Linux base MRTG [References]

Filed under: Linux Related, Mikrotik Related — Tags: , , , , — Syed Jahanzaib / Pinochio~:) @ 2:23 PM

Following post is about how to monitor Mikrotik System / Routerboard to create live graphs with history for every aspect, like CPU, Memory, Voltage, Amps, Interface etc. It comes very useful when you remotely administrating the system and you need to know what happens over the period of time and have an overview on every internal instance of the system.

I wrote another article that explains well on howto install MRTG in Ubuntu and monitor anything with it, but WORDPRESS coding ruins the code badly, but still you can get idea from it from following URL.

https://aacable.wordpress.com/tag/mrtg-installation-on-ubuntu/

So basically you have to first install MRTG on linux, then you can use that mrtg installation to monitor any devices in your network (which supports snmp that includes mikrotik as well)


1- MIKROTIK SECTION

 

# Enabling SNMP on Mikrotik

First Enable SNMP service (with public as community string or whatever you like on your mikrotik. Make sure no firewall rule is blocking SNMP traffic from/to the monitoring server.

Open Terminal in mikrotik and paste the following command. Or you can configure it via going to IP > SNMP


/snmp community
set [ find default=yes ] addresses=0.0.0.0/0 authentication-password="" authentication-protocol=MD5 encryption-password="" encryption-protocol=DES name=public read-access=yes \
security=none write-access=no

/snmp
set contact=Syed-Jahanzaib enabled=yes engine-id="" location=aacable-wordpress-com trap-community=public trap-generators="" trap-target="" trap-version=1

Now proceed further 🙂


 

1. Monitoring Mikrotik Simple Queue with Linux base MRTG (for monitoring performance and track purposes)

To monitor simple QUEUE via Linux base MRTG, you can use following config as an example 😀

First you have to check your simple queue OID (numeric value which is used by MRTG to poll specific data from the remote server) in mikrotik. for example you have a queue to allow 100M for proxy cached marked packets.

sq-ch

Open Terminal and print OID for Simple Queue number 0 (your can be changed, but in general Queue for proxy hit packets should be on ** TOP ** )


[admin@MikroTik] /queue simple print

Flags: X - disabled, I - invalid, D - dynamic

0    ;;; Simple Queue for squid Proxy CACHE HIT Packet Allow Unlimited Bandwidth
name=SQ-for-cache-hit interface=all parent=none packet-marks=Cache direction=both priority=8 queue=default-small/default-small limit-at=0/0 max-limit=100M/100M burst-limit=0/0
burst-threshold=0/0 burst-time=0s/0s total-queue=default-small

Now check its OID value which will be used in MRTG to graph the values. Do remember, If you move/delete/add particular Queue , the oid value will change, simply note down new value and recreate graphs with the new values. Also on every system these OIDs are different, simply take a print oid to use them.


[admin@MikroTik] /queue simple print oid

Flags: X - disabled, I - invalid, D - dynamic

0    ;;; Simple Queue for squid Proxy CACHE HIT Packet Allow Unlimited Bandwidth
name=.1.3.6.1.4.1.14988.1.1.2.1.1.2.12534 bytes-in=.1.3.6.1.4.1.14988.1.1.2.1.1.8.12534 bytes-out=.1.3.6.1.4.1.14988.1.1.2.1.1.8.12534 packets-in=.1.3.6.1.4.1.14988.1.1.2.1.1.10.12534
packets-out=.1.3.6.1.4.1.14988.1.1.2.1.1.11.12534 queues-in=.1.3.6.1.4.1.14988.1.1.2.1.1.12.12534 queues-out=.1.3.6.1.4.1.14988.1.1.2.1.1.13.12534

As showed in the image below . . .

qu-oid

Now we have to take bytes-in and bytes-out value for graphing purposes. which is in our case are

.1.3.6.1.4.1.14988.1.1.2.1.1.8.12534
.1.3.6.1.4.1.14988.1.1.2.1.1.9.12534

Now you can simply use the below CFG file to create Graphs for the mikrotik simple queue.


Target[mikrotik-simple-queue-in-out]: .1.3.6.1.4.1.14988.1.1.2.1.1.8.12534&amp;.1.3.6.1.4.1.14988.1.1.2.1.1.9.12534:public@10.0.0.1
MaxBytes[mikrotik-simple-queue-in-out]: 100000000
#Options[mikrotik-simple-queue-in-out]: bits
Title[mikrotik-simple-queue-in-out]: Mikrotik QUEUE for Cache HIT
PageTop[mikrotik-simple-queue-in-out]: <H1>Mikrotik QUEUE for Cache HIT</H1>
#Maintainer: Syed Jahanzaib

 

SAMPLE GRAPH FOR THE SIMPLE QUEUE MONITORING VIA MRTG (AS SHOWED ABOVE)

QUEUE-SAMPLE


Sample Graphs for ccr_1036

Following are sample graphs showing various aspects of Mikrotik CCR 1036.

 

1- cpu

 

active users

 

combined wan usage for 3 dsl

 

cpu tempr

 

memory monitor

 

ping status

 

rb amp

 

rb fan-2 speed rpm

 

rb tempr

 

rb voltage

 

rb watts

 


 

SAMPLE CFG FILES (WORKING GOOD WITH MIKROTIK CCR_1036  ROUTERBOARD MODEL.

 

Mikrotik CCR General  OID’s

active-fan: .1.3.6.1.4.1.14988.1.1.3.9.0
voltage: .1.3.6.1.4.1.14988.1.1.3.8.0
temperature: .1.3.6.1.4.1.14988.1.1.3.10.0
processor-temperature: .1.3.6.1.4.1.14988.1.1.3.11.0
current: .1.3.6.1.4.1.14988.1.1.3.13.0
power-consumption: .1.3.6.1.4.1.14988.1.1.3.12.0
psu1-state: .1.3.6.1.4.1.14988.1.1.3.15.0
psu2-state: .1.3.6.1.4.1.14988.1.1.3.16.0
18.0 is i guess fan2 rpm oid, not mentioned in print oid.</pre>
<pre>uptime: .1.3.6.1.2.1.1.3.0
total-memory: .1.3.6.1.2.1.25.2.3.1.5.65536
used-memory: .1.3.6.1.2.1.25.2.3.1.6.65536
cpu-frequency: .1.3.6.1.4.1.14988.1.1.3.14.0
build-time: .1.3.6.1.4.1.14988.1.1.7.6.0

 

# 192.168.100.2 is Mikrotik RB IP Address
# MRTG sample cfg files for Mikrotik CCR Routerboard monitoring
# Syed Jahanzaib
# Last Modified at 14th-JAN-2016

### MIKROTIK CCR_1036 192.168.100.2 CPU load ###
Target[192.168.100.2_cpu]: 1.3.6.1.2.1.25.3.3.1.2.1&1.3.6.1.2.1.25.3.3.1.2.1:public@192.168.100.2
AbsMax[192.168.100.2_cpu]: 100
MaxBytes[192.168.100.2_cpu]: 100
Title[192.168.100.2_cpu]: MIKROTIK CCR_1036 CPU load
PageTop[192.168.100.2_cpu]: <H1>MIKROTIK CCR_1036 CPU load</H1>
Options[192.168.100.2_cpu]: gauge,growright,nopercent, noo
YLegend[192.168.100.2_cpu]: CPU load
ShortLegend[192.168.100.2_cpu]: %
LegendI[192.168.100.2_cpu]: CPU load (percentage)

# MIKROTIK CCR_1036 PPPoE ACTIVE Users
Target[mtPPPoEusers]: 1.3.6.1.4.1.9.9.150.1.1.1.0&1.3.6.1.4.1.9.9.150.1.1.1.0:public@192.168.100.2
Title[mtPPPoEusers]: Active PPPoE Users on MIKROTIK CCR_1036
PageTop[mtPPPoEusers]: <H1>Active PPPoE Users on MIKROTIK CCR_1036</H1>
MaxBytes[mtPPPoEusers]: 1000
Colours[mtPPPoEusers]: B#8888ff,B#8888ff,B#5398ff,B#5398ff
Options[mtPPPoEusers]: gauge,nopercent,noo,integer,growright
LegendI[mtPPPoEusers]: Active PPPoE Users on MT
LegendO[mtPPPoEusers]:
YLegend[mtPPPoEusers]: Active PPPoE Users on MT
Legend1[mtPPPoEusers]: Active PPPoE Users on MT
Legend2[mtPPPoEusers]:

### MONITORING MIKROTIK CCR_1036 CPU TEMP ###
Target[mt.cpu.temp]: 1.3.6.1.4.1.14988.1.1.3.11.0&1.3.6.1.4.1.14988.1.1.3.11.0:public@192.168.100.2 / 10
Options[mt.cpu.temp]: gauge, growright, nopercent, noinfo
MaxBytes[mt.cpu.temp]: 100
Colours[mt.cpu.temp]: RED#ff4f27,Y#fffb15,RED#ff4f27,RED#fffb15
#Unscaled[mt.cpu.temp]: dwmy
YLegend[mt.cpu.temp]: CPU Temprature
Title[mt.cpu.temp]: MIKROTIK CCR_1036 RB CPU Temprature
PageTop[mt.cpu.temp]: <H1>MIKROTIK CCR_1036 RB CPU Temprature</H1>
ShortLegend[mt.cpu.temp]: c
LegendI[mt.cpu.temp]:
LegendO[mt.cpu.temp]: CPU Temp
Legend1[mt.cpu.temp]: CPU Temprature
Legend2[mt.cpu.temp]: CPU Temprature

### MONITORING MIKROTIK CCR_1036 RB TEMP ###
Target[mt.rb.temp]: 1.3.6.1.4.1.14988.1.1.3.10.0&1.3.6.1.4.1.14988.1.1.3.10.0:public@192.168.100.2 / 10
Options[mt.rb.temp]: gauge, growright, nopercent, noinfo
MaxBytes[mt.rb.temp]: 100
Colours[mt.rb.temp]: RED#ff4f27,Y#fffb15,RED#ff4f27,RED#fffb15
#Unscaled[mt.rb.temp]: dwmy
YLegend[mt.rb.temp]: RB Temprature
Title[mt.rb.temp]: MIKROTIK CCR_1036 Router Board Temprature
PageTop[mt.rb.temp]: <H1>MIKROTIK CCR_1036 Router Board Temprature</H1>
ShortLegend[mt.rb.temp]: c
LegendI[mt.rb.temp]:
LegendO[mt.rb.temp]: RB Temp
Legend1[mt.rb.temp]: RB Temprature
Legend2[mt.rb.temp]: RB Temprature

### MONITORING MIKROTIK CCR_1036 VOLTAGE Monitor ###
Target[mt-voltage]: .1.3.6.1.4.1.14988.1.1.3.8.0&1.3.6.1.4.1.14988.1.1.3.8.0:public@192.168.100.2 / 10
Options[mt-voltage]: gauge, growright, nopercent, noinfo
MaxBytes[mt-voltage]: 1000
Colours[mt-voltage]: RED#ff4f27,Y#fffb15,RED#ff4f27,RED#fffb15
#Unscaled[mt-voltage]: dwmy
YLegend[mt-voltage]: Voltaeg Monitor
Title[mt-voltage]: MIKROTIK CCR_1036 Cloudcore RB VOLTAGE Monitor
PageTop[mt-voltage]: <H1>MIKROTIK CCR_1036 Cloudcore RB VOLTAGE Monitor</H1>
ShortLegend[mt-voltage]: c
LegendI[mt-voltage]:
LegendO[mt-voltage]: Voltage
Legend1[mt-voltage]: Voltage
Legend2[mt-voltage]: Voltage

### MONITORING MIKROTIK CCR_1036 power.consumption Watt Usage Monitor ###
Target[mt-powerconsumption]: .1.3.6.1.4.1.14988.1.1.3.12.0&.1.3.6.1.4.1.14988.1.1.3.12.0:public@192.168.100.2 / 10
Options[mt-powerconsumption]: gauge, growright, nopercent, noinfo
MaxBytes[mt-powerconsumption]: 1000
Colours[mt-powerconsumption]: RED#ff4f27,Y#fffb15,RED#ff4f27,RED#fffb15
#Unscaled[mt-powerconsumption]: dwmy
YLegend[mt-powerconsumption]: Watts USAGE Monitor
Title[mt-powerconsumption]: MIKROTIK CCR_1036 Cloudcore RB Watts Usage Monitor
PageTop[mt-powerconsumption]: <H1>MIKROTIK CCR_1036 Cloudcore RB Watts Usage Monitor</H1>
ShortLegend[mt-powerconsumption]: W
LegendI[mt-powerconsumption]:
LegendO[mt-powerconsumption]: power.consumption.wts
Legend1[mt-powerconsumption]: power.consumption.wts
Legend2[mt-powerconsumption]: power.consumption.wts

### MONITORING MIKROTIK CCR_1036 FAN-2 Speed Monitor ###
Target[mt-FAN2-speed]: 1.3.6.1.4.1.14988.1.1.3.18.0&1.3.6.1.4.1.14988.1.1.3.18.0:public@192.168.100.2
Options[mt-FAN2-speed]: gauge, growright, nopercent, noinfo
MaxBytes[mt-FAN2-speed]: 10000
Colours[mt-FAN2-speed]: RED#ff4f27,Y#fffb15,RED#ff4f27,RED#fffb15
#Unscaled[mt-FAN2-speed]: dwmy
YLegend[mt-FAN2-speed]: Watts USAGE Monitor
Title[mt-FAN2-speed]: MIKROTIK CCR_1036 Cloudcore RB FAN-2 Speed Monitor
PageTop[mt-FAN2-speed]: <H1>MIKROTIK CCR_1036 Cloudcore RB FAN-2 Speed Monitor</H1>
ShortLegend[mt-FAN2-speed]: RPM
LegendI[mt-FAN2-speed]:
LegendO[mt-FAN2-speed]: fan-2.rpm.speed
Legend1[mt-FAN2-speed]: fan-2.rpm.speed
Legend2[mt-FAN2-speed]: fan-2.rpm.speed

### MONITORING MIKROTIK CCR_1036 AMP Monitor ###
Target[mt-amp-mon]: 1.3.6.1.4.1.14988.1.1.3.13.0&1.3.6.1.4.1.14988.1.1.3.13.0:public@192.168.100.2 / 1000
Options[mt-amp-mon]: gauge, growright, nopercent, noinfo
MaxBytes[mt-amp-mon]: 10000
Colours[mt-amp-mon]: RED#ff4f27,Y#fffb15,RED#ff4f27,RED#fffb15
#Unscaled[mt-amp-mon]: dwmy
YLegend[mt-amp-mon]: AMP USAGE Monitor
Title[mt-amp-mon]: MIKROTIK CCR_1036 Cloudcore RB AMP Monitor
PageTop[mt-amp-mon]: <H1>MIKROTIK CCR_1036 Cloudcore RB AMP Monitor</H1>
ShortLegend[mt-amp-mon]: amp
LegendI[mt-amp-mon]:
LegendO[mt-amp-mon]: amp.speed
Legend1[mt-amp-mon]: amp.speed
Legend2[mt-amp-mon]: amp.speed

# Percent of MIKROTIK CCR_1036 memory used
Target[ccr_server_mem_ram]: ( 1.3.6.1.2.1.25.2.3.1.6.65536&1.3.6.1.2.1.25.2.3.1.6.65536:public@192.168.100.2) * 100 / ( 1.3.6.1.2.1.25.2.3.1.5.65536&1.3.6.1.2.1.25.2.3.1.5.65536:public@192.168.100.2)
Title[ccr_server_mem_ram]: Memory usage for MIKROTIK CCR_1036 CCR Server
PageTop[ccr_server_mem_ram]: <H1>Memory usage for MIKROTIK CCR_1036 CCR Server</H1>
MaxBytes[ccr_server_mem_ram]: 100
ShortLegend[ccr_server_mem_ram]: %
YLegend[ccr_server_mem_ram]: % of Memory
Legend1[ccr_server_mem_ram]: Used Memory
LegendI[ccr_server_mem_ram]: Used :
LegendO[ccr_server_mem_ram]:
Options[ccr_server_mem_ram]: nopercent, gauge, integer, growright
Unscaled[ccr_server_mem_ram]: ymwd

 


TIP:

Combined pppoe users active on two Mikrotik

# Combined users of two mikrotik mrtg graph
Target[mt_1_2_ppp_combined]: 1.3.6.1.4.1.9.9.150.1.1.1.0&1.3.6.1.4.1.9.9.150.1.1.1.0:zaib@192.168.0.1 + 1.3.6.1.4.1.9.9.150.1.1.1.0&1.3.6.1.4.1.9.9.150.1.1.1.0:zaib@192.168.0.2
Title[mt_1_2_ppp_combined]: Combined Active PPPoE Users on Both ZAIB CCR - 192.168.0.1+2
PageTop[mt_1_2_ppp_combined]: <H1>Combined Active PPPoE Users on Both ZAIB CCR - 192.168.0.1+2</H1>
MaxBytes[mt_1_2_ppp_combined]: 3000
Colours[mt_1_2_ppp_combined]: B#8888ff,B#8888ff,B#5398ff,B#5398ff
Options[mt_1_2_ppp_combined]: growright,nopercent,gauge,integer,nobanner,printrouter,pngdate,noo
LegendI[mt_1_2_ppp_combined]: Active Users
LegendO[mt_1_2_ppp_combined]:
YLegend[mt_1_2_ppp_combined]: Active PPPoE Users on both MT
Legend1[mt_1_2_ppp_combined]: Active PPPoE Users on both MT
Legend2[mt_1_2_ppp_combined]:
ShortLegend[mt_1_2_ppp_combined]: .
#Unscaled[mt_1_2_ppp_combined]: dwmy

Regard’s
Syed Jahanzaib

%d bloggers like this: