Commit d11b620e authored by Leigh Stoller's avatar Leigh Stoller

Merge the dbtoir branch!

parent 92481dc1
......@@ -62,6 +62,9 @@ node <node> <type> [<desires>]
A weight >= 1.0 will also result in a violation if
not filled.
OR
link <src> <dst> <bw>
features and desires
--------------------
......
......@@ -770,6 +770,8 @@ fi
#
# Okay, I know this is improper usage of --with. Too bad.
#
......@@ -862,7 +864,7 @@ fi
# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
# ./install, which can be erroneously created by make from ./install.sh.
echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
echo "configure:866: checking for a BSD compatible install" >&5
echo "configure:868: checking for a BSD compatible install" >&5
if test -z "$INSTALL"; then
if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
......@@ -940,14 +942,12 @@ outfiles="$outfiles Makeconf GNUmakefile \
tbsetup/batchexp tbsetup/killbatchexp tbsetup/batch_daemon \
tbsetup/webbatchexp tbsetup/webkillbatchexp \
tbsetup/startexp tbsetup/endexp tbsetup/webstartexp tbsetup/webendexp \
tbsetup/ir/GNUmakefile tbsetup/ir/postassign tbsetup/snmpit \
tbsetup/ir/assign_wrapper tbsetup/ns2ir/GNUmakefile \
tbsetup/ns2ir/postparse tbsetup/ir/handle_os tbsetup/ir/handle_ip \
tbsetup/snmpit tbsetup/ns2ir/GNUmakefile \
tbsetup/ns2ir/parse.tcl tbsetup/ns2ir/tb_compat.tcl \
tbsetup/savevlans tbsetup/ir/extract_tb \
tbsetup/tbprerun tbsetup/tbrun tbsetup/tbend tbsetup/tbreport \
tbsetup/named_setup tbsetup/exports_setup \
tbsetup/checkpass/GNUmakefile \
tbsetup/ns2ir/sim.tcl tbsetup/savevlans \
tbsetup/tbprerun tbsetup/tbswapin tbsetup/tbswapout tbsetup/tbend \
tbsetup/tbreport tbsetup/named_setup tbsetup/exports_setup \
tbsetup/checkpass/GNUmakefile tbsetup/assign_wrapper tbsetup/ptopgen \
tip/GNUmakefile \
tmcd/GNUmakefile tmcd/freebsd/GNUmakefile tmcd/linux/GNUmakefile \
tmcd/netbsd/GNUmakefile \
......@@ -1125,6 +1125,8 @@ s%@TBDBNAME@%$TBDBNAME%g
s%@WWWDEFS@%$WWWDEFS%g
s%@TBOPSEMAIL@%$TBOPSEMAIL%g
s%@TBLOGSEMAIL@%$TBLOGSEMAIL%g
s%@DELAYCAPACITY@%$DELAYCAPACITY%g
s%@IPBASE@%$IPBASE%g
s%@LEDA@%$LEDA%g
s%@OPT_CFLAGS@%$OPT_CFLAGS%g
s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
......
......@@ -18,6 +18,8 @@ AC_SUBST(TBDBNAME)
AC_SUBST(WWWDEFS)
AC_SUBST(TBOPSEMAIL)
AC_SUBST(TBLOGSEMAIL)
AC_SUBST(DELAYCAPACITY)
AC_SUBST(IPBASE)
#
# Okay, I know this is improper usage of --with. Too bad.
......@@ -113,14 +115,12 @@ outfiles="$outfiles Makeconf GNUmakefile \
tbsetup/batchexp tbsetup/killbatchexp tbsetup/batch_daemon \
tbsetup/webbatchexp tbsetup/webkillbatchexp \
tbsetup/startexp tbsetup/endexp tbsetup/webstartexp tbsetup/webendexp \
tbsetup/ir/GNUmakefile tbsetup/ir/postassign tbsetup/snmpit \
tbsetup/ir/assign_wrapper tbsetup/ns2ir/GNUmakefile \
tbsetup/ns2ir/postparse tbsetup/ir/handle_os tbsetup/ir/handle_ip \
tbsetup/snmpit tbsetup/ns2ir/GNUmakefile \
tbsetup/ns2ir/parse.tcl tbsetup/ns2ir/tb_compat.tcl \
tbsetup/savevlans tbsetup/ir/extract_tb \
tbsetup/tbprerun tbsetup/tbrun tbsetup/tbend tbsetup/tbreport \
tbsetup/named_setup tbsetup/exports_setup \
tbsetup/checkpass/GNUmakefile \
tbsetup/ns2ir/sim.tcl tbsetup/savevlans \
tbsetup/tbprerun tbsetup/tbswapin tbsetup/tbswapout tbsetup/tbend \
tbsetup/tbreport tbsetup/named_setup tbsetup/exports_setup \
tbsetup/checkpass/GNUmakefile tbsetup/assign_wrapper tbsetup/ptopgen \
tip/GNUmakefile \
tmcd/GNUmakefile tmcd/freebsd/GNUmakefile tmcd/linux/GNUmakefile \
tmcd/netbsd/GNUmakefile \
......
......@@ -11,7 +11,7 @@ include $(OBJDIR)/Makeconf
BIN_SCRIPTS = mac2if nalloc nfree nodeip
SBIN_SCRIPTS = avail inuse showgraph if2port backup webcontrol node_status \
genelists
LIBEXEC_SCRIPTS = ptopgen
LIBEXEC_SCRIPTS =
LIB_SCRIPTS = libdb.pm
# Stuff installed on plastic for mere users.
......
#!/usr/local/bin/perl -w
#
# Generates a ptop file from the output of an avail command.
# Takes input on STDIN and output goes to STDOUT
#
use Mysql;
my $dbh = Mysql->connect("localhost","tbdb","script","none");
my $d = 0; #debug
my %nodes = ();
my %links = ();
my $node;
while (<>) {
if (!/^\|/ ) {next;}
if ( /^\|node_id/) {next;}
/^\|([a-zA-Z0-9]*).*\|/;
$node=$1;
if ($node =~/^((tb){0,1}pc|sh)/) {
print STDERR "Got '$node' \t" if $d && !defined ($nodes{$node});
$nodes{$node} = 1;
}
}
print STDERR "\n" if $d;
my @list = sort keys %nodes;
foreach my $item (@list) {
print STDERR "From '$item'\t" if $d > 1;
if ($item =~ /^sh/) { # If its a shark
$item = "node_id='$item-1'";
} else {
$item = "node_id='$item'";
}
print STDERR "To '$item'\n" if $d > 1;
}
my $cond = join (" or ",@list);
my $query = "select node_id,card,MAC from interfaces where $cond";
print STDERR "\nUsing query '$query'\n\n" if $d;
my $sth = $dbh->query($query);
while (my @row = $sth->fetchrow()) {
print STDERR "Got '$row[0]'\t'$row[1]'\t'$row[2]'\n" if $d > 1;
if ( $row[0] =~ /^(sh\d+)-\d/ ) {
$links{$1} = $row[2];
} else {
$links{$row[0]."-".$row[1]} = $row[2];
}
}
# In every ptop
print "node cisco switch\n";
# Give the nodes:
foreach $node (sort keys %nodes) {
if ($node =~ /pc/) {
print "node $node pc:1 delay:2\n";
print "link $node-0 $node:$links{$node.'-0'} cisco 100 1\n";
print "link $node-1 $node:$links{$node.'-1'} cisco 100 1\n";
print "link $node-2 $node:$links{$node.'-2'} cisco 100 1\n";
print "link $node-3 $node:$links{$node.'-3'} cisco 100 1\n";
} else {
print "node $node shark-shelf:1\n";
print "link $node $node:$links{$node} cisco 100 1\n";
}
}
# Add lans to ptopfile
# lan nodes do not really exist, they represent the ability of the cisco
# to set up lans via vlans. We set the maxmimum number of links on a lan
# node to some very high number. This should actually be the maximum number
# of ports in a vlan. Which, since by default everything is in a VLAN, this
# maxmimum number will be >= the number of ports on the CISCO(s) so really
# need only be a number higher then we'll ever need.
$maxlans = 20;
for ($i=0;$i<$maxlans;$i++) {
print "node lan$i lan:1\n";
print "link lan$i-link lan$i cisco 100 1000\n";
}
......@@ -8,3 +8,6 @@ TBDBNAME=tbdb
WWWDEFS=default
TBOPSEMAIL=calfeld@flux.cs.utah.edu
TBLOGSEMAIL=calfeld@flux.cs.utah.edu
IPBASE=192.168
DELAYCAPACITY=2
......@@ -8,3 +8,5 @@ TBDBNAME=tbdb
WWWDEFS=default
TBOPSEMAIL='Testbed Operations <testbed-ops@flux.cs.utah.edu>'
TBLOGSEMAIL=testbed-logs@flux.cs.utah.edu
IPBASE=192.168
DELAYCAPACITY=2
\ No newline at end of file
......@@ -5,5 +5,7 @@ TBDBNAME=tbdb
WWWDEFS=newbold-emulab
TBOPSEMAIL=newbold@cs.utah.edu
TBLOGSEMAIL=newbold@cs.utah.edu
IPBASE=192.168
DELAYCAPACITY=2
......@@ -5,5 +5,7 @@ TBDBNAME=tbdb
WWWDEFS=stoller-emulab
TBOPSEMAIL='Leigh B Stoller <stoller@fast.cs.utah.edu>'
TBLOGSEMAIL='Leigh B Stoller <stoller@fast.cs.utah.edu>'
IPBASE=192.168
DELAYCAPACITY=2
......@@ -5,4 +5,6 @@ TBDBNAME=tbdb
WWWDEFS=stoller-home
TBOPSEMAIL='Leigh B Stoller <stoller@stoller.casco.net>'
TBLOGSEMAIL='Leigh B Stoller <stoller@stoller.casco.net>'
IPBASE=192.168
DELAYCAPACITY=2
......@@ -8,9 +8,9 @@ SUBDIR = tbsetup
include $(OBJDIR)/Makeconf
SUBDIRS = checkpass ir ns2ir
SUBDIRS = checkpass ns2ir
BIN_STUFF = power snmpit tbend tbrun tbprerun tbreport \
BIN_STUFF = power snmpit tbend tbswapin tbswapout tbprerun tbreport \
os_load savevlans startexp endexp batchexp killbatchexp \
node_reboot nscheck
......@@ -22,7 +22,8 @@ SBIN_STUFF = resetvlans console_setup.proxy sched_reload named_setup \
LIBEXEC_STUFF = mkprojdir rmproj mkacct-ctrl rmacct-ctrl \
os_setup mkexpdir console_setup webnscheck \
webstartexp webendexp webbatchexp webkillbatchexp
webstartexp webendexp webbatchexp webkillbatchexp \
assign_wrapper ptopgen
LIB_STUFF = libtbsetup.pm libtestbed.pm snmpit_intel.pm \
snmpit_cisco.pm snmpit_lib.pm snmpit_apc.pm
......@@ -35,11 +36,10 @@ all: $(BIN_STUFF) $(SBIN_STUFF) $(LIBEXEC_STUFF) $(LIB_STUFF) $(SUBDIRS)
include $(TESTBED_SRCDIR)/GNUmakerules
.PHONY: checkpass ir ns2ir
.PHONY: checkpass ns2ir
checkpass:
@$(MAKE) -C checkpass all
ir:
@$(MAKE) -C ir all
ns2ir:
@$(MAKE) -C ns2ir all
......@@ -51,7 +51,6 @@ install: all script-install subdir-install
#
subdir-install:
@$(MAKE) -C checkpass install
@$(MAKE) -C ir install
@$(MAKE) -C ns2ir install
script-install: $(addprefix $(INSTALL_BINDIR)/, $(BIN_STUFF)) \
......@@ -99,14 +98,12 @@ clean: subdir-clean
subdir-clean:
@$(MAKE) -C checkpass clean
@$(MAKE) -C ir clean
@$(MAKE) -C ns2ir clean
distclean: subdir-distclean
subdir-distclean:
@$(MAKE) -C checkpass distclean
@$(MAKE) -C ir distclean
@$(MAKE) -C ns2ir distclean
#
......
tbsetup
tbsetup README
----------------------------------------------------------------------
tbprerun
Syntax:
tbprerun <pid> <eid> <ns-file>
tbprerun [-nologfile] <pid> <eid> <ns-file>
tbprerun does all tasks necessary to setup up for an experiment on the
testbed. It generates an IR file that can be used with 'tbrun' to
actually configure the testbed.
tbprerun takes a brand new experiment, checks the syntax of the
ns-file, and then converts the ns-file into a virtual topology stored
in the database.
CAVEAT: This does do actual reservation of nodes. Be sure to free the
nodes using tbend, even if you never run the experiment.
-nologfile - Prevents creation of a logfile.
----------------------------------------------------------------------
tbrun
tbswapin
Syntax:
tbrun <pid> <eid> <ir-file>
tbswapin [-nologfile] <pid> <eid>
tbrun setups up the testbed for the experiment.
tbswapin swaps an experiment in. Specifically it finds and allocates
a set of resources and configures these resources to emulate the
desired experiment. As a part of this it configures the physical
state in the DB.
-nologfile - Prevents creation/appending of a logfile.
tbreport
Exit Codes:
tbswapin exits with a variety of codes to represent different error
conditions. Almost all of these codes are actually generated by
assign_wrapper and just passed through by tbswapin.
Exit 0 : All good
2: Insufficient resources.
X: other errors
1+
4 - Bandwidth violation
8 - Linkusers violation
16 - Desires violation.
I.e.
1 - Unspecified error
5 - Bandwidth error
9 - Linkusers error
17 - Desires error.
13 - Bandwidth and linkusers error.
21 - Bandwidth and desires error.
25 - Linkusers and desires error.
29 - Bandwidth, linkusers, and desires.
----------------------------------------------------------------------
tbswapout
Syntax:
tbreport [-v] <ir-file>
tbswapout [-nologfile] [-force] <pid> <eid>
tbreport parses the IR file and produces a human readable report on
the contents. -v will include extra information.
tbswapout is the inverse of tbswapin. It tears down the physical
state on the testbed, freeing the resources for other use. It clears
the physical state from the DB but leaves the virtual state intact. I.e.
it reverse tbswapin but not tbprerun.
-nologfile - Prevents creation/appending of a logfile.
-force - Does not check current experiment state.
----------------------------------------------------------------------
tbend
Syntax:
tbend <pid> <eid>
tbend [-nologfile] [-force] <pid> <eid>
tbend is the inverse of tbprerun. It clears the virtual state from
the DB and marks the experiment as ended.
-nologfile - Prevents creation/appending of a logfile.
-force - Does not check current experiment state.
----------------------------------------------------------------------
tbreport
Syntax:
tbreport [-v] <pid> <eid>
tbreport displays the current state of an experiment. It displays
both the virtual topology, and, if the experiment is currently
running, the physical mapping.
-v - Display extra information.
----------------------------------------------------------------------
Basic Operation
The basic operation of the tbsetup tools is:
1. tbprerun
2. tbswapin
3. do experiment
4. tbswapout
5. Repeat steps 2-4 as necessary.
6. tbend
It is assumed that an experiment is already entered in the DB before
tbprerun is called. Likewise, tbend does not remove the entry.
----------------------------------------------------------------------
Experimental States
The experiment goes through the following states:
new - Before tbprerun. tbprerun requires the experiment to be in this
state before running.
prerunning - During tbprerun.
swapped - After tbprerun or tbswapout. tbswapin requires the
experiment to be in this state before running. tbend requires this
state unless -force is used.
activating - During tbswapin.
active - After tbswapin. tbswapout requires this state unless -force
is used.
swapping - During tbswapout.
terminating - During tbend.
ended - After tbend.
----------------------------------------------------------------------
Non-symmetries
In general tbprerun and tbend are inverse operations and tbswapin and
tbswapout are inverse operations. This is not completely true,
however. tbswapout does completely reverse tbswapin, and tbend clears
some state that is set in tbswapin, rather than tbprerun.
Node state: For performance reasons tbswapout does not touch node
state. The idea being that node state only matters for active
experiments and these experiments will reconfigure the node state when
they are swapped in.
Portmap: The portmap table in the DB is not cleared by tbswapout.
This is because the portmap table will be used the next time tbswapin
is called to try to match the portmapping of the previous swap. The
purpose behind this is to preserve the same port mapping across swaps.
The portmap table is cleared in tbend instead.
tbend does tear down.
This should be run at the end of every experiment even if nothing was
done in the experiment.
#!/usr/bin/perl -w
# This function as the main assign loop. It converts the virtual
# topology into a top input including LAN and delay translation. It
# then snapshots the current testbed physical state and runs assign,
# looping a couple times if assign fails. When assign successfully
# completes it will interpret the results. Attempt to match any
# existing portmap entries and then update the delays and vlans table.
# Syntax: assign_wrapper <pid> <eid>
# Caveats:
# The support for direct and interswitch links has not been testbed much.
# LANs containing single nodes can result in strange behaviors.
# Settings
# delaythresh is the maximum delay in ms above which a delay node is needed.
# maxrun is maximum number of times we run assign.
$delaythresh = 3;
$maxrun = 5;
use DBI;
$TBROOT = "@prefix@";
$ENV{'PATH'} = "/usr/bin:$TBROOT/libexec:$TBROOT/sbin:$TBROOT/bin";
$TBDB = "@TBDBNAME@";
$dbh = DBI->connect("DBI:mysql:database=$TBDB;host=localhost") ||
die "Could not connect to DB.\n";
$verbose = 0;
if (($#ARGV == 2) && ($ARGV[0] eq "-v")) {
$verbose = 1;
shift;
} elsif ($#ARGV != 1) {
print STDERR "Syntax: $0 pid eid\n";
exit(1);
}
($pid,$eid) = @ARGV;
$ptopfile = "$pid-$eid-$$.ptop";
sub printdb {
if ($verbose) {
print $_[0];
}
};
# Let's read the minimum requirements for this experiment.
$sth = $dbh->prepare("SELECT minimum_nodes from experiments" .
" where pid=\"$pid\" and eid=\"$eid\"");
$sth->execute;
($minimum_nodes) = $sth->fetchrow_array;
$sth->finish;
######################################################################
# Step 1 - Setup virtual topology
#
# Here we need to read the virtual topology in from the virt_nodes
# and virt_lans table. We then need to add delay and lan nodes as
# necessary.
#
# Conversion details:
# Let L be a LAN with N members.
# If N == 2
# Let N1 be node 1
# Let N2 be node 2
# If L is delayed
# Generate delay node D
# Link N1 to D
# Link N2 to D
# Else
# Link N1 to N2
# Else
# Generate lan node A
# If L is delayed
# Foreach node N in L
# Generate delay node DN
# Link A to DN
# Link N to DN
# Else
# Foreach node N in L
# Link N to A
#
# Datastructures:
# nodes is indexed by virtual node and contains the node type.
# nodelans is indexed by virtual node and contains a list of
# <port>:<lan> that it is connected to.
# ips is indexed by nodeport and contains the IP addresses.
# lans is indexed by virtual lan and is a list of nodeport members.
# delayinfo is indexed by virtual lan:node:port and is a list of delay,
# bandwidth, lossrate.
# okbandwidths is indexed by bandwidth and is just a set.
# lannodes is indexed by physical name is the set of lan nodes.
# interfacespeed is indexed by type and contains the bandwidth.
# delaynodes is indexed by link name and contains [delay,bw,loss]
#
# Delay node names:
# delay nodes are named tbdelayXX N > 2
# and tbsdelayXX for N == 2.
#
# Lan node nameS:
# lan nodes are named lan/<virtual lan>
######################################################################
# Shark Hack
# For each LAN we replace all the sharks in the LAN with a single
# shark shelf node. After this goes through assign we pull them
# all back out.
#
# sharkshelves is indexed by virtual shelf name and is a list of
# the virtual nodes in it.
# sharkshelfid is used to generate ids for shark shelves.
$sharkshelfid = 0;
# delayid is used to generate ids for delay nodes.
$delayid = 0;
printdb "Generating TOP file.\n";
# Let's figure out what kind of links we have.
printdb "Finding interface speeds:";
$sth=$dbh->prepare("SELECT type,max_speed from interface_types");
$sth->execute;
while (($type,$bandwidth) = $sth->fetchrow_array) {
$okbandwidths{$bandwidth} = 1;
$interfacespeed{$type} = $bandwidth;
printdb " $bandwidth";
}
$sth->finish;
printdb "\n";
printdb "Loading virt_nodes.\n";
$sth = $dbh->prepare("SELECT vname,ips,type from virt_nodes" .
" where pid=\"$pid\" and eid=\"$eid\"");
$sth->execute;
while (($vname,$ips,$type) = $sth->fetchrow_array) {
printdb " $vname $type $ips\n";
# We need to check the names to make sure they won't clash with
# our internal delay node names.
if (($vname =~ /^tbdelay\d+/) ||
($vname =~ /^tbsdelay\d+/)) {
print STDERR "Warning: $vname is a reserved name. Working around.\n";
($num) = ($vname =~ /(\d+)/);
$delayid = $num + 1;
}
$nodes{$vname} = $type;
$nodelans{$vname} = [];
foreach $ipinfo (split(" ",$ips)) {
($port,$ip) = split(":",$ipinfo);
$ips{"$vname:$port"} = $ip;
}
}
$sth->finish;
printdb "Loading virt_lans.\n";
$sth = $dbh->prepare("SELECT vname,member,delay,bandwidth,lossrate" .
" from virt_lans where pid=\"$pid\" and eid=\"$eid\"");
$sth->execute;
while (($vname,$member,$delay,$bandwidth,$lossrate) = $sth->fetchrow_array) {
if (! defined($lans{$vname})) {
$lans{$vname} = [];
}
push(@{$lans{$vname}},$member);
$delayinfo{"$vname:$member"} = [$delay,$bandwidth,$lossrate];
($node,$port) = split(":",$member);
push(@{$nodelans{$node}},"$port:$vname");
printdb " $vname $member - $delay $bandwidth $lossrate\n";
printdb " Added $port:$vname to nodelans of $node\n";
}
$sth->finish;
# Shark hack
foreach $lan (keys(%lans)) {
$realmembers = [];
$sharks = [];
$hassharks = 0;
foreach $member (@{$lans{$lan}}) {
($node) = (split(":",$member))[0];
if ($nodes{$node} eq "shark") {
push(@$sharks,$member);
$hassharks = 1;
} else {
push(@$realmembers,$member);
}
}
if ($hassharks) {
$shelfid = "sharkshelf$sharkshelfid";
printdb " Creating shark shelf: $shelfid (" .
join(" ",@$sharks) . ")\n";
$sharkshelfid++;
$sharkshelves{$shelfid} = $sharks;
push(@$realmembers,"$shelfid:uplink");
$nodes{$shelfid} = "shark-shelf";
}
$lans{$lan} = $realmembers;
}
# End shark hack
# isdelayed(lan)
# Returns 1 if the lan is a delayed lan and 0 otherwise.
sub isdelayed {
my $lan = $_[0];
my $member;
foreach $member (@{$lans{$lan}}) {
my ($delay,$bandwidth,$lossrate) = @{$delayinfo{"$lan:$member"}};
if (($delay > $delaythresh) ||
(! defined($okbandwidths{$bandwidth })) ||
($lossrate != 0)) {
return 1;
}
}
return 0;
};
# min(a,b)
# Returns the minimum of a and b.
sub min {
my ($a,$b) = @_;
return ($a < $b ? $a : $b);
};
# getbandwidth(bw)
# Returns the lowest ok bandwidth that is greater than or equal to
# the one passed.
sub getbandwidth {
my $targetbandwidth= $_[0];
my $bandwidth;
my $best = 10000000000;
foreach $bandwidth (keys(%okbandwidths)) {
if (($bandwidth >= $targetbandwidth) && ($bandwidth < $best)) {
$best = $bandwidth;
}
}
return $best;
};
# Open the TOP file
$topfile = "$pid-$eid-$$.top";
open(TOPFILE,"> $topfile") || do {
print STDERR "Could not open $topfile.\n";
exit(1);
};
foreach $node (keys(%nodes)) {
# Shark hack
if ($nodes{$node} ne "shark") {
print TOPFILE "node $node $nodes{$node}\n";
}
# End Shark hack
}
foreach $lan (keys(%lans)) {
@members = @{$lans{$lan}};
if ($#members == 1) {
($nodeport0,$nodeport1) = @members;
$node0 = (split(":",$nodeport0))[0];
$node1 = (split(":",$nodeport1))[0];
($delay0,$bw0,$loss0) = @{$delayinfo{"$lan:$nodeport0"}};
($delay1,$bw1,$loss1) = @{$delayinfo{"$lan:$nodeport1"}};
$bandwidth = &getbandwidth(&min($bw0,$bw1));
if (&isdelayed($lan)) {
$delayname = "tbsdelay$delayid";
$delaynodes{"linksdelaysrc/$lan"} = [$delay0+$delay1,
&min($bw0,$bw1),
1-(1-$loss0)*(1-$loss1)];
printdb "Delay node linksdelaysrc/$lan ($delayname) = " .
join(" ",@{$delaynodes{"linksdelaysrc/$lan"}}) . "\n";
$delayid++;
print TOPFILE "node $delayname delay\n";
print TOPFILE "link linksdelaysrc/$lan $node0 $delayname"
. " $bandwidth\n";
print TOPFILE "link linksdelaydst/$lan $node1 $delayname"
. " $bandwidth\n";
} else {
print TOPFILE "link linksimple/$lan $node0 $node1 $bandwidth\n";
}
} else {
print TOPFILE "node lan/$lan lan\n";
$lannodes{"lan/$lan"} = 1;
foreach $member (@members) {
($delay,$bw,$loss) = @{$delayinfo{"$lan:$member"}};