cosmetic
[tests.git] / system / TestPool.py
index 61d24d9..87e1b97 100644 (file)
@@ -1,17 +1,23 @@
 #
-# Thierry Parmentelat - INRIA Sophia Antipolis 
+# Thierry Parmentelat <thierry.parmentelat@inria.fr>
+# Copyright (C) 2010 INRIA 
+#
 #
 # pool class
 # 
 # allows to pick an available IP among a pool
 #
-# input is expressed as a list of tuples ('hostname_or_ip',user_data)
-# can be searched iteratively
+# input is expressed as a list of tuples (hostname,ip,user_data)
+# that can be searched iteratively for a free slot
+# TestPoolIP : look for a free IP address
+# TestPoolQemu : look for a test_box with no qemu running
 # e.g.
-# pool = [ (hostname1,ip1,user_data1),  (hostname2,ip2,user_data2),  
-#          (hostname3,ip3,user_data2),  (hostname4,ip4,user_data4) ]
+# pool = [ (hostname1,ip1,user_data1),  
+#          (hostname2,ip2,user_data2),  
+#          (hostname3,ip3,user_data2),  
+#          (hostname4,ip4,user_data4) ]
 # assuming that ip1 and ip3 are taken (pingable), then we'd get
-# pool=TestPool(pool)
+# pool=TestPoolIP(pool)
 # pool.next_free() -> entry2
 # pool.next_free() -> entry4
 # pool.next_free() -> None
@@ -22,45 +28,98 @@ import utils
 
 class TestPool:
 
-    def __init__ (self, pool, options):
+    def __init__ (self, pool, options,message):
         self.pool=pool
         self.options=options
         self.busy=[]
+        self.message=message
+
+    # let's be flexible
+    def match (self,triple,hostname_or_ip):
+        (h,i,u)=triple
+        return h.find(hostname_or_ip)>=0  or (i and i.find(hostname_or_ip)>=0)
 
-    def locate (self, hostname_or_ip):
+    def locate_entry (self, hostname_or_ip):
         for (h,i,u) in self.pool:
-            if h==hostname_or_ip or i==hostname_or_ip:
+            if self.match ( (h,i,u,), hostname_or_ip):
+                self.busy.append(h)
                 return (h,i,u)
+        utils.header('TestPool.locate_entry: Could not locate entry for %r in pool:'%hostname_or_ip)
         return None
 
-    def next_free (self):
-        # if preferred is provided, let's re-order
-        for (host,ip,user_data) in self.pool:
-            if host in self.busy:
+    # the hostnames provided (from a tracker) are considered last
+    def next_free (self, tracker_hostnames):
+        utils.header('TestPool is looking for a %s'%self.message)
+        # create 2 lists of (h,i,u) entries, the ones not in the tracker, and the ones in the tracker
+        in_track_pool=[]
+        out_track_pool=[]
+        for (h,i,u) in self.pool:
+            in_tracker=False
+            for hostname in tracker_hostnames:
+                if self.match ( (h,i,u,) , hostname) : in_tracker = True
+            if in_tracker: in_track_pool.append  ( (h,i,u,) )
+            else:          out_track_pool.append ( (h,i,u,) )
+        # consider outsiders first
+        for (hostname,ip,user_data) in out_track_pool + in_track_pool:
+            utils.header ('* candidate %s' % hostname)
+        for (hostname,ip,user_data) in out_track_pool + in_track_pool:
+            if hostname in self.busy:
                 continue
-            utils.header('TestPool : checking %s'%host)
-            if not TestPool.check_ping (host):
-                utils.header('%s is available'%host)
-                self.busy.append(host)
-                return (host,ip,user_data)
+            utils.header('TestPool : checking %s'%hostname)
+            if self.free_hostname(hostname):
+                utils.header('%s is available'%hostname)
+                self.busy.append(hostname)
+                return (hostname,ip,user_data)
             else:
-                self.busy.append(host)
-        return None
+                self.busy.append(hostname)
+        raise Exception, "No space left in pool (%s)"%self.message
+
+class TestPoolIP (TestPool):
+
+    def __init__ (self,pool,options):
+        TestPool.__init__(self,pool,options,"free IP address")
+
+    def free_hostname (self, hostname):
+        return not self.check_ping(hostname)
 
 # OS-dependent ping option (support for macos, for convenience)
     ping_timeout_option = None
 # checks whether a given hostname/ip responds to ping
-    @staticmethod
-    def check_ping (hostname):
-        if not TestPool.ping_timeout_option:
+    def check_ping (self,hostname):
+        if not TestPoolIP.ping_timeout_option:
             (status,osname) = commands.getstatusoutput("uname -s")
             if status != 0:
                 raise Exception, "TestPool: Cannot figure your OS name"
             if osname == "Linux":
-                TestPool.ping_timeout_option="-w"
+                TestPoolIP.ping_timeout_option="-w"
             elif osname == "Darwin":
-                TestPool.ping_timeout_option="-t"
+                TestPoolIP.ping_timeout_option="-t"
 
-        command="ping -c 1 %s 1 %s"%(TestPool.ping_timeout_option,hostname)
+        if self.options.verbose:
+            utils.header ("TestPoolIP: pinging %s"%hostname)
+        command="ping -c 1 %s 1 %s"%(TestPoolIP.ping_timeout_option,hostname)
         (status,output) = commands.getstatusoutput(command)
         return status == 0
+
+class TestPoolQemu (TestPool):
+    
+    def __init__ (self,pool,options):
+        TestPool.__init__(self,pool,options,"free qemu box")
+
+    def free_hostname (self, hostname):
+        return not self.busy_qemu(hostname)
+
+    # is there a qemu runing on that box already ?
+    def busy_qemu (self, hostname):
+        if self.options.verbose:
+            utils.header("TestPoolQemu: checking for running qemu instances on %s"%hostname)
+        command="ssh -o ConnectTimeout=5 root@%s ps -e -o cmd"%hostname
+        (status,output) = commands.getstatusoutput(command)
+        # if we fail to run that, let's assume we don't have ssh access, so
+        # we pretend the box is busy
+        if status!=0:
+            return True
+        elif output.find("qemu") >=0 :
+            return True
+        else:
+            return False