use a pool for qemu boxes as well
[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 locate_entry (self, hostname_or_ip, busy=True):
37         for (h,i,u) in self.pool:
38             if h.find(hostname_or_ip)>=0  or i.find(hostname_or_ip)>=0 :
39                 if busy:
40                     self.busy.append(h)
41                 return (h,i,u)
42         return None
43
44     def next_free (self):
45         if self.options.quiet:
46             print 'TestPool is looking for a %s'%self.message,
47         for (hostname,ip,user_data) in self.pool:
48             if hostname in self.busy:
49                 continue
50             if not self.options.quiet:
51                 utils.header('TestPool : checking %s'%hostname)
52             if self.options.quiet:
53                 print '.',
54             if self.free_hostname(hostname):
55                 if not self.options.quiet:
56                     utils.header('%s is available'%hostname)
57                 else:
58                     print ''
59                 self.busy.append(hostname)
60                 return (hostname,ip,user_data)
61             else:
62                 self.busy.append(hostname)
63         raise Exception, "No space left in pool (%s)"%self.message
64
65 class TestPoolIP (TestPool):
66
67     def __init__ (self,pool,options):
68         TestPool.__init__(self,pool,options,"free IP address")
69
70     def free_hostname (self, hostname):
71         return not TestPoolIP.check_ping(hostname)
72
73 # OS-dependent ping option (support for macos, for convenience)
74     ping_timeout_option = None
75 # checks whether a given hostname/ip responds to ping
76     @staticmethod
77     def check_ping (hostname):
78         if not TestPoolIP.ping_timeout_option:
79             (status,osname) = commands.getstatusoutput("uname -s")
80             if status != 0:
81                 raise Exception, "TestPool: Cannot figure your OS name"
82             if osname == "Linux":
83                 TestPoolIP.ping_timeout_option="-w"
84             elif osname == "Darwin":
85                 TestPoolIP.ping_timeout_option="-t"
86
87         command="ping -c 1 %s 1 %s"%(TestPoolIP.ping_timeout_option,hostname)
88         (status,output) = commands.getstatusoutput(command)
89         return status == 0
90
91 class TestPoolQemu (TestPool):
92     
93     def __init__ (self,pool,options):
94         TestPool.__init__(self,pool,options,"free qemu box")
95
96     def free_hostname (self, hostname):
97         return not TestPoolQemu.busy_qemu(hostname)
98
99     # is there a qemu runing on that box already ?
100     @staticmethod
101     def busy_qemu (hostname):
102         command="ssh -o ConnectTimeout=5 root@%s ps -e -o cmd"%hostname
103         (status,output) = commands.getstatusoutput(command)
104         # if we fail to run that, let's assume we don't have ssh access, so
105         # we pretend the box is busy
106         if status!=0:
107             return True
108         elif output.find("qemu") >=0 :
109             return True
110         else:
111             return False