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 ~

Kannel SMS gateway with multiple smsc/modems & CMS Err 330

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

kannellogo

 

Following are reference guide on how you can run Kannel with multiple modems / smsc.
For basic / initial level of KANNEL configuration, please refer to the internet or my previous post at …

https://aacable.wordpress.com/2012/11/26/howto-configure-your-local-http-gateway-using-kannel-on-ubuntu/


Scenario:

Kannel is working as an SMS gateway to send/receive SMS. There is one Serial Modem connected with it using ttyS0 port. Another USB GSM Modem have been added recently which is installed as ttyUSB2.

Now we want to send SMS some specific announcement base messages via Modem#1 and some complaint system messages via Modem#2.


Hardware Used:

OS = Ubuntu 12.4 / Server Edition / x86 [Its support may end in 2017]

SMS Gateway App = Kannel 1.4.3

Modems:

1# Teltonika Modem Model = ModemCOM-G10 [Reliable & solid in performance, serial modems are good and easily configurable]
http://www.teltonika.lt/product/modemcom-g10/

modemcom-g10

 

2# TP-Link MA260 USB GSM Modem [Problematic, avoid using such cheap brands like tplink/dlink etc]
http://www.tp-link.com/en/products/details/cat-5090_MA260.html

 

ma260-01


KANNEL.CONF  Sample


# KANNEL config sample for multiple modem / SMSC
# Syed Jahanzaib

# MODEM 1 / Teltonika Brand / Type : Serial
group = smsc
smsc = at
smsc-id = zaibgsmid1
modemtype = teltonika
device = /dev/ttyS0
transceiver-mode = true
speed = 115200
# MODEM 2 / TP-LINK MA260 Brand / Type : USB
group = smsc
smsc = at
smsc-id = zaibgsmid2
modemtype = teltonika
device = /dev/ttyUSB2
transceiver-mode = true
speed = 115200
# for other models/modems adjust the INIT strings accordingly
group = modems
id = teltonika
name = "Teltonika E12"
detect-string = "Undefined"
enable-mms = true
#init-string = "AT+CNMI=2,2,0,1,1"
init-string = "ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0;+CNMI=1,3,2,2,1;+CMEE=1;+CPMS="SM","SM","SM";+CMGF=0"
speed = 115200
message-storage = "SM"

Make sure to restart the Kannel service and watch the bearerbox.log available at /var/log/kannel for any errors.


Howto send SMS via specific SMSC / MODEM

Now to send SMS using HTTP API method , via modem # 1 or 2, we will use its smsc ID as shown in example below … focus on smsc= part

MODEM#1

http://KANNEL-IP:13013/cgi-bin/sendsms?smsc=zaibgsmid1&username=KANNELID&password=KANNEL-PASSWORD&to=MOBILENO&text=TEST+MSG+FROM+MODEM+1

MODEM#2

http://KANNEL-IP:13013/cgi-bin/sendsms?smsc=zaibgsmid2&username=KANNELID&password=KANNEL-PASSWORD&to=MOBILENO&text=TEST+MSG+FROM+MODEM+2

 


CMS ERROR 330

I was getting cms error 330 in bearerbox.log when sending SMS with specific model of TP-LINK MA260 ,
As shown below …

cms-error

2016-12-01 08:24:05 [13727] [6] DEBUG: AT2[zaibgsmid1]: <– +CMS ERROR: 330
2016-12-01 08:24:05 [13727] [6] ERROR: AT2[zaibgsmid1]: CMS ERROR: +CMS ERROR: 330
2016-12-01 08:24:05 [13727] [6] ERROR: AT2[zaibgsmid1]: CMS ERROR: SMSC address unknown (330)

With some troubleshooting using minicom, I found out that the message center service number was not set, Strange, it should get the value auto (which is normal with other modems) , any way I sort this issue as following

(For ZONG SIM)

AT+CSCA=”+923040000011″

# now Query CSCA number

AT+CSCA?

and you should get the center number fine.

Now send sms using minicom AT commands

# Sets text mode
AT+CMGF=1  
# Now input the msg we want to send to recipient
AT+CMGS=”03333021909″
# Press Enter, and type your desired msg
TEST MSG FROM ZAIB
# Press CTRL+Z and it will send the SMS to the specified number

Some other useful minicom / AT commands with minicom

# To configure Modem ports/etc
minicom -s

General Check
AT

Getting IMEI
AT+CGSN
AT+CIMI

Getting Signal quality and battery charge status
AT+CSQ
AT+CBC

Model Inquiry
AT+CGMI

MINICOM USSD
T+CUSD=1,”your_service_code”,15

so for your case

AT+CUSD=1,”*555*87*1234#
and don’t forget the 15 at the end , this question helped me to solve the problem.

5 Modem At Command Examples in Linux (How to Configure Minicom)


Regard’s
Syed Jahanzaib

November 25, 2016

Mikrotik: Switching between interface

Filed under: Mikrotik Related, Uncategorized — Syed Jahanzaib / Pinochio~:) @ 2:18 PM

f

NOT FOR GENERAL PUBLIC

SHARING IT FOR PERSONAL REFERENCE


Following is an script that switches connectivity between two interfaces (usually wan).

Example: ISP x have provided internet connectivity via fiber link . as a backup / redundancy link ISP have provided second fiber line. Both link have same ip address with same gateway. There are other ways too , this is just one way of achieving task. it can be customized in many ways like forcing specific hosts to be monitored by ping and act accordingly , or by having bridge etc.

IP address example:

in /ip address >>>

sfp1 = 10.0.0.1 (with comments “PRIMARY-SFP1”)
[remains enabled until next status changed]

sfp2 = 10.0.0.1 (with comments “SECONDARY-SFP2”)
[remains enabled until next status changed]

The script will do following.

Check the SFP1 Status, if it does not found “linkok” reply, it will log event, send email/sms alert to admin, and switch to SFP2 port by enabling IP of SFP2 and enable ip on SFP1.

On next run, if it found SFP1 link ok, then it will log event, send email/sms alert to admin, and switch back to SFP1 port by enabling IP of SFP1 and disable ip on SFP2.


# Mikrotik SPF Link monitoring SCRIPT with optional Email and SMS Alert,
# We are using local KANNEL as SMS gateway and GMAIL as email sender
# by Syed Jahanzaib
# https://aacable.wordpress.com
# Email : aacable at hotmail dot com
# Script Last Modified : 25-NOV-2016 / 1400 hours

# PRIMARY FIBER LINK
:local INT "sfp1"
# SECONDARY BACKUP FIBER LINK
:local INT2 "sfp2"
:local i 0;
:local F 0;
:local date;
:local time;
:global sub1 ([/system identity get name])
:global sub2 ([/system clock get time])
:global sub3 ([/system clock get date])
:set date [/system clock get date];
:set time [/system clock get time];
:local cell1 "03333021909"
:global FIBERnetstatus;
:global FIBERnetlastchange;
:global FIBERIP;

# Company Name, do not use spaces in it
:local COMPANY "ZZZ"

