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