allocate from pool : entries in tracker are considered last
[tests.git] / system / TestPool.py
1 #
2 # Thierry Parmentelat - INRIA Sophia Antipolis 
3 #
4 # pool class
5
6 # allows to pick an available IP among a pool
7 #
8 # input is expressed as a list of tuples (hostname,ip,user_data)
9 # that can be searched iteratively for a free slot
10 # TestPoolIP : look for a free IP address
11 # TestPoolQemu : look for a test_box with no qemu running
12 # e.g.
13 # pool = [ (hostname1,ip1,user_data1),  
14 #          (hostname2,ip2,user_data2),  
15 #          (hostname3,ip3,user_data2),  
16 #          (hostname4,ip4,user_data4) ]
17 # assuming that ip1 and ip3 are taken (pingable), then we'd get
18 # pool=TestPoolIP(pool)
19 # pool.next_free() -> entry2
20 # pool.next_free() -> entry4
21 # pool.next_free() -> None
22 # that is, even if ip2 is not busy/pingable when the second next_free() is issued
23
24 import commands
25 import utils
26
27 class TestPool:
28
29     def __init__ (self, pool, options,message):
30         self.pool=pool
31         self.options=options
32         self.busy=[]
33         self.message=message
34
35     # let's be flexible
36     def match (self,triple,hostname_or_ip):
37         (h,i,u)=triple
38         return h.find(hostname_or_ip)>=0  or (i and i.find(hostname_or_ip)>=0)
39
40     def locate_entry (self, hostname_or_ip):
41         for (h,i,u) in self.pool:
42             if self.match ( (h,i,u,), hostname_or_ip):
43                 self.busy.append(h)
44                 return (h,i,u)
45         utils.header('TestPool.locate_entry: Could not locate entry for %r in pool:'%hostname_or_ip)
46         return None
47
48     # the hostnames provided (from a tracker) are considered last
49     def next_free (self, tracker_hostnames):
50         if self.options.quiet:
51             print 'TestPool is looking for a %s'%self.message,
52         # create 2 lists of (h,i,u) entries, the ones not in the tracker, and the ones in the tracker
53         in_track_pool=[]
54         out_track_pool=[]
55         for (h,i,u) in self.pool:
56             in_tracker=False
57             for hostname in tracker_hostnames:
58                 if self.match ( (h,i,u,) , hostname) : in_tracker = True
59             if in_tracker: in_track_pool.append  ( (h,i,u,) )
60             else:          out_track_pool.append ( (h,i,u,) )
61         # consider outsiders first
62         for (hostname,ip,user_data) in out_track_pool + in_track_pool:
63             utils.header ('* candidate %s' % hostname)
64         for (hostname,ip,user_data) in out_track_pool + in_track_pool:
65             if hostname in self.busy:
66                 continue
67             if not self.options.quiet:
68                 utils.header('TestPool : checking %s'%hostname)
69             else:
70                 print '.',
71             if self.free_hostname(hostname):
72                 if not self.options.quiet:
73                     utils.header('%s is available'%hostname)
74                 else:
75                     print ''
76                 self.busy.append(hostname)
77                 return (hostname,ip,user_data)
78             else:
79                 self.busy.append(hostname)
80         raise Exception, "No space left in pool (%s)"%self.message
81
82 class TestPoolIP (TestPool):
83
84     def __init__ (self,pool,options):
85         TestPool.__init__(self,pool,options,"free IP address")
86
87     def free_hostname (self, hostname):
88         return not self.check_ping(hostname)
89
90 # OS-dependent ping option (support for macos, for convenience)
91     ping_timeout_option = None
92 # checks whether a given hostname/ip responds to ping
93     def check_ping (self,hostname):
94         if not TestPoolIP.ping_timeout_option:
95             (status,osname) = commands.getstatusoutput("uname -s")
96             if status != 0:
97                 raise Exception, "TestPool: Cannot figure your OS name"
98             if osname == "Linux":
99                 TestPoolIP.ping_timeout_option="-w"
100             elif osname == "Darwin":
101                 TestPoolIP.ping_timeout_option="-t"
102
103         if self.options.verbose:
104             utils.header ("TestPoolIP: pinging %s"%hostname)
105         command="ping -c 1 %s 1 %s"%(TestPoolIP.ping_timeout_option,hostname)
106         (status,output) = commands.getstatusoutput(command)
107         return status == 0
108
109 class TestPoolQemu (TestPool):
110     
111     def __init__ (self,pool,options):
112         TestPool.__init__(self,pool,options,"free qemu box")
113
114     def free_hostname (self, hostname):
115         return not self.busy_qemu(hostname)
116
117     # is there a qemu runing on that box already ?
118     def busy_qemu (self, hostname):
119         if self.options.verbose:
120             utils.header("TestPoolQemu: checking for running qemu instances on %s"%hostname)
121         command="ssh -o ConnectTimeout=5 root@%s ps -e -o cmd"%hostname
122         (status,output) = commands.getstatusoutput(command)
123         # if we fail to run that, let's assume we don't have ssh access, so
124         # we pretend the box is busy
125         if status!=0:
126             return True
127         elif output.find("qemu") >=0 :
128             return True
129         else:
130             return False