ptopgen.in 20.8 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()
{
14 15
    print("Usage: ptopgen [-v] [-s switch] [-p pid [-e eid]] [-m factor]\n" .
	  "       -p include nodes the project has permission to use\n".
16
	  "       -e include given experiments resources\n" .
17 18
	  "          in the ptopfile (as if they were free)\n" .
	  "       -v Include stuff for topologies with virtual nodes\n".
19
	  "       -r Include stuff for topologies with widearea nodes\n".
20
	  "       -s Include stuff for topologies with simulated nodes\n".
21
	  "       -m Override multiplex_factor\n");
22 23
    exit(-1);
}
24
my $optlist = "s:e:m:vp:rS";
25
my $mfactor;
26
my $virtstuff = 0;
27
my $widearea  = 0;
28
my $simstuff  = 0;
29

30 31 32 33
#
# Turn off line buffering on output
#
$| = 1;
34 35 36 37 38 39 40 41 42 43 44 45

# 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;

46 47 48 49 50 51
#
# 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;

52 53
######################################################################

54
my $TBROOT = "@prefix@";
55
use lib '@prefix@/lib';
56
require exitonwarn;
57
use libdb;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
58

59 60
my $TRACK_INTERSWITCH_BANDWIDTH = "@TRACK_INTERSWITCH_BANDWIDTH@";

61 62 63
my %switches	  = ();
my %used_switches = ();
my %permissions   = ();
64 65
my %typemap       = ();
my %auxtypemap    = ();
66

67 68 69
my $DEADPID = NODEDEAD_PID();
my $DEADEID = NODEDEAD_EID();

70
my $pid;
71
my $exempt_eid;
72
my $switchtouse;
73

74 75 76 77 78 79 80 81 82 83 84 85 86 87
#
# 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"};
}
88 89 90
if (defined($options{"m"})) {
    $mfactor = $options{"m"};
}
91 92 93
if (defined($options{"v"})) {
    $virtstuff = 1;
}
94 95 96
if (defined($options{"r"})) {
    $widearea = 1;
}
97 98 99
if (defined($options{"S"})) {
    $simstuff = 1;
}
100 101 102
if (defined($options{"p"})) {
    $pid = $options{"p"};
}
103
if (defined($options{"e"})) {
104 105 106
    $exempt_eid = $options{"e"};
    usage()
	if (!defined($pid));
107 108
}

109
# Read class/type maps
110
my $result =
111
    DBQueryFatal("select class,type,delay_capacity,".
112 113
		 "   virtnode_capacity,isvirtnode,simnode_capacity, " .
		 "   speed, RAM ".
114
		 "from node_types");
115
while (($class,$type,$delaycapacity,
116 117
	$virtcapacity,$isvirt,$simcapacity, $speed, $ram)
	    = $result->fetchrow_array) {
118 119 120 121
    $map = {};
    $map->{'CLASS'}    = $class;
    $map->{'DELAYCAP'} = $delaycapacity;
    $map->{'VIRTCAP'}  = $virtcapacity;
122
    $map->{'SIMCAP'}   = $simcapacity;
123
    $map->{'ISVIRT'}   = $isvirt;
124 125
    $map->{'SPEED'}    = $speed;
    $map->{'RAM'}      = $ram;
126 127 128
    $map->{'FEATURES'} = [];
    $map->{'AUXTYPES'} = {};
    $typemap{$type} = $map;
129 130 131 132 133 134 135 136 137 138 139

    # 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()) {
140 141 142 143 144 145 146 147 148 149
    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;
150 151
}

152 153 154 155 156 157 158 159 160 161 162 163
#
# 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";
    }
}
164 165 166 167 168 169 170 171 172
#
# 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;
    }
173
    push @{$auxtypes{$node_id}}, "$type:$count";
174
}
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
#
# 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;
    }
195 196
}

197
# Print switches
198
if (defined($switchtouse)) {
199
    print "node $switchtouse switch:1 *lan:*\n";
200 201 202 203 204 205 206
    $switches{$switchtouse} = 1;
}
else {
    $result =
	DBQueryFatal("select node_id from nodes where role = \"testswitch\"");

    while (($switch) = $result->fetchrow_array) {
207
	print "node $switch switch:1 *lan:*\n";
208 209
	$switches{$switch} = 1;
    }
210
}
211

