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