From 238dce73ea9da2cee58455132d424a663e571cf5 Mon Sep 17 00:00:00 2001
From: Robert Ricci <ricci@cs.utah.edu>
Date: Mon, 26 Apr 2004 18:45:35 +0000
Subject: [PATCH] Some big changes to assign, and some related changes to
 assign_wrapper and ptopgen.

Add link typing to assign. Each virtual link is given a single type.
Each physical link is given one or more types. A virtual link will
only be mapped to a physical link which can satisfy its type. In both
the top and ptop files, the link types are now mandatory, and they
fall at the end of the mandatory link arguments.

This differers from the 'regular' type system in two ways. First, a
plink is not constrained to filling only one type at a time. If we are
using emulated links, a plink could satisfy, say, an 'ethernet' link
and an 'fxp' link at the same time. This seems to more naturally match
the way we'll use link types.  Second, there are no counts assoicated
with link types, as there are for node types. ie. a link is not an
'ethernet:1' link, it's an 'ethernet' link. Presumably, when
multiplexing virtual links onto a physical one, it's bandwidth that's
the factor that limits the multiplexing.

The link type is now taken into account when constructing pclasses,
and in the mapping precheck.

As a side-effect of these changes, the silly 'count' argument on the
end of link lines in the ptop file, which was used for the fake LAN
nodes, is no longer supported.

The implementation could be a bit more efficient, but that would mean
tossing more of the stuff we do with boost's graph library. I think
this should happen, but today is not the day.

Modify assign_wrapper and ptopgen to spit out top and ptop files in
the new format.

Changed the constant LINK_UKNOWN to LINK_UNMAPPED - the new name more
accurately reflects the way this constant is used.

Add a new '-n' flag that tells assign not to do annealing, just exit
after the type precheck.

Clarify the usage message for the -c flag.

Removed some dead code for dealing with LAN nodes.
---
 assign/assign.cc          |  64 ++++++++++++--
 assign/parse_ptop.cc      |  40 +++++----
 assign/parse_top.cc       |   8 +-
 assign/pclass.cc          |  63 ++++++++------
 assign/physical.h         |  35 ++++++--
 assign/score.cc           | 175 +++++++-------------------------------
 assign/solution.cc        |  16 ++--
 assign/test.ptop          |  14 +--
 assign/test.top           |   6 +-
 assign/virtual.h          |  15 +++-
 tbsetup/assign_wrapper.in |  22 ++---
 tbsetup/ptopgen.in        |  16 +++-
 12 files changed, 239 insertions(+), 235 deletions(-)

diff --git a/assign/assign.cc b/assign/assign.cc
index 3110020dcc..de6bf189f7 100644
--- a/assign/assign.cc
+++ b/assign/assign.cc
@@ -333,7 +333,8 @@ void print_help()
       << endl;
   cout << "  -u          - Print a summary of the solution." << endl;
   cout << "  -c <float>  - Use the 'connected' pnode finding algorithm " <<
-      "<float>% of the time." << endl;
+      "<float>*100% of the time." << endl;
+  cout << "  -n          - Don't anneal - just do the prechecks." << endl;
   exit(EXIT_FATAL);
 }
  