212
# Find available nodes.
213
#
214 215
# This first query deals with just local nodes. Local nodes can host
# virtnodes, according to the delay_capacity in the types table. 
216
#
217

218
# the ordinary free condition for a local node.
219 220 221
my $free_condition = "(b.node_id is null and ".
                     " (np.eventstate='" . TBDB_NODESTATE_ISUP . "' or ".
                     "  np.eventstate='" . TBDB_NODESTATE_PXEWAIT . "')) ";
222 223 224 225 226

# 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 ".
227
	"(b.pid='$pid' and b.eid='$exempt_eid'))"; 
228
}
Chad Barb's avatar
Chad Barb committed
229

230
$result =
231
    DBQueryFatal("select a.node_id,a.type,a.phys_nodeid,t.class,t.issubnode," .
232 233
		 "a.def_boot_osid, (b.pid is not null and b.eid is not null) " .
		 "from nodes as a ".
234 235
		 "left join reserved as b on a.node_id=b.node_id ".
		 "left join reserved as m on a.phys_nodeid=m.node_id ".
236
		 "left join nodes as np on a.phys_nodeid=np.node_id ".
237
		 "left join node_types as t on t.type=a.type ".
238
		 "where $free_condition and ".
239
		 "      (a.role='testnode' and t.isremotenode=0)");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
240

241 242 243
#
# 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
244 245 246
# to use all nodes), or if there is no entry in the perms table for
# the type/class of node.
#
247
while (($node,$type,$physnode,$class,$issubnode,$def_boot_osid,$reserved) 
248
    = $result->fetchrow_array) {
249 250 251
    $nodes{$node} = $type
	if (!defined($pid) ||
	    ($permissions{$type} && $permissions{$class}));
252 253 254
    if ($issubnode) {
	$subnode_of{$node} = $physnode;
    }
255
    $node_def_boot_osid{$node} = $def_boot_osid;
256 257 258 259 260 261

    if ($reserved) {
	$is_reserved{$node} = 1;
    } else {
	$is_reserved{$node} = 0;
    }
262
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
263 264

foreach $node (keys(%nodes)) {
265 266 267
    my $type  = $nodes{$node};
    my $class = $typemap{$type}->{'CLASS'};
    my $delay_capacity = $typemap{$type}->{'DELAYCAP'};
268
    my $simnode_capacity = $typemap{$type}->{'SIMCAP'};
269 270
    my $cpu_speed = $typemap{$type}->{'SPEED'};
    my $ram = $typemap{$type}->{'RAM'};
271
    
272
    my @types = ("$type:1");
273 274
    my @features;
    my @flags;
275
    my $needvirtgoo = 0;
276

277 278 279 280 281 282 283 284 285 286
    # 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");
    }

287
    # Might be equal, which assign would sum as two, not one!
288 289
    if ($type ne $class) {
	push(@types, "$class:1");
290 291
    }

292
    if ($delay_capacity > 0) {
293
	push @types, "delay:$delay_capacity";
294
    }
295

296 297 298
    #
    # Add any auxiliary types
    #
299 300 301
    foreach my $auxinfo (@{$auxtypes{$node}}) {
	my ($auxtype,$count) = split(":", $auxinfo);
	my $realtype;
302

303 304 305
	# Map an auxtype back to its real type, unless it is a real type.
	if (defined($auxtypemap{$auxtype})) {
	    $realtype = $auxtypemap{$auxtype};
306 307
	}
	else {
308
	    $realtype = $auxtype;
309
	}
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325

	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);
    }

326
    my $cpu_ram_features_present = 0;
327 328 329 330
    #
    # This stuff is extra goo for local virtual nodes.
    # 
    if ($needvirtgoo) {
331
	push @types, "*lan:*";
332
	# Add trivial bw spec.
333
	push @flags, "trivial_bw:400000";
334
	# Add CPU and RAM information
335
	$cpu_ram_features_present++;
336 337
	# This number can be use for fine-tuning packing
	push @features, "?+virtpercent:100";
338 339 340
	# 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";
341 342
    }

343 344 345 346 347 348 349 350 351 352
    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";
	}
