Make PlanetLab select lightly loaded nodes when given the chance (ie, when more candi...
authorClaudio-Daniel Freire <claudio-daniel.freire@inria.fr>
Wed, 14 Sep 2011 04:57:25 +0000 (06:57 +0200)
committerClaudio-Daniel Freire <claudio-daniel.freire@inria.fr>
Wed, 14 Sep 2011 04:57:25 +0000 (06:57 +0200)
src/nepi/testbeds/planetlab/execute.py
src/nepi/testbeds/planetlab/node.py
src/nepi/testbeds/planetlab/resourcealloc.py

index 27ed23d..dcb5023 100644 (file)
@@ -256,9 +256,18 @@ class TestbedController(testbed_impl.TestbedController):
         if nodes and reqs:
             if recover:
                 raise RuntimeError, "Impossible to recover: unassigned host for Nodes %r" % (nodes,)
+
+            def pickbest(fullset, nreq, node=nodes[0]):
+                if len(fullset) > nreq:
+                    fullset = zip(node.rate_nodes(fullset),fullset)
+                    fullset.sort(reverse=True)
+                    del fullset[nreq:]
+                    return set(map(operator.itemgetter(1),fullset))
+                else:
+                    return fullset
             
             try:
-                solution = resourcealloc.alloc(reqs)
+                solution = resourcealloc.alloc(reqs, sample=pickbest)
             except resourcealloc.ResourceAllocationError:
                 # Failed, try again with all nodes
                 reqs = []
@@ -267,7 +276,7 @@ class TestbedController(testbed_impl.TestbedController):
                     candidates -= reserved
                     reqs.append(candidates)
                 
-                solution = resourcealloc.alloc(reqs)
+                solution = resourcealloc.alloc(reqs, sample=pickbest)
                 to_provision.update(solution)
             
             # Do assign nodes
index 5e1445e..b9a7d09 100644 (file)
@@ -64,7 +64,15 @@ class Node(object):
         'maxLoad' : ('load%(timeframe)s', '[value'),
         'minCpu' : ('cpu%(timeframe)s', ']value'),
         'maxCpu' : ('cpu%(timeframe)s', '[value'),
-    }    
+    }
+    
+    RATE_FACTORS = (
+        # (<tag name>, <weight>, <default>)
+        ('bw%(timeframe)s', -0.001, 1024.0),
+        ('cpu%(timeframe)s', 0.1, 40.0),
+        ('load%(timeframe)s', -0.2, 3.0),
+        ('reliability%(timeframe)s', 1, 100.0),
+    )
     
     DEPENDS_PIDFILE = '/tmp/nepi-depends.pid'
     DEPENDS_LOGFILE = '/tmp/nepi-depends.log'
@@ -316,6 +324,28 @@ class Node(object):
         self._node_id = None
         self.__dict__.update(self.__orig_attrs)
     
+    def rate_nodes(self, nodes):
+        rates = collections.defaultdict(int)
+        tags = collections.defaultdict(dict)
+        replacements = {'timeframe':self.timeframe}
+        tagnames = [ tagname % replacements 
+                     for tagname, weight, default in self.RATE_FACTORS ]
+        
+        taginfo = self._api.GetNodeTags(
+            node_id=list(nodes), 
+            tagname=tagnames,
+            fields=('node_id','tagname','value'))
+        
+        for node, tagname, value in taginfo:
+            tags[tagname][int(node)] = float(value)
+        
+        for tagname, weight, default in self.RATE_FACTORS:
+            taginfo = tags[tagname].get
+            for node in nodes:
+                rates[node] += weight * taginfo(node,default)
+        
+        return map(rates.__getitem__, nodes)
+            
     def fetch_node_info(self):
         orig_attrs = {}
         
index bafde65..a5385c3 100644 (file)
@@ -32,7 +32,7 @@ def _log(logstream, message, *args, **kwargs):
             logstream.write(message)
         logstream.write('\n')
 
-def alloc(requests, logstream = None, nonseparable = False, saveinteresting = None, backtracklim = 100000000, verbose = True):
+def alloc(requests, logstream = None, nonseparable = False, saveinteresting = None, backtracklim = 100000000, verbose = True, sample = random.sample):
     """
     Takes an iterable over requests, which are iterables of candidate node ids,
     and returns a specific node id for each request (if successful).
@@ -267,7 +267,7 @@ def alloc(requests, logstream = None, nonseparable = False, saveinteresting = No
                 part = Pavail[i]
                 if len(part) < nreq:
                     raise AssertionError, "Cannot allocate resources for supposedly valid solution!"
-                assigned = set(random.sample(part, nreq))
+                assigned = set(sample(part, nreq))
                 psol |= assigned
                 part -= assigned
             solution[c] = psol