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
	  "       -a Include even reserved nodes\n".
22
	  "       -m Override multiplex_factor\n");
23 24
    exit(-1);
}
25
my $optlist = "s:e:m:vp:rSa";
26
my $mfactor;
27
my $virtstuff = 0;
28
my $widearea  = 0;
29
my $simstuff  = 0;
30
my $allnodes  = 0;
31

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

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

48 49 50 51 52 53
#
# 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;

54 55
######################################################################

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

61 62
my $TRACK_INTERSWITCH_BANDWIDTH = "@TRACK_INTERSWITCH_BANDWIDTH@";

63 64 65
my %switches	  = ();
my %used_switches = ();
my %permissions   = ();
66 67
my %typemap       = ();
my %auxtypemap    = ();
68

69 70 71
my $DEADPID = NODEDEAD_PID();
my $DEADEID = NODEDEAD_EID();

72
my $pid;
73
my $exempt_eid;
74
my $switchtouse;
75

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

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

    # 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()) {
145 146 147 148 149 150 151 152 153 154
    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;
155 156
}

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

202
# Print switches
203
if (defined($switchtouse)) {
204
    print "node $switchtouse switch:1 *lan:*\n";
205 206 207 208 209 210 211
    $switches{$switchtouse} = 1;
}
else {
    $result =
	DBQueryFatal("select node_id from nodes where role = \"testswitch\"");

    while (($switch) = $result->fetchrow_array) {
212
	print "node $switch switch:1 *lan:*\n";
213 214
	$switches{$switch} = 1;
    }
215
}
216

217
# Find available nodes.
218
#
219 220
# This first query deals with just local nodes. Local nodes can host
# virtnodes, according to the delay_capacity in the types table. 
221
#
222

223
# the ordinary free condition for a local node.
224 225 226
my $free_condition = "(b.node_id is null and ".
                     " (np.eventstate='" . TBDB_NODESTATE_ISUP . "' or ".
                     "  np.eventstate='" . TBDB_NODESTATE_PXEWAIT . "')) ";
227 228 229 230 231

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

235 236 237 238 239 240
# If the user wants all nodes, we consider everything to be free (this
# overrides the other possible free conditions
if ($allnodes) {
    $free_condition = "1";
}

241
$result =
242
    DBQueryFatal("select a.node_id,a.type,a.phys_nodeid,t.class,t.issubnode," .
243 244
		 "a.def_boot_osid, (b.pid is not null and b.eid is not null) " .
		 "from nodes as a ".
245 246
		 "left join reserved as b on a.node_id=b.node_id ".
		 "left join reserved as m on a.phys_nodeid=m.node_id ".
247
		 "left join nodes as np on a.phys_nodeid=np.node_id ".
248
		 "left join node_types as t on t.type=a.type ".
249
		 "where $free_condition and ".
250
		 "      (a.role='testnode' and t.isremotenode=0)");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
251

252 253 254
#
# 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
255 256 257
# to use all nodes), or if there is no entry in the perms table for
# the type/class of node.
#
258
while (($node,$type,$physnode,$class,$issubnode,$def_boot_osid,$reserved) 
259
    = $result->fetchrow_array) {
260 261 262
    $nodes{$node} = $type
	if (!defined($pid) ||
	    ($permissions{$type} && $permissions{$class}));
263 264 265
    if ($issubnode) {
	$subnode_of{$node} = $physnode;
    }
266
    $node_def_boot_osid{$node} = $def_boot_osid;
267 268 269 270 271 272

    if ($reserved) {
	$is_reserved{$node} = 1;
    } else {
	$is_reserved{$node} = 0;
    }
273
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
274 275

foreach $node (keys(%nodes)) {
276 277 278
    my $type  = $nodes{$node};
    my $class = $typemap{$type}->{'CLASS'};
    my $delay_capacity = $typemap{$type}->{'DELAYCAP'};
279
    my $simnode_capacity = $typemap{$type}->{'SIMCAP'};
280 281
    my $cpu_speed = $typemap{$type}->{'SPEED'};
    my $ram = $typemap{$type}->{'RAM'};
282
    
283
    my @types = ("$type:1");
284 285
    my @features;
    my @flags;
286
    my $needvirtgoo = 0;
287

288 289 290 291 292 293 294 295 296 297
    # 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");
    }

298
    # Might be equal, which assign would sum as two, not one!
299 300
    if ($type ne $class) {
	push(@types, "$class:1");
301 302
    }

303
    if ($delay_capacity > 0) {
304
	push @types, "delay:$delay_capacity";
305
    }
306

307 308 309
    #
    # Add any auxiliary types
    #
310 311 312
    foreach my $auxinfo (@{$auxtypes{$node}}) {
	my ($auxtype,$count) = split(":", $auxinfo);
	my $realtype;
313

314 315 316
	# Map an auxtype back to its real type, unless it is a real type.
	if (defined($auxtypemap{$auxtype})) {
	    $realtype = $auxtypemap{$auxtype};
317 318
	}
	else {
319
	    $realtype = $auxtype;
320
	}
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336

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

337
    my $cpu_ram_features_present = 0;
338 339 340 341
    #
    # This stuff is extra goo for local virtual nodes.
    # 
    if ($needvirtgoo) {
342
	push @types, "*lan:*";
343
	# Add trivial bw spec.
344
	push @flags, "trivial_bw:400000";
345
	# Add CPU and RAM information
346
	$cpu_ram_features_present++;
347 348
	# This number can be use for fine-tuning packing
	push @features, "?+virtpercent:100";
349 350 351
	# 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";
352 353
    }

354 355 356 357 358 359 360 361 362 363
    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";
	}
364
	# Add CPU and RAM information
365
	$cpu_ram_features_present++;
366 367 368 369
	push @types, "*lan:*";
	# Add trivial bw spec.
	push @flags, "trivial_bw:100000";
    }
