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
20 # step methods must take (self, options) and return a boolean
23 utils.header('Entering StandBy for %d mn'%minutes)
24 time.sleep(60*minutes)
27 def standby_generic (func):
28 def actual(self,options):
29 minutes=int(func.__name__.split("_")[1])
30 return standby(minutes)
35 def __init__ (self,plc_spec):
36 self.plc_spec=plc_spec
37 self.path=os.path.dirname(sys.argv[0])
39 self.vserverip=plc_spec['vserverip']
40 self.vservername=plc_spec['vservername']
41 self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
45 self.url="https://%s:443/PLCAPI/"%plc_spec['hostname']
46 utils.header('Using API url %s'%self.url)
47 self.server=xmlrpclib.Server(self.url,allow_none=True)
50 name=self.plc_spec['name']
52 return name+"[%s]"%self.vservername
54 return name+"[chroot]"
57 return self.plc_spec['hostname']
60 return utils.is_local(self.hostname())
62 # define the API methods on this object through xmlrpc
63 # would help, but not strictly necessary
67 # command gets run in the chroot/vserver
68 def host_to_guest(self,command):
70 return "vserver %s exec %s"%(self.vservername,command)
72 return "chroot /plc/root %s"%utils.backslash_shell_specials(command)
74 # command gets run on the right box
75 def to_host(self,command):
79 return "ssh %s %s"%(self.hostname(),utils.backslash_shell_specials(command))
81 def full_command(self,command):
82 return self.to_host(self.host_to_guest(command))
84 def run_in_guest (self,command):
85 return utils.system(self.full_command(command))
86 def run_in_host (self,command):
87 return utils.system(self.to_host(command))
90 def run_in_guest_piped (self,local,remote):
91 return utils.system(local+" | "+self.full_command(remote))
93 # copy a file to the myplc root image - pass in_data=True if the file must go in /plc/data
94 def copy_in_guest (self, localfile, remotefile, in_data=False):
96 chroot_dest="/plc/data"
98 chroot_dest="/plc/root"
101 utils.system("cp %s %s/%s"%(localfile,chroot_dest,remotefile))
103 utils.system("cp %s /vservers/%s/%s"%(localfile,self.vservername,remotefile))
106 utils.system("scp %s %s:%s/%s"%(localfile,self.hostname(),chroot_dest,remotefile))
108 utils.system("scp %s %s@/vservers/%s/%s"%(localfile,self.hostname(),self.vservername,remotefile))
110 def auth_root (self):
111 return {'Username':self.plc_spec['PLC_ROOT_USER'],
112 'AuthMethod':'password',
113 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
114 'Role' : self.plc_spec['role']
116 def locate_site (self,sitename):
117 for site in self.plc_spec['sites']:
118 if site['site_fields']['name'] == sitename:
120 if site['site_fields']['login_base'] == sitename:
122 raise Exception,"Cannot locate site %s"%sitename
124 def locate_node (self,nodename):
125 for site in self.plc_spec['sites']:
126 for node in site['nodes']:
127 if node['node_fields']['hostname'] == nodename:
129 raise Exception,"Cannot locate node %s"%nodename
131 def locate_key (self,keyname):
132 for key in self.plc_spec['keys']:
133 if key['name'] == keyname:
135 raise Exception,"Cannot locate key %s"%keyname
137 # all different hostboxes used in this plc
138 def gather_hostBoxes(self):
139 # maps on sites and nodes, return [ (host_box,test_node) ]
141 for site_spec in self.plc_spec['sites']:
142 test_site = TestSite (self,site_spec)
143 for node_spec in site_spec['nodes']:
144 test_node = TestNode (self, test_site, node_spec)
145 if not test_node.is_real():
146 tuples.append( (test_node.host_box(),test_node) )
147 # transform into a dict { 'host_box' -> [ hostnames .. ] }
149 for (box,node) in tuples:
150 if not result.has_key(box):
153 result[box].append(node)
156 # a step for checking this stuff
157 def showboxes (self,options):
159 for (box,nodes) in self.gather_hostBoxes().iteritems():
160 print box,":"," + ".join( [ node.name() for node in nodes ] )
163 # make this a valid step
164 def kill_all_qemus(self,options):
165 for (box,nodes) in self.gather_hostBoxes().iteritems():
166 # this is the brute force version, kill all qemus on that host box
167 TestBox(box,options.buildname).kill_all_qemus()
170 # make this a valid step
171 def list_all_qemus(self,options):
172 for (box,nodes) in self.gather_hostBoxes().iteritems():
174 TestBox(box,options.buildname).copy("qemu_kill.sh")
175 # this is the brute force version, kill all qemus on that host box
176 TestBox(box,options.buildname).run_in_buildname("qemu_kill.sh -l")
179 # kill only the right qemus
180 def kill_qemus(self,options):
181 for (box,nodes) in self.gather_hostBoxes().iteritems():
183 TestBox(box,options.buildname).copy("qemu_kill.sh")
184 # the fine-grain version
189 def clear_ssh_config (self,options):
190 # install local ssh_config file as root's .ssh/config - ssh should be quiet
191 # dir might need creation first
192 self.run_in_guest("mkdir /root/.ssh")
193 self.run_in_guest("chmod 700 /root/.ssh")
194 # this does not work - > redirection somehow makes it until an argument to cat
195 #self.run_in_guest_piped("cat ssh_config","cat > /root/.ssh/config")
196 self.copy_in_guest("ssh_config","/root/.ssh/config",True)
199 #################### step methods
202 def uninstall_chroot(self,options):
203 self.run_in_host('service plc safestop')
204 #####detecting the last myplc version installed and remove it
205 self.run_in_host('rpm -e myplc')
206 ##### Clean up the /plc directory
207 self.run_in_host('rm -rf /plc/data')
208 ##### stop any running vservers
209 self.run_in_host('for vserver in $(ls /vservers/* | sed -e s,/vservers/,,) ; do vserver $vserver stop ; done')
212 def uninstall_vserver(self,options):
213 self.run_in_host("vserver --silent %s delete"%self.vservername)
216 def uninstall(self,options):
217 # if there's a chroot-based myplc running, and then a native-based myplc is being deployed
218 # it sounds safer to have the former uninstalled too
219 # now the vserver method cannot be invoked for chroot instances as vservername is required
221 self.uninstall_vserver(options)
222 self.uninstall_chroot(options)
224 self.uninstall_chroot(options)
228 def install_chroot(self,options):
232 # xxx this would not work with hostname != localhost as mylc-init-vserver was extracted locally
233 def install_vserver(self,options):
234 # we need build dir for vtest-init-vserver
236 # a full path for the local calls
237 build_dir=self.path+"/build"
239 # use a standard name - will be relative to HOME
240 build_dir="tests-system-build"
241 build_checkout = "svn checkout %s %s"%(options.build_url,build_dir)
242 if self.run_in_host(build_checkout) != 0:
243 raise Exception,"Cannot checkout build dir"
244 # the repo url is taken from myplc-url
245 # with the last two steps (i386/myplc...) removed
246 repo_url = options.myplc_url
247 repo_url = os.path.dirname(repo_url)
248 create_vserver="%s/vtest-init-vserver.sh %s %s -- --interface eth0:%s"%\
249 (build_dir,self.vservername,repo_url,self.vserverip)
250 if self.run_in_host(create_vserver) != 0:
251 raise Exception,"Could not create vserver for %s"%self.vservername
254 def install(self,options):
256 return self.install_vserver(options)
258 return self.install_chroot(options)
261 def cache_rpm(self,url):
262 self.run_in_host('rm -rf *.rpm')
263 utils.header('Curling rpm from %s'%url)
264 id= self.run_in_host('curl -O '+url)
266 raise Exception,"Could not get rpm from %s"%url
270 def install_rpm_chroot(self,options):
271 rpm = os.path.basename(options.myplc_url)
272 if (not os.path.isfile(rpm)):
273 self.cache_rpm(options.myplc_url)
274 utils.header('Installing the : %s'%rpm)
275 self.run_in_host('rpm -Uvh '+rpm)
276 self.run_in_host('service plc mount')
279 def install_rpm_vserver(self,options):
280 self.run_in_guest("yum -y install myplc-native")
283 def install_rpm(self,options):
285 return self.install_rpm_vserver(options)
287 return self.install_rpm_chroot(options)
290 def configure(self,options):
291 tmpname='%s.plc-config-tty'%(self.name())
292 fileconf=open(tmpname,'w')
293 for var in [ 'PLC_NAME',
297 'PLC_MAIL_SUPPORT_ADDRESS',
304 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
305 fileconf.write('w\n')
306 fileconf.write('q\n')
308 utils.system('cat %s'%tmpname)
309 self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
310 utils.system('rm %s'%tmpname)
313 # the chroot install is slightly different to this respect
314 def start(self, options):
316 self.run_in_guest('service plc start')
318 self.run_in_host('service plc start')
321 def stop(self, options):
323 self.run_in_guest('service plc stop')
325 self.run_in_host('service plc stop')
328 # could use a TestKey class
329 def store_keys(self, options):
330 for key_spec in self.plc_spec['keys']:
331 TestKey(self,key_spec).store_key()
334 def clean_keys(self, options):
335 utils.system("rm -rf %s/keys/"%self.path)
337 def sites (self,options):
338 return self.do_sites(options)
340 def clean_sites (self,options):
341 return self.do_sites(options,action="delete")
343 def do_sites (self,options,action="add"):
344 for site_spec in self.plc_spec['sites']:
345 test_site = TestSite (self,site_spec)
346 if (action != "add"):
347 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
348 test_site.delete_site()
349 # deleted with the site
350 #test_site.delete_users()
353 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
354 test_site.create_site()
355 test_site.create_users()
358 def nodes (self, options):
359 return self.do_nodes(options)
360 def clean_nodes (self, options):
361 return self.do_nodes(options,action="delete")
363 def do_nodes (self, options,action="add"):
364 for site_spec in self.plc_spec['sites']:
365 test_site = TestSite (self,site_spec)
367 utils.header("Deleting nodes in site %s"%test_site.name())
368 for node_spec in site_spec['nodes']:
369 test_node=TestNode(self,test_site,node_spec)
370 utils.header("Deleting %s"%test_node.name())
371 test_node.delete_node()
373 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
374 for node_spec in site_spec['nodes']:
375 utils.pprint('Creating node %s'%node_spec,node_spec)
376 test_node = TestNode (self,test_site,node_spec)
377 test_node.create_node ()
380 # create nodegroups if needed, and populate
381 # no need for a clean_nodegroups if we are careful enough
382 def nodegroups (self, options):
383 # 1st pass to scan contents
385 for site_spec in self.plc_spec['sites']:
386 test_site = TestSite (self,site_spec)
387 for node_spec in site_spec['nodes']:
388 test_node=TestNode (self,test_site,node_spec)
389 if node_spec.has_key('nodegroups'):
390 nodegroupnames=node_spec['nodegroups']
391 if isinstance(nodegroupnames,StringTypes):
392 nodegroupnames = [ nodegroupnames ]
393 for nodegroupname in nodegroupnames:
394 if not groups_dict.has_key(nodegroupname):
395 groups_dict[nodegroupname]=[]
396 groups_dict[nodegroupname].append(test_node.name())
397 auth=self.auth_root()
398 for (nodegroupname,group_nodes) in groups_dict.iteritems():
400 self.server.GetNodeGroups(auth,{'name':nodegroupname})[0]
402 self.server.AddNodeGroup(auth,{'name':nodegroupname})
403 for node in group_nodes:
404 self.server.AddNodeToNodeGroup(auth,node,nodegroupname)
407 def all_hostnames (self) :
409 for site_spec in self.plc_spec['sites']:
410 hostnames += [ node_spec['node_fields']['hostname'] \
411 for node_spec in site_spec['nodes'] ]
414 # gracetime : during the first <gracetime> minutes nothing gets printed
415 def do_nodes_booted (self, minutes, gracetime=2):
417 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
418 graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
419 # the nodes that haven't checked yet - start with a full list and shrink over time
420 tocheck = self.all_hostnames()
421 utils.header("checking nodes %r"%tocheck)
422 # create a dict hostname -> status
423 status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
426 tocheck_status=self.server.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
428 for array in tocheck_status:
429 hostname=array['hostname']
430 boot_state=array['boot_state']
431 if boot_state == 'boot':
432 utils.header ("%s has reached the 'boot' state"%hostname)
434 # if it's a real node, never mind
435 (site_spec,node_spec)=self.locate_node(hostname)
436 if TestNode.is_real_model(node_spec['node_fields']['model']):
437 utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
440 if datetime.datetime.now() > graceout:
441 utils.header ("%s still in '%s' state"%(hostname,boot_state))
442 graceout=datetime.datetime.now()+datetime.timedelta(1)
443 status[hostname] = boot_state
445 tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != 'boot' ]
448 if datetime.datetime.now() > timeout:
449 for hostname in tocheck:
450 utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
452 # otherwise, sleep for a while
454 # only useful in empty plcs
457 def nodes_booted(self,options):
458 return self.do_nodes_booted(minutes=5)
460 #to scan and store the nodes's public keys and avoid to ask for confirmation when ssh
461 def scan_publicKeys(self,hostnames):
463 temp_knownhosts="/root/known_hosts"
464 remote_knownhosts="/root/.ssh/known_hosts"
465 self.run_in_host("touch %s"%temp_knownhosts )
466 for hostname in hostnames:
467 utils.header("Scan Public %s key and store it in the known_host file(under the root image) "%hostname)
468 scan=self.run_in_host('ssh-keyscan -t rsa %s >> %s '%(hostname,temp_knownhosts))
469 #Store the public keys in the right root image
470 self.copy_in_guest(temp_knownhosts,remote_knownhosts,True)
471 #clean the temp keys file used
472 self.run_in_host('rm -f %s '%temp_knownhosts )
473 except Exception, err:
476 def do_check_nodesSsh(self,minutes):
478 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
479 tocheck = self.all_hostnames()
480 self.scan_publicKeys(tocheck)
481 utils.header("checking Connectivity on nodes %r"%tocheck)
483 for hostname in tocheck:
484 # try to ssh in nodes
485 access=self.run_in_guest('ssh -i /etc/planetlab/root_ssh_key.rsa root@%s date'%hostname )
487 utils.header('The node %s is sshable -->'%hostname)
489 tocheck.remove(hostname)
491 (site_spec,node_spec)=self.locate_node(hostname)
492 if TestNode.is_real_model(node_spec['node_fields']['model']):
493 utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
494 tocheck.remove(hostname)
497 if datetime.datetime.now() > timeout:
498 for hostname in tocheck:
499 utils.header("FAILURE to ssh into %s"%hostname)
501 # otherwise, sleep for a while
503 # only useful in empty plcs
506 def nodes_ssh(self, options):
507 return self.do_check_nodesSsh(minutes=2)
509 def bootcd (self, options):
510 for site_spec in self.plc_spec['sites']:
511 test_site = TestSite (self,site_spec)
512 for node_spec in site_spec['nodes']:
513 test_node=TestNode (self,test_site,node_spec)
514 test_node.prepare_area()
515 test_node.create_boot_cd()
516 test_node.configure_qemu()
519 def do_check_intiscripts(self):
520 for site_spec in self.plc_spec['sites']:
521 test_site = TestSite (self,site_spec)
522 test_node = TestNode (self,test_site,site_spec['nodes'])
523 for slice_spec in self.plc_spec['slices']:
524 test_slice=TestSlice (self,test_site,slice_spec)
525 test_sliver=TestSliver(self,test_node,test_slice)
526 init_status=test_sliver.get_initscript(slice_spec)
527 if (not init_status):
531 def check_initscripts(self, options):
532 return self.do_check_intiscripts()
534 def initscripts (self, options):
535 for initscript in self.plc_spec['initscripts']:
536 utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
537 self.server.AddInitScript(self.auth_root(),initscript['initscript_fields'])
540 def slices (self, options):
541 return self.do_slices()
543 def clean_slices (self, options):
544 return self.do_slices("delete")
546 def do_slices (self, action="add"):
547 for slice in self.plc_spec['slices']:
548 site_spec = self.locate_site (slice['sitename'])
549 test_site = TestSite(self,site_spec)
550 test_slice=TestSlice(self,test_site,slice)
552 utils.header("Deleting slices in site %s"%test_site.name())
553 test_slice.delete_slice()
555 utils.pprint("Creating slice",slice)
556 test_slice.create_slice()
557 utils.header('Created Slice %s'%slice['slice_fields']['name'])
560 def check_slices(self, options):
561 for slice_spec in self.plc_spec['slices']:
562 site_spec = self.locate_site (slice_spec['sitename'])
563 test_site = TestSite(self,site_spec)
564 test_slice=TestSlice(self,test_site,slice_spec)
565 status=test_slice.do_check_slice(options)
570 def start_nodes (self, options):
571 utils.header("Starting nodes")
572 for site_spec in self.plc_spec['sites']:
573 TestSite(self,site_spec).start_nodes (options)
576 def stop_nodes (self, options):
577 self.kill_all_qemus(options)
580 def check_tcp (self, options):
581 #we just need to create a sliver object nothing else
582 test_sliver=TestSliver(self,
583 TestNode(self, TestSite(self,self.plc_spec['sites'][0]),
584 self.plc_spec['sites'][0]['nodes'][0]),
585 TestSlice(self,TestSite(self,self.plc_spec['sites'][0]),
586 self.plc_spec['slices']))
587 return test_sliver.do_check_tcp(self.plc_spec['tcp_param'],options)
589 # returns the filename to use for sql dump/restore, using options.dbname if set
590 def dbfile (self, database, options):
591 # uses options.dbname if it is found
594 if not isinstance(name,StringTypes):
597 t=datetime.datetime.now()
600 return "/root/%s-%s.sql"%(database,name)
602 def db_dump(self, options):
604 dump=self.dbfile("planetab4",options)
605 self.run_in_guest('pg_dump -U pgsqluser planetlab4 -f '+ dump)
606 utils.header('Dumped planetlab4 database in %s'%dump)
609 def db_restore(self, options):
610 dump=self.dbfile("planetab4",options)
612 self.run_in_guest('service httpd stop')
613 # xxx - need another wrapper
614 self.run_in_guest_piped('echo drop database planetlab4','psql --user=pgsqluser template1')
615 self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab4')
616 self.run_in_guest('psql -U pgsqluser planetlab4 -f '+dump)
617 ##starting httpd service
618 self.run_in_guest('service httpd start')
620 utils.header('Database restored from ' + dump)
623 def standby_1(): pass
625 def standby_2(): pass
627 def standby_3(): pass
629 def standby_4(): pass
631 def standby_5(): pass
633 def standby_6(): pass
635 def standby_7(): pass
637 def standby_8(): pass
639 def standby_9(): pass
641 def standby_10(): pass
643 def standby_11(): pass
645 def standby_12(): pass
647 def standby_13(): pass
649 def standby_14(): pass
651 def standby_15(): pass
653 def standby_16(): pass
655 def standby_17(): pass
657 def standby_18(): pass
659 def standby_19(): pass
661 def standby_20(): pass