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 TestBox import TestBox
19 # step methods must take (self, options) and return a boolean
22 utils.header('Entering StandBy for %d mn'%minutes)
23 time.sleep(60*minutes)
26 def standby_generic (func):
27 def actual(self,options):
28 minutes=int(func.__name__.split("_")[1])
29 return standby(minutes)
34 def __init__ (self,plc_spec):
35 self.plc_spec=plc_spec
36 self.path=os.path.dirname(sys.argv[0])
38 self.vserverip=plc_spec['vserverip']
39 self.vservername=plc_spec['vservername']
40 self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
44 self.url="https://%s:443/PLCAPI/"%plc_spec['hostname']
45 utils.header('Using API url %s'%self.url)
46 self.server=xmlrpclib.Server(self.url,allow_none=True)
49 name=self.plc_spec['name']
51 return name+"[%s]"%self.vservername
53 return name+"[chroot]"
56 return self.plc_spec['hostname'] == 'localhost'
58 # define the API methods on this object through xmlrpc
59 # would help, but not strictly necessary
63 # command gets run in the chroot/vserver
64 def host_to_guest(self,command):
66 return "vserver %s exec %s"%(self.vservername,command)
68 return "chroot /plc/root %s"%utils.backslash_shell_specials(command)
70 # command gets run on the right box
71 def to_host(self,command):
75 return "ssh %s %s"%(self.plc_spec['hostname'],utils.backslash_shell_specials(command))
77 def full_command(self,command):
78 return self.to_host(self.host_to_guest(command))
80 def run_in_guest (self,command):
81 return utils.system(self.full_command(command))
82 def run_in_host (self,command):
83 return utils.system(self.to_host(command))
86 def run_in_guest_piped (self,local,remote):
87 return utils.system(local+" | "+self.full_command(remote))
89 # copy a file to the myplc root image - pass in_data=True if the file must go in /plc/data
90 def copy_in_guest (self, localfile, remotefile, in_data=False):
92 chroot_dest="/plc/data"
94 chroot_dest="/plc/root"
97 utils.system("cp %s %s/%s"%(localfile,chroot_dest,remotefile))
99 utils.system("cp %s /vservers/%s/%s"%(localfile,self.vservername,remotefile))
102 utils.system("scp %s %s:%s/%s"%(localfile,self.plc_spec['hostname'],chroot_dest,remotefile))
104 utils.system("scp %s %s@/vservers/%s/%s"%(localfile,self.plc_spec['hostname'],self.vservername,remotefile))
106 def auth_root (self):
107 return {'Username':self.plc_spec['PLC_ROOT_USER'],
108 'AuthMethod':'password',
109 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
110 'Role' : self.plc_spec['role']
112 def locate_site (self,sitename):
113 for site in self.plc_spec['sites']:
114 if site['site_fields']['name'] == sitename:
116 if site['site_fields']['login_base'] == sitename:
118 raise Exception,"Cannot locate site %s"%sitename
120 def locate_node (self,nodename):
121 for site in self.plc_spec['sites']:
122 for node in site['nodes']:
123 if node['node_fields']['hostname'] == nodename:
125 raise Exception,"Cannot locate node %s"%nodename
127 def locate_key (self,keyname):
128 for key in self.plc_spec['keys']:
129 if key['name'] == keyname:
131 raise Exception,"Cannot locate key %s"%keyname
133 # all different hostboxes used in this plc
134 def gather_hostBoxes(self):
135 # maps on sites and nodes, return [ (host_box,test_node) ]
137 for site_spec in self.plc_spec['sites']:
138 test_site = TestSite (self,site_spec)
139 for node_spec in site_spec['nodes']:
140 test_node = TestNode (self, test_site, node_spec)
141 if not test_node.is_real():
142 tuples.append( (test_node.host_box(),test_node) )
143 # transform into a dict { 'host_box' -> [ hostnames .. ] }
145 for (box,node) in tuples:
146 if not result.has_key(box):
149 result[box].append(node)
152 # a step for checking this stuff
153 def showboxes (self,options):
155 for (box,nodes) in self.gather_hostBoxes().iteritems():
156 print box,":"," + ".join( [ node.name() for node in nodes ] )
159 # make this a valid step
160 def kill_all_qemus(self,options):
161 for (box,nodes) in self.gather_hostBoxes().iteritems():
162 # this is the brute force version, kill all qemus on that host box
163 TestBox(box).kill_all_qemus()
166 # make this a valid step
167 def list_all_qemus(self,options):
168 for (box,nodes) in self.gather_hostBoxes().iteritems():
170 TestBox(box).copy("qemu_kill.sh")
171 # this is the brute force version, kill all qemus on that host box
172 TestBox(box).run("./qemu_kill.sh -l")
175 # kill only the right qemus
176 def kill_qemus(self,options):
177 for (box,nodes) in self.gather_hostBoxes().iteritems():
179 TestBox(box).copy("qemu_kill.sh")
180 # the fine-grain version
185 def clear_ssh_config (self,options):
186 # install local ssh_config file as root's .ssh/config - ssh should be quiet
187 # dir might need creation first
188 self.run_in_guest("mkdir /root/.ssh")
189 self.run_in_guest("chmod 700 /root/.ssh")
190 # this does not work - > redirection somehow makes it until an argument to cat
191 #self.run_in_guest_piped("cat ssh_config","cat > /root/.ssh/config")
192 self.copy_in_guest("ssh_config","/root/.ssh/config",True)
195 #################### step methods
198 def uninstall_chroot(self,options):
199 self.run_in_host('service plc safestop')
200 #####detecting the last myplc version installed and remove it
201 self.run_in_host('rpm -e myplc')
202 ##### Clean up the /plc directory
203 self.run_in_host('rm -rf /plc/data')
204 ##### stop any running vservers
205 self.run_in_host('for vserver in $(ls /vservers/* | sed -e s,/vservers/,,) ; do vserver $vserver stop ; done')
208 def uninstall_vserver(self,options):
209 self.run_in_host("vserver --silent %s delete"%self.vservername)
212 def uninstall(self,options):
213 # if there's a chroot-based myplc running, and then a native-based myplc is being deployed
214 # it sounds safer to have the former uninstalled too
215 # now the vserver method cannot be invoked for chroot instances as vservername is required
217 self.uninstall_vserver(options)
218 self.uninstall_chroot(options)
220 self.uninstall_chroot(options)
224 def install_chroot(self,options):
228 # xxx this would not work with hostname != localhost as mylc-init-vserver was extracted locally
229 def install_vserver(self,options):
230 # we need build dir for vtest-init-vserver
232 # a full path for the local calls
233 build_dir=self.path+"/build"
235 # use a standard name - will be relative to HOME
236 build_dir="tests-system-build"
237 build_checkout = "svn checkout %s %s"%(options.build_url,build_dir)
238 if self.run_in_host(build_checkout) != 0:
239 raise Exception,"Cannot checkout build dir"
240 # the repo url is taken from myplc-url
241 # with the last two steps (i386/myplc...) removed
242 repo_url = options.myplc_url
243 repo_url = os.path.dirname(repo_url)
244 repo_url = os.path.dirname(repo_url)
245 create_vserver="%s/vtest-init-vserver.sh %s %s -- --interface eth0:%s"%\
246 (build_dir,self.vservername,repo_url,self.vserverip)
247 if self.run_in_host(create_vserver) != 0:
248 raise Exception,"Could not create vserver for %s"%self.vservername
251 def install(self,options):
253 return self.install_vserver(options)
255 return self.install_chroot(options)
258 def install_rpm_chroot(self,options):
259 utils.header('Installing from %s'%options.myplc_url)
260 url=options.myplc_url
261 self.run_in_host('rpm -Uvh '+url)
262 self.run_in_host('service plc mount')
265 def install_rpm_vserver(self,options):
266 self.run_in_guest("yum -y install myplc-native")
269 def install_rpm(self,options):
271 return self.install_rpm_vserver(options)
273 return self.install_rpm_chroot(options)
276 def configure(self,options):
277 tmpname='%s/%s.plc-config-tty'%(options.path,self.name())
278 fileconf=open(tmpname,'w')
279 for var in [ 'PLC_NAME',
283 'PLC_MAIL_SUPPORT_ADDRESS',
290 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
291 fileconf.write('w\n')
292 fileconf.write('q\n')
294 utils.system('cat %s'%tmpname)
295 self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
296 utils.system('rm %s'%tmpname)
299 # the chroot install is slightly different to this respect
300 def start(self, options):
302 self.run_in_guest('service plc start')
304 self.run_in_host('service plc start')
307 def stop(self, options):
309 self.run_in_guest('service plc stop')
311 self.run_in_host('service plc stop')
314 # could use a TestKey class
315 def store_keys(self, options):
316 for key_spec in self.plc_spec['keys']:
317 TestKey(self,key_spec).store_key()
320 def clean_keys(self, options):
321 utils.system("rm -rf %s/keys/"%self.path)
323 def sites (self,options):
324 return self.do_sites(options)
326 def clean_sites (self,options):
327 return self.do_sites(options,action="delete")
329 def do_sites (self,options,action="add"):
330 for site_spec in self.plc_spec['sites']:
331 test_site = TestSite (self,site_spec)
332 if (action != "add"):
333 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
334 test_site.delete_site()
335 # deleted with the site
336 #test_site.delete_users()
339 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
340 test_site.create_site()
341 test_site.create_users()
344 def nodes (self, options):
345 return self.do_nodes(options)
346 def clean_nodes (self, options):
347 return self.do_nodes(options,action="delete")
349 def do_nodes (self, options,action="add"):
350 for site_spec in self.plc_spec['sites']:
351 test_site = TestSite (self,site_spec)
353 utils.header("Deleting nodes in site %s"%test_site.name())
354 for node_spec in site_spec['nodes']:
355 test_node=TestNode(self,test_site,node_spec)
356 utils.header("Deleting %s"%test_node.name())
357 test_node.delete_node()
359 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
360 for node_spec in site_spec['nodes']:
361 utils.show_spec('Creating node %s'%node_spec,node_spec)
362 test_node = TestNode (self,test_site,node_spec)
363 test_node.create_node ()
366 # create nodegroups if needed, and populate
367 # no need for a clean_nodegroups if we are careful enough
368 def nodegroups (self, options):
369 # 1st pass to scan contents
371 for site_spec in self.plc_spec['sites']:
372 test_site = TestSite (self,site_spec)
373 for node_spec in site_spec['nodes']:
374 test_node=TestNode (self,test_site,node_spec)
375 if node_spec.has_key('nodegroups'):
376 nodegroupnames=node_spec['nodegroups']
377 if isinstance(nodegroupnames,StringTypes):
378 nodegroupnames = [ nodegroupnames ]
379 for nodegroupname in nodegroupnames:
380 if not groups_dict.has_key(nodegroupname):
381 groups_dict[nodegroupname]=[]
382 groups_dict[nodegroupname].append(test_node.name())
383 auth=self.auth_root()
384 for (nodegroupname,group_nodes) in groups_dict.iteritems():
386 self.server.GetNodeGroups(auth,{'name':nodegroupname})[0]
388 self.server.AddNodeGroup(auth,{'name':nodegroupname})
389 for node in group_nodes:
390 self.server.AddNodeToNodeGroup(auth,node,nodegroupname)
393 def all_hostnames (self) :
395 for site_spec in self.plc_spec['sites']:
396 hostnames += [ node_spec['node_fields']['hostname'] \
397 for node_spec in site_spec['nodes'] ]
400 # gracetime : during the first <gracetime> minutes nothing gets printed
401 def do_nodes_booted (self, minutes, gracetime=2):
403 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
404 graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
405 # the nodes that haven't checked yet - start with a full list and shrink over time
406 tocheck = self.all_hostnames()
407 utils.header("checking nodes %r"%tocheck)
408 # create a dict hostname -> status
409 status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
412 tocheck_status=self.server.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
414 for array in tocheck_status:
415 hostname=array['hostname']
416 boot_state=array['boot_state']
417 if boot_state == 'boot':
418 utils.header ("%s has reached the 'boot' state"%hostname)
420 # if it's a real node, never mind
421 (site_spec,node_spec)=self.locate_node(hostname)
422 if TestNode.is_real_model(node_spec['node_fields']['model']):
423 utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
426 if datetime.datetime.now() > graceout:
427 utils.header ("%s still in '%s' state"%(hostname,boot_state))
428 graceout=datetime.datetime.now()+datetime.timedelta(1)
429 status[hostname] = boot_state
431 tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != 'boot' ]
434 if datetime.datetime.now() > timeout:
435 for hostname in tocheck:
436 utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
438 # otherwise, sleep for a while
440 # only useful in empty plcs
443 def nodes_booted(self,options):
444 return self.do_nodes_booted(minutes=5)
446 #to scan and store the nodes's public keys and avoid to ask for confirmation when ssh
447 def scan_publicKeys(self,hostnames):
449 temp_knownhosts="/root/known_hosts"
450 remote_knownhosts="/root/.ssh/known_hosts"
451 self.run_in_host("touch %s"%temp_knownhosts )
452 for hostname in hostnames:
453 utils.header("Scan Public %s key and store it in the known_host file(under the root image) "%hostname)
454 scan=self.run_in_host('ssh-keyscan -t rsa %s >> %s '%(hostname,temp_knownhosts))
455 #Store the public keys in the right root image
456 self.copy_in_guest(temp_knownhosts,remote_knownhosts,True)
457 #clean the temp keys file used
458 self.run_in_host('rm -f %s '%temp_knownhosts )
459 except Exception, err:
462 def do_check_nodesSsh(self,minutes):
464 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
465 #graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
466 tocheck = self.all_hostnames()
467 self.scan_publicKeys(tocheck)
468 utils.header("checking Connectivity on nodes %r"%tocheck)
470 for hostname in tocheck:
471 # try to ssh in nodes
472 access=self.run_in_guest('ssh -i /etc/planetlab/root_ssh_key.rsa root@%s date'%hostname )
474 utils.header('The node %s is sshable -->'%hostname)
476 tocheck.remove(hostname)
478 (site_spec,node_spec)=self.locate_node(hostname)
479 if TestNode.is_real_model(node_spec['node_fields']['model']):
480 utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
481 tocheck.remove(hostname)
484 if datetime.datetime.now() > timeout:
485 for hostname in tocheck:
486 utils.header("FAILURE to ssh into %s"%hostname)
488 # otherwise, sleep for a while
490 # only useful in empty plcs
493 def nodes_ssh(self, options):
494 return self.do_check_nodesSsh(minutes=2)
496 def bootcd (self, options):
497 for site_spec in self.plc_spec['sites']:
498 test_site = TestSite (self,site_spec)
499 for node_spec in site_spec['nodes']:
500 test_node=TestNode (self,test_site,node_spec)
501 test_node.create_boot_cd(options.path)
504 def do_check_intiscripts(self):
505 for site_spec in self.plc_spec['sites']:
506 test_site = TestSite (self,site_spec)
507 for slice_spec in self.plc_spec['slices']:
508 test_slice=TestSlice (self,test_site,slice_spec)
509 init_status=test_slice.get_initscript()
510 if (not init_status):
514 def check_initscripts(self, options):
515 return self.do_check_intiscripts()
517 def initscripts (self, options):
518 for initscript in self.plc_spec['initscripts']:
519 utils.show_spec('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
520 self.server.AddInitScript(self.auth_root(),initscript['initscript_fields'])
523 def slices (self, options):
524 return self.do_slices()
526 def clean_slices (self, options):
527 return self.do_slices("delete")
529 def do_slices (self, action="add"):
530 for slice in self.plc_spec['slices']:
531 site_spec = self.locate_site (slice['sitename'])
532 test_site = TestSite(self,site_spec)
533 test_slice=TestSlice(self,test_site,slice)
535 utils.header("Deleting slices in site %s"%test_site.name())
536 test_slice.delete_slice()
538 utils.show_spec("Creating slice",slice)
539 test_slice.create_slice()
540 utils.header('Created Slice %s'%slice['slice_fields']['name'])
543 def check_slices(self, options):
544 for slice_spec in self.plc_spec['slices']:
545 site_spec = self.locate_site (slice_spec['sitename'])
546 test_site = TestSite(self,site_spec)
547 test_slice=TestSlice(self,test_site,slice_spec)
548 status=test_slice.do_check_slice(options)
553 def start_nodes (self, options):
554 utils.header("Starting nodes")
555 for site_spec in self.plc_spec['sites']:
556 TestSite(self,site_spec).start_nodes (options)
559 def stop_nodes (self, options):
560 self.kill_all_qemus()
563 def check_tcp (self, options):
564 print 'check_tcp not yet implemented'
567 # returns the filename to use for sql dump/restore, using options.dbname if set
568 def dbfile (self, database, options):
569 # uses options.dbname if it is found
572 if not isinstance(name,StringTypes):
575 t=datetime.datetime.now()
578 return "/root/%s-%s.sql"%(database,name)
580 def db_dump(self, options):
582 dump=self.dbfile("planetab4",options)
583 self.run_in_guest('pg_dump -U pgsqluser planetlab4 -f '+ dump)
584 utils.header('Dumped planetlab4 database in %s'%dump)
587 def db_restore(self, options):
588 dump=self.dbfile("planetab4",options)
590 self.run_in_guest('service httpd stop')
591 # xxx - need another wrapper
592 self.run_in_guest_piped('echo drop database planetlab4','psql --user=pgsqluser template1')
593 self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab4')
594 self.run_in_guest('psql -U pgsqluser planetlab4 -f '+dump)
595 ##starting httpd service
596 self.run_in_guest('service httpd start')
598 utils.header('Database restored from ' + dump)
601 def standby_1(): pass
603 def standby_2(): pass
605 def standby_3(): pass
607 def standby_4(): pass
609 def standby_5(): pass
611 def standby_6(): pass
613 def standby_7(): pass
615 def standby_8(): pass
617 def standby_9(): pass
619 def standby_10(): pass
621 def standby_11(): pass
623 def standby_12(): pass
625 def standby_13(): pass
627 def standby_14(): pass
629 def standby_15(): pass
631 def standby_16(): pass
633 def standby_17(): pass
635 def standby_18(): pass
637 def standby_19(): pass
639 def standby_20(): pass