9 from types import StringTypes
12 from TestSite import TestSite
13 from TestNode import TestNode
14 from TestUser import TestUser
15 from TestKey import TestKey
16 from TestSlice import TestSlice
17 from TestSliver import TestSliver
18 from TestBox import TestBox
19 from TestSsh import TestSsh
21 # step methods must take (self, options) and return a boolean
24 utils.header('Entering StandBy for %d mn'%minutes)
25 time.sleep(60*minutes)
28 def standby_generic (func):
29 def actual(self,options):
30 minutes=int(func.__name__.split("_")[1])
31 return standby(minutes)
36 def __init__ (self,plc_spec):
37 self.plc_spec=plc_spec
38 self.path=os.path.dirname(sys.argv[0])
39 self.test_ssh=TestSsh(self.plc_spec['hostname'],self.path)
41 self.vserverip=plc_spec['vserverip']
42 self.vservername=plc_spec['vservername']
43 self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
47 self.url="https://%s:443/PLCAPI/"%plc_spec['hostname']
48 # utils.header('Using API url %s'%self.url)
49 self.server=xmlrpclib.Server(self.url,allow_none=True)
52 name=self.plc_spec['name']
54 return name+"[%s]"%self.vservername
56 return name+"[chroot]"
59 return self.plc_spec['hostname']
62 return self.test_ssh.is_local()
64 # define the API methods on this object through xmlrpc
65 # would help, but not strictly necessary
69 def full_command(self,command):
70 return self.test_ssh.to_host(self.host_to_guest(command))
72 def run_in_guest (self,command):
73 return utils.system(self.full_command(command))
75 def run_in_host (self,command):
76 return utils.system(self.test_ssh.to_host(command))
78 #command gets run in the chroot/vserver
79 def host_to_guest(self,command):
81 return "vserver %s exec %s"%(self.vservername,command)
83 return "chroot /plc/root %s"%TestSsh.backslash_shell_specials(command)
85 # copy a file to the myplc root image - pass in_data=True if the file must go in /plc/data
86 def copy_in_guest (self, localfile, remotefile, in_data=False):
88 chroot_dest="/plc/data"
90 chroot_dest="/plc/root"
93 utils.system("cp %s %s/%s"%(localfile,chroot_dest,remotefile))
95 utils.system("cp %s /vservers/%s/%s"%(localfile,self.vservername,remotefile))
98 utils.system("scp %s %s:%s/%s"%(localfile,self.hostname(),chroot_dest,remotefile))
100 utils.system("scp %s %s@/vservers/%s/%s"%(localfile,self.hostname(),self.vservername,remotefile))
104 def run_in_guest_piped (self,local,remote):
105 return utils.system(local+" | "+self.full_command(remote))
108 def auth_root (self):
109 return {'Username':self.plc_spec['PLC_ROOT_USER'],
110 'AuthMethod':'password',
111 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
112 'Role' : self.plc_spec['role']
114 def locate_site (self,sitename):
115 for site in self.plc_spec['sites']:
116 if site['site_fields']['name'] == sitename:
118 if site['site_fields']['login_base'] == sitename:
120 raise Exception,"Cannot locate site %s"%sitename
122 def locate_node (self,nodename):
123 for site in self.plc_spec['sites']:
124 for node in site['nodes']:
125 if node['node_fields']['hostname'] == nodename:
127 raise Exception,"Cannot locate node %s"%nodename
129 def locate_key (self,keyname):
130 for key in self.plc_spec['keys']:
131 if key['name'] == keyname:
133 raise Exception,"Cannot locate key %s"%keyname
135 # all different hostboxes used in this plc
136 def gather_hostBoxes(self):
137 # maps on sites and nodes, return [ (host_box,test_node) ]
139 for site_spec in self.plc_spec['sites']:
140 test_site = TestSite (self,site_spec)
141 for node_spec in site_spec['nodes']:
142 test_node = TestNode (self, test_site, node_spec)
143 if not test_node.is_real():
144 tuples.append( (test_node.host_box(),test_node) )
145 # transform into a dict { 'host_box' -> [ hostnames .. ] }
147 for (box,node) in tuples:
148 if not result.has_key(box):
151 result[box].append(node)
154 # a step for checking this stuff
155 def showboxes (self,options):
157 for (box,nodes) in self.gather_hostBoxes().iteritems():
158 print box,":"," + ".join( [ node.name() for node in nodes ] )
161 # make this a valid step
162 def kill_all_qemus(self,options):
163 for (box,nodes) in self.gather_hostBoxes().iteritems():
164 # this is the brute force version, kill all qemus on that host box
165 TestBox(box,options.buildname).kill_all_qemus()
168 # make this a valid step
169 def list_all_qemus(self,options):
170 for (box,nodes) in self.gather_hostBoxes().iteritems():
171 # this is the brute force version, kill all qemus on that host box
172 TestBox(box,options.buildname).list_all_qemus()
175 # kill only the right qemus
176 def list_qemus(self,options):
177 for (box,nodes) in self.gather_hostBoxes().iteritems():
178 # the fine-grain version
183 # kill only the right qemus
184 def kill_qemus(self,options):
185 for (box,nodes) in self.gather_hostBoxes().iteritems():
186 # the fine-grain version
191 def clear_ssh_config (self,options):
192 # install local ssh_config file as root's .ssh/config - ssh should be quiet
193 # dir might need creation first
194 self.run_in_guest("mkdir /root/.ssh")
195 self.run_in_guest("chmod 700 /root/.ssh")
196 # this does not work - > redirection somehow makes it until an argument to cat
197 #self.run_in_guest_piped("cat ssh_config","cat > /root/.ssh/config")
198 self.copy_in_guest("ssh_config","/root/.ssh/config",True)
201 #################### step methods
204 def uninstall_chroot(self,options):
205 self.run_in_host('service plc safestop')
206 #####detecting the last myplc version installed and remove it
207 self.run_in_host('rpm -e myplc')
208 ##### Clean up the /plc directory
209 self.run_in_host('rm -rf /plc/data')
210 ##### stop any running vservers
211 self.run_in_host('for vserver in $(ls /vservers/* | sed -e s,/vservers/,,) ; do vserver $vserver stop ; done')
214 def uninstall_vserver(self,options):
215 self.run_in_host("vserver --silent %s delete"%self.vservername)
218 def uninstall(self,options):
219 # if there's a chroot-based myplc running, and then a native-based myplc is being deployed
220 # it sounds safer to have the former uninstalled too
221 # now the vserver method cannot be invoked for chroot instances as vservername is required
223 self.uninstall_vserver(options)
224 self.uninstall_chroot(options)
226 self.uninstall_chroot(options)
230 def install_chroot(self,options):
234 def install_vserver(self,options):
235 # we need build dir for vtest-init-vserver
237 # a full path for the local calls
238 build_dir=self.path+"/build"
240 # use a standard name - will be relative to HOME
241 build_dir="options.buildname"
242 # run checkout in any case - would do an update if already exists
243 build_checkout = "svn checkout %s %s"%(options.build_url,build_dir)
244 if self.run_in_host(build_checkout) != 0:
245 raise Exception,"Cannot checkout build dir"
246 # the repo url is taken from myplc-url
247 # with the last two steps (i386/myplc...) removed
248 repo_url = options.myplc_url
249 for level in [ 'rpmname','arch' ]:
250 repo_url = os.path.dirname(repo_url)
251 create_vserver="%s/vtest-init-vserver.sh %s %s -- --interface eth0:%s"%\
252 (build_dir,self.vservername,repo_url,self.vserverip)
253 if self.run_in_host(create_vserver) != 0:
254 raise Exception,"Could not create vserver for %s"%self.vservername
257 def install(self,options):
259 return self.install_vserver(options)
261 return self.install_chroot(options)
264 def cache_rpm(self,url):
265 self.run_in_host('rm -rf *.rpm')
266 utils.header('Curling rpm from %s'%url)
267 id= self.run_in_host('curl -O '+url)
269 raise Exception,"Could not get rpm from %s"%url
273 def install_rpm_chroot(self,options):
274 rpm = os.path.basename(options.myplc_url)
275 if (not os.path.isfile(rpm)):
276 self.cache_rpm(options.myplc_url)
277 utils.header('Installing the : %s'%rpm)
278 self.run_in_host('rpm -Uvh '+rpm)
279 self.run_in_host('service plc mount')
282 def install_rpm_vserver(self,options):
283 self.run_in_guest("yum -y install myplc-native")
286 def install_rpm(self,options):
288 return self.install_rpm_vserver(options)
290 return self.install_rpm_chroot(options)
293 def configure(self,options):
294 tmpname='%s.plc-config-tty'%(self.name())
295 fileconf=open(tmpname,'w')
296 for var in [ 'PLC_NAME',
300 'PLC_MAIL_SUPPORT_ADDRESS',
307 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
308 fileconf.write('w\n')
309 fileconf.write('q\n')
311 utils.system('cat %s'%tmpname)
312 self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
313 utils.system('rm %s'%tmpname)
316 # the chroot install is slightly different to this respect
317 def start(self, options):
319 self.run_in_guest('service plc start')
321 self.run_in_host('service plc start')
324 def stop(self, options):
326 self.run_in_guest('service plc stop')
328 self.run_in_host('service plc stop')
331 # could use a TestKey class
332 def store_keys(self, options):
333 for key_spec in self.plc_spec['keys']:
334 TestKey(self,key_spec).store_key()
337 def clean_keys(self, options):
338 utils.system("rm -rf %s/keys/"%self.path)
340 def sites (self,options):
341 return self.do_sites(options)
343 def clean_sites (self,options):
344 return self.do_sites(options,action="delete")
346 def do_sites (self,options,action="add"):
347 for site_spec in self.plc_spec['sites']:
348 test_site = TestSite (self,site_spec)
349 if (action != "add"):
350 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
351 test_site.delete_site()
352 # deleted with the site
353 #test_site.delete_users()
356 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
357 test_site.create_site()
358 test_site.create_users()
361 def nodes (self, options):
362 return self.do_nodes(options)
363 def clean_nodes (self, options):
364 return self.do_nodes(options,action="delete")
366 def do_nodes (self, options,action="add"):
367 for site_spec in self.plc_spec['sites']:
368 test_site = TestSite (self,site_spec)
370 utils.header("Deleting nodes in site %s"%test_site.name())
371 for node_spec in site_spec['nodes']:
372 test_node=TestNode(self,test_site,node_spec)
373 utils.header("Deleting %s"%test_node.name())
374 test_node.delete_node()
376 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
377 for node_spec in site_spec['nodes']:
378 utils.pprint('Creating node %s'%node_spec,node_spec)
379 test_node = TestNode (self,test_site,node_spec)
380 test_node.create_node ()
383 # create nodegroups if needed, and populate
384 # no need for a clean_nodegroups if we are careful enough
385 def nodegroups (self, options):
386 # 1st pass to scan contents
388 for site_spec in self.plc_spec['sites']:
389 test_site = TestSite (self,site_spec)
390 for node_spec in site_spec['nodes']:
391 test_node=TestNode (self,test_site,node_spec)
392 if node_spec.has_key('nodegroups'):
393 nodegroupnames=node_spec['nodegroups']
394 if isinstance(nodegroupnames,StringTypes):
395 nodegroupnames = [ nodegroupnames ]
396 for nodegroupname in nodegroupnames:
397 if not groups_dict.has_key(nodegroupname):
398 groups_dict[nodegroupname]=[]
399 groups_dict[nodegroupname].append(test_node.name())
400 auth=self.auth_root()
401 for (nodegroupname,group_nodes) in groups_dict.iteritems():
403 self.server.GetNodeGroups(auth,{'name':nodegroupname})[0]
405 self.server.AddNodeGroup(auth,{'name':nodegroupname})
406 for node in group_nodes:
407 self.server.AddNodeToNodeGroup(auth,node,nodegroupname)
410 def all_hostnames (self) :
412 for site_spec in self.plc_spec['sites']:
413 hostnames += [ node_spec['node_fields']['hostname'] \
414 for node_spec in site_spec['nodes'] ]
417 # gracetime : during the first <gracetime> minutes nothing gets printed
418 def do_nodes_booted (self, minutes, gracetime=2):
420 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
421 graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
422 # the nodes that haven't checked yet - start with a full list and shrink over time
423 tocheck = self.all_hostnames()
424 utils.header("checking nodes %r"%tocheck)
425 # create a dict hostname -> status
426 status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
429 tocheck_status=self.server.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
431 for array in tocheck_status:
432 hostname=array['hostname']
433 boot_state=array['boot_state']
434 if boot_state == 'boot':
435 utils.header ("%s has reached the 'boot' state"%hostname)
437 # if it's a real node, never mind
438 (site_spec,node_spec)=self.locate_node(hostname)
439 if TestNode.is_real_model(node_spec['node_fields']['model']):
440 utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
443 if datetime.datetime.now() > graceout:
444 utils.header ("%s still in '%s' state"%(hostname,boot_state))
445 graceout=datetime.datetime.now()+datetime.timedelta(1)
446 status[hostname] = boot_state
448 tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != 'boot' ]
451 if datetime.datetime.now() > timeout:
452 for hostname in tocheck:
453 utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
455 # otherwise, sleep for a while
457 # only useful in empty plcs
460 def nodes_booted(self,options):
461 return self.do_nodes_booted(minutes=5)
463 #to scan and store the nodes's public keys and avoid to ask for confirmation when ssh
464 def scan_publicKeys(self,hostnames):
466 temp_knownhosts="/root/known_hosts"
467 remote_knownhosts="/root/.ssh/known_hosts"
468 self.run_in_host("touch %s"%temp_knownhosts )
469 for hostname in hostnames:
470 utils.header("Scan Public %s key and store it in the known_host file(under the root image) "%hostname)
471 scan=self.run_in_host('ssh-keyscan -t rsa %s >> %s '%(hostname,temp_knownhosts))
472 #Store the public keys in the right root image
473 self.copy_in_guest(temp_knownhosts,remote_knownhosts,True)
474 #clean the temp keys file used
475 self.run_in_host('rm -f %s '%temp_knownhosts )
476 except Exception, err:
479 def do_check_nodesSsh(self,minutes):
481 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
482 tocheck = self.all_hostnames()
483 self.scan_publicKeys(tocheck)
484 utils.header("checking Connectivity on nodes %r"%tocheck)
486 for hostname in tocheck:
487 # try to ssh in nodes
488 access=self.run_in_guest('ssh -o StrictHostKeyChecking=no -o BatchMode=yes -i /etc/planetlab/root_ssh_key.rsa root@%s date'%hostname )
490 utils.header('The node %s is sshable -->'%hostname)
492 tocheck.remove(hostname)
494 (site_spec,node_spec)=self.locate_node(hostname)
495 if TestNode.is_real_model(node_spec['node_fields']['model']):
496 utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
497 tocheck.remove(hostname)
500 if datetime.datetime.now() > timeout:
501 for hostname in tocheck:
502 utils.header("FAILURE to ssh into %s"%hostname)
504 # otherwise, sleep for a while
506 # only useful in empty plcs
509 def nodes_ssh(self, options):
510 return self.do_check_nodesSsh(minutes=2)
512 def bootcd (self, options):
513 for site_spec in self.plc_spec['sites']:
514 test_site = TestSite (self,site_spec)
515 for node_spec in site_spec['nodes']:
516 test_node=TestNode (self,test_site,node_spec)
517 test_node.prepare_area()
518 test_node.create_boot_cd()
519 test_node.configure_qemu()
522 def do_check_intiscripts(self):
523 for site_spec in self.plc_spec['sites']:
524 test_site = TestSite (self,site_spec)
525 test_node = TestNode (self,test_site,site_spec['nodes'])
526 for slice_spec in self.plc_spec['slices']:
527 test_slice=TestSlice (self,test_site,slice_spec)
528 test_sliver=TestSliver(self,test_node,test_slice)
529 init_status=test_sliver.get_initscript(slice_spec)
530 if (not init_status):
534 def check_initscripts(self, options):
535 return self.do_check_intiscripts()
537 def initscripts (self, options):
538 for initscript in self.plc_spec['initscripts']:
539 utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
540 self.server.AddInitScript(self.auth_root(),initscript['initscript_fields'])
543 def slices (self, options):
544 return self.do_slices()
546 def clean_slices (self, options):
547 return self.do_slices("delete")
549 def do_slices (self, action="add"):
550 for slice in self.plc_spec['slices']:
551 site_spec = self.locate_site (slice['sitename'])
552 test_site = TestSite(self,site_spec)
553 test_slice=TestSlice(self,test_site,slice)
555 utils.header("Deleting slices in site %s"%test_site.name())
556 test_slice.delete_slice()
558 utils.pprint("Creating slice",slice)
559 test_slice.create_slice()
560 utils.header('Created Slice %s'%slice['slice_fields']['name'])
563 def check_slices(self, options):
564 for slice_spec in self.plc_spec['slices']:
565 site_spec = self.locate_site (slice_spec['sitename'])
566 test_site = TestSite(self,site_spec)
567 test_slice=TestSlice(self,test_site,slice_spec)
568 status=test_slice.do_check_slice(options)
573 def start_nodes (self, options):
574 utils.header("Starting nodes")
575 for site_spec in self.plc_spec['sites']:
576 TestSite(self,site_spec).start_nodes (options)
579 def stop_nodes (self, options):
580 self.kill_all_qemus(options)
583 def check_tcp (self, options):
584 #we just need to create a sliver object nothing else
585 test_sliver=TestSliver(self,
586 TestNode(self, TestSite(self,self.plc_spec['sites'][0]),
587 self.plc_spec['sites'][0]['nodes'][0]),
588 TestSlice(self,TestSite(self,self.plc_spec['sites'][0]),
589 self.plc_spec['slices']))
590 return test_sliver.do_check_tcp(self.plc_spec['tcp_param'],options)
592 # returns the filename to use for sql dump/restore, using options.dbname if set
593 def dbfile (self, database, options):
594 # uses options.dbname if it is found
597 if not isinstance(name,StringTypes):
600 t=datetime.datetime.now()
603 return "/root/%s-%s.sql"%(database,name)
605 def db_dump(self, options):
607 dump=self.dbfile("planetab4",options)
608 self.run_in_guest('pg_dump -U pgsqluser planetlab4 -f '+ dump)
609 utils.header('Dumped planetlab4 database in %s'%dump)
612 def db_restore(self, options):
613 dump=self.dbfile("planetab4",options)
615 self.run_in_guest('service httpd stop')
616 # xxx - need another wrapper
617 self.run_in_guest_piped('echo drop database planetlab4','psql --user=pgsqluser template1')
618 self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab4')
619 self.run_in_guest('psql -U pgsqluser planetlab4 -f '+dump)
620 ##starting httpd service
621 self.run_in_guest('service httpd start')
623 utils.header('Database restored from ' + dump)
626 def standby_1(): pass
628 def standby_2(): pass
630 def standby_3(): pass
632 def standby_4(): pass
634 def standby_5(): pass
636 def standby_6(): pass
638 def standby_7(): pass
640 def standby_8(): pass
642 def standby_9(): pass
644 def standby_10(): pass
646 def standby_11(): pass
648 def standby_12(): pass
650 def standby_13(): pass
652 def standby_14(): pass
654 def standby_15(): pass
656 def standby_16(): pass
658 def standby_17(): pass
660 def standby_18(): pass
662 def standby_19(): pass
664 def standby_20(): pass