# Setting GMAIL config
:local sub1 ([/system identity get name])
:local sub2 ([/system clock get date])
:local sub3 ([/system clock get time])
:local TO1 "RECIPIENT-1@hotmail.com"
:local gmailid "YOUR-GMAIL-ID@gmail.com"

#If you dont have kannel sms gateway ignore this.
:local KURL "http://10.0.0.1:13013/cgi-bin/sendsms"
:local KID "kannel"
:local KPASS "KANNEL-PASS"

# SMS DOWN status Msg format for Kannel SMS gateway (donot use spaces in it)
:local MSGDOWNSMS "$COMPANY+FIBER+ALERT:%0A$INT+fiber+is+now+DOWN.%0ASwitching+to+$INT2+backup+fiber+link"
# SMS UP status Msg format for Kannel SMS gateway (donot use spaces in it)
:local MSGUPSMS "$COMPANY+FIBER+INFO:%0A$INT+fiber+is+now+UP.%0ASwitching+back+to+$INT"

# EMAIL Msg format for FIBER DOWN
:local MSGDOWNEMAIL "$COMPANY FIBER ALERT: $INT fiber is now DOWN at $sub1 $sub2 $sub3 . Switching to $INT2 backup link. Please Verify it."
# EMAIL Msg format for FIBER UP
:local MSGUPEMAIL "$COMPANY FIBER INFO: $INT fiber is now UP at $sub1 $sub2 $sub3 . Switching back to $INT primary link. Please Verify it."
# LOG error
:local DOWNLOG1 "$COMPANY FIBER ALERT: $INT fiber is now DOWN at $sub1 $sub2 $sub3 . Switching to $INT2 backup link. Please Verify it."
:local UPLOG1 "$COMPANY FIBER INFO: $INT fiber is now UP at $sub1 $sub2 $sub3 . Switching back to $INT primary link. Please Verify it"

######################################
########## Start the SCRIPT ###############
########## DONOT EDIT BELOW ############
######################################

local link;
/interface ethernet cable-test $INT once do={
:set link $"status";
};

:if ($link != "link-ok") do={
:log error "$INT SFP DOWN"

:if (($FIBERnetstatus="UP")) do={
:set FIBERnetstatus "DOWN";

# Also add status in global variables to be used as tracking
:set date [/system clock get date];
:set time [/system clock get time];
:set FIBERnetlastchange ($time . " " . $date);
##################################################
####### FOR DOWN STATUS, CHANGE THE RULES ########
##################################################

# If the link is down, then LOG info and warning in Mikrotik LOG window [Zaib]
:log error "$DOWNLOG1"

# DOWN ACTION , shifting to SFP2 , backup link
/ip address set disabled=yes [find comment="PRIMARY-SFP1"]
/ip address set disabled=no [find comment="BACKUP-SFP2"]

# Adding delay so gateway should be reachable properly
:delay 5s;

:global gmailsmtp
:set gmailsmtp [:resolve "smtp.gmail.com"];

# "Emailing the DOWN status. . . "
/tool e-mail send to=$TO1 subject="$MSGDOWNEMAIL" start-tls=yes body="$MSGDOWNEMAIL"

# Send SMS the DOWN status via KANNEL
/tool fetch url="$KURL\?username=$KID&password=$KPASS&to=$cell1&text=$MSGDOWNSMS"

##################################################
####### FOR UP STATUS, CHANGE THE RULES ########
##################################################

} else={:set FIBERnetstatus "DOWN";}
} else={
:if (($FIBERnetstatus="DOWN")) do={

:set FIBERnetstatus "UP";

# If link is UP, then LOG info and warning in Mikrotik LOG window [Zaib]
:log warning "$UPLOG1"

# UP ACTION , shifting back to SFP1
/ip address set disabled=no [find comment="PRIMARY-SFP1"]
/ip address set disabled=yes [find comment="BACKUP-SFP2"]

# Adding delay so gateway should be reachable properly
:delay 5s;
:global gmailsmtp
:set gmailsmtp [:resolve "smtp.gmail.com"];

# "Emailing the UP status. . . "
/tool e-mail send to=$TO1 subject="$MSGUPEMAIL" start-tls=yes body="$MSGUPEMAIL"

# Send SMS via KANNEL Gateway
/tool fetch url="$KURL\?username=$KID&password=$KPASS&to=$cell1&text=$MSGUPSMS"

:set date [/system clock get date];
:set time [/system clock get time];
:set FIBERnetlastchange ($time . " " . $date);

} else={:set FIBERnetstatus "UP";}
}

November 22, 2016

Query Windows SAP Server Instance status via Linux

Filed under: Linux Related, Microsoft Related — Tags: , , — Syed Jahanzaib / Pinochio~:) @ 3:37 PM

sap.jpg

linux_pengiun

Background:

We are using SAP ECC6 on Windows 2008 R2 64bit server in our data center.
The landscape is as below …

  • PRODUCTION
  • DEV [with 2 instances to support our and parent company as well]
  • Q.A  [with 2 instances to support our and parent company as well]
  • DATA GUARD [For PRD Backup/Replication]

Problem:

During the deployment , we were having strange issue that sometimes any single instance at DEV or QAS stops and we get to know it quite late as it was oftenly happening in saturday sundays when no one is available at data center.


Solution:

