Commit 732d69ce authored by Christopher Alfeld's avatar Christopher Alfeld

assign_hw is now the authoritive version of this system.

parent 7ee00ee0
CXX=/usr/gnu/bin/g++
CC=/usr/gnu/bin/gcc
LEDA=/n/paria/z/dga/LEDA-4.0
OBJS=testbed.o parse_top.o parse_ir.o phys.o parse_phys.o parse_ptop.o \
ptop_to_phys.o
LIBS+=-L${LEDA} -lD3 -lW -lP -lG -lL -L/usr/X11R6/lib -lX11 -lm -L.
LDFLAGS+= -O3 -fomit-frame-pointer -m486
CXXFLAGS = -I${LEDA}/incl
# Pick one of the two below.
CXXFLAGS += -Wall -O3 -fomit-frame-pointer -m486
# CXXFLAGS += -O0 -g
DEPLIBS=$(OBJS)
all: assign irtest testptop
assign: assign.o ${DEPLIBS}
${CXX} assign.o -o assign ${LIBS} $(OBJS) ${LDFLAGS}
assign_p: assign.po $(DEPLIBS) $(POBJS)
${CXX} assign.po -pg -g -o assign_p ${LIBS} $(POBJS)
assign.po: assign.cc
${CXX} -c -pg -g -o assign.po assign.cc ${CXXFLAGS}
clean:
/bin/rm -f *.o assign irtest
irtest: irtest.o ${DEPLIBS}
${CXX} irtest.o -o irtest ${LIBS} testbed.o parse_ir.o ${LDFLAGS}
testptop: testptop.o ${DEPLIBS}
${CXX} testptop.o -o testptop ${LIBS} testbed.o parse_ptop.o \
${LDFLAGS}
The "assign" program takes two inputs:
a) A description of the physical layout of the testbed
b) A description of the virtual network the user wishes to emulate
and maps the virtual network onto the physical network by assigning
nodes in the virtual network (vnodes) to nodes in the physical
network. It treats delay nodes specially because multiple delay
links can be handled by one computer.
Assign uses simulated annealing to determine the mapping.
The basic idea is quite simple:
Set a starting temperature for the system
Make random changes to the the system. In our case, I move a node
from one switch to another.
Compute a "score" for the system which reflects how good the solution
is.
If the score is better, then accept the change. If it is worse,
then accept it with a probability which decreases as the temperature
decreases.
The simulated annealing algorithm I use is quite simple. We perform
one "cycle" at each temperature, and then multiplicatively decrease
the temperature to 90% of its previous value. A cycle consists
of a number of random changes; this number is a function of the
number of nodes and edges in the graph, so as our graph gets
bigger, we take longer and longer in a cycle. We quit once our
temperature falls below 2.
The program uses the LEDA graph library and graph GUI. For
the testbed purposes, the GUI is unnecessary, but it's a fun
addition. More information about LEDA can be found at:
http://www.mpi-sb.mpg.de/LEDA/leda.html
LICENSING ISSUES
----------------
LEDA is commercial software. Dave has a source code license for it
which can be used by anyone at MIT, and presumably, by people at
Utah as well, since their licensing terms say it can apply as a
cite license. Please do NOT distribute the LEDA binaries I've installed.
I'm not sure yet about the resulting licensing terms on binaries we
create. I've contacted LEDA research about it and will update this
file when I hear from them.
FUTURE WORK
-----------
1) Hardwired connections between machines
This will require a big change to the scoring, because now
instead of assigning nodes to switches and doing a simple
greedy assignment from there, we'll need to assign nodes
to nodes. This will slow the program down by
a lot - probably a factor of 100.
thoughts:
Change the annealing to swap two node assignments
instead of simply moving a node to a different graph
There can, of course, be null assignments.
Scoring will need to be updated, but that should be fairly
trivial with the right data structures.
When we add this, we'll need to tweak the annealing parameters
to match the length of time we need to achieve a good solution.
2) DNARDs
If we do 1), then DNARDs follow. Simply call nodes with
dnards attached "dnard handling nodes" and then assign
a cluster of dnards and the router they connect to to a
dnard handling node.
3) Speed
See comments in assign.h. 4 seconds for the assignment is
acceptable, but once we start doing the hardwired connections
and dnards, we'll really need to boost the speed. The best
way to do this is to avoid the global score recomputation
each step by replacing it with an incremental score update,
avoiding a *lot* of work.
BLAME
-----
The simulated annealing is Dave's fault. The physical topology
and parsing stuff is Chris' fault, except where it's an ugly
bad hack, where Dave is probably to blame. :)
LEDA is LEDA's fault.
This diff is collapsed.
#!/usr/local/bin/tclsh
######################################################################
# gentestbed.tcl
#
# gentestbed.tcl <switches> <nodesperswitch> <linkspernode> <internode> <interswitch>
######################################################################
if {[llength $argv] != 5} {
puts "Syntax: gentestbed.tcl <switches> <nodesperswitch> <linkspernode> <internode> <interswitch>"
exit 1
}
set i 0
foreach a {switches nodes links inode iswitch} {
upvar 0 $a v
set v [lindex $argv $i]
incr i
}
for {set s 0} {$s < $switches} {incr s} {
puts "node switch_$s switch"
for {set n 0} {$n < $nodes} {incr n} {
puts "node node_${s}_$n pc"
puts "link node_${s}_$n switch_$s $inode $links"
}
}
for {set s 0} {$s < $switches} {incr s} {
for {set s2 $s} {$s2 < $switches} {incr s2} {
if {$s != $s2} {
puts "link switch_${s} switch_${s2} 1 $iswitch"
}
}
}
#include <iostream.h>
#include <fstream.h>
#include <LEDA/graph_alg.h>
#include <LEDA/graphwin.h>
#include <LEDA/dictionary.h>
#include <LEDA/map.h>
#include <LEDA/graph_iterator.h>
#include "testbed.h"
int main(int argc, char **argv)
{
tbgraph G(1,1);
ifstream infile;
if (argc < 2) {
cerr << "Need to supply a filename, buckeroo" << endl;
exit(-1);
}
infile.open(argv[1]);
parse_ir(G, infile);
cout << "Done\n";
exit(0);
}
/*
* Parse the IR format into a graph type structure.
*
* For ease of use, I'm going to assume the following
* capabilities with the graph:
*
* Graph G;
* node n;
* edge e;
*
* n = G.new_node();
* n.set_name(char *name);
*/
#include <iostream.h>
#include <string.h>
#include <LEDA/graph_alg.h>
#include <LEDA/graphwin.h>
#include <LEDA/dictionary.h>
#include <LEDA/map.h>
#include <LEDA/graph_iterator.h>
#include "testbed.h"
void parse_ir(tbgraph& G, istream& i)
{
char buf[256];
char *hash;
while (!i.eof())
{
i.getline(buf, sizeof(buf));
hash = strchr(buf, '#');
if (hash) *hash = 0;
if (strlen(buf) < 1) { continue; }
printf("Got line: %s\n", buf);
}
}
/*
* Input a simple topology.
*/
#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/*
* Simple topology description:
*
* topology :: 1 or more lines
* line :: <comment> | <switch>
* comment :: # followed by anything
* switch :: <switchname> <nodecount> <nodelist>
*
* nodelist :: <interfaces> <count> <nodelist>
*
* switchname :: [a-z0-9_]+
* interfaces :: number
* count :: number
*
* Example:
*
* # A switch with 6 computers with 4 interfaces, and 2 computers with 8
* # interfaces
* switch1 2 4 6 8 2
* # A switch with 8 computers with 4 interfaces
* switch2 1 4 8
*
*/
#define MAXSW 32
#include "testbed.h"
#include "phys.h"
topology *parse_phys(char *filename)
{
char name[64];
char linebuf[256];
ifstream infile(filename);
topology *topo;
if (!infile.good()) { return NULL; }
topo = new topology(MAXSW); // XXX
topo->switchcount = 0;
for (int i = 0; i < MAXSW; i++)
topo->switches[i] = NULL;
while (!infile.eof()) {
infile >> name;
if (infile.eof() || !infile.good()) { break; }
if (name[0] == '#') {
infile.getline(linebuf, 255);
} else if (name[0] == 0) {
continue;
} else {
int nodecount;
infile >> nodecount;
tbswitch *newsw;
newsw = new tbswitch(nodecount, name);
topo->switches[topo->switchcount++] = newsw;
#ifdef VERBOSE
printf("Creating switch %d : %s\n",
topo->switchcount-1, name);
#endif
if (!newsw) {
perror("Could not allocate a tbswitch");
exit(-1);
}
for (int i = 0; i < nodecount;) {
int ints, count;
infile >> ints;
infile >> count;
for (int j = 0; j < count; j++) {
newsw->nodes[i].ints = ints;
newsw->nodes[i].used = 0;
i++;
}
}
}
}
return topo;
}
/*
* parse ptop files. These are basic topologies
* that are used to represent the physical topology.
*
* format:
* node <name> <type>
* <type> = pc | switch | dnard
* link <src> <dst> <size> <number>
*/
#include <iostream.h>
#include <string.h>
#include <stdio.h>
#include <LEDA/graph_alg.h>
#include <LEDA/graphwin.h>
#include <LEDA/dictionary.h>
#include <LEDA/map.h>
#include <LEDA/graph_iterator.h>
#include "testbed.h"
int ptop_type(char *type)
{
if (strcmp(type,"pc") == 0) return testnode::TYPE_PC;
if (strcmp(type,"switch") == 0) return testnode::TYPE_SWITCH;
if (strcmp(type,"dnard") == 0) return testnode::TYPE_DNARD;
return -1;
}
void parse_ptop(tbgraph &G, istream& i)
{
dictionary<string, node> nmap;
node no1;
edge ed1;
string s1, s2;
char inbuf[255];
char n1[32], n2[32];
char type[32];
int itype;
int size, num;
while (!i.eof()) {
char *ret;
i.getline(inbuf, 254);
ret = strchr(inbuf, '\n');
if (ret) *ret = 0;
if (strlen(inbuf) == 0) { continue; }
if (!strncmp(inbuf, "node", 4)) {
if (sscanf(inbuf, "node %s %s", n1, type) != 2) {
fprintf(stderr, "bad node line: %s\n", inbuf);
} else {
string s1(n1);
itype=ptop_type(type);
if (itype == -1) {
fprintf(stderr, "bad node type for node %s: %s\n",n1,type);
} else {
no1 = G.new_node();
G[no1].name(n1);
G[no1].type(itype);
nmap.insert(s1, no1);
}
}
}
else if (!strncmp(inbuf, "link", 4)) {
if (sscanf(inbuf, "link %s %s %d %d", n1, n2, &size, &num)
!= 4) {
fprintf(stderr, "bad link line: %s\n", inbuf);
} else {
string s1(n1);
string s2(n2);
node node1 = nmap.access(s1);
node node2 = nmap.access(s2);
ed1=G.new_edge(node1, node2);
G[ed1].capacity(size);
G[ed1].number(num);
}
}
else {
fprintf(stderr, "unknown directive: %s\n", inbuf);
}
}
}
/*
* Parse chris' ".top" file format into a LEDA graph
*/
#include <iostream.h>
#include <string.h>
#include <stdio.h>
#include <LEDA/graph_alg.h>
#include <LEDA/graphwin.h>
#include <LEDA/dictionary.h>
#include <LEDA/map.h>
#include <LEDA/graph_iterator.h>
#include "testbed.h"
void parse_top(tbgraph &G, istream& i)
{
dictionary<string, node> nmap;
node no1;
string s1, s2;
char inbuf[255];
char n1[32], n2[32], type[32];
while (!i.eof()) {
char *ret;
i.getline(inbuf, 254);
ret = strchr(inbuf, '\n');
if (ret) *ret = 0;
if (strlen(inbuf) == 0) { continue; }
if (!strncmp(inbuf, "node", 4)) {
if (sscanf(inbuf, "node %s %s", n1, type) < 1) {
fprintf(stderr, "bad node line: %s\n", inbuf);
} else {
string s1(n1);
no1 = G.new_node();
G[no1].name(n1);
nmap.insert(s1, no1);
if (!strcmp(type, "delay")) {
G[no1].type(testnode::TYPE_DELAY);
}
else if (!strcmp(type, "pc")) {
G[no1].type(testnode::TYPE_PC);
}
else if (!strcmp(type, "switch")) {
G[no1].type(testnode::TYPE_SWITCH);
}
else if (!strcmp(type, "dnard")) {
G[no1].type(testnode::TYPE_DNARD);
}
else {
G[no1].type(testnode::TYPE_UNSPECIFIED);
}
}
}
else if (!strncmp(inbuf, "link", 4)) {
if (sscanf(inbuf, "link %s %s", n1, n2) != 2) {
fprintf(stderr, "bad link line: %s\n", inbuf);
} else {
string s1(n1);
string s2(n2);
node node1 = nmap.access(s1);
node node2 = nmap.access(s2);
G.new_edge(node1, node2);
}
}
else {
fprintf(stderr, "unknown directive: %s\n", inbuf);
}
}
}
# Two switches, configured identically - 8 nodes with 4 interfaces
switch1 1 4 8
switch2 1 4 8
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#include "testbed.h"
#include "phys.h"
toponode::toponode() {
ints = used = 0;
}
tbswitch::tbswitch() {
nodes = NULL;
nodecount = 0;
}
tbswitch::tbswitch(int ncount, char *newname) {
nodecount = ncount;
nodes = new toponode[ncount];
name = strdup(newname);
}
void tbswitch::setsize(int ncount) {
if (nodes) { delete(nodes); }
nodecount = ncount;
nodes = new toponode[ncount];
}
tbswitch::~tbswitch() {
if (nodes) { delete(nodes); }
nodecount = 0;
}
inline int tbswitch::numnodes() {
return nodecount;
}
topology::topology(int nswitches) {
switchcount = nswitches;
switches = new tbswitch *[switchcount];
}
topology::~topology() {
if (switches) delete(switches);
}
void topology::print_topo() {
cout << "Topology has " << switchcount << " switches" << endl;
cout << "--------" << endl;
for (int i = 0; i < switchcount; i++) {
cout << " sw " << i << " has " << switches[i]->numnodes()
<< " nodes and bw " << switches[i]->bw << endl;
for (int j = 0; j < switches[i]->nodecount; j++) {
cout << switches[i]->nodes[j].ints <<
"/" << switches[i]->nodes[j].int_bw << " ";
}
cout << endl;
}
}
/*
* A simple representation of the physical topology.
*
* Nodes are simply represented by a number of interfaces.
*
* The assumption is that all of those interfaces are connected
* to the same switch.
*
* Note that nodes MUST be stored in their list in order of increasing
* number of interfaces. A good 10-minute project would be to
* create an "addnode" method which does a sorted insert in the
* list, or a sort function.
*
* This sorting is required by the assignment routine.
*/
#include <LEDA/graph_alg.h>
class toponode {
public:
toponode();
int ints; /* Number of ethernet interfaces */
int int_bw; /* Bandwidth used by each interface */
int used; /* Used by assign. Have we assigned here? */
node n;
};
class tbswitch {
public:
tbswitch();
virtual ~tbswitch();
tbswitch(int ncount, char *newname); // Specify number of toponodes
void setsize(int ncount);
inline int numnodes();
int nodecount; /* Total number of nodes in the switch */
toponode *nodes; /* Sorted list of the nodes */
char *name; /* Anything you want */
int bw; /* Bandwidth between switch and center */
};
class topology {
public:
topology(int nswitches);
void print_topo();
virtual ~topology();
int switchcount; // Number of switches in the topology
tbswitch **switches; // Unordered list of the switches
};
/*
* Parse a simple physical topology description into a topology structure.
*
* Eventually, this will be replaced by parsing Chris' ptop format
* into the topology structure
*/
topology *parse_phys(char *filename);
topology *ptop_to_phys(tbgraph &G);
// This converts a ptop graph to a phys topology description.
// There are several requirements on the ptop graph for this.
// 1. Nodes must be connected to switches, not to other nodes.
// 2. Switches must be connected a switch node called CENTER.
// XXX : Needs lots of error checking!
#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAXSW 32
#define MAXNODES 32
#include "testbed.h"
#include "phys.h"