ptopgen.in 21 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
#!/usr/bin/perl -w
Leigh B. Stoller's avatar
Leigh B. Stoller committed
2 3 4

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

9 10
use English;
use Getopt::Std;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
11

12 13
sub usage()
{
Leigh B. Stoller's avatar
Leigh B. Stoller committed
14 15
    print("Usage: ptopgen [-v] [-s switch] [-p pid [-e eid]] [-m factor] " .
	  "[-n c/e]\n".
16
	  "       -p include nodes the project has permission to use\n".
17
	  "       -e include given experiments resources\n" .
18 19
	  "          in the ptopfile (as if they were free)\n" .
	  "       -v Include stuff for topologies with virtual nodes\n".
20
	  "       -r Include stuff for topologies with widearea nodes\n".
21
	  "       -s Include stuff for topologies with simulated nodes\n".
22
	  "       -a Include even reserved nodes\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
23 24
	  "       -m Override multiplex_factor\n".
	  "       -n Add in modelnet core and edge node features\n");
25 26
    exit(-1);
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
27
my $optlist = "s:e:m:vp:rSan:";
28
my $mfactor;
29
my $virtstuff = 0;
30
my $widearea  = 0;
31
my $simstuff  = 0;
32
my $allnodes  = 0;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
33 34
my $mnetcores = 0;
my $mnetedges = 0;
35

36 37 38 39
#
# Turn off line buffering on output
#
$| = 1;
40 41 42 43 44 45 46 47 48 49 50 51

# Settings - Change these to your particular network.

#
# Also bogus. The interfaces table does not hold entries for the switch
# side of each link. It will have to at some point, when we have something
# other than 100Mbs ports (say, gbit ports).
#
# Speed in in Kbs!
#
my $default_switchport_speed    = 100000;

52 53 54 55 56 57
#
# Yet more bogosity - we hardcode this weight, which is given to wide-area
# (primarily plab) nodes to prefer spreading across sites
#
my $site_weight = 0.99;

58 59
######################################################################

60
my $TBROOT = "@prefix@";
61
use lib '@prefix@/lib';
62
require exitonwarn;
63
use libdb;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
64

65 66
my $TRACK_INTERSWITCH_BANDWIDTH = "@TRACK_INTERSWITCH_BANDWIDTH@";

67 68 69
my %switches	  = ();
my %used_switches = ();
my %permissions   = ();
70 71
my %typemap       = ();
my %auxtypemap    = ();
72

73 74 75
my $DEADPID = NODEDEAD_PID();
my $DEADEID = NODEDEAD_EID();

76
my $pid;
77
my $exempt_eid;
78
my $switchtouse;
79

80 81 82 83 84 85 86 87 88 89 90 91 92 93
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (@ARGV) {
    usage();
}
if (defined($options{"s"})) {
    $switchtouse = $options{"s"};
}
94 95 96
if (defined($options{"m"})) {
    $mfactor = $options{"m"};
}
97 98 99
if (defined($options{"v"})) {
    $virtstuff = 1;
}
100 101 102
if (defined($options{"r"})) {
    $widearea = 1;
}
103 104 105
if (defined($options{"S"})) {
    $simstuff = 1;
}
106 107 108
if (defined($options{"p"})) {
    $pid = $options{"p"};
}
109 110 111
if (defined($options{"a"})) {
    $allnodes = 1;
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
112 113 114 115 116 117 118 119 120
if (defined($options{"n"})) {
    if ($options{"n"} =~ /(\d*),(\d*)/) {
	$mnetcores = $1;
	$mnetedges = $2;
    }
    else {
	usage();
    }
}
121
if (defined($options{"e"})) {
122 123 124
    $exempt_eid = $options{"e"};
    usage()
	if (!defined($pid));
125 126
}

127
# Read class/type maps
128
my $result =
129
    DBQueryFatal("select class,type,delay_capacity,".
130
		 "   virtnode_capacity,isvirtnode,simnode_capacity, " .
131
		 "   speed, RAM, trivlink_maxspeed ".
132
		 "from node_types");
133
while (($class,$type,$delaycapacity,
134
	$virtcapacity,$isvirt,$simcapacity, $speed, $ram, $trivspeed)
135
	    = $result->fetchrow_array) {
136 137 138 139
    $map = {};
    $map->{'CLASS'}    = $class;
    $map->{'DELAYCAP'} = $delaycapacity;
    $map->{'VIRTCAP'}  = $virtcapacity;
140
    $map->{'SIMCAP'}   = $simcapacity;
141
    $map->{'ISVIRT'}   = $isvirt;
142 143
    $map->{'SPEED'}    = $speed;
    $map->{'RAM'}      = $ram;
144
    $map->{'TRIVSPEED'}= $trivspeed;
145 146 147
    $map->{'FEATURES'} = [];
    $map->{'AUXTYPES'} = {};
    $typemap{$type} = $map;
148 149 150 151 152 153 154 155 156 157 158

    # Default is permission to use all types,classes. 
    $permissions{$class} = 1;
    $permissions{$type}  = 1;
}

#
# Read the features table for each type.
# 
$result = DBQueryFatal("select type, feature, weight from node_type_features");
while (my ($type, $feature, $weight) = $result->fetchrow()) {
159 160 161 162 163 164 165 166 167 168
    push(@{$typemap{$type}->{'FEATURES'}}, "$feature:$weight");
}

#
# Read the auxtypes for each type.
# 
$result = DBQueryFatal("select auxtype,type from node_types_auxtypes");
while (my ($auxtype,$type) = $result->fetchrow()) {
    $typemap{$type}->{'AUXTYPES'}->{$auxtype} = 1;
    $auxtypemap{$auxtype} = $type;
169 170
}

171 172 173 174 175 176 177 178 179 180 181 182
#
# Read the features table for each individual node
#
$result = DBQueryFatal("select node_id, feature, weight from node_features");
while (my ($node_id, $feature, $weight) = $result->fetchrow()) {
    if (! defined($features{$node_id})) {
	$features{$node_id} = ["$feature:$weight"];
	next;
    } else {
	push @{$features{$node_id}}, "$feature:$weight";
    }
}
183 184 185 186 187 188 189 190 191
#
# Read in the node_auxtypes table for each node.
#
$result = DBQueryFatal("select node_id, type, count from node_auxtypes");
while (my ($node_id, $type, $count) = $result->fetchrow()) {
    if (! defined($auxtypes{$node_id})) {
	$auxtypes{$node_id} = ["$type:$count"];
	next;
    }
192
    push @{$auxtypes{$node_id}}, "$type:$count";
193
}
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
#
# Read the permission table if given a pid. If there is an entry in
# the table for a type/class, then permission is granted only if there
# is a record with the pid. If not, remove the permission granted above.
# 
if (defined($pid)) {
    $result =
	DBQueryFatal("select type from nodetypeXpid_permissions");
    
    while (my ($type) = $result->fetchrow_array) {
	$permissions{$type} = 0;
    }
    
    $result =
	DBQueryFatal("select type from nodetypeXpid_permissions ".
		     "where pid='$pid'");
    
    while (my ($type) = $result->fetchrow_array) {
	$permissions{$type} = 1;
    }
214 215
}

216
# Print switches
217
if (defined($switchtouse)) {
218
    print "node $switchtouse switch:1 *lan:*\n";
219 220 221 222 223 224 225
    $switches{$switchtouse} = 1;
}
else {
    $result =
	DBQueryFatal("select node_id from nodes where role = \"testswitch\"");

    while (($switch) = $result->fetchrow_array) {
226
	print "node $switch switch:1 *lan:*\n";
227 228
	$switches{$switch} = 1;
    }
229
}
230

231
# Find available nodes.
232
#
233 234
# This first query deals with just local nodes. Local nodes can host
# virtnodes, according to the delay_capacity in the types table. 
235
#
236

237
# the ordinary free condition for a local node.
238 239 240
my $free_condition = "(b.node_id is null and ".
                     " (np.eventstate='" . TBDB_NODESTATE_ISUP . "' or ".
                     "  np.eventstate='" . TBDB_NODESTATE_PXEWAIT . "')) ";
241 242 243 244 245

# if the user has specified an exempt pid/eid, 
# then view any node which is reserved to that experiment as available.
if (defined($exempt_eid)) {
    $free_condition = "($free_condition or ".
246
	"(b.pid='$pid' and b.eid='$exempt_eid'))"; 
247
}
Chad Barb's avatar
Chad Barb committed
248

249 250 251 252 253 254
# If the user wants all nodes, we consider everything to be free (this
# overrides the other possible free conditions
if ($allnodes) {
    $free_condition = "1";
}

255
$result =
256
    DBQueryFatal("select a.node_id,a.type,a.phys_nodeid,t.class,t.issubnode," .
257 258
		 "a.def_boot_osid, (b.pid is not null and b.eid is not null) " .
		 "from nodes as a ".
259 260
		 "left join reserved as b on a.node_id=b.node_id ".
		 "left join reserved as m on a.phys_nodeid=m.node_id ".
261
		 "left join nodes as np on a.phys_nodeid=np.node_id ".
262
		 "left join node_types as t on t.type=a.type ".
263
		 "where $free_condition and ".
264
		 "      (a.role='testnode' and t.isremotenode=0)");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
265

266 267 268
#
# Scan the results, checking permissions and adding to the list
# You get to use a node type if no pid was specified (that is, you get
269 270 271
# to use all nodes), or if there is no entry in the perms table for
# the type/class of node.
#
272
while (($node,$type,$physnode,$class,$issubnode,$def_boot_osid,$reserved) 
273
    = $result->fetchrow_array) {
274 275 276
    $nodes{$node} = $type
	if (!defined($pid) ||
	    ($permissions{$type} && $permissions{$class}));
277 278 279
    if ($issubnode) {
	$subnode_of{$node} = $physnode;
    }
280
    $node_def_boot_osid{$node} = $def_boot_osid;
281 282 283 284 285 286

    if ($reserved) {
	$is_reserved{$node} = 1;
    } else {
	$is_reserved{$node} = 0;
    }
287
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
288 289

foreach $node (keys(%nodes)) {
290 291 292
    my $type  = $nodes{$node};
    my $class = $typemap{$type}->{'CLASS'};
    my $delay_capacity = $typemap{$type}->{'DELAYCAP'};
293
    my $simnode_capacity = $typemap{$type}->{'SIMCAP'};
294 295
    my $cpu_speed = $typemap{$type}->{'SPEED'};
    my $ram = $typemap{$type}->{'RAM'};
296
    my $trivspeed = $typemap{$type}->{'TRIVSPEED'};
297
    
298
    my @types = ("$type:1");
299 300
    my @features;
    my @flags;
301
    my $needvirtgoo = 0;
302

303 304 305 306 307 308 309 310 311 312
    # XXX temporary hack until node reboot avoidance 
    # is available. Nodes running the FBSD-NSE image
    # will have a feature def-osid-fbsd-nse 0.0
    # This is used by assign to prefer these pnodes
    # first before using others.
    if( $node_def_boot_osid{$node} eq 
	TBOSID(TB_OPSPID, "FBSD-NSE") ) { 
	push(@features, "FBSD-NSE:0.0");
    }

313
    # Might be equal, which assign would sum as two, not one!
314 315
    if ($type ne $class) {
	push(@types, "$class:1");
316 317
    }

318
    if ($delay_capacity > 0) {
319
	push @types, "delay:$delay_capacity";
320
    }
321

322 323 324
    #
    # Add any auxiliary types
    #
325 326 327
    foreach my $auxinfo (@{$auxtypes{$node}}) {
	my ($auxtype,$count) = split(":", $auxinfo);
	my $realtype;
328

329 330 331
	# Map an auxtype back to its real type, unless it is a real type.
	if (defined($auxtypemap{$auxtype})) {
	    $realtype = $auxtypemap{$auxtype};
332 333
	}
	else {
334
	    $realtype = $auxtype;
335
	}
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351

	if ($typemap{$realtype}->{'ISVIRT'} && $count > 0) {
	    next
		if (! $virtstuff);
		
	    if (defined($mfactor) && $mfactor <= $count) {
		$auxinfo = "$auxtype:$mfactor";
	    }
	    else {
		$auxinfo = "$auxtype:$count";
	    }
	    $needvirtgoo = 1;
	}
	push(@types, $auxinfo);
    }

352
    my $cpu_ram_features_present = 0;
353 354 355 356
    #
    # This stuff is extra goo for local virtual nodes.
    # 
    if ($needvirtgoo) {
357
	push @types, "*lan:*";
358 359 360 361
	# Add trivial bw spec., but only if the node type has it
	if ($trivspeed) {
	    push @flags, "trivial_bw:$trivspeed";
	}
362
	# Add CPU and RAM information
363
	$cpu_ram_features_present++;
364 365
	# This number can be use for fine-tuning packing
	push @features, "?+virtpercent:100";
366 367 368
	# Put this silly feature in so that we can try to keep vnodes
	# on the same pnode they were before
	push @features, "${node}:0.0";
369 370
    }

371 372 373 374 375 376 377 378 379 380
    if ($simstuff && $simnode_capacity > 0) {
	#
	# Use user specified multiplex factor
	#
	if (defined($mfactor) && $mfactor <= $simnode_capacity) {
	    push @types, "sim:$mfactor";
	}
	else {
	    push @types, "sim:$simnode_capacity";
	}
381
	# Add CPU and RAM information
382
	$cpu_ram_features_present++;
383 384 385 386
	push @types, "*lan:*";
	# Add trivial bw spec.
	push @flags, "trivial_bw:100000";
    }
387 388 389 390
    if ($cpu_ram_features_present) {
	# Add CPU and RAM information
	push @features, "?+cpu:$cpu_speed";
	push @features, "?+ram:$ram";
391 392
	push @features, "?+cpupercent:92"; # XXX Hack
	push @features, "?+rampercent:80"; # XXX Hack
393
    }
394

395
    # Add features
396
    push(@features, @{$typemap{$type}->{'FEATURES'}});
397 398
    if (defined($features{$node})) {
	push @features, @{$features{$node}};
399 400
    }

401 402
    # This is for the case that we are modifying an existing experiment - tell
    # assign to prefer nodes the user has already allocated
403
    if ($exempt_eid && !$allnodes && $is_reserved{$node}) {
404 405 406
	push(@features,"already_reserved:0");
    }

Leigh B. Stoller's avatar
Leigh B. Stoller committed
407 408 409 410 411 412 413 414
    # Add in modelnet stuff.
    if ($mnetcores) {
	push(@types, "modelnet-core:$mnetcores");
    }
    if ($mnetedges) {
	push(@types, "modelnet-edge:$mnetedges");
    }

415 416 417 418 419 420 421 422 423 424 425
    #
    # Handle subnodes
    #
    if ($subnode_of{$node}) {
	# We don't want to include subnodes unless their parent node is going
	# to be in the ptop file too
	if (!$nodes{$subnode_of{$node}}) {
	    # In fact, nuke it from %nodes so that we don't include its links,
	    # either
	    delete $nodes{$node};
	    next;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
426
	}
427 428
	# Push the subnode's information into its flags
	push @flags, "subnode_of:$subnode_of{$node}";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
429
    }
430 431 432 433 434

    my $text = "node $node " . join(" ",@types) . " - " . join(" ",@features) .
	" - " . join(" ",@flags);


435
    print "$text\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
436 437
}

438 439 440 441 442 443 444 445 446 447 448
#
# Widearea Nodes. Includes plab nodes. Note that widearea nodes are never
# allocated directly (they are in a holding experiment), but assign deals
# with it by allocating multiple vnodes on a pnode.
#
# The underlying physnode has to be "up", as determined by the
# autostatus stuff; this will prevent us from allocating a dead
# virtual node to an experiment.  This is especially hacky. We need
# another mechanism for this. We only add virtnodes when assign says
# we need them. This reduces the problem size for assign.
#
449 450 451 452
# The types we lay out are only those in the auxtypes table for the node,
# since that is where we define what vtypes are hosted on a particular
# physnode. 
#
453 454
if ($widearea) {
    $result =
Leigh B. Stoller's avatar
Leigh B. Stoller committed
455 456 457 458 459
	DBQueryFatal("select n.node_id,nt.type,ns.status,r.pid,r.eid,wn.site ".
		     "from nodes as n ".
		     "left join node_types as nt on nt.type=n.type ".
		     "left join reserved as r on r.node_id=n.node_id ".
		     "left join node_status as ns on ns.node_id=n.node_id ".
460
		     "left join widearea_nodeinfo as wn on ".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
461 462 463
		     "     wn.node_id=n.node_id ".
		     "where  (n.role='testnode' and nt.isremotenode=1 and ".
		     "        nt.isvirtnode=0)");
464
    
Leigh B. Stoller's avatar
Leigh B. Stoller committed
465 466
    while (($physnode,$ptype,$status,$mpid,$meid,$site)
	   = $result->fetchrow_array) {
467
	my $class = $typemap{$ptype}->{'CLASS'};
468 469 470 471
	my @types;
	my @features;
	my @flags;

472 473 474 475 476
	#
	# Mark any nodes that are not up with a feature, so that they won't
	# normally get assigned. We want to include them, though, because we
	# allow people to do fix-node to down nodes
	#
477 478
	if (($status && ($status ne 'up')) ||
	    ($mpid eq $DEADPID && $meid eq $DEADEID)) {
479 480 481
	    push @features, "down:1";
	}

482 483 484 485 486 487 488
	#
	# Mark which site this node belongs to
	#
	if ($site) {
	    push @features, "*&$site:$site_weight";
	}

489 490 491
	#
	# Add any auxiliary types.
	#
492 493
	foreach my $auxinfo (@{$auxtypes{$physnode}}) {
	    my ($auxtype,$count) = split(":", $auxinfo);
494

Leigh B. Stoller's avatar
Leigh B. Stoller committed
495 496 497 498 499
	    if (defined($mfactor) && $mfactor <= $count) {
		$auxinfo = "$auxtype:$mfactor";
	    }
	    else {
		$auxinfo = "$auxtype:$count";
500
	    }
501
	    push(@types, $auxinfo);
502 503 504 505 506
	}

	# Add trivial bw spec.
	push @flags, "trivial_bw:400000";

507 508 509
	# Indicate that these nodes are beautiful and unique snowflakes
	push @flags, "unique";

510
	# Add features
511
	push(@features, @{$typemap{$ptype}->{'FEATURES'}});
512 513
	if (defined($features{$physnode})) {
	    push @features, @{$features{$physnode}};
514 515 516 517 518 519 520 521 522 523
	}

	my $text = "node $physnode " .
	           join(" ",@types) . " - " . join(" ",@features) .
		   " - " . join(" ",@flags);

	print "$text\n";
    }
}

524
# Read interfaces
525
$result = DBQueryFatal("SELECT node_id,card,port,iface,interface_type" .
526
		     " from interfaces");
527
while (($node,$card,$port,$iface,$type) = $result->fetchrow_array) {
528
    $interfacemap{"$node:$card:$port"} = $iface;
Chad Barb's avatar
 
Chad Barb committed
529
    if ((defined $type) && ($type ne "")) {
530 531 532 533
	$interfacetypes{"$node:$card:$port"} = $type;
    }
}

534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
# Read interface types. First need to find the protocols an interface supports
# and then then the speed for each of those protocols.
# Note that we are going to assume anything attached to a switch is ethernet.
$result = DBQueryFatal("SELECT type,capkey,capval from interface_capabilities ".
		       "where capkey='protocols' or capkey like '%_defspeed'"); 
while (($type,$capkey,$capval) = $result->fetchrow_array) {
    if ($capkey eq "protocols") {
	$interfaceprotocols{$type} = [ split(",", $capval) ];
    }
    elsif ($capkey =~ /^([-\w]+)_defspeed$/) {
	$interfacespeeds{$type}{$1} = $capval;
    }
    else {
	die("Improper defspeed $capval for $type!\n");
    }
549 550
}

551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
# Read interface switches
$result = DBQueryFatal("SELECT node_id1, iface, node_id2 FROM wires AS w " .
	"LEFT JOIN interfaces as i ON w.node_id1=i.node_id AND w.card1=i.card");
while (($node,$iface,$switch) = $result->fetchrow_array) {
    if ($node && $iface) {
	$interfaceswitches{"$node:$iface"} = $switch;
    }
}

# Read interface cards and ports
$result = DBQueryFatal("SELECT node_id, iface, card, port FROM interfaces");
while (($node,$iface,$card,$port) = $result->fetchrow_array) {
    $interfacecardports{"$node:$iface"} = [$card,$port];
}

566
$result = DBQueryFatal("SELECT node_id1,card1,port1,node_id2,card2,port2" .
567
 		    " from wires where type=\"Node\" or type=\"Trunk\"");
568
while (($node1,$card1,$port1,$node2,$card2,$port2) = 
569
       $result->fetchrow_array) {
570
    if ((defined($nodes{$node1}) || defined($switches{$node1})) && 
571
	(defined($nodes{$node2}) || defined($switches{$node2}))) {
572 573
	$iface1 = get_iface($node1,$card1,$port1);
	$iface2 = get_iface($node2,$card2,$port2);
574 575
	$iface1bw = get_ifacebw($node1,$card1,$port1,"ethernet");
	$iface2bw = get_ifacebw($node2,$card2,$port2,"ethernet");
576 577 578 579 580 581
	# XXX - This is a bad, bad hack - we  use our knowledge that in the
	# wires table links to the switch always come as node2. We also assume
	# that node interfaces are plugged into switch ports of the same speed.
	# Chris has something better on the way (storing the speed on the
	# switch side), so this is just a temp. hack.
	if (!defined($switches{$node1}) && defined($switches{$node2})) {
582 583
	    $bw = $iface1bw;
	} else {
584 585 586 587 588
	    if ($iface1bw < $iface2bw) {
		$bw = $iface1bw;
	    } else {
		$bw = $iface2bw;
	    }
589
	}
590
	if (defined($switches{$node2})) {
591 592
	    $used_switches{$node2} = 1;
	}
593 594 595 596 597 598 599 600 601 602 603 604 605 606
	if (defined($switches{$node1})) {
	    $used_switches{$node1} = 1;
	}
	if (defined($switches{$node1}) && defined($switches{$node2})) {
	    # interswitch link
	    if (defined($interconnects{"$node1:$node2"})) {
		$interconnects{"$node1:$node2"} += $bw;
	    } else {
		$interconnects{"$node1:$node2"} = $bw;
	    }
	} else {
	    # !!! - Here we use our knowledge that in the wires table links
	    # to the switch always come as node2.
	    print "link link-$node1:$iface1-$node2:$iface2 $node1:$node1/$iface1" .
607
		" $node2:$iface2 $bw 0 0 1 ethernet\n";
608 609
	}
    } 
610
}
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630

#
# If we're supposed to track interswitch bandwidth, subtract out the amount
# that's already in use
#
if ($TRACK_INTERSWITCH_BANDWIDTH) {
    #
    # Get a list of all VLANs
    #
    my $result = DBQueryFatal("SELECT virtual, members FROM vlans");
    while (my ($virtual, $members) = $result->fetchrow()) {
	#
	# Split apart the space-separated list of members
	#
	my @members = split /\s+/, $members;
	my %switches = ();
	foreach my $member (@members) {
	    my ($node,$iface) = split /:/, $member;
	    my $switch = get_ifaceswitch($node,$iface);
	    my ($card, $port) = get_ifacecardport($node,$iface);
631
	    my $bw = get_ifacebw($node,$card,$port,"ethernet");
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
	    $switches{$switch} += $bw;
	}

	#
	# Check to see if more than one switch was found among the member
	# list, and if so, go through all the pairs
	#
	my @switches = keys %switches;
	if (@switches > 1) {
	    for (my $i = 0; $i < (@switches -1); $i++) {
		my $switch1 = $switches[$i];
		my $switch2 = $switches[$i+1];
		my $bw = $switches{$switch1};
		if ($switches{$switch2} < $bw) {
			$bw = $switches{$switch2};
		}
		#
		# The trunk link could be listed in either order
		#
		if ($interconnects{"$switch1:$switch2"}) {
		    $interconnects{"$switch1:$switch2"} -= $bw;
		} elsif ($interconnects{"$switch2:$switch1"}) {
		    $interconnects{"$switch2:$switch1"} -= $bw;
		}
	    }
	}
    }
}

661 662
foreach $interconnect (keys(%interconnects)) {
    ($src,$dst) = split(":",$interconnect);
663 664
    print "link link-$interconnect $src $dst $interconnects{$interconnect} " .
	"0 0 1 ethernet\n";
665
}
666

667
#
668
# Fake switch. Hardwired for now. 
669
#
670 671 672
my @wireless_protos = ("80211", "80211a", "80211b", "80211g");
my $fake_switch = "airswitch";
print "node $fake_switch ", join(" ",map("*$_:*",@wireless_protos)), "\n";
673

674 675
foreach my $interface (keys(%interfacetypes)) {
    my ($node,$card,$port) = split(":", $interface);
676

677 678
    next
    if (!defined($nodes{$node}));
679
	
680 681 682
    my $type    = $interfacetypes{$interface};
    my @protos  = @{ $interfaceprotocols{$type} };
    my $iface   = get_iface($node,$card,$port);
683

684 685 686 687 688 689 690 691
    #
    # Get the intersection of the protocols supported by this interface, and
    # the wireless protocols we know about
    #
    my (%union, %intersection);
    foreach $proto (@protos, @wireless_protos) {
	$union{$proto}++ && $intersection{$proto}++;
    }
692

693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
    my @intersection = keys %intersection;

    #
    # Skip this interface if it speaks no wireless protocols
    #
    next unless @intersection;

    #
    # Find the max bandwidth supported by any of the wireless protocols
    # supported by this interface
    #
    my $max_bw = 0;
    foreach my $proto (@intersection) {
	my $ifacebw = get_ifacebw($node,$card,$port,$proto);
	if ($ifacebw > $max_bw) {
	    $max_bw = $ifacebw;
709 710
	}
    }
711 712 713
    print "link link-$node:$iface-$fake_switch:(null) ".
	"$node:$node/$iface $fake_switch:(null) $max_bw 0 0 1 ".
	join(" ",@intersection). "\n";
714 715 716 717
}

exit(0);

718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
# Print out links
sub get_iface {
    ($node,$card,$port) = @_;
    if (defined($interfacemap{"$node:$card:$port"})) {
	return $interfacemap{"$node:$card:$port"};
    } else {
	# shark hack
	if ($node =~ /^sh/) {
	    return "eth0";
	} else {
	    return "(null)";
	}
	# end shark hack 
    }
};

734 735
# Find the bandwidth for an interface, when using the given protocol (which
# most of the time is ethernet).
736
sub get_ifacebw {
737
    ($node,$card,$port,$protocol) = @_;
738
    if (defined($interfacetypes{"$node:$card:$port"})) {
739
	return $interfacespeeds{$interfacetypes{"$node:$card:$port"}}{$protocol};
740 741 742
    }
    else {
	return $default_switchport_speed;
743 744
    }
}
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762

sub get_ifaceswitch {
    ($node,$iface) = @_;
    if (defined($interfaceswitches{"$node:$iface"})) {
	return $interfaceswitches{"$node:$iface"};
    } else {
	return undef;
    }
}

sub get_ifacecardport {
    ($node,$iface) = @_;
    if (defined($interfacecardports{"$node:$iface"})) {
	return @{$interfacecardports{"$node:$iface"}};
    } else {
	return ();
    }
}