Therefore I made the following Linux bash script which performs the following functions >

  1. Check Server PING for its availability
  2. Check ORACLE and SAP services and add there name and status in the INFO
  3. Check SAP Instance using sapcontrol command Using WINEXE (Linux utility to execute command on remote windows pc. If it donot find 3 GREEN status in the instance query, then SEND email / SMS to admin.
  4. At next run, check if service/instance is still down, and the alert is already been sent, do not repeat the alert sending until next status changes.
  5. At next run, if the service/instance is UP, then send alert.

Requirements:

  1. Linux (any flavor, I used Ubuntu 12.4 in this guide)
  2. Winexe (Linux utility to execute command on remote windows pc via Linux CLI)
  3. NET RPC tools to check remote windows services via Linux CLI
  4. sendEMAIL tool (To send email using your GMAIL account, you can use your own app too)
  5. KANNEL SMS Gateway (To send sms, you can change it , or disable it if not required)

TO DO LIST


the SCRiPT ~

#!/bin/bash
# Script to check windows based SAP SERVER PING / SERVICES and INSTANCE status via linux query
# and send sms/email accordingly using NET RPC / winexe / sendEmail / Kannel
# Created by SYED JAHANZAIB / aacable at hotmail dot com
# CREATED: 21-NOV-2016
# LAST REVISION: 26-NOV-2016
#set -x

ESC_SEQ="\x1b["
COL_RESET=$ESC_SEQ"39;49;00m"
COL_RED=$ESC_SEQ"31;01m"
COL_GREEN=$ESC_SEQ"32;01m"
COL_YELLOW=$ESC_SEQ"33;01m"
DATE=`date`

# TEMPORARY FOLDER NAME
TEMP="temp"

# Checking if $TEMP folder is previously present or not [To hold all status]. . .
{
if [ ! -d "/$TEMP" ]; then
echo
echo -e "$COL_RED /$TEMP folder not found, Creating it so all TEMPORARY STATUS / HOLDERS will be placed there . . . $COL_RESET"
mkdir /$TEMP
else
echo > /dev/null
fi
}

# SAP-SERVER RELATED INFO
SAPSRVIP="10.0.0.1"
SAPSRVNAME="SAP - DEV-0"
DOMAIN="DOMAIN-OR-WORKGROUP"
ADMINID="ADMINID"
ADMINPASS="PASSWORD"
# You must change following path to point sapcontrol.exe
COMPATH="E:\usr\sap\R3d\DVEBMGS00\exe\sapcontrol"
WINEXE="/usr/sbin/winexe"
# If you have multiple instance, you may change it as required
INSTANCE="0"
SRV1="OracleServiceR3D"
SRV2="OracleServiceODV"
SRV1_STATUS1="/$TEMP/$SAPSRVIP.$INSTANCE.$SRV1.status1.txt"
SRV1_STATUS2="/$TEMP/$SAPSRVIP.$INSTANCE.$SRV1.status2.txt"
SRV2_STATUS1="/$TEMP/$SAPSRVIP.$INSTANCE.$SRV2.status1.txt"
SRV2_STATUS2="/$TEMP/$SAPSRVIP.$INSTANCE.$SRV2.status2.txt"

# COMPANY NAME
COMPANY="-ZAIB-"
FOOTER="Powered by $COMPANY SYS ADMIN"

# KANNEL SMS Gateway Info to send SMS if required
KANNELURL="KANNEL-IP:13013"
KANNELID="kannel"
KANNELPASS="PASSWORD"
# ZAIB CELL
CELL1="03333021909"

# GMAIL DETAILS to send EMAIL alert
GMAILID="YOUR-GMAIL-ID@gmail.com"
GMAILPASS="GMAIL-PASS"
# Add recipient email address below
ADMINMAIL1="syed.jahanzaib@ZAIB"
SENDMAILAPP="/temp/sendEmail-v1.56/sendEmail"

# Various holders to store different Status
SENTSMSRESULT="/$TEMP/$SAPSRVIP.$INSTANCE.txt"
STATUS_HOLDER="/$TEMP/$SAPSRVIP.$INSTANCE.status.txt"
SRV_HOLDER="/$TEMP/$SAPSRVIP.$INSTANCE.srvstatus.txt"
SRV_HOLDER_HEADER="$SAPSRVNAME Services Status ..."
UPMSG="/$TEMP/$SAPSRVIP.$INSTANCE.up.sms"
DOWNMSG="/$TEMP/$SAPSRVIP.$INSTANCE.down.sms"
SAPINSTSTATUS1="/$TEMP/$SAPSRVIP.$INSTANCE.inst-try1.txt"
SAPINSTSTATUS2="/$TEMP/$SAPSRVIP.$INSTANCE.inst-try2.txt"
> $SAPINSTSTATUS1
> $SAPINSTSTATUS2
echo "" > $SRV_HOLDER
touch  $DOWNMSG
> $DOWNMSG
> $UPMSG
touch $STATUS_HOLDER

# Messages which will be sent via email/sms
MSG_UP_SUB="INFO: $SAPSRVNAME - $SAPSRVIP -  Instance $INSTANCE Restored"
MSG_DOWN_SUB="ALERT:$SAPSRVNAME - $SAPSRVIP -  Instance $INSTANCE IS DOWN"
MSG_SRV_DOWN_SUB="ALERT: $SAPSRVNAME - $SAPSRVIP -  One or More Services are Down DOWN"

#######################
### PING SECTION STARTS
#######################

PING_DOWN_MSG="ALERT: $AGPSRVNAME  $SAPSRVIP - PING COMMUNICATION FAILED @ $DATE"
PING_UP_MSG="INFO: $AGPSRVNAME  $SAPSRVIP - PING COMMUNICATION RESTORED - OK @ $DATE"
PING_NORMAL_MSG="INFO: $AGPSRVNAME  $SAPSRVIP - PING COMMUNICATION - OK @ $DATE"
PING_STATUS_HOLDER="/$TEMP/$SAPSRVIP.ping.status"
FINAL_PING_STATUS="1"
# PING Attempts
PING_ATEMPTS="5"

# Check if SERVER is accessibel or not, then inform if sms/email is already been sent / zaib
## IF PING FAILS  then send sms and email, IF NOT ALREAY SENT
echo -e "$COL_YELLOW>Checking PING response at $SAPSRVNAME $SAPSRVIP ... $COL_RESET"
if [[ $(ping -q -c $PING_ATEMPTS $SAPSRVIP) == @(*100% packet loss*) ]]; then
echo ""
FINAL_PING_STATUS="DOWN"
else
echo ""
FINAL_PING_STATUS="UP"
fi

echo $SRV_HOLDER_HEADER >> $SRV_HOLDER

##########################
### PING SECTION ENDS HERE
##########################

# If temporary status holder is not present , then create it,
# forumla is being applied to prevent repeated attempt of file creation / zaib
if [ ! -f $STATUS_HOLDER ]; then
echo -e "Creating Status Holder for first time usage"
touch $STATUS_HOLDER
fi

#######################################
#### QUERY SERVICES SECTION STARTS HERE
#######################################

# $SRV - 1 - CHECK
echo -e "$COL_YELLOW>Checking $SRV1 Service status at $SAPSRVNAME $SAPSRVIP ... Check#1 $COL_RESET"
# IF SAP SERVICE QUERY result is not running, then UPDATE its status accordingly
net rpc service status $SRV1 -I $SAPSRVIP --user=$DOMAIN/$ADMINID%$ADMINPASS |grep running > $SRV1_STATUS1
sleep 5
echo -e "$COL_YELLOW>Checking $SRV1 Service status at $SAPSRVNAME $SAPSRVIP ... Check#2 $COL_RESET"
net rpc service status $SRV1 -I $SAPSRVIP --user=$DOMAIN/$ADMINID%$ADMINPASS |grep running > $SRV1_STATUS2
QSRV1_STATUS1=`cat $SRV1_STATUS1 | grep -o running | wc -l`
QSRV1_STATUS2=`cat $SRV1_STATUS2 | grep -o running | wc -l`
if [[ $QSRV1_STATUS1 -gt 0 ]] && [[ $QSRV1_STATUS1 -gt 0 ]]; then
echo "$SRV1 UP" >> $SRV_HOLDER
else
echo "ALERT: $SRV1 Service is DOWN ...."
echo "$SRV1 DOWN" >> $SRV_HOLDER
fi

# $SRV - 2 - CHECK
# IF SAP SERVICE QUERY result is not running, then UPDATE its status accordingly
echo -e "$COL_YELLOW>Checking $SRV2 Service status at $SAPSRVNAME $SAPSRVIP ... Check#1 $COL_RESET"
net rpc service status $SRV2 -I $SAPSRVIP --user=$DOMAIN/$ADMINID%$ADMINPASS |grep running > $SRV2_STATUS1
sleep 5
echo -e "$COL_YELLOW>Checking $SRV2 Service status at $SAPSRVNAME $SAPSRVIP ... Check#2 $COL_RESET"
net rpc service status $SRV1 -I $SAPSRVIP --user=$DOMAIN/$ADMINID%$ADMINPASS |grep running > $SRV2_STATUS2
QSRV2_STATUS1=`cat $SRV2_STATUS1 | grep -o running | wc -l`
QSRV2_STATUS2=`cat $SRV2_STATUS2 | grep -o running | wc -l`
if [[ $QSRV2_STATUS1 -gt 0 ]] && [[ $QSRV2_STATUS1 -gt 0 ]]; then
echo "$SRV2 UP" >> $SRV_HOLDER
else
echo "ALERT: $SRV2 Service is DOWN ...."
echo "$SRV2 DOWN" >> $SRV_HOLDER
fi

echo "" >> $SRV_HOLDER

# MESSAGES
# SMS and email msg fromat for up n down
NORMSG="$SAPSRVNAME - Oracle Services and SAP instance $INSTANCE QUERY is OK !- All Seems OK @ $DATE!"
LOWMSG="ALERT: $SAPSRVNAME - $SAPSRVIP -  Instance $INSTANCE is DOWN @ $DATE"
OKMSG="INFO: $SAPSRVNAME - $SAPSRVIP -  Instance $INSTANCE is UP now @ $DATE"

##########################################################
#### CHECK FOR RUNNING SERVICES LIKE ORACLE and others ...
##########################################################
CHKGSRVDOWNSTATUS=`cat $SRV_HOLDER | grep -o DOWN | wc -l`
if [[ $CHKGSRVDOWNSTATUS -gt 0 ]]; then
echo -e "$COL_YELLOW >PING STATUS = $FINAL_PING_STATUS"
echo "PING STATUS = $FINAL_PING_STATUS" >> $DOWNMSG
echo "" >> $DOWNMSG
echo ""
echo -e "$COL_RED >ALERT: FOLLOWING SERVICES ARE DOWN ... $COL_RESET"
cat $SRV_HOLDER
NORMSG="$SAPSRVNAME - Oracle Services seems to be $COL_RED DOWN $COL_RESET BUT SAP instance $INSTANCE QUERY is $COL_GREN OK $COL_RESET ! @ $DATE!"

echo "ALERT: FOLLOWING SERVICES ARE DOWN ..." >> $DOWNMSG
cat $SRV_HOLDER >> $DOWNMSG

else
echo -e "$COL_GREEN>INFO: FOLLOWING SERVICES ARE UP at $SAPSRVNAME $SAPSRVIP ... $COL_RESET"
cat  $SRV_HOLDER
fi

#############################################################################
#############################################################################
###
### CHECK SAP INSTANCE FOR 3 GREEN COUNTS and inform if alert is already sent
###
#############################################################################
#############################################################################

# IF SAP INSTANCE QUERY result does not contains 3 GREEN, then send sms and email
# To prevent FALSE alaram, I have added double check : ) HURAAAH, Allah Shuker / zaib
echo -e "$COL_YELLOW> Testing SAP Instance No $INSTANCE ... Check#1 $COL_RESET"
$WINEXE --user=$DOMAIN/$ADMINID%$ADMINPASS //$SAPSRVIP "$COMPATH -nr $INSTANCE -function GetProcessList" > $SAPINSTSTATUS1
sleep 5
echo -e "$COL_YELLOW> Testing SAP Instance No $INSTANCE ... Check#2 $COL_RESET"
$WINEXE --user=$DOMAIN/$ADMINID%$ADMINPASS //$SAPSRVIP "$COMPATH -nr $INSTANCE -function GetProcessList" > $SAPINSTSTATUS2
sleep 1
CHKGREENSTATUS1=`cat $SAPINSTSTATUS1 | grep -o GREEN | wc -l`
CHKGREENSTATUS2=`cat $SAPINSTSTATUS2 | grep -o GREEN | wc -l`

if [[ $CHKGREENSTATUS1 -lt 3 ]] && [[ $CHKGREENSTATUS2 -lt 3 ]]; then
if  [ $(grep -c "TEMP" "$STATUS_HOLDER") -eq 1 ]; then
echo -e "$COL_RED>ALERT: $LOWMSG$COL_RESET"
echo "$LOWMSG" >> $DOWNMSG
echo "SMS/Email for DOWN have already been sent"

fi
fi

if [[ $CHKGREENSTATUS1 -lt 3 ]] && [[ $CHKGREENSTATUS2 -lt 3 ]]; then
if  [ $(grep -c "TEMP" "$STATUS_HOLDER") -eq 0 ]; then
echo  "ALERT: $LOWMSG
SENDING DOWN SMS/Email .... "
echo "$LOWMSG" > $DOWNMSG
echo "" >> $DOWNMSG
echo "PING STATUS = $FINAL_PING_STATUS" >> $DOWNMSG
cat $SRV_HOLDER >> $DOWNMSG
echo "" >> $DOWNMSG
echo "$FOOTER" >> $DOWNMSG
echo "TEMP" > $STATUS_HOLDER

# Sending DOWN Alert SMS/EMAIL
cat $DOWNMSG | curl "http://$KANNELURL/cgi-bin/sendsms?username=$KANNELID&password=$KANNELPASS&to=$CELL1" -G --data-urlencode text@-
$SENDMAILAPP -u "$MSG_DOWN_SUB" -o tls=yes -s smtp.gmail.com:587 -t $ADMINMAIL1 -xu $GMAILID -xp $GMAILPASS -f $GMAILID  -o message-file=$DOWNMSG -o message-content-type=text

fi
else

# SETTING NEW MSG
echo -e "$COL_GREEN $NORMSG ... $COL_RESET"
if  [ $(grep -c "TEMP" "$STATUS_HOLDER") -eq 1 ]; then
echo -e "$OKMSG
SENDING OK UP SMS/Email .... "
echo $MSG_UP_SUB > $UPMSG
echo "" >> $UPMSG
echo "PING STATUS = $FINAL_PING_STATUS" >> $UPMSG
cat $SRV_HOLDER >> $UPMSG
echo "" >> $UPMSG
echo "$FOOTER" >> $UPMSG
sed -i "/TEMP/d" "$STATUS_HOLDER"
# Sending UP INFO SMS/EMAIL
cat $UPMSG | curl "http://$KANNELURL/cgi-bin/sendsms?username=$KANNELID&password=$KANNELPASS&to=$CELL1" -G --data-urlencode text@-
$SENDMAILAPP -u "$MSG_UP_SUB" -o tls=yes -s smtp.gmail.com:587 -t $ADMINMAIL1 -xu $GMAILID -xp $GMAILPASS -f $GMAILID  -o message-file=$UPMSG -o message-content-type=text

fi
fi

# Script Ends Here
# Syed Jahanzaib / aacable @ hotmail . com
# http:// aacable . wordpress . com

Result:

Run the bash script which we created.

CLI RESULT:

1-all-ok

2-all-down

Email Result:

1-down

2-up

SMS  Result:

screenshot_2016-11-22-14-21-49


~ TIPS  ‘N’  TRICKS ~

To install sendEmail tool (using GMAIL account), use following

First install Supporting Libraries
For Ubuntu

apt-get -y install libio-socket-ssl-perl libnet-ssleay-perl perl

for centos

yum -y install perl perl-Crypt-SSLeay perl-IO-Socket-SSL

Now Download and unzip the sendEmail tool

mkdir /temp
cd /temp
wget http://caspian.dotconf.net/menu/Software/SendEmail/sendEmail-v1.56.tar.gz
tar zxvf sendEmail-v1.56.tar.gz
cd sendEmail-v1.56/

To test use following

/temp/sendEmail-v1.56/sendEmail -t DESTINATIONMAIL@hotmail.com -u "Test Email" -s smtp.gmail.com:587 -xu YOURGMAILID@gmail.com -xp GMAILPASS -f YOURGMAILID -o tls=yes -m "hi"
Nov 22 15:16:46 linux sendEmail[12561]: Email was sent successfully!

to install NET tools/command on Ubuntu 12.4 [zaib]

 sudo apt-get install samba-common

to install WINEXE on Ubuntu 12.4 [zaib]

mkdir /temp && cd /temp
apt-get install python-all-dev
wget http://downloads.sourceforge.net/project/winexe/winexe-1.00.tar.gz
tar xzvf winexe-1.00.tar.gz
cd winexe-1.00/source4/
./autogen.sh
./configure
make basics bin/winexe
./bin/winexe -V
# copy winexe binary to /usr/sbin   so that it can be called from any path
# cp /temp/winexe-1.00/source4/bin  /usr/sbin

To query Remote SAP Server instance status use following command

*Make sure you change the path of sapcontrol path/folder to match with your local installation folder structure

# Remote Server with workgroup/standalone
/temp/winexe-1.00/source4/bin/winexe -U ADMINID%PASSWORD //10.0.0.1 "E:\usr\sap\ECC\DVEBMGS00\exe\sapcontrol -nr 0 -function GetProcessList"

# Remote Server with DOMAIN base authentication
winexe --user=DOMAINNAME/ADMINID%PASSWORD //10.0.0.1 "e:\usr\sap\R3d\DVEBMGS00\exe\sapcontrol -nr 0 -function GetProcessList"

To query Remote windows services status , use following command

install the require tool by following

sudo apt-get install samba-common

List all services on remote windows server by following

net rpc service list -I 10.0.0.1 -U DOMAIN/ADMINID%PASSWORD

now query the service STATUS with following

net rpc service status OracleServiceR3D -I 10.0.0.1 -U DOMAIN/ADMINID%PASSWORD

net rpc service status OracleServiceR3D -I 10.0.0.1 -U DOMAIN/ADMINID%PASSWORD | grep running

to stop or start

net rpc service stop SERVICENAME -I IPADDRESS -U USERNAME%PASSWORD
net rpc service start SERVICENAME -I IPADDRESS -U USERNAME%PASSWORD

Regard’s
SYED JAHANZAIB

November 13, 2016

mysql unable to connect with remote mysql host/database

Filed under: Linux Related — Tags: , , — Syed Jahanzaib / Pinochio~:) @ 8:30 PM