353
	# Add CPU and RAM information
354
	$cpu_ram_features_present++;
355 356 357 358
	push @types, "*lan:*";
	# Add trivial bw spec.
	push @flags, "trivial_bw:100000";
    }
359 360 361 362 363
    if ($cpu_ram_features_present) {
	# Add CPU and RAM information
	push @features, "?+cpu:$cpu_speed";
	push @features, "?+ram:$ram";
    }
364

365
    # Add features
366
    push(@features, @{$typemap{$type}->{'FEATURES'}});
367 368
    if (defined($features{$node})) {
	push @features, @{$features{$node}};
369 370
    }

371 372 373 374 375 376
    # This is for the case that we are modifying an existing experiment - tell
    # assign to prefer nodes the user has already allocated
    if ($exempt_eid && $is_reserved{$node}) {
	push(@features,"already_reserved:0");
    }

377 378 379 380 381 382 383 384 385 386 387
    #
    # 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
388
	}
389 390
	# Push the subnode's information into its flags
	push @flags, "subnode_of:$subnode_of{$node}";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
391
    }
392 393 394 395 396

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


397
    print "$text\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
398 399
}

400 401 402 403 404 405 406 407 408 409 410
#
# 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.
#
411 412 413 414
# 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. 
#
415
if ($widearea) {
416 417 418 419 420 421 422 423 424
    #
    # If we are spposed to exempt a certain eid from being considered down,
    # build up a clause to do that - we consider all nodes already used by that
    # experiment to be available
    #
    my $exempt_condition = "0";
    if (defined($exempt_eid)) {
	$free_condition = "(r.pid='$pid' and r.eid='$exempt_eid')"; 
    }
425
    $result =
426
	DBQueryFatal("select count(a.node_id),a.phys_nodeid,aa.type, ".
427
		     "  ns.status,m.pid,m.eid,wn.site ".
428 429 430 431 432 433 434
		     "  from nodes as a ".
 		     "left join reserved as b on a.node_id=b.node_id ".
		     "left join nodes as aa on aa.node_id=a.phys_nodeid ".
		     "left join reserved as m on a.phys_nodeid=m.node_id ".
		     "left join node_status as ns on ".
		     "     a.phys_nodeid=ns.node_id ".
		     "left join node_types as t on t.type=a.type ".
435 436
		     "left join widearea_nodeinfo as wn on ".
		     "     wn.node_id=a.phys_nodeid ".
437
		     "where  (a.role='virtnode' and t.isremotenode=1 and ".
438
		     "        b.node_id is null) ".
439 440
		     "group by a.phys_nodeid");
    
441
    while (($count,$physnode,$ptype,$status,$mpid,$meid,$site)
442
	    = $result->fetchrow_array) {
443
	my $class = $typemap{$ptype}->{'CLASS'};
444 445 446 447
	my @types;
	my @features;
	my @flags;

448 449 450 451 452
	# capacity is smaller of mfactor, number of unallocated virtual node
	# and the per-auxtype capacity below.
	my $virtnode_capacity = $count;
	if (defined($mfactor) && $mfactor <= $virtnode_capacity) {
	    $virtnode_capacity = $mfactor;
453 454
	}

455 456 457 458 459
	#
	# 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
	#
460 461
	if (($status && ($status ne 'up')) ||
	    ($mpid eq $DEADPID && $meid eq $DEADEID)) {
462 463 464
	    push @features, "down:1";
	}

465 466 467 468 469 470 471
	#
	# Mark which site this node belongs to
	#
	if ($site) {
	    push @features, "*&$site:$site_weight";
	}

472 473 474
	#
	# Add any auxiliary types.
	#
475 476
	foreach my $auxinfo (@{$auxtypes{$physnode}}) {
	    my ($auxtype,$count) = split(":", $auxinfo);
477

478 479
	    if ($count > $virtnode_capacity) {
		$auxinfo = "$auxtype:$virtnode_capacity";
480
	    }
481 482

	    push(@types, $auxinfo);
483 484 485 486 487
	}

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

488 489 490
	# Indicate that these nodes are beautiful and unique snowflakes
	push @flags, "unique";

491
	# Add features
492
	push(@features, @{$typemap{$ptype}->{'FEATURES'}});
493 494
	if (defined($features{$physnode})) {
	    push @features, @{$features{$physnode}};
495 496 497 498 499 500 501 502 503 504
	}

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

	print "$text\n";
    }
}

