Commit fdc02843 authored by Chad Barb's avatar Chad Barb
Browse files

New visualizer; uses "vis_nodes" table in database to store precomputed solutions.
Added "prerender" call to tbprerun and tbend.
"prerender_all" can be used to precompute vis solutions for all experiments.
Also, removed legacy vis cruft.
parent 68bf3e49
...@@ -398,9 +398,9 @@ outfiles="$outfiles Makeconf GNUmakefile \ ...@@ -398,9 +398,9 @@ outfiles="$outfiles Makeconf GNUmakefile \
utils/firstuser utils/export_tables \ utils/firstuser utils/export_tables \
utils/cvsupd.pl \ utils/cvsupd.pl \
www/GNUmakefile www/defs.php3 www/dbdefs.php3 \ www/GNUmakefile www/defs.php3 www/dbdefs.php3 \
vis/GNUmakefile vis/vistopology vis/webvistopology vis/top2gif \ vis/GNUmakefile vis/webvistopology \
vis/dbvistopology vis/dbtopper \ vis/dbvistopology \
vis/top2png vis/render \ vis/prerender vis/prerender_all vis/render \
rc.d/GNUmakefile rc.d/2.mysql-server.sh rc.d/3.testbed.sh \ rc.d/GNUmakefile rc.d/2.mysql-server.sh rc.d/3.testbed.sh \
rc.d/2.elvind.sh \ rc.d/2.elvind.sh \
tools/GNUmakefile \ tools/GNUmakefile \
......
...@@ -27,7 +27,7 @@ my $TBROOT = "@prefix@"; ...@@ -27,7 +27,7 @@ my $TBROOT = "@prefix@";
# Untaint the path # Untaint the path
$ENV{'PATH'} = "/usr/bin:$TBROOT/libexec:$TBROOT/libexec/ns2ir" . $ENV{'PATH'} = "/usr/bin:$TBROOT/libexec:$TBROOT/libexec/ns2ir" .
":$TBROOT/sbin:$TBROOT/bin"; ":$TBROOT/libexec/vis:$TBROOT/sbin:$TBROOT/bin";
# #
# Testbed Support libraries # Testbed Support libraries
...@@ -105,6 +105,9 @@ DBQueryWarn("DELETE from ipsubnets where pid='$pid' and eid='$eid'") or ...@@ -105,6 +105,9 @@ DBQueryWarn("DELETE from ipsubnets where pid='$pid' and eid='$eid'") or
DBQueryWarn("DELETE from ipport_ranges where pid='$pid' and eid='$eid'") or DBQueryWarn("DELETE from ipport_ranges where pid='$pid' and eid='$eid'") or
$errors++; $errors++;
print "Removing visualization data...\n";
system("prerender -r $pid $eid");
if ($errors == 0) { if ($errors == 0) {
SetExpState($pid, $eid, EXPTSTATE_TERMINATED) or SetExpState($pid, $eid, EXPTSTATE_TERMINATED) or
$errors++; $errors++;
......
...@@ -31,7 +31,7 @@ my $TBROOT = "@prefix@"; ...@@ -31,7 +31,7 @@ my $TBROOT = "@prefix@";
# Untaint the path # Untaint the path
$ENV{'PATH'} = "/usr/bin:$TBROOT/libexec:$TBROOT/libexec/ns2ir" . $ENV{'PATH'} = "/usr/bin:$TBROOT/libexec:$TBROOT/libexec/ns2ir" .
":$TBROOT/sbin:$TBROOT/bin"; ":$TBROOT/libexec/vis:$TBROOT/sbin:$TBROOT/bin";
# #
# Testbed Support libraries # Testbed Support libraries
...@@ -95,6 +95,7 @@ sub cleanup { ...@@ -95,6 +95,7 @@ sub cleanup {
DBQueryWarn("DELETE from eventlist where pid='$pid' and eid='$eid'"); DBQueryWarn("DELETE from eventlist where pid='$pid' and eid='$eid'");
DBQueryWarn("DELETE from ipsubnets where pid='$pid' and eid='$eid'"); DBQueryWarn("DELETE from ipsubnets where pid='$pid' and eid='$eid'");
SetExpState($pid, $eid, EXPTSTATE_NEW); SetExpState($pid, $eid, EXPTSTATE_NEW);
system("prerender -r $pid $eid");
} }
# This setups virt_nodes, virt_names including all IP address calculation # This setups virt_nodes, virt_names including all IP address calculation
...@@ -109,6 +110,10 @@ if (system("parse-ns $pid $eid $nsfile")) { ...@@ -109,6 +110,10 @@ if (system("parse-ns $pid $eid $nsfile")) {
TBDebugTimeStamp("parser finished"); TBDebugTimeStamp("parser finished");
print "Parser done! " . TBTimeStamp() . "\n"; print "Parser done! " . TBTimeStamp() . "\n";
TBDebugTimeStamp("prerender started in background");
print "Precomputing visualization (in background)...\n";
system("prerender $pid $eid &");
TBDebugTimeStamp("routes started"); TBDebugTimeStamp("routes started");
print "Setting up static routes (if requested) ... \n"; print "Setting up static routes (if requested) ... \n";
if (system("staticroutes $pid $eid")) { if (system("staticroutes $pid $eid")) {
......
...@@ -11,9 +11,9 @@ SUBDIR = vis ...@@ -11,9 +11,9 @@ SUBDIR = vis
include $(OBJDIR)/Makeconf include $(OBJDIR)/Makeconf
BIN_SCRIPTS = vistopology dbvistopology BIN_SCRIPTS = dbvistopology
LIBEXEC_SCRIPTS = webvistopology LIBEXEC_SCRIPTS = webvistopology
LIBEXEC_VIS = topper render top2gif top2png dbtopper LIBEXEC_VIS = prerender render prerender_all
# #
# Force dependencies on the scripts so that they will be rerun through # Force dependencies on the scripts so that they will be rerun through
......
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
use Getopt::Std;
use lib '@prefix@/lib';
use libdb;
my $optlist = "d:";
%options = ();
if (! getopts($optlist, \%options)) {
printf( "Usage:\ndbtopper [-d <detaillevel>] <pid> <eid>\n" );
die;
}
my $detail = "0";
if (defined($options{"d"})) {
($options{"d"} =~ /^([0-9]+)$/) or die "argument to -d must be integer, not '$detail'.";
$detail = $1;
}
if (@ARGV != 2) { die "Usage:\ndbtopper [-d <detaillevel>] <pid> <eid>\n"; }
my $pid = $ARGV[0];
my $eid = $ARGV[1];
my $result = DBQueryFatal("SELECT ips, type, vname FROM virt_nodes " .
"WHERE pid='$pid' AND eid='$eid'");
my $nodes = ();
my $links = ();
while (my ($ips, $type, $vname) = $result->fetchrow) {
$ips =~ s/^\d\://g;
$ips =~ s/\s\d\:/ /g;
$ips =~ s/^192\.168//g;
$ips =~ s/\s192\.168/ /g;
$nodes{$vname}{"ips"} = $ips;
$nodes{$vname}{"type"} = $type;
# $nodes{$vname}{"color"} = "skyblue";
}
$result = DBQueryFatal("SELECT vname, delay, bandwidth, lossrate," .
"rdelay, rbandwidth, rlossrate," .
"member FROM virt_lans " .
"WHERE pid='$pid' AND eid='$eid'");
while (my ($vname, $delay, $bandwidth, $lossrate, $rdelay, $rbandwidth, $rlossrate, $member) =
$result->fetchrow) {
$member =~ s/\:.*//;
$links{$vname}{$member}{"delay"} = $delay;
$links{$vname}{$member}{"bw"} = $bandwidth;
$links{$vname}{$member}{"loss"} = $lossrate;
$links{$vname}{$member}{"rdelay"} = $rdelay;
$links{$vname}{$member}{"rbw"} = $rbandwidth;
$links{$vname}{$member}{"rloss"} = $rlossrate;
}
print "graph G {\n";
foreach my $i (keys %nodes) {
my $label = "";
if ($detail > 0) {
$label = $i . "(" . $nodes{$i}{"type"} . ") " . $nodes{$i}{"ips"};
} else {
$label = $i;
}
print "{node [label = \"$label\", shape = box, color = skyblue] " . gvclean($i) . "}\n";
}
foreach my $lan (keys %links) {
if ((keys %{$links{$lan}}) == 2) {
# amalgamate into 2 member link.
my $a = (keys %{$links{$lan}})[0];
my $b = (keys %{$links{$lan}})[1];
my $delaya2b = $links{$lan}{$a}{"delay"} + $links{$lan}{$b}{"rdelay"};
my $delayb2a = $links{$lan}{$b}{"delay"} + $links{$lan}{$a}{"rdelay"};
my $bwa2b = min( $links{$lan}{$a}{"bw"}, $links{$lan}{$b}{"rbw"} );
my $bwb2a = min( $links{$lan}{$b}{"bw"}, $links{$lan}{$a}{"rbw"} );
my $lossa2b = combineloss( $links{$lan}{$a}{"loss"}, $links{$lan}{$b}{"rloss"} );
my $lossb2a = combineloss( $links{$lan}{$b}{"loss"}, $links{$lan}{$a}{"rloss"} );
my $desc = "";
if ($detail > 0) {
$desc = gendesc( $delaya2b, $delayb2a, $bwa2b, $bwb2a, $lossa2b, $lossb2a );
}
print gvclean($a) . " -- " . gvclean($b). " [label = \"$desc\"];\n";
} else {
# make a lan node.
print "{node [label = \"$lan\", shape = box, color = green] " . gvclean($lan) . "}\n";
foreach my $node (keys %{$links{$lan}}) {
my $delayin = $links{$lan}{$node}{"delay"};
my $delayout = $links{$lan}{$node}{"rdelay"};
my $bwin = $links{$lan}{$node}{"bw"};
my $bwout = $links{$lan}{$node}{"rbw"};
my $lossin = $links{$lan}{$node}{"loss"};
my $lossout = $links{$lan}{$node}{"rloss"};
my $desc = "";
if ($detail > 0) {
$desc = gendesc( $delayin, $delayout, $bwin, $bwout, $lossin, $lossout );
}
print gvclean($lan) . " -- " . gvclean($node) . " [label = \"$desc\"];\n";
}
}
}
print "}\n";
exit;
sub min {
my ($a, $b) = @_;
if ($a < $b) { return $a; }
return $b;
}
sub combineloss {
my ($a, $b) = @_;
# print "$a $b";
# $a = $a / 100.0;
# $b = $b / 100.0;
return (1.0 - ((1.0 - $a) * (1.0 - $b)));
}
sub gvclean {
my $n = shift;
$n =~ s/\W/_/g;
$n = "_x1823Y_" . $n;
return $n;
}
sub reportbw {
my $bandwidth = shift;
if ($bandwidth >= 5000000) {
return sprintf( "%.0f", ($bandwidth / 1000000) ) . "Gb";
} elsif ($bandwidth >= 1000000) {
return sprintf( "%.1f", ($bandwidth / 1000000) ) . "Gb";
} elsif ($bandwidth >= 5000) {
return sprintf( "%.0f", ($bandwidth / 1000) ) . "Mb";
} elsif ($bandwidth >= 1000) {
return sprintf( "%.1f", ($bandwidth / 1000) ) . "Mb";
} elsif ($bandwidth >= 5) {
return sprintf( "%.0f", $bandwidth ) . "kb";
} else {
return sprintf( "%.1f", $bandwidth ) . "kb";
}
}
sub reportdelay {
my $delay = shift;
if ($delay == 0) { return "0msec"; }
if ($delay >= 10) {
return sprintf( "%.0f", $delay ) . "msec";
} else {
return sprintf( "%.1f", $delay ) . "msec";
}
}
sub reportloss {
my $losspct = shift;
$losspct *= 100;
if ($losspct < 0.0001) { return "0\%loss"; }
if ($losspct > 5) {
return sprintf( "%.0f", $losspct ) . "\%loss";
} elsif ($losspct > 1) {
return sprintf( "%.1f", $losspct ) . "\%loss";
} elsif ($losspct > 0.1) {
return sprintf( "%.2f", $losspct ) . "\%loss";
} else {
return sprintf( "%.3f", $losspct ) . "\%loss";
}
}
sub gendesc {
my ($delay0, $delay1, $bw0, $bw1, $loss0, $loss1) = @_;
my $desc = "";
if ($bw0 == $bw1) {
$desc .= reportbw( $bw0 ) . " ";
} else {
$desc .= reportbw( $bw0 ) . "/" . reportbw( $bw1 ) . " ";
}
if ($delay0 == $delay1) {
if ($delay0 != 0) {
$desc .= reportdelay( $delay0 ) . " ";
}
} else {
$desc .= reportdelay( $delay0 ) . "/" . reportdelay( $delay1 ) . " ";
}
if ($loss0 == $loss1) {
if ($loss0 != 0.0) {
$desc .= reportloss( $loss0 ) . " ";
}
} else {
$desc .= reportloss( $loss0 ) . "/" . reportloss( $loss1 ) . " ";
}
$desc =~ s/\s$//g;
return $desc;
}
...@@ -32,10 +32,8 @@ use lib "/usr/testbed/lib"; ...@@ -32,10 +32,8 @@ use lib "/usr/testbed/lib";
use libdb; use libdb;
use libtestbed; use libtestbed;
my $neato = "neato";
my $dbtopper = "$TB/libexec/vis/dbtopper";
my $render = "$TB/libexec/vis/render"; my $render = "$TB/libexec/vis/render";
my $tbdata = "tbdata"; # my $tbdata = "tbdata";
my $output; my $output;
my $zoom; my $zoom;
my $detail; my $detail;
...@@ -129,25 +127,26 @@ if (! TBExptAccessCheck($UID, $pid, $eid, TB_EXPT_READINFO)) { ...@@ -129,25 +127,26 @@ if (! TBExptAccessCheck($UID, $pid, $eid, TB_EXPT_READINFO)) {
" You do not have permission to view experiment $pid/$eid\n"); " You do not have permission to view experiment $pid/$eid\n");
} }
my $args; my $args = "";
if (defined($output)) {
$args = "> $output";
} else {
$args = "2> /dev/null";
}
if (defined($zoom)) { if (defined($zoom)) {
$args = "-z $zoom $args"; $args .= "-z $zoom ";
} }
if (defined($detail)) { if (defined($detail)) {
$topperargs = "-d $detail"; $args .= "-d $detail ";
}
$args .= "$pid $eid ";
if (defined($output)) {
$args .= "> $output";
} else { } else {
$topperargs = ""; $args .= "2> /dev/null";
} }
if (system("$dbtopper $topperargs $pid $eid | $neato | $render $args") != 0) { if (system("$render $args") != 0) {
exit(1); exit(1);
} }
......
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
use Getopt::Std;
# Need this module to use mktemp (commented out below)
# use File::MkTemp;
# Configure variables
my $TB = "@prefix@";
use lib '@prefix@/lib';
use libdb;
# Turn off line buffering on output
$| = 1;
# Untaint the path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#my $NEATO_CMD = "neato -Gstart=5 -Gepsilon=0.0001 -Goverlap=scale -Gsep=1";
#my $NEATO_CMD = "neato -Gstart=rand -Gepsilon=0.001 -Goverlap=scale -Gpack=true -Gsep=1 -Gmclimit=30";
#my $NEATO_CMD = "neato -Gstart=rand -Gepsilon=0.001 -Goverlap=scale -Gpack=true -Gsep=4 -Gmclimit=30";
#my $NEATO_CMD = "neato -Gstart=rand -Gepsilon=0.005 -Goverlap=scale -Gmaxiter=20000 -Gpack=true";
my $NEATO_CMD = "neato -Gstart=rand -Gepsilon=0.005 -Gmaxiter=20000 -Gpack=true";
sub dprint($);
sub usage {
die "Usage:\nprerender [-v] [-r] <pid> <eid>\n" .
" -r Just remove vis info from DB\n";
}
my $optlist = "rv";
%options = ();
if (!getopts($optlist, \%options)) { usage; }
if (@ARGV != 2) { usage; }
my $debug = 0;
if ( defined($options{"v"}) ) { $debug++; }
# for the mungeName function, below.
$mungeUID = 0;
my $pid = $ARGV[0];
my $eid = $ARGV[1];
### First, wipe out the old layout.
DBQueryFatal("DELETE FROM vis_nodes WHERE pid='$pid' AND eid='$eid'");
### If they specified -r, meaning they just wanted to remove vis info
### from the DB, we quit now.
if (defined($options{"r"})) { exit; }
### Now, read experiment virtual info from the DB.
# read nodes
my $result = DBQueryFatal("SELECT vname FROM virt_nodes " .
"WHERE pid='$pid' AND eid='$eid'");
my $nodes = ();
my $lans = ();
my $links = ();
# all virt_nodes are visualizer nodes of type "node"
while (my ($vname) = $result->fetchrow) {
$nodes{$vname}{"type"} = "node";
}
# read lans
$result = DBQueryFatal("SELECT vname, member FROM virt_lans " .
"WHERE pid='$pid' AND eid='$eid'");
while (my ($vname, $member) = $result->fetchrow) {
$member =~ s/\:.*//;
$lans{$vname}{$member} = 1;
}
# construct links from lans
foreach my $lan (keys %lans) {
if ((keys %{$lans{$lan}}) == 2) {
# make a link
my $a = (keys %{$lans{$lan}})[0];
my $b = (keys %{$lans{$lan}})[1];
$links{"$a $b"} = "pair";
} else {
# it's a lan; we need a visualizer node for it (of type "lan").
if (exists $nodes{$lan}) { die "LAN $lan name collided."; }
$nodes{$lan}{"type"} = "lan";
# make the appropriate links (from each member to the LAN)
foreach my $node (keys %{$lans{$lan}}) {
$links{"$node $lan"} = "lan";
}
}
}
### Write topology to temporary file
# Ideally, we could use mktemp, but module isn't installed.
# my $tempfile = mktemp("prerenderXXXXXX.topfile");
# Could use ($ENV{'TMP'} || "/tmp") but not sure if that is safe.
my $tempfile = "/tmp/prerender.$$-" . sprintf("%i",rand(65536));
open (TOPO, ">$tempfile" ) or die( "Couldn't open temporary file '$tempfile'\n" );
# write topology to neato
print TOPO "graph G {\n";
foreach my $node (keys %nodes) {
print TOPO "{node [shape = box] " . mungeName($node) . "}\n";
}
#foreach my $a (keys %nodes) {
# foreach my $b (keys %nodes) {
# if (!exists $links{"$a $b"}) {
# print TOPO mungeName($a) . " -- " . mungeName($b) . " [len = 4];\n";
# print TOPO mungeName($a) . " -- " . mungeName($b) . " ;\n";
# }
# }
#}
foreach my $link (keys %links) {
my ($a, $b) = $link =~ /(\S+)\s(\S+)/;
if ($links{$link} eq "pair") {
# print TOPO mungeName($a) . " -- " . mungeName($b) . " [len = 3];\n";
print TOPO mungeName($a) . " -- " . mungeName($b) . " ;\n";
} else {
# print TOPO mungeName($a) . " -- " . mungeName($b) . " [len = 5];\n";
print TOPO mungeName($a) . " -- " . mungeName($b) . " ;\n";
}
}
print TOPO "}\n";
close(TOPO);
my $bestOverlap;
# loop
for ($attempt = 0; $attempt < 16; $attempt++) {
### Send topology to neato.
open (NEATO, "$NEATO_CMD $tempfile |" );
### Parse results from neato.
%props = ();
%nodeProps = ();
# General neato output parser.
# stashes away all properties returned for each graph node.
# (All we care about for now is "pos")
# Links are ignored.
while (<NEATO>) {
if (/^\s*(\w+)\s\[([^\]]*)\]/) {
# this line is a property set
($cmd, $props) = ($1, $2);
$props =~ s/[\=\,]/ /g;
while (($props =~ s/^\s*(\w+)\s+((\"[^\"]*\")|(\w+))\s*//)) {
# add each property to %props
($k, $v) = ($1, $2);
$v =~ s/\"//g;
$props{$k} = $v;
# print "property $k gets value $v\n";
}
if ($cmd =~ /^node$/) {
# print "node property $props\n";
} elsif ($cmd =~ /^graph$/) {
# print "graph thingee (ignored)\n";
} else {
# there is a name here, not "node" or "graph"
# so it terminates the node.. store props away.
$nodeProps{$cmd} = {%props};
%props = ();
}
}
}
# done with neato process.
close(NEATO);
# obtain X,Y for each node from stashed Properties.
foreach $node (keys %nodes) {
my $mungedName = mungeName( $node );
if (exists $nodeProps{$mungedName}{"pos"}) {
$s = $nodeProps{$mungedName}{"pos"};
$s =~ /^\s*(\d+)\s+(\d+)/;
$nodes{ $node }{"x"} = $1;
$nodes{ $node }{"y"} = $2;
} else {
warn "No position information returned for $node (mungedName=$mungedName)";
}
}
### Fixup positions (eat free columns and rows)
my $subX = 0;
my $lastX = 0;
foreach $i (sort {$nodes{$a}{"x"} <=> $nodes{$b}{"x"}} keys %nodes) {
my $xDiff = $nodes{$i}{"x"} - $lastX;
if ($xDiff > 60) {
$subX += $xDiff - 60;
}
# last should be the position _before_ transform
$lastX = $nodes{$i}{"x"};
$nodes{$i}{"x"} -= $subX;
}
my $subY = 0;
my $lastY = 0;
foreach $i (sort {$nodes{$a}{"y"} <=> $nodes{$b}{"y"}} keys %nodes) {
my $yDiff = $nodes{$i}{"y"} - $lastY;
if ($yDiff > 60) {
$subY += $yDiff - 60;
}