If you are trying to connect with remote mysql database to perform remote management or connectivity with some web app to add data into the db , and sees the following error

Connection failed: Access denied for user ‘zaib’@’x.x.x.x’ (using password: YES)

OR

Host ‘xxx.xx.xxx.xxx’ is not allowed to connect to this MySQL server

Then you need to grant access to the user from any hostname. This is how you add new privilege from mysql command line.

NOTE: Please beware that this is for just example purpose only , and you MUST take good security measures like Strong Password, and allow only specific IP address to access etc.

 

CREATE USER 'zaib'@'localhost' IDENTIFIED BY 'zaib1234';

GRANT ALL PRIVILEGES ON *.* TO 'zaib'@'localhost' WITH GRANT OPTION;

CREATE USER 'zaib'@'%' IDENTIFIED BY 'zaipassword';

GRANT ALL PRIVILEGES ON *.* TO 'zaib'@'%' WITH GRANT OPTION;

FLUSH PRIVILEGES;


 

Sample PHP page to test Remote Mysql connection

Now you can test it with sample php page. change the host name of remote mysql server, and the id , password we just created in above steps.

 

<?php
$servername = "remote.mysql.host.ip.or.name";
$username = "zaib";
$password = "zaibpass";

// Create connection
$conn = new mysqli($servername, $username, $password);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully. Hurraahhhhh/Alhamdolillah";
?>

 