505
# Read interfaces
506
$result = DBQueryFatal("SELECT node_id,card,port,iface,interface_type" .
507
		     " from interfaces");
508
while (($node,$card,$port,$iface,$type) = $result->fetchrow_array) {
509
    $interfacemap{"$node:$card:$port"} = $iface;
Chad Barb's avatar
 
Chad Barb committed
510
    if ((defined $type) && ($type ne "")) {
511 512 513 514
	$interfacetypes{"$node:$card:$port"} = $type;
    }
}

515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
# 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");
    }
530 531
}

532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
# 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];
}

547
$result = DBQueryFatal("SELECT node_id1,card1,port1,node_id2,card2,port2" .
548
 		    " from wires where type=\"Node\" or type=\"Trunk\"");
549
while (($node1,$card1,$port1,$node2,$card2,$port2) = 
550
       $result->fetchrow_array) {
551
    if ((defined($nodes{$node1}) || defined($switches{$node1})) && 
552
	(defined($nodes{$node2}) || defined($switches{$node2}))) {
553 554
	$iface1 = get_iface($node1,$card1,$port1);
	$iface2 = get_iface($node2,$card2,$port2);
555 556
	$iface1bw = get_ifacebw($node1,$card1,$port1,"ethernet");
	$iface2bw = get_ifacebw($node2,$card2,$port2,"ethernet");
557 558 559 560 561 562
	# 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})) {
563 564
	    $bw = $iface1bw;
	} else {
565 566 567 568 569
	    if ($iface1bw < $iface2bw) {
		$bw = $iface1bw;
	    } else {
		$bw = $iface2bw;
	    }
570
	}
571
	if (defined($switches{$node2})) {
572 573
	    $used_switches{$node2} = 1;
	}
574 575 576 577 578 579 580 581 582 583 584 585 586 587
	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" .
588
		" $node2:$iface2 $bw 0 0 1 ethernet\n";
589 590
	}
    } 
591
}
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611

#
# 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);
612
	    my $bw = get_ifacebw($node,$card,$port,"ethernet");
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 640 641
	    $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;
		}
	    }
	}
    }
}

642 643
foreach $interconnect (keys(%interconnects)) {
    ($src,$dst) = split(":",$interconnect);
644 645
    print "link link-$interconnect $src $dst $interconnects{$interconnect} " .
	"0 0 1 ethernet\n";
646
}
647

648
#
649
# Fake switch. Hardwired for now. 
650
#
651 652 653
my @wireless_protos = ("80211", "80211a", "80211b", "80211g");
my $fake_switch = "airswitch";
print "node $fake_switch ", join(" ",map("*$_:*",@wireless_protos)), "\n";
654

655 656
foreach my $interface (keys(%interfacetypes)) {
    my ($node,$card,$port) = split(":", $interface);
657

658 659
    next
    if (!defined($nodes{$node}));
660
	
661 662 663
    my $type    = $interfacetypes{$interface};
    my @protos  = @{ $interfaceprotocols{$type} };
    my $iface   = get_iface($node,$card,$port);
664

665 666 667 668 669 670 671 672
    #
    # 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}++;
    }
673

674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
    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;
690 691
	}
    }
692 693 694
    print "link link-$node:$iface-$fake_switch:(null) ".
	"$node:$node/$iface $fake_switch:(null) $max_bw 0 0 1 ".
	join(" ",@intersection). "\n";
695 696 697 698
}

exit(0);

699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
# 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 
    }
};

715 716
# Find the bandwidth for an interface, when using the given protocol (which
# most of the time is ethernet).
717
sub get_ifacebw {
718
    ($node,$card,$port,$protocol) = @_;
719
    if (defined($interfacetypes{"$node:$card:$port"})) {
720
	return $interfacespeeds{$interfacetypes{"$node:$card:$port"}}{$protocol};
721 722 723
    }
    else {
	return $default_switchport_speed;
724 725
    }
}
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743

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 ();
    }
}