parse_ptop.cc 11.5 KB
Newer Older
Robert Ricci's avatar
Robert Ricci committed
1
/*
Robert Ricci's avatar
Robert Ricci committed
2
 * Copyright (c) 2000-2010 University of Utah and the Flux Group.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 
 * {{{EMULAB-LICENSE
 * 
 * This file is part of the Emulab network testbed software.
 * 
 * This file is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This file is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 * License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * }}}
Robert Ricci's avatar
Robert Ricci committed
22 23
 */

24 25
static const char rcsid[] = "$Id: parse_ptop.cc,v 1.44 2009-05-20 18:06:08 tarunp Exp $";

26 27 28 29
#include "port.h"

#include <boost/graph/adjacency_list.hpp>

30 31 32
#include <iostream>

#include <stdio.h>
33 34 35 36

using namespace boost;

#include "delay.h"
37
#include "physical.h"
38
#include "parser.h"
39

40 41 42
#include <string>
using namespace std;

43 44
extern name_pvertex_map pname2vertex;

45
#define ptop_error(s) errors++;cout << "PTOP:" << line << ": " << s << endl; exit(EXIT_FATAL)
46
#define ptop_error_noline(s) errors++;cout << "PTOP: " << s << endl
47 48 49 50 51

// Used to do late binding of subnode names to pnodes, so that we're no
// dependant on their ordering in the ptop file, which can be annoying to get
// right.
// Returns the number of errors found
52
int bind_ptop_subnodes(tb_pgraph &pg) {
53 54 55 56
    int errors = 0;

    // Iterate through all pnodes looking for ones that are subnodes
    pvertex_iterator vit,vendit;
57
    tie(vit,vendit) = vertices(pg);
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
    for (;vit != vendit;++vit) {
	tb_pnode *pnode = get(pvertex_pmap,*vit);
	if (!pnode->subnode_of_name.empty()) {
	    if (pname2vertex.find(pnode->subnode_of_name) ==
		    pname2vertex.end()) {
		ptop_error_noline(pnode->name << " is a subnode of a " <<
			"non-existent node, " << pnode->subnode_of_name << ".");
			continue;
		}
		pvertex parent_pv = pname2vertex[pnode->subnode_of_name];
		pnode->subnode_of = get(pvertex_pmap,parent_pv);
		pnode->subnode_of->has_subnode = true;
	}
    }

    return errors;
}
75