370 371 372 373 374
    if ($cpu_ram_features_present) {
	# Add CPU and RAM information
	push @features, "?+cpu:$cpu_speed";
	push @features, "?+ram:$ram";
    }
375

376
    # Add features
377
    push(@features, @{$typemap{$type}->{'FEATURES'}});
378 379
    if (defined($features{$node})) {
	push @features, @{$features{$node}};
380 381
    }

382 383
    # This is for the case that we are modifying an existing experiment - tell
    # assign to prefer nodes the user has already allocated
384
    if ($exempt_eid && !$allnodes && $is_reserved{$node}) {
385 386 387
	push(@features,"already_reserved:0");
    }

388 389 390 391 392 393 394 395 396 397 398
    #
    # 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
399
	}
400 401
	# Push the subnode's information into its flags
	push @flags, "subnode_of:$subnode_of{$node}";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
402
    }
403 404 405 406 407

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


408
    print "$text\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
409 410
}

411 412 413 414 415 416 417 418 419 420 421
#
# 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.
#
422 423 424 425
# 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. 
#
426
if ($widearea) {
427 428 429
    my $free_condition = "b.node_id is null";
    if ($allnodes) {
	$free_condition = "1";
430
    }
431
    $result =
432
	DBQueryFatal("select count(a.node_id),a.phys_nodeid,aa.type, ".
433
		     "  ns.status,m.pid,m.eid,wn.site ".
434 435 436 437 438 439 440
		     "  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 ".
441 442
		     "left join widearea_nodeinfo as wn on ".
		     "     wn.node_id=a.phys_nodeid ".
443
		     "where  (a.role='virtnode' and t.isremotenode=1 and ".
444
		     "        $free_condition) ".
445 446
		     "group by a.phys_nodeid");
    
447
    while (($count,$physnode,$ptype,$status,$mpid,$meid,$site)
448
	    = $result->fetchrow_array) {
449
	my $class = $typemap{$ptype}->{'CLASS'};
450 451 452 453
	my @types;
	my @features;
	my @flags;

454 455 456 457 458
	# 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;
459 460
	}

461 462 463 464 465
	#
	# 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
	#
466 467
	if (($status && ($status ne 'up')) ||
	    ($mpid eq $DEADPID && $meid eq $DEADEID)) {
468 469 470
	    push @features, "down:1";
	}

471 472 473 474 475 476 477
	#
	# Mark which site this node belongs to
	#
	if ($site) {
	    push @features, "*&$site:$site_weight";
	}

478 479 480
	#
	# Add any auxiliary types.
	#
481 482
	foreach my $auxinfo (@{$auxtypes{$physnode}}) {
	    my ($auxtype,$count) = split(":", $auxinfo);
483

484 485
	    if ($count > $virtnode_capacity) {
		$auxinfo = "$auxtype:$virtnode_capacity";
486
	    }
487 488

	    push(@types, $auxinfo);
489 490 491 492 493
	}

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

494 495 496
	# Indicate that these nodes are beautiful and unique snowflakes
	push @flags, "unique";

497
	# Add features
498
	push(@features, @{$typemap{$ptype}->{'FEATURES'}});
499 500
	if (defined($features{$physnode})) {
	    push @features, @{$features{$physnode}};
501 502 503 504 505 506 507 508 509 510
	}

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

	print "$text\n";
    }
}

511
# Read interfaces
512
$result = DBQueryFatal("SELECT node_id,card,port,iface,interface_type" .
513
		     " from interfaces");
514
while (($node,$card,$port,$iface,$type) = $result->fetchrow_array) {
515
    $interfacemap{"$node:$card:$port"} = $iface;
Chad Barb's avatar
 
Chad Barb committed
516
    if ((defined $type) && ($type ne "")) {
517 518 519 520
	$interfacetypes{"$node:$card:$port"} = $type;
    }
}

521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
# 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");
    }
536 537
}

538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
# 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];
}

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

#
# 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);
618
	    my $bw = get_ifacebw($node,$card,$port,"ethernet");
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
	    $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;
		}
	    }
	}
    }
}

648 649
foreach $interconnect (keys(%interconnects)) {
    ($src,$dst) = split(":",$interconnect);
650 651
    print "link link-$interconnect $src $dst $interconnects{$interconnect} " .
	"0 0 1 ethernet\n";
652
}
653

654
#
655
# Fake switch. Hardwired for now. 
656
#
657 658 659
my @wireless_protos = ("80211", "80211a", "80211b", "80211g");
my $fake_switch = "airswitch";
print "node $fake_switch ", join(" ",map("*$_:*",@wireless_protos)), "\n";
660

661 662
foreach my $interface (keys(%interfacetypes)) {
    my ($node,$card,$port) = split(":", $interface);
663

664 665
    next
    if (!defined($nodes{$node}));
666
	
667 668 669
    my $type    = $interfacetypes{$interface};
    my @protos  = @{ $interfaceprotocols{$type} };
    my $iface   = get_iface($node,$card,$port);
670

671 672 673 674 675 676 677 678
    #
    # 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}++;
    }
679

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

exit(0);

705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
# 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 
    }
};

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

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