minor updates
[tests.git] / qaapi / qa / tests / boot_node.py
1 #!/usr/bin/python
2
3 import os
4 import sys
5 import signal
6 import time
7 import tempfile
8 import select
9 import base64
10 import traceback
11 from Test import Test
12 from qa import utils
13 from qa.Nodes import Node, Nodes
14
15 image_types = ['node-iso', 'node-usb', 'generic-iso', 'generic-usb']
16
17 def killqemu(pid):
18     try:
19         os.kill(pid, signal.SIGKILL)
20         os.waitpid(pid,os.WNOHANG)
21     except:
22         pass
23
24 class boot_node(Test):
25     """
26     Attempts to boot the specified node using qemu. 
27     """
28
29     def state_nanny(self):
30         hostname = self.hostname
31         nodes = self.config.api.GetNodes(self.config.auth, [hostname], ['boot_state'])
32         node = nodes[0]
33         boot_state = node['boot_state']
34         if True or self.config.verbose:
35             utils.header("%(hostname)s boot_state is %(boot_state)s" % locals(), False, self.config.logfile) 
36             
37         if boot_state in ['boot']:
38             self.exit = True
39
40         if self.config.verbose:
41             if boot_state in ['boot']:
42                 utils.header("%(hostname)s correctly installed and booted" % locals(), False, self.config.logfile)
43             else:
44                 utils.header("%(hostname)s not fully booted" % locals(), False, self.config.logfile)
45         self.boot_state = boot_state
46         return self.exit
47         
48     def console_nanny(self,console_states):
49         #ready = select.select([],[self.stdout],[],1)[1]
50         output = 0
51         if True or len(ready)>0:
52             output = 1
53             retry = True
54             while retry:
55                 try:
56                     lines = self.stdout.readlines()
57                     retry = False
58                 except IOError, e:
59                     pass
60
61             #for searchstring in console_states.keys():
62             #    result = line.find(searchstring)
63                 # if result... ret = console_states[searchstring]
64             #    break
65                 # check result for whether we found it
66
67             # for now just print it out
68             for line in lines:
69                 print line
70                 utils.header(line, logfile = self.config.logfile) 
71             # should be parsing for special strings that indicate whether
72             # we've reached a particular state within the boot sequence
73
74         return output
75
76     def call(self, plc_name, hostname, image_type = 'node-iso', disk_size="17G", wait = 30):
77
78         
79         # Get this nodes configuration 
80         node = self.config.get_node(hostname)
81         node.rotate_logfile()
82
83         # Which plc does this node talk to 
84         plc = self.config.get_plc(plc_name)
85         api = plc.config.api
86         auth = plc.config.auth
87         path = node.get_path()
88         host = node['host']
89         homedir = node['homedir']
90         tmpdir = '/usr/tmp/'
91         bootimage_filename = "%(hostname)s-bootcd.iso" % locals()
92         diskimage_filename = "%(hostname)s-hda.img" % locals() 
93         bootimage = "%(homedir)s/%(bootimage_filename)s" % locals()
94         diskimage = "%(homedir)s/%(diskimage_filename)s" % locals()
95         print bootimage
96         print diskimage 
97         diskimage_path = "/%(path)s/%(diskimage)s" % locals() 
98         bootimage_tmppath = "%(tmpdir)s/%(bootimage_filename)s" % locals()
99         bootimage_path = "/%(path)s/%(bootimage)s" % locals()
100         remote_bootimage_path = "%(homedir)s/%(bootimage_filename)s" % locals() 
101         print bootimage_path
102         print diskimage_path
103         print remote_bootimage_path 
104         # wait up to 30 minutes for a node to boot and install itself correctly
105         self.hostname = hostname
106         self.totaltime = 60*60*wait
107
108         # validate hostname
109         nodes = api.GetNodes(auth, [hostname], ['hostname', 'boot_state'])
110         if not nodes:
111             raise Exception, "%s not found at plc  %s" % (hostname, plc['name'])
112         node.update(nodes[0])
113
114         # try reinstalling the node if it is in debug state
115         if node['boot_state'] in ['dbg']:
116             if self.config.verbose:
117                 utils.header("%(hostname)s is in debug state. Attempting a re-install" % locals(), logfile = self.config.logfile)
118             api.UpdateNode(auth, node['hostname'], {'boot_state': 'rins'}) 
119         
120         # Create boot image
121         if self.config.verbose:
122             utils.header("Creating bootcd for %(hostname)s at %(bootimage_path)s" % locals(), logfile = self.config.logfile)    
123         nodeimage = api.GetBootMedium(auth, hostname, image_type, '', ['serial'])
124         fp = open(bootimage_tmppath, 'w')
125         fp.write(base64.b64decode(nodeimage))
126         fp.close()
127
128         # Move the boot image to the nodes home directory
129         node.host_commands("mkdir -p %(homedir)s" % locals())
130         node.scp_to_host(bootimage_tmppath, "%(remote_bootimage_path)s" % locals())
131
132         # If node is vm (qemu) try installing kqemu
133         #node.host_commands("yum -y install kqemu", False)
134         #node.host_commands("modprobe kqemu", False)
135         
136         # Create a temporary disk image if it doesnt already exist or we are reinstalling
137         img_check_cmd =  "ls -ld %(diskimage)s" % locals()
138         (status, output) = node.host_commands(img_check_cmd, False)
139         if status != 0 or node['boot_state'] in ['rins', 'inst']:
140             qemu_img_cmd = "/usr/bin/qemu-img create -f qcow2 %(diskimage)s %(disk_size)s" % locals()
141             node.host_commands(qemu_img_cmd)
142
143         
144         if self.config.verbose:
145             utils.header("Booting %(hostname)s" % locals(), logfile = self.config.logfile)
146
147         # Attempt to boot this node image
148
149         # generate a temp filename to which qemu should store its pid (crappy approach)
150         tmp = tempfile.mkstemp(".pid","qemu_")
151         pidfile=tmp[1]
152         os.unlink(pidfile)
153         
154         os.close(tmp[0])
155         # boot node with ramsize memory
156         ramsize=1024
157
158         # always use the 64 bit version of qemu, as this will work on both 32 & 64 bit host kernels
159         bootcmd = "/usr/bin/qemu-system-x86_64" 
160         # tell qemu to store its pid ina  file
161         bootcmd = bootcmd + " -pidfile %(pidfile)s " % locals()
162         # boot with ramsize memory
163         bootcmd = bootcmd + " -m %(ramsize)s" % locals()
164         # uniprocessor only
165         bootcmd = bootcmd + " -smp 1"
166         # redirect incomming tcp connections on specified port to guest node
167         if 'redir_ssh_port' in node and node['redir_ssh_port']:
168             port = node['redir_ssh_port']
169             ip = node['nodenetworks'][0]['ip']
170             bootcmd = bootcmd + " -redir tcp:%(port)s:%(ip)s:22" % locals() 
171         # no graphics support -> assume we are booting via serial console
172         bootcmd = bootcmd + " -nographic"
173         # boot from the supplied cdrom iso file
174         bootcmd = bootcmd + " -boot d"
175         bootcmd = bootcmd + " -cdrom %(bootimage)s" % locals()
176         # hard disk image to use for the node
177         bootcmd = bootcmd + " %(diskimage)s" % locals()
178         # redirect stdout, stderr to logfile
179         bootcmd = bootcmd + " 2>&1 >> %s " % (node.logfile.filename)
180         
181         # kill any old qemu processes for this node
182         pid_cmd = "ps -elfy | grep qemu | grep -v grep | grep %(hostname)s | awk '{print$3}'" % locals()
183         (status, output) = node.host_commands(pid_cmd)
184         pids = " ".join(output.split("\n")).strip() 
185         if pids:
186             kill_cmd = "kill %(pids)s" % locals()  
187             (status, output) = node.host_commands(kill_cmd)
188         
189         time.sleep(2)
190         
191         # launch qemu
192         (self.stdin, self.stdout, self.stderr) = node.host_popen3(bootcmd)
193         
194         # wait for qemu to start up
195         time.sleep(5)
196         # get qemu's pid from its pidfile (crappy approach)
197         pid_cmd = "cat %(pidfile)s" % locals()
198         (staus, output)  = node.host_commands(pid_cmd)
199         self.pid = output.strip()   
200         #fp = file(pidfile)
201         #buf=fp.read()
202         #self.pid=int(buf)
203         #fp.close()
204         #os.unlink(pidfile)
205         
206         # loop until the node is either fully booted, some error
207         # occured, or we've reached our totaltime out
208         def catch(sig, frame):
209             self.totaltime = self.totaltime -1
210             utils.header("beep %d\n" %self.totaltime, False, logfile = self.config.logfile )
211             total = self.totaltime
212             if (total == 0) or \
213                    (((total % 60)==0) and self.state_nanny()):
214                 killqemu(self.pid)
215                 self.exit = True
216             else:
217                 signal.alarm(1)
218
219         try:
220             signal.signal(signal.SIGALRM, catch)
221             signal.alarm(1)
222             self.exit = False
223
224             #console_states = {"login:":1}
225             while not self.exit:
226                 pass
227                 #try:
228                 #    self.console_nanny(console_states)
229                 #except: # need a better way to catch exceptions
230                 #    traceback.print_exc()
231                 #    pass
232
233             signal.alarm(0)
234         except:
235             signal.alarm(0)
236
237         killqemu(self.pid)
238         return 1
239
240 if __name__ == '__main__':
241     args = tuple(sys.argv[1:])
242     boot_node()(*args)