assign_wrapper.in 59.3 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
#!/usr/bin/perl -w

Leigh B. Stoller's avatar
Leigh B. Stoller committed
3
4
5
6
7
8
9
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group.
# All rights reserved.
#


Leigh B. Stoller's avatar
Leigh B. Stoller committed
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 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.

# Settings
# delaythresh is the maximum delay in ms above which a delay node is needed.
24
#  (Note that the DB represents delays as floating point numbers)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
25
# maxrun is maximum number of times we run assign.
26
27
# delaywithswitch if 1 will use the switch to delay when possible.  Currently
#  this only works for 10mbit links.
28
$delaythresh = 2;
29
$maxrun = 20;
30
$delaywithswitch=0;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#
# Some handy constants. Speed in Mbits/sec and Kbits/sec units.
# The conversion routine is to make explicit that we operate with two
# different sets of units. One is the topology, which is in Kbps now.
# The second is the physical description, which has been changed to
# Kbps in the DB (and in ptopgen).
#
# Its probably a good idea to leave portbw (current_speed) in Mbs, since
# those numbers are used at the switch and the interfaces, which really
# only think in Mbps.
#
my $S10Mbs  = 10;
my $S100Mbs = 100;
my $S10Kbs  = 10000;
my $S100Kbs = 100000;
sub BWConvert($) {
    #
    # Use this if physical units are in Mbs (used to be).
    #
    #my $bw = $_[0] / 1000;
    
    my $bw = $_[0];
    return $bw;
}

57
$DELAYCAPACITY = @DELAYCAPACITY@;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
58
$TBROOT = "@prefix@";
59
$ENV{'PATH'} = "/usr/bin:$TBROOT/libexec:$TBROOT/sbin:$TBROOT/bin";
60

61
62
use lib '@prefix@/lib';
use libdb;
63
use libtestbed;
64
require exitonwarn;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
65

66
67
68
69
70
#
# Turn off line buffering on output
#
$| = 1;

71
72
73
74
75
76
77
use Getopt::Std;

getopts('v',\%opt);

sub usage {
	print "Usage: $0 [-v] pid eid\n";
	print "		-v enables verbose output\n";
78
	exit(-1);
79
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
80

81
my $verbose = 0;
82
83
84
85
86
if ($opt{v}) {
	$verbose = 1;
}

if (@ARGV != 2) {
87
	usage();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
88
}
89

Leigh B. Stoller's avatar
Leigh B. Stoller committed
90
91
92
93
94
95
96
97
98
99
($pid,$eid) = @ARGV;

$ptopfile = "$pid-$eid-$$.ptop";

sub printdb {
    if ($verbose) {
	print $_[0];
    }
};

100
101
TBDebugTimeStamp("assign_wrapper started");

Leigh B. Stoller's avatar
Leigh B. Stoller committed
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
######################################################################
# 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.
133
134
#  isremotenode is indexed by virtual node and says whether the node is
#    is remote. We let wanassign deal with those nodes.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
135
136
137
138
139
#  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,
Christopher Alfeld's avatar
Christopher Alfeld committed
140
141
#    bandwidth, lossrate, rdelay, rbandwidth, rlossrate.  Where r* indicates
#    switch->node and the others indicate node->switch.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
142
143
144
#  okbandwidths is indexed by bandwidth and is just a set.
#  lannodes is indexed by physical name is the set of lan nodes.
#  delaynodes is indexed by link name and contains [delay,bw,loss]
145
#  portbw is indexed by virtual nodeport and contains the bandwidth
146
147
148
149
#    of that port. Note that port bandwidth in the interfaces table is
#    left in Mbps units for now. Thats inconsistent, I know. For LANs with
#    other bandwidths the port speed will be 100 and a delay node will
#    be inserted.
150
151
#  fixed_nodes is indexed by virtual node name and points to physical node
#   name.
Christopher Alfeld's avatar
Christopher Alfeld committed
152
#  vtypes is indexed by vtype name and is a list of {weight types}.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#
# 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";

177
178
TBDebugTimeStamp("top started");

Leigh B. Stoller's avatar
Leigh B. Stoller committed
179
180
# Let's figure out what kind of links we have.
printdb "Finding interface speeds:";
181
182
my $result = DBQueryFatal("SELECT type,max_speed from interface_types");
while (($type,$bandwidth) = $result->fetchrow_array) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
183
184
185
    $okbandwidths{$bandwidth} = 1;
    printdb " $bandwidth";
}
186
$result->finish;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
187
188
printdb "\n";

Shashi Guruprasad's avatar
Shashi Guruprasad committed
189
190
191
192
193
194
# XXX NSE hack: List of simulated nodes. All these are gonna go
# into one pc850. Needs to change in distributed nse
my @simnodelist;
my %simnode_iplist = ();
my %iptonodemap = ();

Leigh B. Stoller's avatar
Leigh B. Stoller committed
195
196
197
198
# XXX Remote/Virt node hacks. Turns out to be similar to NSE.
my %isremotenode = ();
my %isvirtnode   = ();

Leigh B. Stoller's avatar
Leigh B. Stoller committed
199
printdb "Loading virt_nodes.\n";
200
$result =
Leigh B. Stoller's avatar
Leigh B. Stoller committed
201
202
    DBQueryFatal("select distinct vname,ips,vn.type,fixed, ".
		 " nt.isremotenode,nt.isvirtnode ".
203
204
205
206
207
		 " from virt_nodes as vn ".
		 "left join node_types as nt on ".
		 " nt.type=vn.type or nt.class=vn.type ".
		 "where pid='$pid' and eid='$eid'");

