emulab/theory.py
changeset 0 2b3e5ec03512
equal deleted inserted replaced
-1:000000000000 0:2b3e5ec03512
       
     1 #!/usr/bin/python
       
     2 
       
     3 import sys
       
     4 from heapq import *
       
     5 from optparse import OptionParser
       
     6 
       
     7 def dprint(msg):
       
     8     print "[%d]: %s" % (time, msg)
       
     9     
       
    10 arglist = ("count", "size", "num_hops", "bw", "hop_mode", "conn", "uptime", "downtime")
       
    11 
       
    12 usage = "usage: %prog [options]"
       
    13 parser = OptionParser(usage)
       
    14 parser.add_option('--size', type='int', help='size of each message')
       
    15 parser.add_option('--count', type='int', help='number of messages')
       
    16 parser.add_option('--num_hops', type='int', help='number of hops')
       
    17 parser.add_option('--bw', type='int', help='bandwidth')
       
    18 parser.add_option('--hop_mode', help='hop mode (hop or e2e)')
       
    19 parser.add_option('--conn', help='connectivity mode')
       
    20 parser.add_option('--uptime', type='int', help='uptime in seconds')
       
    21 parser.add_option('--downtime', type='int', help='downtime in seconds')
       
    22 
       
    23 parser.set_defaults(num_hops=5)
       
    24 parser.set_defaults(uptime=60)
       
    25 parser.set_defaults(downtime=240)
       
    26 
       
    27 (opts, args) = parser.parse_args()
       
    28 
       
    29 def die():
       
    30     parser.print_help()
       
    31     sys.exit(0)
       
    32 
       
    33 if opts.count    == None or \
       
    34    opts.size     == None or \
       
    35    opts.num_hops == None or \
       
    36    opts.bw       == None or \
       
    37    opts.hop_mode == None or \
       
    38    opts.conn     == None or \
       
    39    opts.uptime   == None or \
       
    40    opts.downtime == None: die()
       
    41 
       
    42 count    = opts.count
       
    43 size     = opts.size
       
    44 num_hops = opts.num_hops
       
    45 bw       = opts.bw
       
    46 uptime   = opts.uptime
       
    47 downtime = opts.downtime
       
    48 hop_mode = opts.hop_mode
       
    49 conn     = opts.conn
       
    50 
       
    51 last = num_hops - 1
       
    52 total_size = count * size * 8
       
    53 
       
    54 amount = map(lambda x: 0, range(0, num_hops))
       
    55 links  = map(lambda x: True, range(0, num_hops))
       
    56 amount[0] = total_size
       
    57 q = []
       
    58 
       
    59 class SimDoneEvent:
       
    60     def __init__(self, time):
       
    61         self.time = time
       
    62 
       
    63     def run(self):
       
    64         print "maximum simulation time (%d) reached... ending simulation" % self.time
       
    65         sys.exit(1)
       
    66         
       
    67 class LinkEvent:
       
    68     def __init__(self, time, link, mode):
       
    69         self.time = time
       
    70         self.link = link
       
    71         self.mode = mode
       
    72         
       
    73     def __cmp__(self, other):
       
    74         return self.time.__cmp__(other.time)
       
    75 
       
    76     def __str__(self):
       
    77         return "Event @%d: link %d %s" % (self.time, self.link, self.mode)
       
    78 
       
    79     def run(self):
       
    80         if self.mode == 'up':
       
    81             dprint('opening link %d' % self.link)
       
    82             links[self.link] = True
       
    83             self.time += uptime
       
    84             self.mode = 'down'
       
    85         else:
       
    86             dprint('closing link %d' % self.link)
       
    87             links[self.link] = False
       
    88             self.time += downtime
       
    89             self.mode = 'up'
       
    90 
       
    91         queue_event(self)
       
    92 
       
    93 class CompletedEvent:
       
    94     def __init__(self, time, node):
       
    95         self.time = time
       
    96         self.node = node
       
    97 
       
    98     def run(self):
       
    99         pass
       
   100 
       
   101 def queue_event(e):
       
   102     global q
       
   103 #    dprint('queuing event %s' % e)
       
   104     heappush(q, e)
       
   105 
       
   106 # simulator completion event
       
   107 time = 0
       
   108 queue_event(SimDoneEvent(60*30))
       
   109 
       
   110 # initial link events
       
   111 if (conn == 'conn'):
       
   112     pass
       
   113 
       
   114 elif (conn == 'all2'):
       
   115     for i in range(1, num_hops):
       
   116         queue_event(LinkEvent(uptime, i, 'down'))
       
   117 
       
   118 elif (conn == 'sequential'):
       
   119     queue_event(LinkEvent(uptime, 1, 'down'))
       
   120     for i in range(2, num_hops):
       
   121         links[i] = False
       
   122         queue_event(LinkEvent((i-1) * 60, i, 'up'))
       
   123 
       
   124 elif (conn == 'offset2'):
       
   125     for i in range (1, num_hops):
       
   126         if i % 2 == 0:
       
   127             links[i] = False
       
   128             queue_event(LinkEvent(120, i, 'up'))
       
   129         else:
       
   130             queue_event(LinkEvent(uptime, i, 'down'))
       
   131 
       
   132 elif (conn == 'shift10'):
       
   133     if num_hops * 10 > 60:
       
   134         raise(ValueError("can't handle more than 6 hops"))
       
   135 
       
   136     queue_event(LinkEvent(uptime, 1, 'down'))
       
   137     for i in range (2, num_hops):
       
   138         links[i] = False
       
   139         queue_event(LinkEvent(10 * (i-1), i, 'up'))
       
   140 
       
   141 else:
       
   142     raise(ValueError("conn mode %s not defined" % conn))
       
   143 
       
   144 print 'initial link states:'
       
   145 for i in range(0, num_hops):
       
   146     print '\t%d: %s' % (i, links[i])
       
   147 
       
   148 
       
   149 def can_move(i):
       
   150     if hop_mode == 'hop':
       
   151         dest = i+1
       
   152         hops = (i+1,)
       
   153     else:
       
   154         dest = last
       
   155         hops = range(i+1, last+1)
       
   156         
       
   157     for j in hops:
       
   158         if links[j] != True:
       
   159 #            dprint("can't move data from %d to %d since link %d closed" % (i, dest, j))
       
   160             return False
       
   161     return True
       
   162     
       
   163 # proc to shuffle a given amount of data through the network
       
   164 def move_data(interval):
       
   165     dprint('%d seconds elapsed... trying to move data' % interval)
       
   166     
       
   167     for i in range(0, last):
       
   168         if not can_move(i):
       
   169             continue
       
   170 
       
   171         if hop_mode == 'hop':
       
   172             dest = i+1
       
   173         else:
       
   174             dest = last
       
   175         
       
   176         amt = min(amount[i], interval * bw)
       
   177         
       
   178         if (amt != 0):
       
   179             dprint('moving %d/%d bits (%d msgs) from %d to %d' %
       
   180                    (amt, amount[i], amt / (size*8), i, dest))
       
   181             amount[i]    -= amt
       
   182             amount[dest] += amt
       
   183 
       
   184         if dest == last and amount[dest] == total_size:
       
   185             print "all data transferred..."
       
   186             print "ELAPSED %d" % (time + interval)
       
   187             sys.exit(0)
       
   188 
       
   189 def blocked():
       
   190     for i in range(0, last):
       
   191         if can_move(i):
       
   192             return False
       
   193     return True
       
   194 
       
   195 def completion_time():
       
   196     # if nothing can move, then we have infinite completion time
       
   197     if blocked():
       
   198         return 9999999999.0
       
   199     
       
   200     return float(sum(amount[:-1])) / float(bw)
       
   201     
       
   202 while True:
       
   203     try:
       
   204         next_event = heappop(q)
       
   205     except:
       
   206         raise RuntimeError('no events in queue but not complete')
       
   207 
       
   208     tcomplete = completion_time()
       
   209     elapsed = next_event.time - time
       
   210     if (tcomplete < elapsed):
       
   211         dprint('trying to move last chunk')
       
   212         move_data(tcomplete)
       
   213 
       
   214     time = next_event.time
       
   215     if (elapsed != 0 and not blocked()):
       
   216         move_data(elapsed)
       
   217 
       
   218     next_event.run()
       
   219