Regard’s
Syed Jahanzaib

 

November 8, 2016

Cisco Switch Howto Factory Default / Password Recovery


Following is a quick method to reset Cisco switch to factory default settings. This method can also be used to recover password.

Requirements:

  • Cisco Switch
  • Console Cable
  • PC/Laptop with Serial Port

Method#1 – Reset Everything – Factory Default

  • Connect your console cable from the switch Console port to PC/Laptop serial Port.
  • Open the HyperTerminal application, and select settings as per your hardware. Following is one sample for my desktop. Different switches connect with different Bits per Second setting . Try to lower it to 9600 for some models.

hyperterminal.PNG

  • Power Off the switch / Now hold the MODE button on the switch and power ON the switch.

sw-power.jpg

  • Once the switch initialize itself (may take 1-2 minutes), This will interrupt the boot process before the Flash file system can initialize, and after a short while (continue holding the “mode” button) you will see the following prompt:

sw-1

Once you see the

The password-recovery mechanism is enabled.

you can release the Mode button.

  • Now issue following commands to initialize flash and delete files which contains the switch configuration

flash_init
del flash:config.text
del flash:vlan.dat

Once its done, you may continue booting the switch which will allow you to do any configuration of your choice.

 

boot

 

This will boot the switch and all of old / previous settings will be cleared.

If it asks
Would you like to enter the initial configuration dialog? [yes/no]:

Type no and continue with below ..

To add ip use following

en
config t
interface vlan 1
ip address 101.11.100.1 255.0.0.0
ip default-gateway 101.11.100.255
exit
exit
wr

sw-2.PNG

Connect ethernet cable to the switch port and see the ping result.

ping.PNG

 

Now you will be able to connect with the switch using IP.

Cisco configuration assistant software is the best GUI tool to configure the switch with ease and it will save you from remembering all the commands. Although command is more powerful in most cases but still GUI is feasible for most beginners.

cisco-network-gui

You can download Cisco Network Assistant it from my google drive.

https://drive.google.com/drive/folders/0B8B_P2ljEc2xQlpvRUQ2QWVfR0E


 

TIPS:


Add ENABLE section Password

en
config t
enable secret zaib1234
exit
wr

Reboot Switch

wr mem
reload