Leigh B. Stoller's avatar
Leigh B. Stoller committed
208
209
210
211
212
while (($vname,$ips,$type,$fixed,$isremote,$isvirt) = $result->fetchrow_array){
    if (defined($fixed) && $fixed eq "") {
	undef($fixed);
    }
    
213
    # REMOTENODE HACK
214
    #
215
    # if its a vtype, no entry in node_types. vtypes break remote nodes.
216
217
218
    # Need to look inside the vtype and make sure no mixing of vnodes and
    # physnodes. Later ...
    #
219
    if (! defined($isremote)) {$isremote = 0;}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
220
221
222
223
    if (! defined($isvirt)) {$isvirt = 0;}
    
    printdb "  $vname $type $ips";
    printdb " " . (defined($fixed) ? $fixed : "") . " $isremote\n";
224
    
Leigh B. Stoller's avatar
Leigh B. Stoller committed
225
226
227
228
229
230
231
232
233
234
    # 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} = [];
235

Shashi Guruprasad's avatar
Shashi Guruprasad committed
236
237
238
239
    if( $type eq "sim" ) {
      push( @simnodelist, $vname );
      $simnode_iplist{$vname} = [];
    }    
240
    # REMOTENODE HACK
241
    # 
242
    $isremotenode{$vname} = $isremote;
243
244
245
    foreach $ipinfo (split(" ",$ips)) {
	($port,$ip) = split(":",$ipinfo);
	$ips{"$vname:$port"} = $ip;
Shashi Guruprasad's avatar
Shashi Guruprasad committed
246
247
248
249
	if( $type eq "sim" ) {
	  push(@{$simnode_iplist{$vname}}, $ip);
	}
	$iptonodemap{$ip} = $vname;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
250
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
251
252
    $isvirtnode{$vname} = $isvirt;
    if (defined($fixed)) {
253
254
	$fixed_nodes{$vname} = $fixed;
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
255
}
256
$result->finish;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
257

Leigh B. Stoller's avatar
Leigh B. Stoller committed
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#
# VIRTNODES HACK: Sanity check. Local virtnodes have to be assigned to
# another node. Remote virtnodes can also be fixed, and they are done
# specially too! See below.
# Note, node it is fixed to is a physnode (not another virtnode). Once
# we assign the physnode, we assign the virtnodes to that physnode later
# on. Yep, its confusing and overlaps with simnodes.
#
foreach my $vname (keys(%isvirtnode)) {
    next
	if (! $isvirtnode{$vname});
    
    #
    # Mixing local and remote virtnodes is actually broken right now. 
    #
    next
	if ($isremotenode{$vname});

    if (! defined($fixed_nodes{$vname})) {
	die("*** $0:\n".
	    "    Local virtnode $vname must be fixed to a real node!\n");
    }
    #
    # The node it is fixed to must not be another virtnode!
    #
    if ($isvirtnode{$fixed_nodes{$vname}}) {
	die("*** $0:\n".
	    "    Local virtnode $vname is fixed to another virt node!\n");
    }
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
289
printdb "Loading virt_lans.\n";
290
291
$result =
    DBQueryFatal("select vname,member,delay,bandwidth,lossrate," .
292
		 "rdelay,rbandwidth,rlossrate,widearea " .
293
294
295
		 "from virt_lans where pid='$pid' and eid='$eid'");

#
296
# REMOTENODE HACK: Remote nodes are special.
297
#
298
# A list of all lans that have remote nodes as members,
299
my %rnodelans = ();
300
301
302
# A list of all the tunnels we need to build. Each list member is a list
# of the nodes in the tunnel.
my %tunnels   = ();
303
304
305
306

#
# Now do the real pass.
# 
Christopher Alfeld's avatar
Christopher Alfeld committed
307
while (($vname,$member,$delay,$bandwidth,$lossrate,
308
	$rdelay,$rbandwidth,$rlossrate,$widearea) = $result->fetchrow_array) {
309
310
311
    ($node,$port) = split(":",$member);

    #
312
    # REMOTENODE HACK: 
313
    #
314
    # If its a duplex link involving a remotenode, skip it. 
315
    # We do not want to have assign deal with these. The nodes are
316
317
    # allocated as unconnected by another program, and we deal with
    # it later by creating tunnels.
318
    # 
319
    if ($widearea) {
320
321
322
323
	if (! defined($tunnels{$vname})) {
	    $tunnels{$vname} = [];
	}
	push(@{$tunnels{$vname}},$member);
324
	$rnodelans{$vname} = 1;
325
326
	printdb "    Added $member to tunnels of $vname\n";
	next;
327
328
    }
    
Leigh B. Stoller's avatar
Leigh B. Stoller committed
329
330
331
332
    if (! defined($lans{$vname})) {
	$lans{$vname} = [];
    }
    push(@{$lans{$vname}},$member);
Christopher Alfeld's avatar
Christopher Alfeld committed
333
334
    $delayinfo{"$vname:$member"} = [$delay,$bandwidth,$lossrate,
				    $rdelay,$rbandwidth,$rlossrate];
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

    #
    # Grab the Q stuff from virt_lans. I'm keeping this separate for
    # now until I understand whats going on. There are no "r" params
    # either. I have no idea how do to this stuff for lans, and for
    # duplex links the "r" params are not necessary. Each virt_lans
    # entry gives the params towards the switch, which equal the
    # reverse params for the other member. 
    #
    my $query_result =
	DBQueryFatal("select q_limit,q_maxthresh,q_minthresh,q_weight, ".
		     "       q_linterm,q_qinbytes,q_bytes,q_meanpsize, ".
		     "       q_wait,q_setbit,q_droptail,q_red,q_gentle ".
		     "from virt_lans ".
		     "where pid='$pid' and eid='$eid' and ".
		     "      vname='$vname' and member='$member'");
    
    my ($q_limit,$q_maxthresh,$q_minthresh,$q_weight,$q_linterm,
	$q_qinbytes,$q_bytes,$q_meanpsize,$q_wait,$q_setbit,
	$q_droptail,$q_red,$q_gentle) = $query_result->fetchrow_array;
    
    $queueinfo{"$vname:$member"} =
	[$q_limit,$q_maxthresh,$q_minthresh,$q_weight,$q_linterm,
	 $q_qinbytes,$q_bytes,$q_meanpsize,$q_wait,$q_setbit,
	 $q_droptail,$q_red,$q_gentle];

    if ($q_red) {
	$mustdelay{$vname} = 1;
    }
    
365
366
367
368
369
370
371
372
373
374
375
376
    #
    # XXX - Whenever a delay node is inserted, port speeds are set to
    #       100Mbs, even if they requested exactly 10Mbs. This is a
    #       simplification. At some point we might want to force all the
    #       ports along the way to 10Mbs, and have the delay node worry
    #       about delay only, and not bandwidth. That will be harder to
    #       to do in this mess. See companion XXX below where the delays
    #       table is initialized. Initially, we set the speed to 10Mbs,
    #       if a delay node is insterted below, it resets this to 100Mbs.
    # 
    if ($bandwidth == $S10Kbs && $delaywithswitch) {
	$portbw{$member} = $S10Mbs;
377
    } else {
378
	$portbw{$member} = $S100Mbs;
379
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
380
    push(@{$nodelans{$node}},"$port:$vname");
381
    printdb "  portbw of $member = $portbw{$member}\n";
Christopher Alfeld's avatar
Christopher Alfeld committed
382
    printdb "  $vname $member - $delay $bandwidth $lossrate $rdelay $rbandwidth $rlossrate\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
383
384
    printdb "    Added $port:$vname to nodelans of $node\n";
}
385
$result->finish;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
386

387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
#
# Check event list. Anytime we find an event to control a link, we need
# to drop a delay node in. start/stop especially, since thats the easiest
# way to do that, even if the link has no other traffic shaping in it. 
# 
printdb "Checking events for LINK commands.\n";
$result =
    DBQueryFatal("select distinct vname from eventlist as ex ".
		 "left join event_eventtypes as et on ex.eventtype=et.idx ".
		 "left join event_objecttypes as ot on ex.objecttype=ot.idx ".
		 "where ot.type='LINK' and ex.pid='$pid' and ex.eid='$eid'");
while (($vname) = $result->fetchrow_array) {
    $mustdelay{$vname} = 1;
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
402
403
404
405
406
407
408
# Shark hack
foreach $lan (keys(%lans)) {
    $realmembers = [];
    $sharks = [];
    $hassharks = 0;
    foreach $member (@{$lans{$lan}}) {
	($node) = (split(":",$member))[0];
409
	if (($nodes{$node} eq "shark") || ($nodes{$node} eq "dnard")) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
410
411
412
413
414
415
416
417
418
419
420
421
	    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;
422
	$delayinfo{"$lan:$shelfid:uplink"} = [0,$S100Kbs,0.0,
423
					      0,$S100Kbs,0.0];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
424
425
426
427
428
429
430
	push(@$realmembers,"$shelfid:uplink");
	$nodes{$shelfid} = "shark-shelf";
    }
    $lans{$lan} = $realmembers;
}
# End shark hack

Christopher Alfeld's avatar
Christopher Alfeld committed
431
432
433
434
435
436
437
438
439
# Load virt types
printdb "Loading virt_vtypes.\n";
$result = DBQueryFatal("SELECT name,weight,members from virt_vtypes" .
		       " where pid=\"$pid\" and eid=\"$eid\"");
while (($name,$weight,$types) = $result->fetchrow_array) {
    printdb "  $name $weight $types\n";
    $vtypes{$name} = "$weight $types";
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
440
# Open the TOP file
441
$topfile = "$eid.top";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
442
open(TOPFILE,"> $topfile") || do {
443
444
    die("*** $0:\n".
	"    Could not open $topfile.\n");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
445
446
};

Christopher Alfeld's avatar
Christopher Alfeld committed
447
448
449
450
# Print out vtypes
foreach $vtype (keys(%vtypes)) {
    print TOPFILE "make-vclass $vtype $vtypes{$vtype}\n";
}
451
452
453
454

$nodes=0;
$delaynodes=0;

Leigh B. Stoller's avatar
Leigh B. Stoller committed
455
456
foreach $node (keys(%nodes)) {
    # Shark hack
457
    if (($nodes{$node} ne "shark") &&
Leigh B. Stoller's avatar
Leigh B. Stoller committed
458
459
	($nodes{$node} ne "dnard") &&
	!$isremotenode{$node} && !$isvirtnode{$node}) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
460
	print TOPFILE "node $node $nodes{$node}\n";
461
462
463
	if ($nodes{$node} ne "shark-shelf") {
	    $nodes++;
	}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
464
465
466
467
    }
    # End Shark hack
}

Shashi Guruprasad's avatar
Shashi Guruprasad committed
468
469
470
471
472
473
474
475
#
# lans that have simulated nodes
my %simnodelans = ();

#
# lans that have real nodes
my %realnodelans = ();

Leigh B. Stoller's avatar
Leigh B. Stoller committed
476
477
foreach $lan (keys(%lans)) {
    @members = @{$lans{$lan}};
478
    printdb "$lan - " . join(" ",@members) . "\n";
479
480
481
    # Shark hack for rvr
    $sharks = 0;
    $nonsharks = 0;
Shashi Guruprasad's avatar
Shashi Guruprasad committed
482
483
    $simnodes = 0;
    $realnodes = 0;
484
485
486
487
    foreach $member (@members) {
	($node) = (split(":",$member))[0];
	if ($nodes{$node} eq "shark-shelf") {
	    $sharks++;
Shashi Guruprasad's avatar
Shashi Guruprasad committed
488
489
490
491
	  } elsif ($nodes{$node} eq "sim") {
	    $simnodes++;
	    $simnodelans{$lan} = 1;
	  } else {
492
	    $nonsharks++;
Shashi Guruprasad's avatar
Shashi Guruprasad committed
493
494
	    $realnodes++;
	    $realnodelans{$lan} = 1;
495
496
	}
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
497
498
499
500
    if ($#members == 1) {
	($nodeport0,$nodeport1) = @members;
	$node0 = (split(":",$nodeport0))[0];
	$node1 = (split(":",$nodeport1))[0];
Christopher Alfeld's avatar
Christopher Alfeld committed
501
502
503
504
505
506
507
508
509
510
511
512
513
	($delay0,$bw0,$loss0,
	 $rdelay0,$rbw0,$rloss0) = @{$delayinfo{"$lan:$nodeport0"}};
	($delay1,$bw1,$loss1,
	 $rdelay1,$rbw1,$rloss1) = @{$delayinfo{"$lan:$nodeport1"}};
	# Here the r's aregoing to be 1->0 and the others 0->1
	$delay = $delay0+$rdelay1;
	$loss = 1-(1-$loss0)*(1-$rloss1);
	$bw = &min($bw0,$rbw1);
	$rdelay = $rdelay0+$delay1;
	$rloss = 1-(1-$rloss0)*(1-$loss1);
	$rbw = &min($rbw0,$bw1);
	$bandwidth = &getbandwidth(&min($bw0,$rbw1));
	$rbandwidth = &getbandwidth(&min($rbw0,$bw1));
Shashi Guruprasad's avatar
Shashi Guruprasad committed
514
	if (((($delay >= $delaythresh) ||
515
516
517
518
519
	     (($bw != $S100Kbs) && ($bw != $S10Kbs)) ||
	     (($delaywithswitch == 0) && 
	      (($bw != $S100Kbs) && (($sharks == 0) || ($nonsharks > 1)))) || 
	     ($loss != 0)) ||
	    (defined($mustdelay{$lan})) ||
Christopher Alfeld's avatar
Christopher Alfeld committed
520
521
522
523
	    (($rdelay >= $delaythresh) ||
	     (($rbw != $S100Kbs) && ($rbw != $S10Kbs)) ||
	     (($delaywithswitch == 0) && 
	      (($rbw != $S100Kbs) && (($sharks == 0) || ($nonsharks > 1)))) || 
Shashi Guruprasad's avatar
Shashi Guruprasad committed
524
525
526
527
528
529
	     ($rloss != 0))) &&
	    # XXX simulated nodes hack. We don't want to put delay nodes between
	    # simulated nodes. If there is a link between a simulated and a real
	    # node, we might need to put in delay nodes
	    ($realnodes != 0)
	   ) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
530
	    $delayname = "tbsdelay$delayid";
Christopher Alfeld's avatar
Christopher Alfeld committed
531
532
	    $delaynodes{"linksdelaysrc/$lan"} = [$delay,$bw,$loss,
						 $rdelay,$rbw,$rloss];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
533
534
535
536
	    printdb "Delay node linksdelaysrc/$lan ($delayname) = " . 
		join(" ",@{$delaynodes{"linksdelaysrc/$lan"}}) . "\n";
	    $delayid++;
	    print TOPFILE "node $delayname delay\n";
537
	    $delaynodes++;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
538
539
540
541
	    print TOPFILE "link linksdelaysrc/$lan $node0 $delayname"
		. " $bandwidth\n";
	    print TOPFILE "link linksdelaydst/$lan $node1 $delayname"
		. " $bandwidth\n";
542
543
544
545
546
547
548
549
	    #
	    # Ports are set to 100Mbs when a link gets a delay node.
	    # This can override initialization above cause we could not
	    # tell earlier if the link was going to get a real delay node
	    # or just a delaywithswitch.
	    #
	    $portbw{$nodeport0} = $S100Mbs;	    
	    $portbw{$nodeport1} = $S100Mbs;	    
Leigh B. Stoller's avatar
Leigh B. Stoller committed
550
551
552
	} else {
	    print TOPFILE "link linksimple/$lan $node0 $node1 $bandwidth\n";
	}
553
    } elsif ($#members != 0) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
554
555
556
	print TOPFILE "node lan/$lan lan\n";
	$lannodes{"lan/$lan"} = 1;
	foreach $member (@members) {
Christopher Alfeld's avatar
Christopher Alfeld committed
557
558
	    ($delay,$bw,$loss,
	     $rdelay,$rbw,$rloss) = @{$delayinfo{"$lan:$member"}};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
559
	    $bandwidth = &getbandwidth($bw);
Christopher Alfeld's avatar
Christopher Alfeld committed
560
	    $rbandwidth = &getbandwidth($rbw);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
561
	    ($node) = (split(":",$member))[0];
562
563
# XXX The expression below should be modified for better bandwidth support.
# Probably needs to happen post assign somehow.
Shashi Guruprasad's avatar
Shashi Guruprasad committed
564
	    if (((($delay >= $delaythresh) ||
565
566
567
568
569
570
		 (($bw != $S100Kbs) && ($bw != $S10Kbs))  ||
		 (($delaywithswitch == 0) && 
		  (($bw != $S100Kbs) && (($sharks == 0) ||
					 ($nonsharks > 1)))) ||
		 ($loss != 0)) || 
		(defined($mustdelay{$lan})) ||
Christopher Alfeld's avatar
Christopher Alfeld committed
571
572
573
574
575
		(($rdelay >= $delaythresh) ||
		 (($rbw != $S100Kbs) && ($rbw != $S10Kbs))  ||
		 (($delaywithswitch == 0) && 
		  (($rbw != $S100Kbs) && (($sharks == 0) ||
					 ($nonsharks > 1)))) ||
Shashi Guruprasad's avatar
Shashi Guruprasad committed
576
577
578
579
		 ($rloss != 0))) &&
		# if we have 1 real node in the LAN, we may need to create a lan
		($realnodes != 0)
	       ) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
580
		$delayname = "tbdelay$delayid";
581
582
		$delaynodes{"linkdelaysrc/$lan/$member"} =
		    [$delay,$bw,$loss,$rdelay,$rbw,$rloss];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
583
584
585
586
		printdb "Delay node linkdelaysrc/$lan/$member ($delayname) " .
		    " = " . join(" ",@{$delaynodes{"linkdelaysrc/$lan/$member"}}) . "\n";
		$delayid++;
		print TOPFILE "node $delayname delay\n";
587
		$delaynodes++;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
588
589
590
591
		print TOPFILE "link linkdelaysrc/$lan/$member" .
		    " $node $delayname $bandwidth\n";
		print TOPFILE "link linkdelaydst/$lan/$member" .
		    " lan/$lan $delayname $bandwidth\n";
592
593
594
595
596
597
598
		#
		# Port is set to 100Mbs when the link gets a delay node.
		# This can override initialization above cause we could not
		# tell earlier if the link was going to get a real delay node
		# or just a delaywithswitch.
		#
		$portbw{$member} = $S100Mbs;	    
Leigh B. Stoller's avatar
Leigh B. Stoller committed
599
600
601
602
603
604
	    } else {
		print TOPFILE "link linklan/$lan/$member $node lan/$lan" .
		    " $bandwidth\n";
	    }
	}
    }
605
    # If a LAN has only one member we don't do anything.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
606
607
}

Shashi Guruprasad's avatar
Shashi Guruprasad committed
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
# XXX NSE hack
# Will find a free randomly chosen pc850 and fix sim nodes to it
# Assign's scoring needs to be fixed so that the solution has
# as many sim nodes on a phy node as possible while maxing out
# its interfaces. Currently having sim:N where N is a large
# number in the ptop file does not guarantee that all these
# get mapped to a single phy node even when that mapping has no
# violations and a low score. distributed nse will fix this
#$result =
#  DBQueryFatal("select a.node_id,a.type from nodes as a ".
#	   "left join reserved as b on a.node_id=b.node_id ".
#	   "left join reserved as m on a.phys_nodeid=m.node_id ".
#	   "where b.node_id is null and (a.role='testnode' and ".
#	   "      a.type='pc850' and ".
#	   "       (m.node_id is null or ".
#	   "        m.pid!='$DEADPID' or m.eid!='$DEADEID'))");
if( scalar(@simnodelist) > 0 ) {
  open(AVAIL,"$TBROOT/sbin/avail type=pc rand |")
    or die "*** $0:\n".
           "    avail failed\n";

  my $num = 0;
  while (<AVAIL>) {
    if (! /^\|/) {next};
    if (/node_id/) {next;}
    ($fixednode,$type) = /^\|([-a-zA-Z0-9]+)\s*\|(\w+)\s*\|(\w+)\s*\|$/;    
    $num++;
    last;
  }
  close(AVAIL);
  
  if( $num == 0 ) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
640
    print STDERR "$0: *** Insufficient PCs available.\n";
Shashi Guruprasad's avatar
Shashi Guruprasad committed
641
642
643
644
645
646
647
648
    exit(2);
  }

  foreach $simnode (@simnodelist) {
    print TOPFILE "fix-node $simnode $fixednode\n";
  }
}

649
650
# Print out fixed nodes
foreach $fixed (keys(%fixed_nodes)) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
651
    if (!$isremotenode{$fixed} && !$isvirtnode{$fixed}) {
652
653
	print TOPFILE "fix-node $fixed $fixed_nodes{$fixed}\n";
    }
654
655
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
656
657
close TOPFILE;

658
659
660
# Set estimations
$minimum_nodes = $nodes + $delaynodes/$DELAYCAPACITY;
$maximum_nodes = $nodes + $delaynodes;
661
DBQueryFatal("UPDATE experiments set maximum_nodes=$maximum_nodes, " .
662
	 "minimum_nodes=$minimum_nodes where pid=\"$pid\" and eid=\"$eid\"");
663
print "Minimum nodes = $minimum_nodes\n";
664
665
print "Maximum nodes = $maximum_nodes\n";

666
667
TBDebugTimeStamp("top finished");

Leigh B. Stoller's avatar
Leigh B. Stoller committed
668
669
670
671
672
673
674
675
676
######################################################################
# Step 2 - Assign Loop
# 
# Here we loop up to maxrun times.  In each loop we snapshot the
# current testbed state into a ptop file.  We then run assign.  If
# assign succeeds we attempt to reserve the resources.  If that works
# we're done with step 2 otherwise we loop again.
#
# v2pmap is indexed by virtual and contains the physical node.
Shashi Guruprasad's avatar
Shashi Guruprasad committed
677
# p2vmap is indexed by physical and contains one or more virtual nodes
678
# p2pmap is indexed by physical and contains the physical node.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
679
680
681
682
683
# plinks is indexed by virtual name and contains 
#  (pnodeportA,pnodeportB) .  If one is a delay node it is always
#  the second.
#######################################################################

684
TBDebugTimeStamp("assign_loop started");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
685
686
687
688
689
$currentrun = 1;
while (1) {
    print "Assign Run $currentrun\n";

    # Violation counts
690
    $unassigned = -1;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
691
692
693
694
695
696
697
698
    $linkusers = -1;
    $bandwidth = -1;
    $desires = -1;

    # Clear v2pmap, p2vmap, and plinks
    undef %v2pmap;
    undef %p2vmap;
    undef %plinks;
Shashi Guruprasad's avatar
Shashi Guruprasad committed
699
    
700
    TBDebugTimeStamp("ptopgen started");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
701
    # Snapshot
702
    system("ptopgen > $ptopfile");
703
    TBDebugTimeStamp("ptopgen finished");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
704
705

    # Get number of nodes
706
707
    my $numnodes_result = 
	DBQueryFatal("select a.node_id,a.type from" .
Leigh B. Stoller's avatar
Leigh B. Stoller committed
708
709
710
		     " nodes as a left join reserved as b" .
		     " on a.node_id=b.node_id" .
		     " where b.node_id is null" .
711
		     " and a.role='testnode' and a.type!='dnard'");
712
    $numnodes = $numnodes_result->numrows;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
713
714
    
    if ($numnodes < $minimum_nodes) {
715
	print STDERR "$0: *** Insufficient nodes available.\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
716
717
718
	exit(2);
    }

719
    TBDebugTimeStamp("assign started");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
720
721
722
    # Run assign
    $fail = 0;
    print "assign -b -t $ptopfile $topfile\n";
723
    open(ASSIGNFP,"assign -b -t $ptopfile $topfile | tee assign.log |");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
724
725
726
727
728
729
730
731
    $violations = 0;
    $score = -1;

    # read output
    # Header
    printdb "Reading assign results.\n";
    while (<ASSIGNFP>) {
	chop;
732
733
734
735
	/No physical nodes of type (.+)$/ && do {
	    $score=-2;
	    print $_ . "\n";
	};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
736
737
738
739
740
741
	/^With ([0-9]+) violations$/ && do {
	    $violations = $1;
	    last;
	};
	/^[ \t]+BEST SCORE: [ \t]+([0-9]+\.[0-9]+)/ && ($score=$1);
    }
742
743
744
745
746
    if ($score == -2) {
	# Type error
	print "Giving up.\n";
	exit(2);
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
747
748
749
750
751
752
753
754
755
756
757
758
759
    printdb "Found score $score, violations $violations.\n";

    # We don't bother reading anything else if violations occured.
    if (($violations == 0) && ($score != -1)) {
	# read nodes section
	while (<ASSIGNFP> !~ /^Nodes:/) {}
	printdb "Nodes:\n";
	while (<ASSIGNFP>) {
	    chop;
	    /^End Nodes$/ && last;
	    @info = split;
	    ($virtual,$physical) = @info[0,2];
	    $v2pmap{$virtual} = $physical;
Shashi Guruprasad's avatar
Shashi Guruprasad committed
760
761
762
763
	    if( ! defined($p2vmap{$physical}) ) {
	      $p2vmap{$physical} = [];
	    }
	    push(@{$p2vmap{$physical}}, $virtual);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
764
765
766
767
768
769
770
771
	    printdb "  $virtual $physical\n";
	}

	# read Edges
	# By convention, in plinks, the delay node is always the second
	# entry.
	while (<ASSIGNFP> !~ /^Edges:/) {}
	printdb "Edges:\n";
Shashi Guruprasad's avatar
Shashi Guruprasad committed
772
773
	EDGEWHILE: while (<ASSIGNFP>) {
	    /^End Edges$/ && last EDGEWHILE;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
774
775
776
777
778
779
780
781
782
	    @info = split;
	    $line = $_;
	    $_ = $info[1]; # type
	  SWITCH1: {
	      /^intraswitch$/ && do {
		  ($vlink,$rawA,$rawB) = @info[0,3,5];
		  last SWITCH1;
	      };
	      /^interswitch$/ && do {
783
784
		  ($vlink,$rawA,$rawB) = @info[0,3,$#info];
		  last SWITCH1;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
785
786
	      };
	      /^direct$/ && do {
787
788
		  die("*** $0:\n".
		      "    Unsupported link type: direct.\n");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
789
	      };
Shashi Guruprasad's avatar
Shashi Guruprasad committed
790
791
792
793
794
795
	      /^trivial$/ && do {
		  # we don't have plinks for trivial links
		  $vlink = $info[0];
		  $plinks{$vlink} = [];
		  next EDGEWHILE;
	      };
Leigh B. Stoller's avatar
Leigh B. Stoller committed
796
797
	      print "Found garbage: $line\n";
	  }
798
799
800
801
	    $nodeportA = &getnodeport($rawA);
	    $nodeportB = &getnodeport($rawB);
	    $nodeportA =~ s/\//:/;
	    $nodeportB =~ s/\//:/;
Shashi Guruprasad's avatar
Shashi Guruprasad committed
802
            if (&isdelay($nodeportB)) {
803
804
805
806
		$plinks{$vlink} = [$nodeportA,$nodeportB];
	    } else {
		$plinks{$vlink} = [$nodeportB,$nodeportA];
	    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
807
808
809
810
811
812
	    printdb "  $vlink " . join(" ",@{$plinks{$vlink}}) . "\n";
	}
    } else {
	# spit out up to nodes
	print "ASSIGN FAILED:\n";       
	while (<ASSIGNFP>) {
813
	    if (/link_users:\s*(\d+)$/) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
814
		$linkusers = $1;
815
	    } elsif (/bandwidth:\s*(\d+)$/) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
816
		$bandwidth = $1;
817
	    } elsif (/unassigned:\s*(\d+)$/) {
818
		$unassigned = $1;
819
	    } elsif (/desires:\s*(\d+)$/) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
820
821
822
823
824
825
826
827
828
		$desires = $1;
	    }
	    if (/^Nodes:/) {last;}
	    print "$_";
	}
	$fail = 1;
    }
    while (<ASSIGNFP>) { } # Read anything left in the pipe before closing
    close(ASSIGNFP);
829
    TBDebugTimeStamp("assign finished");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
830

831
    TBDebugTimeStamp("reserving started");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
832
    # Reserve resources
833
    if (!$fail) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
834
835
836
	print "Reserving resources.\n";
	@toreserve = ();
	# We don't reserve LAN nodes.
Shashi Guruprasad's avatar
Shashi Guruprasad committed
837
838
839
840
	foreach $node (keys(%p2vmap)) {
	  if ( ! defined($lannodes{$p2vmap{$node}[0]})) {
	    push(@toreserve,$node);
	  }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
841
842
843
844
845
	}
	if (system("nalloc $pid $eid " . join(" ",@toreserve))) {
	    print "Failed to reserve resources.  Trying again.\n";
	} else {
	    print "Successfully reserved resources.  Finishing setup.\n";
846
	    TBDebugTimeStamp("reserving finished");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
	    last;
	}
    }

    # Check for exit
    if ($currentrun >= $maxrun) {
	$exitcode = 1;
	if ($bandwidth > 0) {
	    $exitcode += 4;
	} 
	if ($linkusers > 0) {
	    $exitcode += 8;
	}
	if ($desires > 0) {
	    $exitcode += 16;
	}
863
864
865
	if ($unassigned > 0) {
	    $exitcode += 32;
	}
866
867
	print "*** $0:\n".
	      "    Reached run limit. Giving up. Exitcode: $exitcode.\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
868
869
870
871
872
	exit($exitcode);
    }
    $currentrun++;
}

873
874
TBDebugTimeStamp("assign_loop finished");

875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
###########################################################################
# Step 2A
#
# We run the wanassigner to allocate remote nodes. We do this after cause
# it takes so long. We run it just once.
#
# wanassign does its own nalloc.
#
###########################################################################

if (scalar(keys(%isremotenode))) {
    my $success  = 0;
    my $doremote = 0;
    
    foreach my $value (values(%isremotenode)) {
	if ($value) {
	    $doremote = 1;
	}
    }
    
    if ($doremote) {
	print "Running 'wanassign -d $pid $eid'\n";
	open(WANFP,"wanassign -d $pid $eid 2>&1 | tee wanassign.log |") or
	    die("*** $0:\n".
		"    Failed to start wanassign: $!\n");

	printdb "Reading wanassign results.\n";
	while (<WANFP>) {
	    chop;
	    if ($_ =~ /(\S+) mapsto (\S+)/) {
		$v2pmap{$1} = $2;
Shashi Guruprasad's avatar
Shashi Guruprasad committed
906
		push(@{$p2vmap{$2}}, $1);
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
		printdb "  $1 $2\n";
	    }
	    if ($_ =~ /^Success/) {
		$success = 1;
	    }
	    # Skip other output. Usually its debugging output.
	}
	close(WANFP) or
	    die("*** $0:\n".
		"    wanassign: " . $? ? "exited with status: $?.\n" :
		                         "error closing pipe: $!\n");

	if (!$success) {
	    die("*** $0:\n".
		"    wanassign could not find a solution!\n");
	}
	
	TBDebugTimeStamp("wanassign finished");
    }
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
#
# VIRTNODES HACK: Local virtnodes have to be mapped now. As mentioned
# above, local virtnodes are "fixed" to a local real node. Now that the
# real nodes have been allocated, fix the requested virtnodes to them.
#
foreach my $vname (keys(%isvirtnode)) {
    next
	if (! $isvirtnode{$vname});
    
    #
    # Mixing local and remote virtnodes is actually broken right now. 
    #
    next
	if ($isremotenode{$vname});

    my $fixed = $fixed_nodes{$vname};
    my $pnode;
    my $type;

    #
    # Run avail to see if there are any virtnodes free on the (phys) node 
    # that its being fixed to. 
    # 
    open(AVAIL,"$TBROOT/sbin/avail virtonly=$v2pmap{$fixed} rand |")
	or die("*** $0:\n".
	       "    avail failed\n");

    my $num = 0;
    while (<AVAIL>) {
	next
	    if (! /^\|/);
	next
	    if (/node_id/);
	
	($pnode) = /^\|([-a-zA-Z0-9]+)\s*\|(\w+)\s*\|(\w+)\s*\|$/;
	last;
    }
    close(AVAIL);

    if (!defined($pnode)) {
	print STDERR "*** $0:\n".
	    "Could not map $vname to a virtual node on $fixed\n";
	exit(2);
    }

    #
    # Try to allocate. Note, if this fails we are done for. Okay for now
    # since it is never the case that it should fail given the current
    # requirement that all local vnodes are "fixed" to a real node.
    # 
    if (system("nalloc $pid $eid $pnode")) {
	die("*** $0:\n".
	    "Failed to reserve $pnode (on $fixed)\n");
    }

    $v2pmap{$vname} = $pnode;
    push(@{$p2vmap{$pnode}}, $vname);
    printdb "  Mapping $vname to $pnode on $fixed\n";
}

988
989
990
991
992
#
# VIRTNODES HACK: physical nodes (from the nodes table) might really
# be a virtual node :-) Must get the underlying phys_nodeid.
#
my %p2pmap = ();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
993
my %jailed = ();
994
995
996

foreach my $pnode (keys(%p2vmap)) {
    my $phys_nodeid;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
997
    my $jailflag;
998

999
    # Skip special LAN nodes.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1000
1001
1002
1003
1004
1005
1006
1007
    if ( scalar(@{$p2vmap{$pnode}}) == 1 &&
	 defined($lannodes{$p2vmap{$pnode}[0]})) {
	next;
    }

    if (! TBIsNodeVirtual($pnode, \$jailflag)) {
	$jailed{$pnode} = 0;
	$p2pmap{$pnode} = $pnode;
1008
1009
1010
	next;
    }

1011
1012
1013
1014
    if (! TBPhysNodeID($pnode, \$phys_nodeid)) {
	die("*** $0:\n".
	    "    Cannot determine phys_nodeid for $pnode!\n");
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1015
1016
    
    $jailed{$pnode} = $jailflag;
1017
1018
    $p2pmap{$pnode} = $phys_nodeid;
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1019
TBExptSetPortRange();
1020

Leigh B. Stoller's avatar
Leigh B. Stoller committed
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
######################################################################
# Step 3 - Convert to vlans, delays, and portmap
# 
# Here we convert the plinks into vlans, delays, and portmap.  We
# convert them first into internal datastructure.  After Step 4
# when we do some port swapping we'll upload the modified versions
# of these structures into the database.
#
# vlans is indexed by an internal ID contains:
#  [vname, members]
# delays is indexed by an internal ID and contains:
#  [pnode, int0, int1, vname, delay, bandwidth, lossrate]
# portmap is indexed by <virtual node>:<virtual port> and contains
#  the physical port.
# nodevlans is indexed by physical node and contains the VLANs
#  it's interfaces are in.  It does not contain entries for delay
#  nodes.
#
# vlan ids
#  vlan ids are increasing integers in the case of node<->delay connections.
#  In the case of actual LANs either of real node or of delay nodes
#  they are indexed by virtual lan name.
# delay ids
#  delay ids are increasing integers.  We could have used a list of
# delays just as well.  Having it as an array may prove useful for
# future changes however.
######################################################################

$vlanid = 0;
$delayid = 0;
foreach $pnode (keys(%p2vmap)) {
    $nodevlans{$pnode} = [];
}

printdb "Interpreting results.\n";
1056
TBDebugTimeStamp("interpreting started");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1057
foreach $plink (keys(%plinks)) {
Shashi Guruprasad's avatar
Shashi Guruprasad committed
1058
1059
1060
1061
1062
1063

    # trival links dont have physical links
    if( scalar(@{$plinks{$plink}}) == 0 ) {
      next;
    }
    
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
    ($nodeportA,$nodeportB) = @{$plinks{$plink}};
    ($nodeA,$portA) = split(":",$nodeportA);
    ($nodeB,$portB) = split(":",$nodeportB);
    printdb "plink $plink - $nodeportA $nodeportB\n";
    if (($lan) = ($plink =~ m|^linksdelaysrc/(.+)$|)) {
	# Node has a single entry in lan.
	# Node is nodeportA
	# Delay node is nodeportB
	# Other end of delay node will be given by plink
	#   linksdelaydst/lan where nodeportA will be the other node in
	#   the virtual LAN and nodeportB will be the other end of the
	#   delay node.

	($nodeportC,$nodeportD) = @{$plinks{"linksdelaydst/$lan"}};
	($nodeC,$portC) = split(":",$nodeportC);
	($nodeD,$portD) = split(":",$nodeportD);
	printdb "LINK delay: other end = $nodeportC $nodeportD\n";

	# assert nodeB == nodeD

	printdb "  VLANS:\n";
	$vlans{$vlanid} = [$lan,[$nodeportA, $nodeportB]];
	push(@{$nodevlans{$nodeA}},$vlanid);
	printdb "    $vlanid = $lan, \[$nodeportA, $nodeportB\]\n";
	$vlanid++;
	$vlans{$vlanid} = [$lan,[$nodeportC, $nodeportD]];
	push(@{$nodevlans{$nodeC}},$vlanid);
	printdb "    $vlanid = $lan, \[$nodeportC, $nodeportD\]\n";
	$vlanid++;

Christopher Alfeld's avatar
Christopher Alfeld committed
1094
1095
	($delay,$bandwidth,$lossrate,
	 $rdelay,$rbandwidth,$rlossrate) = @{$delaynodes{$plink}};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1096
	$delays{$delayid} = [$nodeB,$portB,$portD,$lan,
Christopher Alfeld's avatar
Christopher Alfeld committed
1097
			     $delay,$bandwidth,$lossrate,
Christopher Alfeld's avatar
Christopher Alfeld committed
1098
1099
			     $rdelay,$rbandwidth,$rlossrate,
			     $nodeportA,$nodeportC];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1100
	printdb "  Delays: $delayid = \[$nodeB,$portB,$portD,$lan" .
Christopher Alfeld's avatar
Christopher Alfeld committed
1101
1102
	    ",$delay,$bandwidth,$lossrate,$rdelay,$rbandwidth," .
		"$rlossrate,$nodeportA,$nodeportC\]\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1103
	$delayid++;
Shashi Guruprasad's avatar
Shashi Guruprasad committed
1104
1105
1106
1107

	# XXX NSE hack: p2vmap is an array now. I'm sure we
	# could do the same in cleaner, more efficient way
	# Right now, trying to get something working
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1108
	printdb "  Portmap:\n";
Shashi Guruprasad's avatar
Shashi Guruprasad committed
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
	foreach $vnode (@{$p2vmap{$nodeA}}) {
	  $virtA = &find_vport($vnode,$lan);
	  if( $virtA ne "" ) {
	    $portmap{"$vnode:$virtA"} = $portA;
	    printdb "    $vnode:$virtA = $portA\n";
	  }
	}
	foreach $vnode (@{$p2vmap{$nodeC}}) {
	  $virtC = &find_vport($vnode,$lan);
	  if( $virtC ne "" ) {
	    $portmap{"$vnode:$virtC"} = $portC;
	    printdb "    $vnode:$virtC = $portC\n";
	  }
	}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
    } elsif (($lan,$member) = ($plink =~ m|^linkdelaysrc/([^/]+)/(.+)$|)) {
	# Node may have multiple entries in lan.
	# Delay node is nodeB and portB.
	# Other end of delay node will be given by plink
	#  linkdelaydst/lan/node where nodeportA will the LAN node and
	#  nodeportB will be the other end of the delay node.
	($nodeportC,$nodeportD) = @{$plinks{"linkdelaydst/$lan/$member"}};
	($nodeC,$portC) = split(":",$nodeportC);
	($nodeD,$portD) = split(":",$nodeportD);
	printdb "LAN delay src: other end = $nodeportC $nodeportD\n";
	
	$vlans{$vlanid} = [$lan,[$nodeportA, $nodeportB]];
	push(@{$nodevlans{$nodeA}},$vlanid);
	printdb "  VLANS:\n";
	printdb "    $vlanid = $lan, \[$nodeportA $nodeportB\]\n";
	$vlanid++;
	
	if (! defined($vlans{$lan})) {
	    $vlans{$lan} = [$lan,[]];
	}
	$members = (@{$vlans{$lan}})[1];
	push(@$members,$nodeportD);
	printdb "    $lan = $lan, \[" . join(" ",@$members) . "\]\n"; 

Christopher Alfeld's avatar
Christopher Alfeld committed
1147
1148
	($delay,$bandwidth,$loss,
	 $rdelay,$rbandwidth,$rloss) = @{$delaynodes{$plink}};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1149
	$delays{$delayid} = [$nodeB,$portB,$portD,$lan,
Christopher Alfeld's avatar
Christopher Alfeld committed
1150
			     $delay,$bandwidth,$loss,
Christopher Alfeld's avatar
Christopher Alfeld committed
1151
1152
			     $rdelay,$rbandwidth,$rloss,
                             $nodeportA,$nodeportC];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1153
	printdb "  Delays: $delayid = \[$nodeB,$portB,$portD,$lan," .
Christopher Alfeld's avatar
Christopher Alfeld committed
1154
1155
	    "$delay,$bandwidth,$loss,$rdelay,$rbandwidth,$rloss," .
             "$nodeportA,$nodeportC\]\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1156
	$delayid++;
Shashi Guruprasad's avatar
Shashi Guruprasad committed
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169

	# XXX NSE hack: p2vmap is an array now. I'm sure we
	# could do the same in cleaner, more efficient way
	# Right now, trying to get something working
	printdb "  Portmap:\n";
	foreach $vnode (@{$p2vmap{$nodeA}}) {
	  $virtA = &find_vport($vnode,$lan);
	  if( $virtA ne "" ) {
	    $portmap{"$vnode:$virtA"} = $portA;
	    printdb "    $vnode:$virtA = $portA\n";
	  }
	}
     } elsif (($lan) = ($plink =~ m|^linksimple/(.+)$|)) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1170
1171
1172
1173
1174
1175
1176
1177
	# nodeportA and nodeportB are the only two nodes in the
	# virtual LAN and there are no delays.
	printdb "Simple Link\n";
	$vlans{$lan} = [$lan,[$nodeportA,$nodeportB]];
	printdb "  VLANS: $lan = $lan, \[$nodeportA $nodeportB\]\n";
	
	push(@{$nodevlans{$nodeA}},$lan);
	push(@{$nodevlans{$nodeB}},$lan);
Shashi Guruprasad's avatar
Shashi Guruprasad committed
1178
1179
1180
1181

	# XXX NSE hack: p2vmap is an array now. I'm sure we
	# could do the same in cleaner, more efficient way
	# Right now, trying to get something working
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1182
	printdb "  Portmap:\n";
Shashi Guruprasad's avatar
Shashi Guruprasad committed
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
	foreach $vnode (@{$p2vmap{$nodeA}}) {
	  $virtA = &find_vport($vnode,$lan);
	  if( $virtA ne "" ) {
	    $portmap{"$vnode:$virtA"} = $portA;
	    printdb "    $vnode:$virtA = $portA\n";
	  }
	}
	foreach $vnode (@{$p2vmap{$nodeB}}) {
	  $virtB = &find_vport($vnode,$lan);
	  if( $virtB ne "" ) {
	    $portmap{"$vnode:$virtB"} = $portB;
	    printdb "    $vnode:$virtB = $portB\n";
	  }
	}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
    } elsif (($lan) = ($plink =~ m|^linklan/([^/]+)/.+$|)) {
	# node may be the LAN multiple times.
	# nodeportA is the node.
	# nodeportB is the LAN
	# No delays
	printdb "LAN\n";
	if (! defined($vlans{$lan})) {
	    $vlans{$lan} = [$lan,[]];
	}
	$members = (@{$vlans{$lan}})[1];
	push(@$members,$nodeportA);
	printdb "  VLANS: $lan = $lan,\[" . join(" ",@$members) . "\]\n";

Shashi Guruprasad's avatar
Shashi Guruprasad committed
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
	# XXX NSE hack: p2vmap is an array now. I'm sure we
	# could do the same in cleaner, more efficient way
	# Right now, trying to get something working
	printdb "  Portmap:\n";
	foreach $vnode (@{$p2vmap{$nodeA}}) {
	  $virtA = &find_vport($vnode,$lan);
	  if( $virtA ne "" ) {
	    $portmap{"$vnode:$virtA"} = $portA;
	    printdb "    $vnode:$virtA = $portA\n";
	  }
	}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1221
1222
1223
    }
    # Else delaysrc case, will be handled by one of the other cases.
}
1224
TBDebugTimeStamp("interpreting finished");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1225
1226
1227
1228
1229
1230
1231

######################################################################
# Step 4 - Upload to DB
#
# Nothing fancy.
######################################################################
printdb "Uploading to DB\n";
1232
TBDebugTimeStamp("uploading started");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1233
1234
foreach $vlan (keys(%vlans)) {
    ($lan,$members) = @{$vlans{$vlan}};
1235
    DBQueryFatal("insert into vlans (id,pid,eid,virtual,members) values" .
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1236
	     " (0,\"$pid\",\"$eid\",\"$lan\",\"" .
1237
	     join(" ",@$members) . "\")");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1238
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1239
1240
my $pipeid = 100;

Leigh B. Stoller's avatar
Leigh B. Stoller committed
1241
foreach $delay (keys(%delays)) {
Christopher Alfeld's avatar
Christopher Alfeld committed
1242
1243
    # So r* indicates int1->int0 and others are int0->int1
    ($pnode,$int0,$int1,$vname,$delay,$bandwidth,$lossrate,
Christopher Alfeld's avatar
Christopher Alfeld committed
1244
     $rdelay,$rbandwidth,$rlossrate,$np0,$np1) = 
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1245
	@{$delays{$delay}};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1246
1247
1248
    my $pipe0 = $pipeid;
    my $pipe1 = $pipeid + 10;
    $pipeid += 100;
1249
1250
1251
1252
1253
1254
1255
1256
1257

    #
    # Okay, this is terible. If this is link (not a lan) then find the
    # the queue info. We do not support queues in lans yet.
    #
    my @members = @{$lans{$vname}};
    if (@members == 2) {
	my ($nodeport0,$nodeport1) = @members;

Christopher Alfeld's avatar
Christopher Alfeld committed
1258
	$np0node = (split(":",$np0))[0];
Shashi Guruprasad's avatar
Shashi Guruprasad committed
1259
1260
1261
1262
1263
1264
	#$np1node = (split(":",$np1))[0];

	# XXX NSE hack: p2vmap is an array now
	# Commented out coz this is used onlu\y in printdb
	#$np0vnode = $p2vmap{$np0node};
	#$np1vnode = $p2vmap{$np1node};
Christopher Alfeld's avatar
Christopher Alfeld committed
1265
1266
1267
1268

	$node0 = (split(":",$np0))[0];
	$node1 = (split(":",$np1))[0];

Shashi Guruprasad's avatar
Shashi Guruprasad committed
1269
1270
1271
1272
1273
	#printdb("np0 = $np0, np0vnode = $np0vnode, np1 = $np1, ".
	#	"np1vnode = $np1vnode, nodeport0 = $nodeport0, ".
	#	"nodeport1 = $nodeport1\n");
	printdb("np0 = $np0, np1 = $np1, ".
		"nodeport0 = $nodeport0, ".
1274
		"nodeport1 = $nodeport1\n");
Christopher Alfeld's avatar
Christopher Alfeld committed
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284

	# This is pretty hackish
	if ($node0 eq $np0node) {
	    $vnp0 = $nodeport0;
	    $vnp1 = $nodeport1;
	} else {
	    $vnp0 = $nodeport1;
	    $vnp1 = $nodeport0;
	}

1285
1286
1287
	my ($q0_limit,$q0_maxthresh,$q0_minthresh,$q0_weight,$q0_linterm,
	    $q0_qinbytes,$q0_bytes,$q0_meanpsize,$q0_wait,$q0_setbit,
	    $q0_droptail,$q0_red,$q0_gentle) =
Christopher Alfeld's avatar
Christopher Alfeld committed
1288
		@{$queueinfo{"$vname:$vnp0"}};
1289
1290
1291
1292

	my ($q1_limit,$q1_maxthresh,$q1_minthresh,$q1_weight,$q1_linterm,
	    $q1_qinbytes,$q1_bytes,$q1_meanpsize,$q1_wait,$q1_setbit,
	    $q1_droptail,$q1_red,$q1_gentle) =
Christopher Alfeld's avatar
Christopher Alfeld committed
1293
		          @{$queueinfo{"$vname:$vnp1"}};
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
    
	DBQueryFatal("insert into delays " .
	     " (pid,eid,node_id,vname,iface0,iface1" .
	     ",pipe0,delay0,bandwidth0,lossrate0" .
	     ",pipe1,delay1,bandwidth1,lossrate1" .
	     ",q0_limit,q0_maxthresh,q0_minthresh,q0_weight,q0_linterm" .
	     ",q0_qinbytes,q0_bytes,q0_meanpsize,q0_wait,q0_setbit" .
	     ",q0_droptail,q0_red,q0_gentle" .
	     ",q1_limit,q1_maxthresh,q1_minthresh,q1_weight,q1_linterm" .
	     ",q1_qinbytes,q1_bytes,q1_meanpsize,q1_wait,q1_setbit" .
	     ",q1_droptail,q1_red,q1_gentle)" .
	     " values ('$pid','$eid','$pnode','$vname','$int0','$int1'".
	     ",$pipe0,$delay,$bandwidth,$lossrate".
	     ",$pipe1,$rdelay,$rbandwidth,$rlossrate".
	     ",$q0_limit,$q0_maxthresh,$q0_minthresh,$q0_weight,$q0_linterm".
	     ",$q0_qinbytes,$q0_bytes,$q0_meanpsize,$q0_wait,$q0_setbit".
	     ",$q0_droptail,$q0_red,$q0_gentle".
  	     ",$q1_limit,$q1_maxthresh,$q1_minthresh,$q1_weight,$q1_linterm".
	     ",$q1_qinbytes,$q1_bytes,$q1_meanpsize,$q1_wait,$q1_setbit".
	     ",$q1_droptail,$q1_red,$q1_gentle)");
    }
    else {
	DBQueryFatal("insert into delays" .
	     " (pid,eid,node_id,vname,iface0,iface1" .
	     ",pipe0,delay0,bandwidth0,lossrate0" .
	     ",pipe1,delay1,bandwidth1,lossrate1)" .
	     " values ('$pid','$eid','$pnode','$vname','$int0','$int1'".
	     ",$pipe0,$delay,$bandwidth,$lossrate".
	     ",$pipe1,$rdelay,$rbandwidth,$rlossrate)");
    }
    
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
    #
    # XXX - Whenever a delay node is inserted, port speeds are set to
    #       100Mbs on the delay node ports. This is to ensure that
    #       they get a valid number instead of something left over, but
    #       also because this is a simplification.
    #       At some point we might want to force all the
    #       ports along the way to 10Mbs, and have the delay node worry
    #       about delay only, and not bandwidth. That will be harder to
    #       to do in this mess. See companion XXX above where portbw hash
    #       is set.
    #
    DBQueryFatal("update interfaces set " .
		 "current_speed='$S100Mbs' " .
		 "where node_id='$pnode' and ".
		 "(iface='$int0' or iface='$int1')");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1340
}
1341

Shashi Guruprasad's avatar
Shashi Guruprasad committed
1342
1343
1344
1345
1346
# NSE hack: list of ip addresses of simulated nodes that
# have an external link/lan and therefore have the corresponding
# ip on the interface of the physical node
my %simnode_extips = ();

Leigh B. Stoller's avatar
Leigh B. Stoller committed
1347
1348
1349
1350
1351
1352
1353
1354
foreach $vnodeport (keys(%portmap)) {
    ($vnode,$vport) = split(":",$vnodeport);
    $pport = $portmap{$vnodeport};
    # Shark Hack
    if ($nodes{$vnode} eq "shark-shelf") {
	$shelf = $v2pmap{$vnode};
	$i = 1;
	foreach $shark (@{$sharkshelves{$vnode}}) {
1355
1356
	    DBQueryFatal("update interfaces set IPalias=\"$ips{$shark}\" " .
		    "where node_id = \"$shelf-$i\"");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1357
1358
1359
	    $i++;
	}
    } else {
1360
1361
	DBQueryFatal("update interfaces set IP=\"$ips{$vnodeport}\" where" .
		 " node_id = \"$v2pmap{$vnode}\" and iface = \"$pport\"");
Shashi Guruprasad's avatar
Shashi Guruprasad committed
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371

	# place to append to nseconfigs
	if( $nodes{$vnode} eq "sim" ) {

	  if( ! defined( $simnode_extips{$vnode}) ) {
	    $simnode_extips{$vnode} = [];
	  }

	  push(@{$simnode_extips{$vnode}}, $ips{$vnodeport});
	}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1372
1373
1374
    }
    # End Shark Hack
}
Shashi Guruprasad's avatar
Shashi Guruprasad committed
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480

# XXX NSE hack: We'll load the virt_routes table so as to
# calculate next physical hop info for sim nodes.

# This has a list of routes per node
my %noderoutes = ();
# This has routes based on node:dstip
my %routes = ();

my $res = DBQueryFatal("SELECT vname,dst,dst_type,dst_mask,nexthop " .
		       "from virt_routes where pid='$pid' and eid='$eid'");
while (($vname,$dstip,$dst_type,$dst_mask,$nexthop) = $res->fetchrow_array) {
  if( ! defined($noderoutes{$vname}) ) {
    $noderoutes{$vname} = [];
  }
  push(@{$noderoutes{$vname}}, [$dstip,$dst_type,$dst_mask,$nexthop]);
  $routes{"$vname:$dstip"} = $nexthop;
}

# contains routes for sim nodes that have external links
# This is again used to append to nseconfigs
my %modified_simroutes = ();
  
# These hacks wont work in the distributed nse case. Needs fixing...
# The algorithm traverses each route on the border sim node (i.e.
# nodes that capture and inject packets into real links) to its
# destination untill the hop is not a simulated node. 
foreach $simnode (keys(%simnode_extips)) {
  $modified_simroutes{$simnode} = [];
  foreach $route (@{$noderoutes{$simnode}}) {
    
    my ($dstip,$dst_type,$dst_mask,$nexthop) =
      @{$route}[0,1,2,3];

    printdb "on simnode $simnode $dstip -> $nexthop with $dst_mask\n";
    my $nhopip = $nexthop;
    my $nhopnode = $iptonodemap{$nhopip};
    while( 1 ) {

      if( $nodes{$nhopnode} ne "sim" ) {
	push( @{$modified_simroutes{$simnode}}, [$dstip, $dst_mask, $nhopip]);
	last;
      }

      # store prev values
      my $phopip =