@@ -386,7 +387,7 @@ int type_precheck() {
     }
 
     if (ok) {
-      cout << "Type preecheck passed." << endl;
+      cout << "Type precheck passed." << endl;
       return 1;
     } else {
       cout << "Type precheck failed!" << endl;
@@ -424,12 +425,16 @@ int mapping_precheck() {
 
 	// This constitutes a list of the number of ptypes that matched the
 	// criteria. We use to guess what's wrong with the vnode.
-	int matched_links = 0;
 	int matched_bw = 0;
 	// Keep track of desires had how many 'hits', so that we can tell
 	// if any simply were not matched
 	tb_vnode::desires_count_map matched_desires;
 
+	// Keep track of which link types had how many 'hits', so that we can
+	// tell which type(s) caused this node to fail
+	tb_vnode::link_counts_map matched_link_counts;
+	map<crope,bool> matched_links;
+
 	tb_vclass *vclass = v->vclass;
 	tb_vclass::members_map::iterator mit;
 	if (vclass) {
@@ -452,6 +457,7 @@ int mapping_precheck() {
 		// Grab the first node of the pclass as a representative sample
 		tb_pnode *pnode = *((*it)->members[this_type]->L.begin());
 
+#if 0
 		// Check the number of interfaces - if the pnode is a switch,
 		// for now, we don't check this, since it can end up with more
 		// 'interfaces' due to the fact that it can have interswitch
@@ -462,6 +468,10 @@ int mapping_precheck() {
 		} else {
 		    potential_match = false;
 		}
+#endif
+
+		// Check to see if any of the link that this pnode has are of
+		// the correct type for the virtual node
 
 		// Check bandwidth on emulated links
 		if (pnode->total_bandwidth >= v->total_bandwidth) {
@@ -528,6 +538,26 @@ int mapping_precheck() {
 		    }
 		}
 
+		// Check link types
+		tb_vnode::link_counts_map::iterator vit;
+		for (vit = v->link_counts.begin(); vit != v->link_counts.end();
+		    vit++) {
+		  crope type = vit->first;
+		  int count = vit->second;
+		  if (pnode->link_counts.find(type) !=
+			pnode->link_counts.end()) {
+		    // Found at least one link of this type
+		    matched_links[type] = true;
+		    if (pnode->link_counts[type] >= count) {
+		      // Great, there are enough, too
+		      matched_link_counts[type]++;
+		    } else {
+		      potential_match = false;
+		    }
+		  } else {
+		    potential_match = false;
+		  }
+		}
 
 		if (potential_match) {
 		    vec->push_back(*it);
@@ -553,8 +583,20 @@ int mapping_precheck() {
 	if (vnode_type_table[v->name].first == 0) {
 	    cout << "  *** No possible mapping for " << v->name << endl;
 	    // Make an attempt to figure out why it didn't match
-	    if (!matched_links) {
-		cout << "      Too many links!" << endl;
+	    
+	    // Check all of its link types
+	    tb_vnode::link_counts_map::iterator lit;
+	    for (lit = v->link_counts.begin(); lit != v->link_counts.end();
+		lit++) {
+	      crope type = lit->first;
+	      if (!matched_links[type]) {
+		cout << "      No links of type " << type << " found!" << endl;
+	      } else {
+		if (!matched_link_counts[type]) {
+		  cout << "      Too many links of type " << type << "!"
+		    << endl;
+		}
+	      }
 	    }
 
 	    if (!matched_bw) {
@@ -612,12 +654,13 @@ int main(int argc,char **argv)
   int seed = 0;
   crope viz_prefix;
   bool scoring_selftest = false;
+  bool prechecks_only = false;
   
   // Handle command line
   char ch;
   timelimit = 0.0;
   timetarget = 0.0;
-  while ((ch = getopt(argc,argv,"s:v:l:t:rpPTdH:oguc:")) != -1) {
+  while ((ch = getopt(argc,argv,"s:v:l:t:rpPTdH:oguc:n")) != -1) {
     switch (ch) {
     case 's':
       if (sscanf(optarg,"%d",&seed) != 1) {
@@ -674,6 +717,10 @@ int main(int argc,char **argv)
 	print_help();
       }
       break;
+    case 'n':
+      prechecks_only = true;
+      cout << "Doing only prechecks, exiting early" << endl;
+      break;
     default:
       print_help();
     }
@@ -753,6 +800,11 @@ int main(int argc,char **argv)
       exit(EXIT_UNRETRYABLE);
   }
 
+  // Bomb out early if we're only doing the prechecks
+  if (prechecks_only) {
+      exit(EXIT_SUCCESS);
+  }
+
 #ifdef PER_VNODE_TT
   if (prune_pclasses) {
       prune_unusable_pclasses();
diff --git a/assign/parse_ptop.cc b/assign/parse_ptop.cc
index b1864ed37b..93ba170ed0 100644
--- a/assign/parse_ptop.cc
+++ b/assign/parse_ptop.cc
@@ -178,27 +178,21 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i)
 	pname2vertex[name] = pv;
       }
     } else if (command.compare("link") == 0) {
-      if (parsed_line.size() < 7) {
+      if (parsed_line.size() < 8) {
 	ptop_error("Bad link line, too few arguments.");
       }
       int num = 1;
 #ifdef PENALIZE_BANDWIDTH
       float penalty;
-      if (parsed_line.size() == 8) {
-	if (sscanf(parsed_line[7].c_str(),"%f",&penalty) != 1) {
-	  ptop_error("Bad number argument: " << parsed_line[7] << ".");
+      if (parsed_line.size() == 9) {
+	if (sscanf(parsed_line[8].c_str(),"%f",&penalty) != 1) {
+	  ptop_error("Bad number argument: " << parsed_line[8] << ".");
 	  penalty=1.0;
 	}
       }
-#else
-      if (parsed_line.size() == 8) {
-	if (sscanf(parsed_line[7].c_str(),"%d",&num) != 1) {
-	  ptop_error("Bad number argument: " << parsed_line[7] << ".");
-	  num=1;
-	}
-      }
 #endif
 
+#if 0
 #ifdef FIX_PLINK_ENDPOINTS
       bool fixends;
 #ifdef FIX_PLINKS_DEFAULT
@@ -206,16 +200,18 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i)
 #else
       fixends = false;
 #endif
-      if (parsed_line.size() == 9) {
-	  if (parsed_line[8].compare("fixends") == 0) {
+      if (parsed_line.size() == 10) {
+	  if (parsed_line[9].compare("fixends") == 0) {
 	      fixends = true;
 	  }
       }
 #else
-      if (parsed_line.size() > 8) {
+      if (parsed_line.size() > 9) {
 	ptop_error("Bad link line, too many arguments.");
       }
 #endif
+#endif
+
       crope name = parsed_line[1];
       crope src,srcmac;
       split_two(parsed_line[2],':',src,srcmac,"(null)");
@@ -224,6 +220,7 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i)
       crope bw = parsed_line[4];
       crope delay = parsed_line[5];
       crope loss = parsed_line[6];
+      crope link_type = parsed_line[7];
       int ibw,idelay;
       double gloss;
 
@@ -252,14 +249,17 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i)
       
       for (int cur = 0;cur<num;++cur) {
 	pedge pe = (add_edge(srcv,dstv,PG)).first;
-	tb_plink *pl = new tb_plink(name,tb_plink::PLINK_NORMAL,srcmac,dstmac);
+	tb_plink *pl = new
+	    tb_plink(name,tb_plink::PLINK_NORMAL,link_type,srcmac,dstmac);
 	put(pedge_pmap,pe,pl);
 	pl->delay_info.bandwidth = ibw;
 	pl->delay_info.delay = idelay;
 	pl->delay_info.loss = gloss;
+#if 0
 #ifdef FIX_PLINK_ENDPOINTS
 	pl->fixends = fixends;
 #endif
+#endif
 #ifdef PENALIZE_BANDWIDTH
 	pl->penalty = penalty;
 #endif
@@ -275,11 +275,19 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i)
 	    tb_slink *sl = new tb_slink();
 	    put(sedge_pmap,swedge,sl);
 	    sl->mate = pe;
-	    pl->type = tb_plink::PLINK_INTERSWITCH;
+	    pl->is_type = tb_plink::PLINK_INTERSWITCH;
 	  }
 	}
 	srcnode->total_interfaces++;
 	dstnode->total_interfaces++;
+	srcnode->link_counts[link_type]++;
+	dstnode->link_counts[link_type]++;
+	for (int i = 8; i < parsed_line.size(); i++) {
+	  crope link_type = parsed_line[i];
+	  pl->types.insert(link_type);
+	  srcnode->link_counts[link_type]++;
+	  dstnode->link_counts[link_type]++;
+	}
 	if (ISSWITCH(srcnode) &&
 	    ! ISSWITCH(dstnode)) {
 	  dstnode->switches.insert(srcv);
diff --git a/assign/parse_top.cc b/assign/parse_top.cc
index dfa6520f57..ea41e24583 100644
--- a/assign/parse_top.cc
+++ b/assign/parse_top.cc
@@ -158,12 +158,13 @@ int parse_top(tb_vgraph &VG, istream& i)
 	}
       }
     } else if (command.compare("link") == 0) {
-      if (parsed_line.size() < 7) {
+      if (parsed_line.size() < 8) {
 	top_error("Bad link line, too few arguments.");
       } else {
 	crope name = parsed_line[1];
 	crope src = parsed_line[2];
 	crope dst = parsed_line[3];
+	crope link_type = parsed_line[7];
 	crope bw,bwunder,bwover;
 	crope delay,delayunder,delayover;
 	crope loss,lossunder,lossover;
@@ -238,6 +239,7 @@ int parse_top(tb_vgraph &VG, istream& i)
 	tb_vlink *l = new tb_vlink();
 	l->src = node1;
 	l->dst = node2;
+	l->type = link_type;
 	put(vedge_pmap,e,l);
 
 	if ((sscanf(bw.c_str(),"%d",&(l->delay_info.bandwidth)) != 1) ||
@@ -264,7 +266,7 @@ int parse_top(tb_vgraph &VG, istream& i)
 #endif
 	l->emulated = false;
 	
-	for (unsigned int i = 7;i < parsed_line.size();++i) {
+	for (unsigned int i = 8;i < parsed_line.size();++i) {
 	  if (parsed_line[i].compare("nodelay") == 0) {
 	    l->allow_delayed = false;
 	  } else if (parsed_line[i].compare("emulated") == 0) {
@@ -288,6 +290,8 @@ int parse_top(tb_vgraph &VG, istream& i)
 	} else {
 	    vnode1->num_links++;
 	    vnode2->num_links++;
+	    vnode1->link_counts[link_type]++;
+	    vnode2->link_counts[link_type]++;
 	}
 #endif
       }
diff --git a/assign/pclass.cc b/assign/pclass.cc
index 8e9ebf26ea..7ea07ba45e 100644
--- a/assign/pclass.cc
+++ b/assign/pclass.cc
@@ -11,7 +11,7 @@
 #include <hash_map>
 #include <rope>
 #include <queue>
-#include <slist>
+#include <list>
 #include <algorithm>
 
 #include <boost/config.hpp>
@@ -48,26 +48,15 @@ extern pclass_list pclasses;
 // length of the array.
 extern pclass_types type_table;
 
-typedef pair<pvertex,int> link_info; // dst, bw
-
-struct hashlinkinfo {
-  size_t operator()(link_info const &A) const {
-    hashptr<void *> ptrhash;
-    return ptrhash(A.first)/2+A.second;
-  }
-};
-
 // returns 1 if a and b are equivalent.  They are equivalent if the
 // type and features information match and if there is a one-to-one
 // mapping between links that preserves bw, and destination.
 int pclass_equiv(tb_pgraph &PG, tb_pnode *a,tb_pnode *b)
 {
-  typedef hash_multiset<link_info,hashlinkinfo> link_set;
-
   // The unique flag is used to signify that there is some reason that assign
   // is not aware of that the node is unique, and shouldn't be put into a
   // pclass. The usual reason for doing this is for scoring purposes - ie.
-  // don't prefer one just because it's the same pclass as another that, in
+  // don't prefer one just because it's the same pclass over another that, in
   // reality, is very different.
   if (a->unique || b->unique) {
       return 0;
@@ -129,33 +118,51 @@ int pclass_equiv(tb_pgraph &PG, tb_pnode *a,tb_pnode *b)
     }
   }
 
-  // check links - to do this we first create sets of every link in b.
-  // we then loop through every link in a, find a match in the set, and
-  // remove it from the set.
+  // Check links
   pvertex an = pnode2vertex[a];
   pvertex bn = pnode2vertex[b];
 
-  link_set b_links;
+  // Make a list of all links for node b
+  typedef list<pedge> link_list;
+  link_list b_links;
 
   poedge_iterator eit,eendit;
   tie(eit,eendit) = out_edges(bn,PG);
   for (;eit != eendit;++eit) {
     pvertex dst = target(*eit,PG);
-    if (dst == bn)
-      dst = source(*eit,PG);
-    b_links.insert(link_info(dst,get(pedge_pmap,*eit)->delay_info.bandwidth));
+    b_links.push_back(*eit);
   }
+  
+  // Go through all of a's links, trying to find matches on node b. If we find
+  // a match, we remove it from the list
   tie(eit,eendit) = out_edges(an,PG);
   for (;eit != eendit;++eit) {
-    pvertex dst = target(*eit,PG);
-    if (dst == an)
-      dst = source(*eit,PG);
-    int bw = get(pedge_pmap,*eit)->delay_info.bandwidth;
-    link_info tomatch = link_info(dst,bw);
-    link_set::iterator found = b_links.find(tomatch);
-    if (found == b_links.end()) return 0;
-    else b_links.erase(found);
+    tb_plink *plink_a = get(pedge_pmap,*eit);
+    pvertex dest_pv_a = target(*eit,PG);
+    if (dest_pv_a == a)
+      dest_pv_a = source(*eit,PG);
+
+    link_list::iterator bit;
+    for (bit = b_links.begin(); bit != b_links.end(); bit++) {
+      tb_plink *plink_b = get(pedge_pmap,*bit);
+      pvertex dest_pv_b = target(*bit,PG);
+      if (dest_pv_b == b)
+	dest_pv_b = source(*bit,PG);
+
+      // If links are equivalent, remove this link in b from further
+      // consideration, and go to the next link in a
+      if ((dest_pv_a == dest_pv_b) && plink_a->is_equiv(*plink_b)) {
+	b_links.erase(bit);
+	break;
+      }
+    }
+    // If we never found a match, these nodes aren't equivalent
+    if (bit == b_links.end()) {
+      return 0;
+    }
   }
+
+  // Make sure node b has no extra links
   if (b_links.size() != 0) return 0;
   return 1;
 }
diff --git a/assign/physical.h b/assign/physical.h
index e8b8c41f41..81d6e42039 100644
--- a/assign/physical.h
+++ b/assign/physical.h
@@ -61,6 +61,8 @@ typedef hash_map<svertex,switch_dist_map*>switch_dist_map_map;
 typedef list<pedge> pedge_path;
 typedef list<pvertex> pvertex_list;
 
+typedef hash_map<crope,int> link_type_count_map;
+
 // Globals, declared in assign.cc
 
 extern tb_pgraph_vertex_pmap pvertex_pmap;
@@ -156,6 +158,9 @@ public:
 
   bool is_switch;		// Indicates whether or not this pnode is a
                                 // switch
+				//
+  link_type_count_map link_counts; // Counts how many links of each type this
+  				   // node has 
 	
   bool set_current_type(crope type) {
       if (types.find(type) == types.end()) {
@@ -228,17 +233,22 @@ typedef hash_map<nodepair,int,pairhash<crope> > nodepair_count_map;
 class tb_plink {
 public:
   typedef enum {PLINK_NORMAL,PLINK_INTERSWITCH,PLINK_LAN} plinkType;
+  typedef hash_set<crope> type_set;
 
-  tb_plink(crope _name, plinkType _type, crope _srcmac, crope _dstmac)
-    : name(_name), srcmac(_srcmac), dstmac(_dstmac), type(_type),
+  tb_plink(crope _name, plinkType _is_type, crope _type, crope _srcmac, crope
+      _dstmac)
+    : name(_name), srcmac(_srcmac), dstmac(_dstmac), is_type(_is_type),
       delay_info(), bw_used(0), emulated(0), nonemulated(0),
       penalty(0.0), fixends(false), current_endpoints(), current_count(0),
-      vedge_counts() {;}
+      vedge_counts() {
+	  types.insert(_type);
+      }
 
   crope name;			// the name
   crope srcmac,dstmac;		// source and destination MAC addresses.
 
-  plinkType type;		// type of the link
+  plinkType is_type;		// inter-switch type of the link
+  type_set types;		// type (ie. ethernet) of the link
   tb_delay_info delay_info;	// the delay characteristics of this link
   int bw_used;			// how much is used
 
@@ -258,7 +268,7 @@ public:
   {
     o << "tb_plink: " << link.name << " (" << &link << ")" << endl;
     o << "  type: ";
-    switch (link.type) {
+    switch (link.is_type) {
     case tb_plink::PLINK_NORMAL:
       o << "normal" << endl;
       break;
@@ -276,6 +286,21 @@ public:
     o << link.delay_info;
     return o;
   }
+
+  // Return true if the two plinks are equivalent, in terms of type and
+  // bandwidth.
+  // NOTE: should probably use a helper function in delay_info, but right now,
+  // we only care about bandwidth
+  const bool is_equiv(const tb_plink& link) {
+      if (types == link.types) {
+	  return false;
+      }
+      if (delay_info.bandwidth != link.delay_info.bandwidth) {
+	  return false;
+      }
+
+      return true;
+  }
 };
 
 class tb_slink {
diff --git a/assign/score.cc b/assign/score.cc
index b5352af4c4..3e952adaae 100644
--- a/assign/score.cc
+++ b/assign/score.cc
@@ -129,7 +129,7 @@ void init_score()
   tie(vedge_it,end_vedge_it) = edges(VG);
   for (;vedge_it!=end_vedge_it;++vedge_it) {
     tb_vlink *vlink=get(vedge_pmap,*vedge_it);
-    vlink->link_info.type=tb_link_info::LINK_UNKNOWN;
+    vlink->link_info.type_used=tb_link_info::LINK_UNMAPPED;
     vlink->no_connection=false;
   }
   pvertex_iterator pvertex_it,end_pvertex_it;
@@ -166,7 +166,7 @@ void unscore_link_info(vedge ve,tb_pnode *src_pnode,tb_pnode *dst_pnode, tb_vnod
 
   // Handle vnodes that are not allowed to have a mix of trivial and
   // non-trivial links
-  if (vlink->link_info.type == tb_link_info::LINK_TRIVIAL) {
+  if (vlink->link_info.type_used == tb_link_info::LINK_TRIVIAL) {
       src_vnode->trivial_links--;
       dst_vnode->trivial_links--;
       if (src_vnode->disallow_trivial_mix &&
@@ -185,7 +185,7 @@ void unscore_link_info(vedge ve,tb_pnode *src_pnode,tb_pnode *dst_pnode, tb_vnod
 	  violated--;
 	  vinfo.trivial_mix--;
       }
-  } else if (vlink->link_info.type != tb_link_info::LINK_UNKNOWN) {
+  } else if (vlink->link_info.type_used != tb_link_info::LINK_UNMAPPED) {
       src_vnode->nontrivial_links--;
       dst_vnode->nontrivial_links--;
       if (src_vnode->disallow_trivial_mix &&
@@ -207,7 +207,7 @@ void unscore_link_info(vedge ve,tb_pnode *src_pnode,tb_pnode *dst_pnode, tb_vnod
   }
 
   // Unscore the link itself
-  if (vlink->link_info.type == tb_link_info::LINK_DIRECT) {
+  if (vlink->link_info.type_used == tb_link_info::LINK_DIRECT) {
     // DIRECT LINK
     SDEBUG(cerr << "   direct link" << endl);
     src_pnode->nontrivial_bw_used -= vlink->delay_info.bandwidth;
@@ -216,7 +216,7 @@ void unscore_link_info(vedge ve,tb_pnode *src_pnode,tb_pnode *dst_pnode, tb_vnod
     SSUB(SCORE_DIRECT_LINK);
     unscore_link(vlink->link_info.plinks.front(),ve,src_pnode,dst_pnode);
     vlink->link_info.plinks.clear();
-  } else if (vlink->link_info.type == tb_link_info::LINK_INTERSWITCH) {
+  } else if (vlink->link_info.type_used == tb_link_info::LINK_INTERSWITCH) {
     // INTERSWITCH LINK
     SDEBUG(cerr << "  interswitch link" << endl);
     src_pnode->nontrivial_bw_used -= vlink->delay_info.bandwidth;
@@ -253,7 +253,7 @@ void unscore_link_info(vedge ve,tb_pnode *src_pnode,tb_pnode *dst_pnode, tb_vnod
       the_switch->nontrivial_bw_used -= vlink->delay_info.bandwidth * 2;
     }
     vlink->link_info.switches.clear();
-  } else if (vlink->link_info.type == tb_link_info::LINK_INTRASWITCH) {
+  } else if (vlink->link_info.type_used == tb_link_info::LINK_INTRASWITCH) {
     // INTRASWITCH LINK
     SDEBUG(cerr << "   intraswitch link" << endl);
     src_pnode->nontrivial_bw_used -= vlink->delay_info.bandwidth;
@@ -276,7 +276,7 @@ void unscore_link_info(vedge ve,tb_pnode *src_pnode,tb_pnode *dst_pnode, tb_vnod
   }
 
 #ifdef TRIVIAL_LINK_BW
-  else if (vlink->link_info.type == tb_link_info::LINK_TRIVIAL) {
+  else if (vlink->link_info.type_used == tb_link_info::LINK_TRIVIAL) {
       // Trivial link - we may get to remove violations
       SDEBUG(cerr << "  trivial bandwidth used " <<
 	      src_pnode->trivial_bw_used << " max is " <<
@@ -539,7 +539,7 @@ void score_link_info(vedge ve, tb_pnode *src_pnode, tb_pnode *dst_pnode, tb_vnod
 {
   tb_vlink *vlink = get(vedge_pmap,ve);
   tb_pnode *the_switch;
-  switch (vlink->link_info.type) {
+  switch (vlink->link_info.type_used) {
   case tb_link_info::LINK_DIRECT:
     SADD(SCORE_DIRECT_LINK);
     src_pnode->nontrivial_bw_used += vlink->delay_info.bandwidth;
@@ -628,7 +628,7 @@ void score_link_info(vedge ve, tb_pnode *src_pnode, tb_pnode *dst_pnode, tb_vnod
     }
     break;
 #endif
-  case tb_link_info::LINK_UNKNOWN:
+  case tb_link_info::LINK_UNMAPPED:
     cout << "Internal error: Should not be here either." << endl;
     exit(EXIT_FATAL);
     break;
@@ -636,7 +636,7 @@ void score_link_info(vedge ve, tb_pnode *src_pnode, tb_pnode *dst_pnode, tb_vnod
 
   // Handle vnodes that are not allowed to have a mix of trivial and
   // non-trivial links
-  if (vlink->link_info.type == tb_link_info::LINK_TRIVIAL) {
+  if (vlink->link_info.type_used == tb_link_info::LINK_TRIVIAL) {
       src_vnode->trivial_links++;
       dst_vnode->trivial_links++;
       if (src_vnode->disallow_trivial_mix &&
@@ -849,7 +849,7 @@ int add_node(vvertex vv,pvertex pv, bool deterministic, bool is_fixed)
 	    seen_loopback_links.insert(vlink);
 	}
 	if (allow_trivial_links && vlink->allow_trivial) {
-	    vlink->link_info.type = tb_link_info::LINK_TRIVIAL;
+	    vlink->link_info.type_used = tb_link_info::LINK_TRIVIAL;
 	    // XXX - okay, this is really bad, but score_link_info doesn't
 	    // usually get called for trivial links, and letting them fall
 	    // through into the 'normal' link code below is disatrous!
@@ -875,7 +875,7 @@ int add_node(vvertex vv,pvertex pv, bool deterministic, bool is_fixed)
 	pedge pe;
 	// Direct link
 	if (find_best_link(dest_pv,pv,vlink,pe)) {
-	  resolutions[resolution_index].type = tb_link_info::LINK_DIRECT;
+	  resolutions[resolution_index].type_used = tb_link_info::LINK_DIRECT;
 	  resolutions[resolution_index].plinks.push_back(pe);
 	  resolution_index++;
 	  total_weight += LINK_RESOLVE_DIRECT;
@@ -924,7 +924,8 @@ int add_node(vvertex vv,pvertex pv, bool deterministic, bool is_fixed)
 	      }
 
 
-	    resolutions[resolution_index].type = tb_link_info::LINK_INTRASWITCH;
+	    resolutions[resolution_index].type_used =
+		tb_link_info::LINK_INTRASWITCH;
 	    if (flipped) { // Order these need to go in depends on flipped bit
 	      if (second_link) {
 		resolutions[resolution_index].plinks.push_back(second);
@@ -997,7 +998,8 @@ int add_node(vvertex vv,pvertex pv, bool deterministic, bool is_fixed)
 		}
 	      }
 
-	      resolutions[resolution_index].type = tb_link_info::LINK_INTERSWITCH;
+	      resolutions[resolution_index].type_used =
+		  tb_link_info::LINK_INTERSWITCH;
 	      if (flipped) { // Order these need to go in depends on flipped bit
 		if (second_link) {
 		  resolutions[resolution_index].plinks.push_front(second);
@@ -1033,7 +1035,7 @@ int add_node(vvertex vv,pvertex pv, bool deterministic, bool is_fixed)
 	  SADD(SCORE_NO_CONNECTION);
 	  vlink->no_connection=true;
 	  vinfo.no_connection++;
-	  vlink->link_info.type = tb_link_info::LINK_UNKNOWN;
+	  vlink->link_info.type_used = tb_link_info::LINK_UNMAPPED;
 	  violated++;
 	} else {
 	  // Check to see if we are fixing a violation
@@ -1051,14 +1053,14 @@ int add_node(vvertex vv,pvertex pv, bool deterministic, bool is_fixed)
 	    float choice;
 	    choice = std::random()%(int)total_weight;
 	    for (index = 0;index < resolution_index;++index) {
-	      switch (resolutions[index].type) {
+	      switch (resolutions[index].type_used) {
 	      case tb_link_info::LINK_DIRECT:
 		choice -= LINK_RESOLVE_DIRECT; break;
 	      case tb_link_info::LINK_INTRASWITCH:
 		choice -= LINK_RESOLVE_INTRASWITCH; break;
 	      case tb_link_info::LINK_INTERSWITCH:
 		choice -= LINK_RESOLVE_INTERSWITCH; break;
-	      case tb_link_info::LINK_UNKNOWN:
+	      case tb_link_info::LINK_UNMAPPED:
 	      case tb_link_info::LINK_TRIVIAL:
 		cerr << "Internal error: Should not be here." << endl;
 		exit(EXIT_FATAL);
@@ -1194,6 +1196,11 @@ bool find_best_link(pvertex pv,pvertex switch_pv,tb_vlink *vlink,
     if (dest_pv == switch_pv) {
       tb_plink *plink = get(pedge_pmap,*pedge_it);
 
+      // Skip any links whose type is wrong (ie. doesn't match the vlink)
+      if (plink->types.find(vlink->type) == plink->types.end()) {
+	  continue;
+      }
+
       // Get delay characteristics - NOTE: Currently does not actually do
       // anything
       tb_delay_info physical_delay;
@@ -1303,7 +1310,7 @@ void score_link(pedge pe,vedge ve,tb_pnode *src_pnode, tb_pnode *dst_pnode)
   cerr << *vlink;
 #endif
   
-  if (plink->type == tb_plink::PLINK_NORMAL) {
+  if (plink->is_type == tb_plink::PLINK_NORMAL) {
     // need to account for three things here, the possiblity of a new plink
     // the user of a new emulated link, and a possible violation.
     if (vlink->emulated) {
@@ -1367,7 +1374,7 @@ void score_link(pedge pe,vedge ve,tb_pnode *src_pnode, tb_pnode *dst_pnode)
   }
 #endif
 
-  if (plink->type != tb_plink::PLINK_LAN) {
+  if (plink->is_type != tb_plink::PLINK_LAN) {
     tb_delay_info physical_delay;
     physical_delay.bandwidth = plink->delay_info.bandwidth - plink->bw_used;
     physical_delay.delay = plink->delay_info.delay;
@@ -1414,7 +1421,7 @@ void unscore_link(pedge pe,vedge ve, tb_pnode *src_pnode, tb_pnode *dst_pnode)
   // This is not in the slightest bit graceful! This function was not designed
   // for use with trivial links (which have no plink,) but I would like to call
   // it for symmetry
-  if (vlink->link_info.type == tb_link_info::LINK_TRIVIAL) {
+  if (vlink->link_info.type_used == tb_link_info::LINK_TRIVIAL) {
     goto UNSCORE_TRIVIAL;
   }
 
@@ -1429,7 +1436,7 @@ void unscore_link(pedge pe,vedge ve, tb_pnode *src_pnode, tb_pnode *dst_pnode)
   cerr << *vlink;
 #endif
 
-  if (plink->type == tb_plink::PLINK_NORMAL) {
+  if (plink->is_type == tb_plink::PLINK_NORMAL) {
     if (vlink->emulated) {
       plink->emulated--;
       SSUB(SCORE_EMULATED_LINK);
@@ -1508,7 +1515,7 @@ void unscore_link(pedge pe,vedge ve, tb_pnode *src_pnode, tb_pnode *dst_pnode)
 #endif
   
   // bandwidth check
-  if (plink->type != tb_plink::PLINK_LAN) {
+  if (plink->is_type != tb_plink::PLINK_LAN) {
 #ifdef PENALIZE_BANDWIDTH
     SSUB(plink->penalty * (vlink->delay_info.bandwidth * 1.0) / (plink->delay_info.bandwidth));
 #endif
@@ -1543,7 +1550,7 @@ void unscore_link(pedge pe,vedge ve, tb_pnode *src_pnode, tb_pnode *dst_pnode)
   }
 
 UNSCORE_TRIVIAL:
-  vlink->link_info.type = tb_link_info::LINK_UNKNOWN;
+  vlink->link_info.type_used = tb_link_info::LINK_UNMAPPED;
 }
 
 double fd_score(tb_vnode *vnode,tb_pnode *pnode,int &fd_violated,
@@ -1792,125 +1799,3 @@ void remove_global_fds(tb_vnode *vnode,tb_pnode *pnode) {
     }
   }
 }
-
-/* make_lan_node(vvertex vv)
- * This routines create a physical lan node and connects it to a switch
- * with a LAN plink.  Most of the code is in determining which switch to
- * connect the LAN node to.  Specifically, it connects it to the switch
- * which will maximize the number of intra (rather than inter) links for
- * assigned adjancent nodes of vv.
- *
- * NOTE: This function is deprecated, since there are no longer special
- * physical LAN nodes.
- *
- */
-pvertex make_lan_node(vvertex vv)
-{
-  typedef hash_map<pvertex,int,hashptr<void *> > switch_int_map;
-  switch_int_map switch_counts;
-
-  tb_vnode *vnode = get(vvertex_pmap,vv);
-
-  SDEBUG(cerr << "make_lan_node(" << vnode->name << ")" << endl);
-  
-  // Choose switch
-  pvertex largest_switch;
-  int largest_switch_count=0;
-  voedge_iterator vedge_it,end_vedge_it;
-  tie(vedge_it,end_vedge_it) = out_edges(vv,VG);
-  for (;vedge_it!=end_vedge_it;++vedge_it) {
-    vvertex dest_vv = target(*vedge_it,VG);
-    if (dest_vv == vv)
-      dest_vv = source(*vedge_it,VG);
-    tb_vnode *dest_vnode = get(vvertex_pmap,dest_vv);
-    if (dest_vnode->assigned) {
-      pvertex dest_pv = dest_vnode->assignment;
-      tb_pnode *dest_pnode = get(pvertex_pmap,dest_pv);
-      for (pvertex_set::iterator switch_it = dest_pnode->switches.begin();
-	   switch_it != dest_pnode->switches.end();switch_it++) {
-	if (switch_counts.find(*switch_it) != switch_counts.end()) {
-	  switch_counts[*switch_it]++;
-	} else {
-	  switch_counts[*switch_it]=1;
-	}
-	if (switch_counts[*switch_it] > largest_switch_count) {
-	  largest_switch = *switch_it;
-	  largest_switch_count = switch_counts[*switch_it];
-	}
-      }
-    }
-  }
-
-  SDEBUG(cerr << "  largest_switch=" << largest_switch <<
-	 " largest_switch_count=" << largest_switch_count << endl);
-  
-  pvertex pv = add_vertex(PG);
-  tb_pnode *p = new tb_pnode(vnode->name);
-  put(pvertex_pmap,pv,p);
-  p->types["lan"] = new tb_pnode::type_record(1,false);
-  p->set_current_type("lan");
-  
-  // If the below is false then we have an orphaned lan node which will
-  // quickly be destroyed when add_node fails.
-  if (largest_switch_count != 0) {
-    pedge pe = (add_edge(pv,largest_switch,PG)).first;
-
-    p->name = "lan-";
-    p->name += get(pvertex_pmap,largest_switch)->name;
-    p->name += "-";
-    p->name += vnode->name;
-
-    // Build a link name that looks like the ones we used to supply in the ptop
-    // file
-    crope link_name = "link-";
-    link_name += p->name;
-    tb_plink *pl = new tb_plink(link_name, tb_plink::PLINK_LAN,
-	    p->name, "(null)");
-
-    p->switches.insert(largest_switch);
-    put(pedge_pmap,pe,pl);
-
-#ifdef FIX_PLINK_ENDPOINTS
-    pl->fixends = false;
-#endif
-  } else {
-    p->name += "orphan";
-  }
-
-  return pv;
-}
-
-/* delete_lan_node(pvertex pv)
- * Removes the physical lan node and the physical lan link.  Assumes that
- * nothing is assigned to it.
- *
- * NOTE: This function is deprecated, since there are no longer special
- * physical LAN nodes.
- *
- */
-void delete_lan_node(pvertex pv)
-{
-  tb_pnode *pnode = get(pvertex_pmap,pv);
-
-  SDEBUG(cerr << "delete_lan_node(" << pnode->name << ")" << endl);
-
-  // delete LAN link
-  typedef list<pedge> pedge_list;
-  pedge_list to_free;
-  
-  poedge_iterator pedge_it,end_pedge_it;
-  tie(pedge_it,end_pedge_it) = out_edges(pv,PG);
-  // We need to copy because removing edges invalidates out iterators.
-  for (;pedge_it != end_pedge_it;++pedge_it) {
-    to_free.push_front(*pedge_it);
-  }
-  for (pedge_list::iterator free_it = to_free.begin();
-       free_it != to_free.end();++free_it) {
-    delete(get(pedge_pmap,*free_it));
-    remove_edge(*free_it,PG);
-  }
-
-  remove_vertex(pv,PG);
-  delete pnode;
-}
-
diff --git a/assign/solution.cc b/assign/solution.cc
index bef8d54166..2b776ce06f 100644
--- a/assign/solution.cc
+++ b/assign/solution.cc
@@ -49,21 +49,23 @@ void print_solution()
 
 	cout << vlink->name;
 
-	if (vlink->link_info.type == tb_link_info::LINK_DIRECT) {
+	if (vlink->link_info.type_used == tb_link_info::LINK_DIRECT) {
 	    // Direct link - just need the source and destination
 	    tb_plink *p = get(pedge_pmap,vlink->link_info.plinks.front());
 	    tb_plink *p2 = get(pedge_pmap,vlink->link_info.plinks.back());
 	    cout << " direct " << p->name << " (" <<
 		p->srcmac << "," << p->dstmac << ") " <<
 		p2->name << " (" << p2->srcmac << "," << p2->dstmac << ")";
-	} else if (vlink->link_info.type == tb_link_info::LINK_INTRASWITCH) {
+	} else if (vlink->link_info.type_used ==
+		tb_link_info::LINK_INTRASWITCH) {
 	    // Intraswitch link - need to grab the plinks to both nodes
 	    tb_plink *p = get(pedge_pmap,vlink->link_info.plinks.front());
 	    tb_plink *p2 = get(pedge_pmap,vlink->link_info.plinks.back());
 	    cout << " intraswitch " << p->name << " (" <<
 		p->srcmac << "," << p->dstmac << ") " <<
 		p2->name << " (" << p2->srcmac << "," << p2->dstmac << ")";
-	} else if (vlink->link_info.type == tb_link_info::LINK_INTERSWITCH) {
+	} else if (vlink->link_info.type_used ==
+		tb_link_info::LINK_INTERSWITCH) {
 	    // Interswitch link - interate through each intermediate link
 	    cout << " interswitch ";
 	    for (pedge_path::iterator it=vlink->link_info.plinks.begin();
@@ -72,7 +74,7 @@ void print_solution()
 		cout << " " << p->name << " (" << p->srcmac << "," <<
 		    p->dstmac << ")";
 	    }
-	} else if (vlink->link_info.type == tb_link_info::LINK_TRIVIAL) {
+	} else if (vlink->link_info.type_used == tb_link_info::LINK_TRIVIAL) {
 	    // Trivial link - we really don't have useful information to
 	    // print, but we'll fake a bunch of output here just to make it
 	    // consistent with other (ie. intraswitch) output
@@ -211,7 +213,7 @@ void pedge_writer::operator()(ostream &out,const pedge &p) const {
     out << plink->delay_info.bandwidth << "/" <<
 	plink->delay_info.delay << "/" << plink->delay_info.loss << "\"";
 #endif
-    if (plink->type == tb_plink::PLINK_INTERSWITCH) {
+    if (plink->is_type == tb_plink::PLINK_INTERSWITCH) {
 	out << " style=dashed";
     }
     tb_pnode *src = get(pvertex_pmap,source(p,PG));
@@ -259,8 +261,8 @@ void solution_edge_writer::operator()(ostream &out,const vedge &v) const {
     crope style;
     crope color;
     crope label;
-    switch (linfo.type) {
-	case tb_link_info::LINK_UNKNOWN: style="dotted";color="red"; break;
+    switch (linfo.type_used) {
+	case tb_link_info::LINK_UNMAPPED: style="dotted";color="red"; break;
 	case tb_link_info::LINK_DIRECT: style="dashed";color="black"; break;
 	case tb_link_info::LINK_INTRASWITCH:
 	    style="solid";color="black";
diff --git a/assign/test.ptop b/assign/test.ptop
index d3880e4f24..75ab987a1b 100644
--- a/assign/test.ptop
+++ b/assign/test.ptop
@@ -3,9 +3,11 @@ node pa pc:1
 node pb pc:1
 node pc pc:1
 node pd pc:1
-link l1 pa S 100 0 0
-link l2 pa S 100 0 0
-link l3 pb S 100 0 0
-link l4 pb S 100 0 0
-link l5 pc S 100 0 0
-link l6 pc S 100 0 0
+link l1 pa S 100 0 0 ethernet
+link l2 pa S 100 0 0 ethernet 
+link l3 pb S 100 0 0 ethernet
+link l4 pb S 100 0 0 ethernet
+link l5 pc S 100 0 0 ethernet
+link l6 pc S 100 0 0 ethernet
+link l7 pd S 100 0 0 80211
+link l8 pd S 100 0 0 80211
diff --git a/assign/test.top b/assign/test.top
index c3049fd641..148f0860ef 100644
--- a/assign/test.top
+++ b/assign/test.top
@@ -1,6 +1,6 @@
 node A pc
 node B pc
 node C pc
-link vl1 A B 100 0 0
-link vl2 A C 100 0 0
-link vl3 B C 100 0 0
+link vl1 A B 100 0 0 ethernet
+link vl2 A C 100 0 0 ethernet
+link vl3 B C 100 0 0 ethernet
diff --git a/assign/virtual.h b/assign/virtual.h
index 06bae3d041..94b1d208be 100644
--- a/assign/virtual.h
+++ b/assign/virtual.h
@@ -30,20 +30,22 @@ typedef graph_traits<tb_vgraph>::vertex_iterator vvertex_iterator;
 typedef graph_traits<tb_vgraph>::edge_iterator vedge_iterator;
 typedef graph_traits<tb_vgraph>::out_edge_iterator voedge_iterator;
 
+
 class tb_link_info {
 public:
-  typedef enum {LINK_UNKNOWN, LINK_DIRECT,
+  typedef enum {LINK_UNMAPPED, LINK_DIRECT,
 		LINK_INTRASWITCH, LINK_INTERSWITCH,
 		LINK_TRIVIAL, LINK_DELAYED} linkType;
-  linkType type;
+  linkType type_used;		// type of physical link used to satisfy this
+  				// virtual link
   pedge_path plinks;		// the path of pedges
   pvertex_list switches;	// what switches were used
 
   friend ostream &operator<<(ostream &o, const tb_link_info& link)
   {
     o << "  Type: ";
-    switch (link.type) {
-    case LINK_UNKNOWN : o << "LINK_UNKNOWN"; break;
+    switch (link.type_used) {
+    case LINK_UNMAPPED : o << "LINK_UNMAPPED"; break;
     case LINK_DIRECT : o << "LINK_DIRECT"; break;
     case LINK_INTRASWITCH : o << "LINK_INTRASWITCH"; break;
     case LINK_INTERSWITCH : o << "LINK_INTERSWITCH"; break;
@@ -115,6 +117,10 @@ public:
   subnode_list subnodes;
   crope subnode_of_name;
 
+  // Counts how many links of each type this virtual node has
+  typedef hash_map<crope,int> link_counts_map;
+  link_counts_map link_counts;
+
 };
 
 class tb_vlink {
@@ -134,6 +140,7 @@ public:
   tb_delay_info delay_info;	// the delay characteristics of the link
   tb_link_info link_info;	// what it's mapped to
   crope name;			// name
+  crope type;			// type of this link
   bool emulated;		// is this an emulated link, i.e. can it
 				// share a plink withouter emulated vlinks
   bool no_connection;		// true if this link should be satisfied
diff --git a/tbsetup/assign_wrapper.in b/tbsetup/assign_wrapper.in
index df0b248a66..f57020d8a9 100644
--- a/tbsetup/assign_wrapper.in
+++ b/tbsetup/assign_wrapper.in
@@ -3890,7 +3890,8 @@ sub CreateTopFile()
 		my ($node) = (split(":",$member))[0];
 		my $bandwidth = &getbandwidth($node, $lan, $bw);
 		    
-		print TOPFILE "link $plink $node fakelan/$lan $bandwidth 0 0\n";
+		print TOPFILE "link $plink $node fakelan/$lan $bandwidth " .
+		    "0 0 wireless\n";
 	    }
 	}
 	elsif ($#members == 1) {
@@ -3940,7 +3941,7 @@ sub CreateTopFile()
 		    my $plink = "linksimple/$lan/$nodeport0,$nodeport1";
 		    
 		    print(TOPFILE "link $plink $node0 $node1 ".
-			  max($bw,$rbw) . " 0 0" .
+			  max($bw,$rbw) . " 0 0 ethernet " .
 			  ($emulated ? " emulated" : "") .
 			  ($trivial_ok ? " trivial_ok\n" : "\n"));
 
@@ -3970,10 +3971,10 @@ sub CreateTopFile()
 		    print TOPFILE "node $delayname delay\n";
 		    print TOPFILE
 			"link linksdelaysrc/$lan/$nodeport0,$nodeport1 ".
-			"$node0 $delayname $bandwidth 0 0\n";
+			"$node0 $delayname $bandwidth 0 0 ethernet\n";
 		    print TOPFILE
 			"link linksdelaydst/$lan/$nodeport1,$nodeport0 ".
-			"$node1 $delayname $bandwidth 0 0\n";
+			"$node1 $delayname $bandwidth 0 0 ethernet\n";
 
 		    $delaynodes{$delayname} = $delayname;
 		    
@@ -3994,10 +3995,10 @@ sub CreateTopFile()
 		
 		print TOPFILE "link $plink $node0 $node1";
 		if ($emulated) {
-		    print TOPFILE " " . max($bw,$rbw) . " 0 0 emulated";
+		    print TOPFILE " " . max($bw,$rbw) . " 0 0 ethernet emulated";
 		}
 		else {
-		    print TOPFILE " $bandwidth 0 0";
+		    print TOPFILE " $bandwidth 0 0 ethernet";
 		}
 		if ($trivial_ok) {
 		    print TOPFILE " trivial_ok";
@@ -4061,7 +4062,7 @@ sub CreateTopFile()
 			     $member,$rdelay,$rbw,$rloss,0];
 
 			print(TOPFILE "link $plink $node lan/$lan " .
-			      max($bw,$rbw) . " 0 0" .
+			      max($bw,$rbw) . " 0 0 ethernet" .
 			      ($emulated ? " emulated" : "") .
 			      ($trivial_ok ? " trivial_ok\n" : "\n"));
 
@@ -4078,9 +4079,9 @@ sub CreateTopFile()
 			
 			print TOPFILE "node $delayname delay\n";
 			print TOPFILE "link linkdelaysrc/$lan/$member" .
-			    " $node $delayname $bandwidth 0 0\n";
+			    " $node $delayname $bandwidth 0 0 ethernet\n";
 			print TOPFILE "link linkdelaydst/$lan/$member" .
-			    " lan/$lan $delayname $bandwidth 0 0\n";
+			    " lan/$lan $delayname $bandwidth 0 0 ethernet\n";
 			
 			$delaynodes{$delayname} = $delayname;
 
@@ -4098,7 +4099,8 @@ sub CreateTopFile()
 		else {
 		    my $plink = "linklan/$lan/$member";
 		    
-		    print TOPFILE "link $plink $node lan/$lan $bandwidth 0 0";
+		    print TOPFILE "link $plink $node lan/$lan $bandwidth " .
+			"0 0 ethernet";
 		    if ($emulated) {
 			print TOPFILE " emulated";
 		    }
diff --git a/tbsetup/ptopgen.in b/tbsetup/ptopgen.in
index 0afe8cd212..a1a03a9fb8 100644
--- a/tbsetup/ptopgen.in
+++ b/tbsetup/ptopgen.in
@@ -413,6 +413,15 @@ foreach $node (keys(%nodes)) {
 # physnode. 
 #
 if ($widearea) {
+    #
+    # 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')"; 
+    }
     $result =
 	DBQueryFatal("select count(a.node_id),a.phys_nodeid,aa.type, ".
 		     "  ns.status,m.pid,m.eid,wn.site ".
@@ -576,7 +585,7 @@ while (($node1,$card1,$port1,$node2,$card2,$port2) =
 	    # !!! - 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" .
-		" $node2:$iface2 $bw 0 0 1\n";
+		" $node2:$iface2 $bw 0 0 1 ethernet\n";
 	}
     } 
 }
@@ -632,7 +641,8 @@ if ($TRACK_INTERSWITCH_BANDWIDTH) {
 
 foreach $interconnect (keys(%interconnects)) {
     ($src,$dst) = split(":",$interconnect);
-    print "link link-$interconnect $src $dst $interconnects{$interconnect} 0 0 1\n";
+    print "link link-$interconnect $src $dst $interconnects{$interconnect} " .
+	"0 0 1 ethernet\n";
 }
 
 #
@@ -659,7 +669,7 @@ foreach my $switchtype (("80211a", "80211b", "80211g")) {
 		#print "$node $card $port $type $proto\n";
 		
 		print "link link-$node:$iface-$switchname:(null) ".
-		    "$node:$node/$iface $switchname:(null) $ifacebw 0 0 1\n";
+		    "$node:$node/$iface $switchname:(null) $ifacebw 0 0 1 80211\n";
 	    }
 	}
     }
-- 
GitLab