Vlan1 is administratively down, line protocol is down

en
config t
interface vlan 1
no shut


Enable TELNET access

config
line vty 0 15
transport input telnet
password zaibtelnetpass
login
exit
exit
wr


Enable SSH access

*** Set hostname and domain-name
config t
hostname cisco-switch
ip domain-name zaib.com

*** Generate the RSA Keys
crypto key generate rsa
“How many bits in the modulus [512]: 1024”

*** Set Up the Line VTY configurations
transport input ssh
login local
password zaib1234
exit

*** Set the console line
line console 0
logging synchronous
login local

*** Create the username password
config t
username zaib password zaib1234
enable secret zaib1234
service password-encryption


Method#2 – Password Recovery Only:
(While Keeping existing running configuration intact)

  • Connect Hyperterminal with the switch using console cable. [as mentioned in the beginning of this guide]
  • Power ON the switch while pressing mode button, one you see the “The password-recovery mechanism is enabled.”

More example for switch models:

sw-1

Now issue following commands one by one …

flash_init
dir flash:
rename flash:config.text flash:config.old
(The config.text file contains the password)

Now continue the boot process by following command

boot

Enter n at the prompt to abort the initial configuration dialog.

Now rename the config.old file to config.text so that we can get our existing settings restored , and still let us change the password


rename flash:config.old flash:config.text
Press enter when ask for destination file name

Now save the config in running-config so that switch must load all the settings on next boot

copy flash:config.text system:running-config
Press enter when ask for destination file name

Changing Password …

*** Overwrite the existing secret/password

conf t
enable secret zaib1234
enabled password zaib1234

*** Overwrite the existing vty password

line vty 0 15
password zaib1234

*** Overwrite the existing console password

line con 0
password zaib1234
exit
exit
wr mem
DONE !

Regard’s
Syed Jahanzaib

 

 

 

November 4, 2016

Windows batch files to get file/dir size in backup

Filed under: Microsoft Related — Tags: , , , , — Syed Jahanzaib / Pinochio~:) @ 3:28 PM

batch file icon.png

Following are some references to create windows based batch file which performs backups and sends email using windows cmd functions. The task can be achieved by builtin or 3rd party backup applications, but most of the time , there is an Worm which always crawled in my mind and it forces me to do things oddly and above all strangely it somehow works amazingly good with the exact results I want!


Get File Size!

@echo off
rem *** This batch file will list file size ***
 setlocal enableextensions disabledelayedexpansion

rem Change the file name/location in below line
set "file=d:\zaib\setup.exe"

for %%z in ("%file%") do for /f "tokens=1,2" %%a in ('
 robocopy "%%~dpz." "%%~dpz." "%%~nxz" /l /nocopy /is /njh /njs /ndl /nc
 ') do if "%%~dz"=="%%~db" (
 echo "%%~z" : [%%a]
 ) else (
 echo "%%~z" : [%%a%%b] 
 )

Result:

1- file size.PNG


Get Folder Size!


@echo off

rem *** This batch file will list FOLDER size ***
setlocal enableextensions disabledelayedexpansion

set "folder=%~f1" & if not defined folder set "folder=%cd%"

set "size=" & for %%z in ("%folder%") do for /f "skip=2 tokens=2,3 delims=: " %%a in ('
robocopy "%%~fz\." "%%~fz\." /l /nocopy /s /is /njh /nfl /ndl /r:0 /w:0 /xjd /xjf /np
^| find ":"
') do if not defined size (
(for /f "delims=0123456789." %%c in ("%%b") do (break)) && (
set "size=%%a%%b"
) || (
set "size=%%a"
)
)

echo "%folder%" : [%size%]

Result:

2- folder size.PNG

 


 

Backup File Example:

I made following batch file long time ago which does the following

  1. Check for MAP drive , if not attache then reconnect it,
  2. If MAP drive is still not available , then break the script and send email to admin
  3. execute oracle exp command to export the DB into the map drive
  4. Delete files older then 15 days to prevent disk fill up
  5. email the result to the admin.

These are just for example only, just to give you an idea only

@echo off
rem # Syed Jahanzaib #
rem # ORACLE-DB SAS BACKUP SCRIPT BY zaib
rem #######################################################
rem Setting various Descriptions via environment variables
rem #######################################################
set dt=%date:~-4,4%%date:~-10,2%%date:~-7,2%
for /F "usebackq tokens=1,2 delims==" %%i in (`wmic os get LocalDateTime /VALUE 2^>NUL`) do if '.%%i.'=='.LocalDateTime.' set ldt=%%j
set ldt=%ldt:~0,4%-%ldt:~4,2%-%ldt:~6,2%__%ldt:~8,2%-%ldt:~10,2%
set mail-to="ADMINMAIL@GMAIL.COM"
set attachment=C:\backup\last-%ldt%.log
set srvname=ORACLE-DB-SAS.AGP1
set mail-subject=ORACLE-DB-SAS DB %ldt% Dump/Export Report by_Syed_Jahanzaib

break > %attachment%

if exist b:\ (
echo Map Drive is present. Hurraaahhhh zaib you got it Alhamdulillah
) ELSE (
net use B: \\agpinf03\datapark
)

if not exist b:\ (
cho Sorry unable to MAP Drive.
c:\backup\blat\blat.exe %attachment% -to %mail-to% -i %srvname% -s "ERROR UNABLE TO MAP DRIVE - PLEASE CHECK IT. NO BACKUP"
exit /b )

set logpath="B:\ORACLE-DB-sas"
set fname="%logpath%\ORACLE-DB-sas-daily-backup-%ldt%"
echo Executing Backup to DUMP ORACLE-DB-sas Database export Now ...
echo .

exp userid=SAS/ARORACLE-DBS file=%fname% direct=y COMPRESS=y

echo .
echo This script is made by AGP IS Dept. to export daily dump from ORACLE-DB system > %attachment%
echo Please ntoe that Files older then 15 days will be deleted from Fileserver DATAPARK folder %logpath% >> %attachment%
echo Database Export Done. now deleting files older then 15 days
echo .
echo Deleting B:\ORACLE-DB-sas\*.DMP files older then 15 Days from File Server.
echo ****** >> %attachment%
echo Last file name exported is >> %attachment%
forfiles -p "b:\ORACLE-DB-sas" -s -m *.dmp -d 0 -c "cmd /c dir @path" >> %attachment%
echo ****** >> %attachment%
echo File Size is >> %attachment%
ls -lh %fname%.dmp | awk " {print $4;} " >> %attachment%
echo ****** >> %attachment%
echo ****** >> %attachment%
echo Following Files DELETED as per policy if applied >> %attachment%
forfiles -p "b:\ORACLE-DB-sas" -s -m *.dmp -d -15 -c "cmd /c del @path" >> %attachment%
echo ****** >> %attachment%
echo SCRIPT ENDS HERE >> %attachment%
echo powered by Syed Jahanzaib >> %attachment%
echo Done.

c:\backup\blat\blat.exe %attachment% -to %mail-to% -i %srvname% -s "%mail-subject%"