76
int parse_ptop(tb_pgraph &pg, tb_sgraph &sg, istream& input)
77
{
78 79
  int num_nodes = 0;
  int line=0,errors=0;
80
  char inbuf[16384];
81 82
  string_vector parsed_line;

83
  while (!input.eof()) {
84
    line++;
85
    input.getline(inbuf,16384);
86 87 88
    parsed_line = split_line(inbuf,' ');
    if (parsed_line.size() == 0) {continue;}

89
    string command = parsed_line[0];
90

91
    if (command == "node") {
92 93
      if (parsed_line.size() < 3) {
	ptop_error("Bad node line, too few arguments.");
94
      } else {
95
	num_nodes++;
96
	fstring name = parsed_line[1];
97
	bool isswitch = false;
98
	pvertex pv = add_vertex(pg);
99
	tb_pnode *p = new tb_pnode(name);
100 101 102 103 104
	put(pvertex_pmap,pv,p);
	
	unsigned int i;
	for (i = 2;
	     (i < parsed_line.size()) &&
105 106 107 108
	       (parsed_line[i] != "-");++i) {
	  string stype,load;
	  if (split_two(parsed_line[i],':',stype,load,"1") != 0) {
	    ptop_error("Bad node line, no load for type: " << stype << ".");
109
	  }
110
          fstring type(stype);
111 112 113 114 115 116
	  // Check to see if this is a static type
	  bool is_static = false;
	  if (type[0] == '*') {
	    is_static = true;
	    type.pop_front();
	  }
117
	  int iload;
118 119
	  if (load == "*") { // Allow * to mean unlimited
            // (okay, a really big number)
120
	    iload = 10000;
121
	  } else if (sscanf(load.c_str(),"%d",&iload) != 1) {
122 123
	    ptop_error("Bad node line, bad load: " << load << ".");
	    iload = 1;
124
	  }
125 126 127 128 129

	  /*
	   * Make a tb_ptype structure for this guy - or just add this node to
	   * it if it already exists
	   */
130
	  if (ptypes.find(type) == ptypes.end()) {
131
	      ptypes[type] = new tb_ptype(type);
132
	  }
133 134 135
	  ptypes[type]->add_slots(iload);
	  tb_ptype *ptype = ptypes[type];

136
	  if (type == "switch") {
137
	    isswitch = true;
138
	    p->is_switch = true;
139
	    p->types["switch"] = new tb_pnode::type_record(1,false,ptype);
140
	    svertex sv = add_vertex(sg);
141 142 143 144
	    tb_switch *s = new tb_switch();
	    put(svertex_pmap,sv,s);
	    s->mate = pv;
	    p->sgraph_switch = sv;
145
	    p->switches.insert(pv);
146
	  } else {
147
	    p->types[type] = new tb_pnode::type_record(iload,is_static,ptype);
148
	  }
149
	  p->type_list.push_back(p->types[type]);
150
	}
151 152 153
	for (i=i+1;(i<parsed_line.size()) && (parsed_line[i] != "-") ;++i) {
	  string sfeature,cost;
	  if (split_two(parsed_line[i],':',sfeature,cost,"0") != 0) {
154
	    ptop_error("Bad node line, no cost for feature: " <<
155
		       sfeature << ".");
156
	  }
157 158
      fstring feature(sfeature);

159 160 161 162
	  double gcost;
	  if (sscanf(cost.c_str(),"%lg",&gcost) != 1) {
	    ptop_error("Bad node line, bad cost: " << gcost << ".");
	    gcost = 0;
163
	  }
164 165 166

	  p->features.push_front(tb_node_featuredesire(feature,gcost));

167
	}
168
	/*
169
	 * Parse any other node options or flags
170 171
	 */
	for (i=i+1; i < parsed_line.size(); ++i) {
172
	    string flag,value;
173
	    split_two(parsed_line[i],':',flag,value,"(null)");
174
	    if (flag == "trivial_bw") {
175
		// Handle trivial bandwidth spec
176 177 178 179 180 181 182
		int trivial_bw;
		if (sscanf(value.c_str(),"%i",&trivial_bw) != 1) {
		    ptop_error("Bad bandwidth given for trivial_bw: " << value
			    << endl);
		    trivial_bw = 0;
		}
		p->trivial_bw = trivial_bw;
183
	    } else if (flag == "subnode_of") {
184 185 186 187 188 189 190 191 192
		// Handle subnode relationships
		if (!p->subnode_of_name.empty()) {
		    ptop_error("Can't be a subnode of two nodes");
		    continue;
		} else {
		    // Just store the name for now, we'll do late binding to
		    // an actual pnode later
		    p->subnode_of_name = value;
		}
193
	    } else if (flag == "unique") {
194 195 196
		// Means that we should never put this pnode into a ptype with
		// other pnodes
		p->unique = true;
197 198 199 200
	    } else {
		ptop_error("Bad flag given: " << flag << ".");
	    }
	}
201
	p->features.sort();
202
	pname2vertex[name] = pv;
203
      }
204
    } else if (command == "link") {
205
      if (parsed_line.size() < 8) {
206 207 208 209 210
	ptop_error("Bad link line, too few arguments.");
      }
      int num = 1;
#ifdef PENALIZE_BANDWIDTH
      float penalty;
211 212 213
      if (parsed_line.size() == 9) {
	if (sscanf(parsed_line[8].c_str(),"%f",&penalty) != 1) {
	  ptop_error("Bad number argument: " << parsed_line[8] << ".");
214
	  penalty=1.0;
215
	}
216 217 218
      }
#endif

219
#if 0
220
#ifdef FIX_PLINK_ENDPOINTS
Robert Ricci's avatar
Robert Ricci committed
221 222 223 224 225 226
      bool fixends;
#ifdef FIX_PLINKS_DEFAULT
      fixends = true;
#else
      fixends = false;
#endif
227
      if (parsed_line.size() == 10) {
228
	  if (parsed_line[9] == "fixends") {
229 230 231 232
	      fixends = true;
	  }
      }
#else
233
      if (parsed_line.size() > 9) {
234 235 236
	ptop_error("Bad link line, too many arguments.");
      }
#endif
237 238
#endif

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
      fstring name = parsed_line[1];
      string ssrc,ssrcmac;
      split_two(parsed_line[2],':',ssrc,ssrcmac,"(null)");
      string ssrcn, ssrciface;
      split_two(ssrcmac,'/',ssrcn,ssrciface,"(null)");
      fstring src = ssrc;
      fstring srcmac = ssrcmac;
      fstring srciface = ssrciface;
      string sdst,sdstmac;
      split_two(parsed_line[3],':',sdst,sdstmac,"(null)");
      string sdstn, sdstiface;
      split_two(sdstmac,'/',sdstn,sdstiface,"(null)");
      fstring dst(sdst), dstmac(sdstmac), dstiface(sdstiface);
      string bw = parsed_line[4];
      string delay = parsed_line[5];
      string loss = parsed_line[6];
      fstring link_type = parsed_line[7];
256 257 258 259 260 261 262 263 264 265
      int ibw,idelay;
      double gloss;

      
      if ((sscanf(bw.c_str(),"%d",&ibw) != 1) ||
	  (sscanf(delay.c_str(),"%d",&idelay) != 1) ||
	  (sscanf(loss.c_str(),"%lg",&gloss) != 1)) {
	ptop_error("Bad link line, bad delay characteristics.");
      }

266 267 268 269
      if (ibw <= 0) {
          ptop_error("Bad link line - negative or zero bandwidth.");
      }

270
#define ISSWITCH(n) (n->types.find("switch") != n->types.end())
271 272
      // Check to make sure the nodes in the link actually exist
      if (pname2vertex.find(src) == pname2vertex.end()) {
273
	  ptop_error("Bad link line, non-existent src node " << src);
274 275 276
	  continue;
      }
      if (pname2vertex.find(dst) == pname2vertex.end()) {
277
	  ptop_error("Bad link line, non-existent dst node " << dst);
278 279 280
	  continue;
      }

281 282 283 284 285 286
      pvertex srcv = pname2vertex[src];
      pvertex dstv = pname2vertex[dst];
      tb_pnode *srcnode = get(pvertex_pmap,srcv);
      tb_pnode *dstnode = get(pvertex_pmap,dstv);
      
      for (int cur = 0;cur<num;++cur) {
287
	pedge pe = (add_edge(srcv,dstv,pg)).first;
288
	tb_plink *pl = new
289 290
	    tb_plink(name,tb_plink::PLINK_NORMAL,link_type,srcnode->name,srcmac,
		     srciface,dstnode->name,dstmac,dstiface);
291 292 293 294
	put(pedge_pmap,pe,pl);
	pl->delay_info.bandwidth = ibw;
	pl->delay_info.delay = idelay;
	pl->delay_info.loss = gloss;
295
#if 0
296 297 298
#ifdef FIX_PLINK_ENDPOINTS
	pl->fixends = fixends;
#endif
299
#endif
300 301 302 303 304 305 306 307 308 309 310
#ifdef PENALIZE_BANDWIDTH
	pl->penalty = penalty;
#endif
	if (ISSWITCH(srcnode) && ISSWITCH(dstnode)) {
	  if (cur != 0) {
	    cout <<
	      "Warning: Extra links between switches will be ignored. (" <<
	      name << ")" << endl;
	  } else {
	    svertex src_switch = get(pvertex_pmap,srcv)->sgraph_switch;
	    svertex dst_switch = get(pvertex_pmap,dstv)->sgraph_switch;
311
	    sedge swedge = add_edge(src_switch,dst_switch,sg).first;
312 313 314
	    tb_slink *sl = new tb_slink();
	    put(sedge_pmap,swedge,sl);
	    sl->mate = pe;
315
	    pl->is_type = tb_plink::PLINK_INTERSWITCH;
316
	  }
317
	}
318 319
	srcnode->total_interfaces++;
	dstnode->total_interfaces++;
320 321
	srcnode->link_counts[link_type]++;
	dstnode->link_counts[link_type]++;
322 323 324 325 326 327
	// There can be more than one link type
	for (size_t i = 8; i < parsed_line.size(); i++) {
	  fstring extra_link_type = parsed_line[i];
	  pl->types.insert(extra_link_type);
	  srcnode->link_counts[extra_link_type]++;
	  dstnode->link_counts[extra_link_type]++;
328
	}
329 330 331
	if (ISSWITCH(srcnode) &&
	    ! ISSWITCH(dstnode)) {
	  dstnode->switches.insert(srcv);
332 333 334
#ifdef PER_VNODE_TT
	  dstnode->total_bandwidth += ibw;
#endif
335 336 337 338
	}
	else if (ISSWITCH(dstnode) &&
		 ! ISSWITCH(srcnode)) {
	  srcnode->switches.insert(dstv);
339 340 341
#ifdef PER_VNODE_TT
	  srcnode->total_bandwidth += ibw;
#endif
342 343 344 345 346 347 348
	} else {
          // Neither is a switch - a direct node->node link
#ifdef PER_VNODE_TT
          dstnode->total_bandwidth += ibw;
          srcnode->total_bandwidth += ibw;
#endif
        }
349
      }
350
    } else if (command == "set-type-limit") {
351 352 353
      if (parsed_line.size() != 3) {
	ptop_error("Bad set-type-limit line, requires two arguments.");
      }
354
      fstring type = parsed_line[1];
355 356 357 358 359 360 361 362 363 364 365
      int max;
      if (sscanf(parsed_line[2].c_str(),"%u",&max) != 1) {
	  ptop_error("Bad number argument: " << parsed_line[2] << ".");
      }

      // Look for a record for this ptype - create it if it doesn't exist
      if (ptypes.find(type) == ptypes.end()) {
	  ptypes[type] = new tb_ptype(type);
      }

      ptypes[type]->set_max_users(max);
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
    } else if (command == "policy") {
	if (parsed_line.size() < 3) {
	    ptop_error("No policy type given.");
	} else {
	    if (parsed_line[1] == "desire") {
		fstring desire = parsed_line[2];
		fstring type = parsed_line[3];
		tb_featuredesire *fd_obj =
		    tb_featuredesire::get_featuredesire_by_name(desire);
		if (fd_obj != NULL) {
		    if (type == "disallow") {
			fd_obj->disallow_desire();  
		    } else if (type == "limit") {
			if (parsed_line.size() != 5) {
			    ptop_error("Missing desire limit");
			} else {
			    double limit;
			    if (sscanf(parsed_line[4].c_str(),"%lf",&limit)
				    != 1) {
				ptop_error("Malformed desire limit");
			    } else {
				fd_obj->limit_desire(limit);  
			    }
			}
		    } else {
			ptop_error("Unknown policy for desire");
		    }
		} else {
		ptop_error("Only desire policies are supported."); 
	    }
	}
	}
398
    } else {
399
      ptop_error("Unknown directive: " << command << ".");
400 401
    }
  }
402 403
    
  errors += bind_ptop_subnodes(pg);
404

405
  if (errors > 0) {exit(EXIT_FATAL);}
406 407
  
  return num_nodes;
408 409
}