5353: Fix typo in _nodes_wanted(). Calculate number of nodes that can boot
authorPeter Amstutz <peter.amstutz@curoverse.com>
Wed, 18 Nov 2015 14:25:32 +0000 (09:25 -0500)
committerPeter Amstutz <peter.amstutz@curoverse.com>
Wed, 18 Nov 2015 14:25:32 +0000 (09:25 -0500)
based on price cap.  Don't add jobs to wishlist that exceed max price cap.

services/nodemanager/arvnodeman/daemon.py
services/nodemanager/arvnodeman/jobqueue.py
services/nodemanager/arvnodeman/launcher.py
services/nodemanager/tests/test_jobqueue.py

index 884567d3297f5063a9d0359ece288881159a8ee2..64bb177de56f84e75e2b01dca644db7f35d9433f 100644 (file)
@@ -272,12 +272,14 @@ class NodeManagerDaemonActor(actor_class):
                                            self._nodes_missing(size))
 
         wanted = self._size_wishlist(size) - up_count
-        if wanted > 0 and self.max_total_price and ((total_price + size.price) > self.max_total_price):
+        if wanted > 0 and self.max_total_price and ((total_price + (size.price*wanted)) > self.max_total_price):
+            can_boot = int((self.max_total_price - total_price) / size.price)
+            if can_boot == 0:
                 self._logger.info("Not booting %s (price %s) because with it would exceed max_total_price of %s (current total_price is %s)",
                                   size.name, size.price, self.max_total_price, total_price)
-                return 0
-
-        return
+            return can_boot
+        else:
+            return wanted
 
     def _nodes_excess(self, size):
         up_count = self._nodes_up(size) - self._size_shutdowns(size)
index 06f66b71c244f5662fb0c9871f7c92b0603f1617..8f78ba1df56715a1b0e394a03f1c7da553703892 100644 (file)
@@ -38,11 +38,12 @@ class ServerCalculator(object):
             return True
 
 
-    def __init__(self, server_list, max_nodes=None):
+    def __init__(self, server_list, max_nodes=None, max_price=None):
         self.cloud_sizes = [self.CloudSizeWrapper(s, **kws)
                             for s, kws in server_list]
         self.cloud_sizes.sort(key=lambda s: s.price)
         self.max_nodes = max_nodes or float('inf')
+        self.max_price = max_price or float('inf')
         self.logger = logging.getLogger('arvnodeman.jobqueue')
         self.logged_jobs = set()
 
@@ -75,7 +76,7 @@ class ServerCalculator(object):
                 if job['uuid'] not in self.logged_jobs:
                     self.logged_jobs.add(job['uuid'])
                     self.logger.debug("job %s not satisfiable", job['uuid'])
-            elif (want_count <= self.max_nodes):
+            elif (want_count <= self.max_nodes) and (want_count*cloud_size.price <= self.max_price):
                 servers.extend([cloud_size.real] * max(1, want_count))
         self.logged_jobs.intersection_update(seen_jobs)
         return servers
index 592d217583c481893f9294315c626a0f55153e0e..e8c2fe661e5203469a1ee87341159aeb6cdc1aec 100644 (file)
@@ -62,7 +62,8 @@ def build_server_calculator(config):
     if not cloud_size_list:
         abort("No valid node sizes configured")
     return ServerCalculator(cloud_size_list,
-                            config.getint('Daemon', 'max_nodes'))
+                            config.getint('Daemon', 'max_nodes'),
+                            config.getfloat('Daemon', 'max_total_price'))
 
 def launch_pollers(config, server_calculator):
     poll_time = config.getint('Daemon', 'poll_time')
index 2ddecd0973d08faabecf87287417efc1472e8762..d4dc42f13959e5bef28aad66dd3ef4d0e0abe13e 100644 (file)
@@ -48,6 +48,16 @@ class ServerCalculatorTestCase(unittest.TestCase):
                                   {'min_scratch_mb_per_node': 200})
         self.assertEqual(6, len(servlist))
 
+    def test_ignore_too_expensive_jobs(self):
+        servcalc = self.make_calculator([1, 2], max_nodes=12, max_price=6)
+        servlist = self.calculate(servcalc,
+                                  {'min_cores_per_node': 1, 'min_nodes': 6})
+        self.assertEqual(6, len(servlist))
+
+        servlist = self.calculate(servcalc,
+                                  {'min_cores_per_node': 2, 'min_nodes': 6})
+        self.assertEqual(0, len(servlist))
+
     def test_job_requesting_max_nodes_accepted(self):
         servcalc = self.make_calculator([1], max_nodes=4)
         servlist = self.calculate(servcalc, {'min_nodes': 4})