Sample Result:

backup-email-sample

September 30, 2016

Linux pppoe client disconnects after x minutes issue

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

dc


Scenario:

Mikrotik is acting as PPPoE server.

We have one Linux (Ubuntu) base client and wants to connect it to pppoe server using pppoe client. We have created the pppoe dialer using ‘pppoeconfig‘ CLI tool. Its connecting fine using pon dsl-rpovider command.


Problem:

PPPoE Dialer is regularly disconnecting after x minutes and ` /var/log/syslog ` is showing following error

Sep 30 20:40:44 ubuntu pppd[4375]: No response to 4 echo-requests
Sep 30 20:40:44 ubuntu pppd[4375]: Serial link appears to be disconnected.
Sep 30 20:40:44 ubuntu pppd[4375]: Connect time 5.5 minutes.
Sep 30 20:40:44 ubuntu pppd[4375]: Sent 6536 bytes, received 2983 bytes.
Sep 30 20:40:44 ubuntu pppd[4375]: restoring old default route to eth0 [192.168.1.1]
Sep 30 20:40:50 ubuntu pppd[4375]: Connection terminated.
Sep 30 20:40:50 ubuntu pppd[4375]: Modem hangup

Possible Reason:

The other side of the PPP link probably doesn’t support LCP echo. I have seen this with Mikrotik base pppoe server. [zaib]


Solution:

Oddly it was a rear issue i guess and internet is a bit silent about it.

Any way put this line in the relevant ppp options file and try again.

[ Example: /etc/ppp/peers/dsl-provider ]

echo-requests

lcp-echo-interval 0

Save and redial connection by

poff dsl-provider

pon dsl-provider


Complete DSL-PROVIDER file for pppoe client config.


# pppoe dialer config sample, by
# syed jahanzaib

noipdefault
defaultroute
replacedefaultroute
hide-password
lcp-echo-interval 0
noauth
persist
#mtu 1492
#persist
#maxfail 0
#holdoff 20
plugin rp-pppoe.so eth0
usepeerdns
maxfail 0
persist
user "PPPOE-USER-ID"

Regard’s
Syed Jahanzaib

September 27, 2016

Howto add Simple VPN (PPTP) Server in Ubuntu

Filed under: Linux Related — Tags: , , — Syed Jahanzaib / Pinochio~:) @ 9:41 AM

VPN (PPTP) Server in Ubuntu


This post contains short notes on HOW-TO add simple pptp based VPN server in Ubuntu. It it useful in many scenarios example if you have Linux server in the office, and want to connect to it from anywhere in the world. OR you can use it in a situation where you want your billing server to act like a centralized billing system for all the remote locations NASES.

Ideally you should have static Public IP on this vpn server, but you can bypass this requirement by adding an DDNS name as well as a workaround like changeip.com. I remember that I did similar configuration at some philippine network and one at cloud.

 

Components Used:

OS = Ubuntu 12.4 on Cloud with static Public IP address
Clients = Windows 7 / Mikrotik Routerboards

Let’s Start…


Install PPTPD Package

Install PPTPD package in ubuntu by issuing following command

sudo apt-get -y install pptpd

Now Issue following to add vpn server options like local/remote ip addresses. I used echo command to directly insert text/data into the config files rather than editing by nano/vim. use whatever is easier for you.

# This will be the virtual ip of the vpn server
echo "localip 192.168.10.1" >> /etc/pptpd.conf

# Remote vpn client will get IP from this ip pool range
echo "remoteip 192.168.10.10-20" >> /etc/pptpd.conf

#This is to provide DNS , but in this example I really dont need natting for remote vpn client, 
#as remote mikrotik will sue this server for radius autehnticaiton only, not as there default gateway🙂
echo "ms-dns 8.8.8.8" >> /etc/pptpd.conf 

Add a TEST account for client in /etc/ppp/chap-secrets

echo "zaibid pptpd zaibpassword *" >> /etc/ppp/chap-secrets

Description:
zaibid = username for remote vpn client
pptpd = service type
zaibpassword = Password for remote vpn client
* = any ip from the pool, if you want to provide same ip to client every time, add ip like 192.168.1.1


TIP:
To Assign static ip to any remote vpn client

To assign static ip to remove vpn client, you can use following

zaibid pptpd zaibpassword 192.168.100.25

Finally restart pptpd service …

/etc/init.d/pptpd restart

Make sure the service is started

service pptpd status

or

ps aux |grep pptpd

If you see it’s running, our Server End is probably OK.


VPN CLIENT in WINDOWS 7

Create a VPN dialer in Windows

Snapshots just for reference …

windows-7-vpn-client


VPN (pptp) CLIENT in Mikrotik RouterOS

vpn-connected-from-mikrotik-client


VPN Server Log Window.

