I believe I am doing what you are after... Got mine working just today, actually... I basically define the icon I want to overlay on my map inside my .conf file, and it is dynamically overlayed (in place of the node label)each time the map is redrawn. In my map, I have a mixture of routers and telco providers that I overlay as nodes.
I also changed it to make a time-stamped copy of the map in a seperate folder (planning on animating them each night once I find an .mpg creator I like), so strip out that part of the code if you don't want that. (or make a valid path in the config section for $ANIM_OUTPUT if you want to keep it)
Code: Select all
#!/usr/bin/perl
# Network Wearthermap - version 1.1.1 (20040422)
# http://netmon.grnet.gr/weathermap/
# Panagiotis Christias, <christias@noc.ntua.gr>
$VERSION = "1.1.1";
use Getopt::Long;
use GD;
$t=localtime(time);
$t =~ s/:/_/g;
################################################################
#
# Configuration parameters
#
$WGET = "../wget/wget -qO -";
$CONFIG = "SatDelUtil.conf";
$OUTPUT = "../cacti/images/maps/SatDelUtil.png";
$MAPNAME ="SatDelUtil"; #name prepended to animated file names
$ANIM_OUTPUT = "../cacti/images/maps/animated/" . $MAPNAME . " " . $t . ".png";
$DEBUG = 1;
$WIDTH = 800;
$HEIGHT = 493;
#
################################################################
#%optctl=();
#GetOptions(\%optctl, "config:s", "output:s", "version", "help", "debug", "") || exit(1);
#if($optctl{"config"}) { $CONFIG = $optctl{"config"} };
#if($optctl{"output"}) { $OUTPUT = $optctl{"output"} };
#if($optctl{"version"}) { &version; exit; }
#if($optctl{"help"}) { &usage; exit; }
#if($optctl{"debug"}) { $DEBUG=1; }
&read_config($CONFIG);
if($background){
open (PNG,"$background") || die "$background: $!\n";
$map = newFromPng GD::Image(PNG) || die "newFromPng failed.";
close PNG;
} else {
$map=new GD::Image($WIDTH,$HEIGHT)
}
#open(PNG, "router_ok.png");
#$router = newFromPng GD::Image(PNG);
#close PNG;
&alloc_colors;
print "Opening log files...\n\n" if($DEBUG);
foreach $link (keys %target){
$data = $target{$link};
print "FILE: $data\n" if($DEBUG);
if(($data =~ /^https?:\/\//i) || ($data =~ /^ftp:\/\//i) ) {
open(LOG, "$WGET $data |") or warn "$data: $!\n";
} else {
open(LOG, "$data") or warn "data file $data: $!\n";
}
while(<LOG>){
# <!-- cuin d 5585966 -->
# <!-- cuout d 10589424 -->
if(/<\!-- cuin d (\d+) -->/){
$input{$link}=$1;
print "LINK: $link, Input: $input{$link}\n" if($DEBUG);
}
if(/<\!-- cuout d (\d+) -->/){
$output{$link}=$1;
print "LINK: $link, Output: $output{$link}\n" if($DEBUG);
}
}
close(LOG);
}
print "\nCalculating rates...\n\n" if($DEBUG);
foreach $link (keys %target){
$outrate=(int(($output{$link}/$maxbytes{$link}+0.005)*100)>100) ? 100:int(($output{$link}/$maxbytes{$link}+0.005)*100);
$inrate=(int(($input{$link}/$maxbytes{$link}+0.005)*100)>100) ? 100:int(($input{$link}/$maxbytes{$link}+0.005)*100);
if($output{$link} != 0 && $outrate == 0) { $outrate=1 }
if($input{$link} != 0 && $inrate == 0) { $inrate=1 }
print "$target{$link}: outrate=$outrate%, inrate=$inrate%\n" if($DEBUG);
# draw lines...
$width=3;
&draw_arrow(
$xpos{$nodea{$link}},
$ypos{$nodea{$link}},
&middle($xpos{$nodea{$link}},$xpos{$nodeb{$link}}),
&middle($ypos{$nodea{$link}},$ypos{$nodeb{$link}}),
$width, 1, &select_color($outrate));
&draw_arrow(
$xpos{$nodea{$link}},
$ypos{$nodea{$link}},
&middle($xpos{$nodea{$link}},$xpos{$nodeb{$link}}),
&middle($ypos{$nodea{$link}},$ypos{$nodeb{$link}}),
$width, 0, $black);
&label(&middle($xpos{$nodea{$link}},&middle($xpos{$nodea{$link}},$xpos{$nodeb{$link}})),
&middle($ypos{$nodea{$link}},&middle($ypos{$nodea{$link}},$ypos{$nodeb{$link}})),
$outrate . "%", 0);
&draw_arrow(
$xpos{$nodeb{$link}},
$ypos{$nodeb{$link}},
&middle($xpos{$nodea{$link}},$xpos{$nodeb{$link}}),
&middle($ypos{$nodea{$link}},$ypos{$nodeb{$link}}),
$width, 1, &select_color($inrate));
&draw_arrow(
$xpos{$nodeb{$link}},
$ypos{$nodeb{$link}},
&middle($xpos{$nodea{$link}},$xpos{$nodeb{$link}}),
&middle($ypos{$nodea{$link}},$ypos{$nodeb{$link}}),
$width, 0, $black);
&label(&middle($xpos{$nodeb{$link}},&middle($xpos{$nodea{$link}},$xpos{$nodeb{$link}})),
&middle($ypos{$nodeb{$link}},&middle($ypos{$nodea{$link}},$ypos{$nodeb{$link}})),
$inrate . "%", 0);
}
print "\n" if($DEBUG);
foreach(keys %xpos){
open(PNG, $icon_image{$_});
$icon = newFromPng GD::Image(PNG);
close PNG;
my ($width,$height) = $icon->getBounds();
$map->copy($icon,($xpos{$_}-($width/2)),($ypos{$_}-($height/2)),0,0,$width,$height);
}
&annotation;
# print image...
print "Generating image file $OUTPUT...\n\n" if($DEBUG);
open(PNG,">$OUTPUT")||die("$OUTPUT: $!\n");
binmode(PNG);
print PNG $map->png;
close PNG;
open(PNG,">$ANIM_OUTPUT")||die("$ANIM_OUTPUT: $!\n");
binmode(PNG);
print PNG $map->png;
close PNG;
# hint, resizing the image could make it look better
exit;
# print labels
sub label{
my($xpos,$ypos,$label,$pad)=@_;
my($strwidth)=gdSmallFont->width*length($label);
my($strheight)=gdSmallFont->height;
$map->filledRectangle(
$xpos-$strwidth/2-$pad-2, $ypos-$strheight/2-$pad+1,
$xpos+$strwidth/2+$pad+1, $ypos+$strheight/2+$pad,
$black);
$map->filledRectangle(
$xpos-$strwidth/2-$pad-1, $ypos-$strheight/2-$pad+2,
$xpos+$strwidth/2+$pad, $ypos+$strheight/2+$pad-1,
$white);
$map->string(gdSmallFont,
$xpos-$strwidth/2, $ypos-$strheight/2+1,
$label, $black)
}
# print annotation
sub annotation{
my($title)="Circuit Load";
$strwidth=gdSmallFont->width*length($label{$_});
$strheight=gdSmallFont->height;
$t=localtime(time);
# $t=gmtime(time);
$map->string(gdSmallFont, 1, 480, "Last update on $t CST", $black);
$map->filledRectangle($keyxpos,$keyypos,
$keyxpos+gdSmallFont->width*length($title)+10,
$keyypos+gdSmallFont->height*($scales+1)+10,
$gray);
$map->rectangle($keyxpos,$keyypos,
$keyxpos+gdSmallFont->width*length($title)+10,
$keyypos+gdSmallFont->height*($scales+1)+10,
$black);
$map->string(gdSmallFont,
$keyxpos+4,
$keyypos+4,
"Circuit Load", $black);
my($i)=1;
foreach(sort {$scale_low{$a}<=>$scale_low{$b}} keys %scale_low){
$map->filledRectangle(
$keyxpos+6,
$keyypos+gdSmallFont->height*$i+8,
$keyxpos+6+16,
$keyypos+gdSmallFont->height*$i+gdSmallFont->height+6,
$color{$_});
$map->string(gdSmallFont,
$keyxpos+6+20,
$keyypos+gdSmallFont->height*$i+8,
"$scale_low{$_}-$scale_high{$_}%", $black);
$i++
}
}
sub select_color {
my($rate)=($_[0]>100) ? 100:$_[0];
if($rate=="0"){return($darkgray)}
foreach(sort {$scale_high{$a}<=>$scale_high{$b}} keys %scale_high){
if($scale_low{$_}<=$rate && $rate<=$scale_high{$_}){
return($color{$_});
}
}
}
sub alloc_colors {
$white=$map->colorAllocate(255,255,255);
$gray=$map->colorAllocate(248,248,248);
$black=$map->colorAllocate(0,0,0);
$darkgray=$map->colorAllocate(128,128,128);
foreach(keys %scale_red){
$color{$_} = $map->colorAllocate($scale_red{$_},$scale_green{$_},$scale_blue{$_});
}
}
sub read_config {
my($config)=shift;
my($node,$link);
print "\nReading configuration file...\n\n" if($DEBUG);
$scales=0;
open(CONF,$config) or die "$config: $!\n";
while(<CONF>){
if(/^\s*BACKGROUND\s+(\S+)/i){
if(-s "$1"){
$background=$1;
print "found BACKGROUND: $background\n" if($DEBUG);
}
}
if(/^\s*WIDTH\s+(\d+)/i){
if("$1" ne ""){
$WIDTH=$1;
print "found WIDTH: $WIDTH\n" if($DEBUG);
}
}
if(/^\s*HEIGHT\s+(\d+)/i){
if("$1" ne ""){
$HEIGHT=$1;
print "found HEIGHT: $HEIGHT\n" if($DEBUG);
}
}
if(/^\s*NODE\s+(\w+)/i){
$node=$1;
print "found NODE: $node\n" if($DEBUG);
}
if(/^\s*POSITION\s+(\d+)\s+(\d+)/i){
$xpos{$node}=$1;
$ypos{$node}=$2;
print "found NODE: $node XPOS: $xpos{$node} YPOS: $xpos{$node}\n" if($DEBUG);
}
if(/^\s*LABEL\s+(\S+)/i){
$label{$node}=$1;
print "found NODE: $node LABEL: $label{$node}\n" if($DEBUG);
}
if(/^\s*ICON\s+(\S+)/i){
$icon_image{$node}=$1;
print "found ICON: $node ICON: $icon_image{$node}\n" if($DEBUG);
}
if(/^\s*LINK\s+(\S+)/i){
$link=$1;
print "found LINK: $link\n" if($DEBUG);
}
if(/^\s*NODES\s+(\S+)\s+(\S+)/i){
$nodea{$link}=$1;
$nodeb{$link}=$2;
print "found LINK: $link NODEA: $nodea{$link} NODEB: $nodeb{$link}\n" if($DEBUG);
}
if(/^\s*TARGET\s+(\S+)/i){
$target{$link}=$1;
print "found LINK: $link TARGET: $target{$link}\n" if($DEBUG);
}
if(/^\s*BANDWIDTH\s+(\d+)/i){
$bandwidth{$link}=$1;
$maxbytes{$link}=$bandwidth{$link}*1024/8;
print "found LINK: $link BANDWIDTH: $bandwidth{$link}\n" if($DEBUG);
}
if(/^\s*KEYPOS\s+(\d+)\s+(\d+)/i){
$keyxpos=$1;
$keyypos=$2;
print "found KEY POSITION: $keyxpos $keyypos\n" if($DEBUG);
}
if(/^\s*SCALE\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/i){
$scale_low{"$1:$2"}=$1;
$scale_high{"$1:$2"}=$2;
$scale_red{"$1:$2"}=$3;
$scale_green{"$1:$2"}=$4;
$scale_blue{"$1:$2"}=$5;
$scales++;
print "found SCALE DATA: $1:$2 $3:$4:$5\n" if($DEBUG);
}
}
print "\n" if($DEBUG);
}
sub middle{
return int( $_[0] + ($_[1]-$_[0])/2 )
}
sub dist{
return int( sqrt( $_[0]*$_[0] + $_[1]*$_[1] ) )
}
sub newx{
my($a,$b,$x,$y)=@_;
return int( cos( atan2($y,$x) + atan2($b,$a) ) * sqrt( $x*$x + $y*$y ) );
}
sub newy{
my($a,$b,$x,$y)=@_;
return int( sin( atan2($y,$x) + atan2($b,$a) ) * sqrt( $x*$x + $y*$y ) );
}
sub draw_arrow {
my($x1,$y1,$x2,$y2,$w,$solid,$color)=($_[0],$_[1],$_[2],$_[3],$_[4],$_[5],$_[6]);
my($arrow)=new GD::Polygon;
$arrow->addPt(
$x1 + &newx($x2-$x1, $y2-$y1, 0, $w),
$y1 + &newy($x2-$x1, $y2-$y1, 0, $w)
);
$arrow->addPt(
$x2 + &newx($x2-$x1, $y2-$y1, -4*$w, $w),
$y2 + &newy($x2-$x1, $y2-$y1, -4*$w, $w)
);
$arrow->addPt(
$x2 + &newx($x2-$x1, $y2-$y1, -4*$w, 2*$w),
$y2 + &newy($x2-$x1, $y2-$y1, -4*$w, 2*$w)
);
$arrow->addPt( $x2, $y2);
$arrow->addPt(
$x2 + &newx($x2-$x1, $y2-$y1, -4*$w, -2*$w),
$y2 + &newy($x2-$x1, $y2-$y1, -4*$w, -2*$w)
);
$arrow->addPt(
$x2 + &newx($x2-$x1, $y2-$y1, -4*$w, -$w),
$y2 + &newy($x2-$x1, $y2-$y1, -4*$w, -$w)
);
$arrow->addPt(
$x1 + &newx($x2-$x1, $y2-$y1, 0, -$w),
$y1 + &newy($x2-$x1, $y2-$y1, 0, -$w)
);
if($solid){
$map->filledPolygon($arrow,$color);
}else{
$map->polygon($arrow,$color);
}
}
sub version {
print <<EOM;
Network Wearthermap v$VERSION - http://netmon.grnet.gr/weathermap/
EOM
}
sub usage {
print <<EOM;
Network Wearthermap v$VERSION - http://netmon.grnet.gr/weathermap/
Usage: $0 [OPTION]...
-c, --config=FILE configuration file (default $CONFIG)
-o, --output=FILE output image file default (default $OUTPUT)
-v, --version print version
-h, --help print this text
-d, --debug enable debug output
EOM
}
Then in your .conf file (mine's named satdelutil.conf in this case), add a line to each of your nodes to define the icon you want to overlay on your map.
In my case, I just threw in some small .png files called "sprint_small.png", "router_ok.png", and "mci_small.png" into the same directory as my .conf file. Each of my icons are about 35x35 pixels. I did them in Visio, and finished them up (shrank them and saved for web) in Photoshop. They will overlay in place of the node name label from the original weathermap code.
My next plan is to have an ok, warning, and alert version of each of the icons and have them displayed (a bright red router for example). After THAT, I want to take it beyond circuit utilization and have it be for pretty much anything... Say a Visio diagram of a rack of computers and a script that checks the RRD's for anything from fan speed to disk space, and overlays a red server icon instead of the all's well icon when something is amiss. I'm currently thinking this is it easiest way for me to get thresholds into Cacti data with my limited programming ability......