(You can enable log in by uncomment word #debug in /etc/pptpd.conf)

Sep 27 04:15:22 radius pptpd[7582]: MGR: Launching /usr/sbin/pptpctrl to handle client
Sep 27 04:15:22 radius pptpd[7582]: CTRL: local address = 192.168.0.1
Sep 27 04:15:22 radius pptpd[7582]: CTRL: remote address = 192.168.0.234
Sep 27 04:15:22 radius pptpd[7582]: CTRL: pppd options file = /etc/ppp/pptpd-options
Sep 27 04:15:22 radius pptpd[7582]: CTRL: Client X.X.X.X control connection started
Sep 27 04:15:22 radius pptpd[7582]: CTRL: Received PPTP Control Message (type: 1)
Sep 27 04:15:22 radius pptpd[7582]: CTRL: Made a START CTRL CONN RPLY packet
Sep 27 04:15:22 radius pptpd[7582]: CTRL: I wrote 156 bytes to the client.
Sep 27 04:15:22 radius pptpd[7582]: CTRL: Sent packet to client
Sep 27 04:15:22 radius pptpd[7582]: CTRL: Received PPTP Control Message (type: 7)
Sep 27 04:15:22 radius pptpd[7582]: CTRL: Set parameters to 10000000 maxbps, 100 window size
Sep 27 04:15:22 radius pptpd[7582]: CTRL: Made a OUT CALL RPLY packet
Sep 27 04:15:22 radius pptpd[7582]: CTRL: Starting call (launching pppd, opening GRE)
Sep 27 04:15:22 radius pptpd[7582]: CTRL: pty_fd = 6
Sep 27 04:15:22 radius pptpd[7582]: CTRL: tty_fd = 7
Sep 27 04:15:22 radius pptpd[7582]: CTRL: I wrote 32 bytes to the client.
Sep 27 04:15:22 radius pptpd[7582]: CTRL: Sent packet to client
Sep 27 04:15:22 radius pptpd[7583]: CTRL (PPPD Launcher): program binary = /usr/sbin/pppd
Sep 27 04:15:22 radius pptpd[7583]: CTRL (PPPD Launcher): local address = 192.168.0.1
Sep 27 04:15:22 radius pptpd[7583]: CTRL (PPPD Launcher): remote address = 192.168.0.234
Sep 27 04:15:22 radius pppd[7583]: Plugin /usr/lib/pptpd/pptpd-logwtmp.so loaded.
Sep 27 04:15:22 radius pppd[7583]: pppd 2.4.5 started by root, uid 0
Sep 27 04:15:22 radius pppd[7583]: Using interface ppp0
Sep 27 04:15:22 radius pppd[7583]: Connect: ppp0 <--> /dev/pts/1
Sep 27 04:15:22 radius pptpd[7582]: GRE: Bad checksum from pppd.
Sep 27 04:15:22 radius pptpd[7582]: GRE: accepting packet #0
Sep 27 04:15:23 radius pptpd[7582]: GRE: accepting packet #1
Sep 27 04:15:23 radius pptpd[7582]: GRE: accepting packet #2
Sep 27 04:15:23 radius pptpd[7582]: GRE: accepting packet #3
Sep 27 04:15:23 radius pptpd[7582]: GRE: accepting packet #4
Sep 27 04:15:23 radius pppd[7583]: peer from calling number "X.X.X.X" authorized
Sep 27 04:15:23 radius pptpd[7582]: GRE: accepting packet #5
Sep 27 04:15:23 radius pptpd[7582]: GRE: accepting packet #6
Sep 27 04:15:23 radius pptpd[7582]: GRE: accepting packet #7
Sep 27 04:15:23 radius pptpd[7582]: GRE: accepting packet #8
Sep 27 04:15:23 radius pptpd[7582]: GRE: accepting packet #9
Sep 27 04:15:23 radius pptpd[7582]: GRE: accepting packet #10
Sep 27 04:15:23 radius pppd[7583]: MPPE 128-bit stateless compression enabled
Sep 27 04:15:23 radius pptpd[7582]: GRE: accepting packet #11
Sep 27 04:15:23 radius pptpd[7582]: GRE: accepting packet #12
Sep 27 04:15:24 radius pptpd[7582]: GRE: accepting packet #13
Sep 27 04:15:24 radius pptpd[7582]: GRE: accepting packet #14
Sep 27 04:15:24 radius pppd[7583]: Cannot determine ethernet address for proxy ARP
Sep 27 04:15:24 radius pppd[7583]: local IP address 192.168.0.1
Sep 27 04:15:24 radius pppd[7583]: remote IP address 192.168.0.234
Sep 27 04:15:52 radius pptpd[7582]: CTRL: Received PPTP Control Message (type: 5)
Sep 27 04:15:52 radius pptpd[7582]: CTRL: Made a ECHO RPLY packet
Sep 27 04:15:52 radius pptpd[7582]: CTRL: I wrote 20 bytes to the client.
Sep 27 04:15:52 radius pptpd[7582]: CTRL: Sent packet to client
Sep 27 04:15:53 radius pptpd[7582]: GRE: accepting packet #15

September 7, 2016

Sample PHP page to insert data into kannel/sqlbox

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

centralized-sms-ystem


Other kannel references:

Before Proceeding further, read the following links for Kannel Installation and configure SLQBOX.

https://aacable.wordpress.com/2012/11/26/howto-configure-your-local-http-gateway-using-kannel-on-ubuntu/

https://aacable.wordpress.com/2016/09/06/kannel-sms-gateway-delivery-reports-in-mysql-via-sqlbox/


Following is a sample PHP file which can be called by BASH/CURL or any remote system to send SMS via inserting the message into SND database which will send SMS to destination with the message. So it will act as a centralized SMS submission process that will keep record of every outgoing Sent SMS along with any detail that we requires, example sender (the requesting party), recipient, and the message (OR etc etc)

This is just for reference purposes of DOABLE things, and by no means it should be deployed in production environment. These are just short references, and must be modified according to the safety standards.

 


Background:

Currently we are using KANNEL as SMS gateway to send messages to users,admins about various events and information. This SMS Gateway is used by the Linux , Mikrotik Router, and windows base systems. But the problem is that using standard kannel HTTP submission method, we cannot track any message and its status. Its like blind system. And recently it was asked by few OP to track the Sent sms Numbers, status for a particular given time or over all month report.

 

Standard Examples of sending SMS via kannel HTTP submission:

Linux Example:

curl "$KURL/cgi-bin/sendsms?username=$KID&password=$KPASS&to=$mobile" -G --data-urlencode text@/tmp/$USR.sms
s

Windows Example:

C:\wget\wget.exe  "http://192.168.0.1:13013/cgi-bin/sendsms?username=kannel&password=KANNELPASS&to=03333021909&text=INFO: [TimeAndDate] UPS is working on KESC now"

Mikrotik Example:

/tool fetch url="$KURL\?username=$KID&password=$KPASS&to=$cell1+$cell2&text=$MSGUPSMS"

Requirements:

I wanted to have a simple PHP page that can be called by any system with only three fields

1- Sender (Example server1 or ExpiryDaemon)
2- To (mobile number of the recipient)
3- Message (which is usually large with multiple lines)

One Example for Linux base system as following:

The scheduled bash script runs on Linux base billing system which submits various messages to users like account expiry, password change info, etc via submitting messages with destination numbers using CURL.


Solution:

For this purpose we made simple PHP page that can be called from any remote system and will act using the provided url parameters. thanks to mr tapan.kumar for providing sample php page for this purpose.

Example of CURL/BASH script which fetch data from mysql table for  logged in alert for manager/admin into panel.

KANNELURL="127.0.0.1"
SENDER="Billing-adminlogin"
MOBILE="1234567890"
MSG="Dear $MANAGER ,
You have successfully logged-in to billing admin panel.
ID = $ID
DATE = $DATE
Thank You"

# Curl example that will call test.php with 3 parameters ...
curl -G "http://$KANNELURL/test.php?sender=php&to=$MOBILE" --data-urlencode "message=$MSG"

 

TEST.PHP SAMPLE


<?php
$host = 'localhost';
$user = 'root';
$password = 'SQLPASS';
$database = 'kannel_db';
$port = 3306;
$conn = mysql_connect($host, $user, $password);
mysql_select_db($database, $conn);
if (!$conn) {
die("Unable to connect with MySQL database.\n");
}
$sender = trim($_GET['sender']);
$to = trim($_GET['to']);
$message = trim($_GET['message']);
// I have already configured the SQLBOX so any entry made into SND DB, it will automatically move
// data into send_sms and will update the SND status as well on receiving DLR
$sqlQuery = "insert into snd (snd_sender,snd_to,snd_txt) values('$sender','$to','$message')";
mysql_query($sqlQuery);
echo 'OK';
?>

When the curl will call test.php , it will simply enter the data into SND DB.

Example:

snd_example

and from mikrotik it would be like

mikrotik-send-sms

 

This way we can have a centralized SMS submission system , which will keep track of every SENT sms with the details we require according the local need. It can be modified as per the requirements. These are just my findings from Google and tips with some working sample code from few resources.

 

Regard’s
Syed Jahanzaib

Older Posts »

Create a free website or blog at WordPress.com.