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).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).copy("qemu_kill.sh")
175 # this is the brute force version, kill all qemus on that host box
176 TestBox(box).run("./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).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 repo_url = os.path.dirname(repo_url)
249 create_vserver="%s/vtest-init-vserver.sh %s %s -- --interface eth0:%s"%\
250 (build_dir,self.vservername,repo_url,self.vserverip)
251 if self.run_in_host(create_vserver) != 0:
252 raise Exception,"Could not create vserver for %s"%self.vservername
255 def install(self,options):
257 return self.install_vserver(options)
259 return self.install_chroot(options)
262 def install_rpm_chroot(self,options):
263 utils.header('Installing from %s'%options.myplc_url)
264 url=options.myplc_url
265 self.run_in_host('rpm -Uvh '+url)
266 self.run_in_host('service plc mount')
269 def install_rpm_vserver(self,options):
270 self.run_in_guest("yum -y install myplc-native")
273 def install_rpm(self,options):
275 return self.install_rpm_vserver(options)
277 return self.install_rpm_chroot(options)
280 def configure(self,options):
281 tmpname='%s/%s.plc-config-tty'%(options.path,self.name())
282 fileconf=open(tmpname,'w')
283 for var in [ 'PLC_NAME',
287 'PLC_MAIL_SUPPORT_ADDRESS',
294 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
295 fileconf.write('w\n')
296 fileconf.write('q\n')
298 utils.system('cat %s'%tmpname)
299 self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
300 utils.system('rm %s'%tmpname)
303 # the chroot install is slightly different to this respect
304 def start(self, options):
306 self.run_in_guest('service plc start')
308 self.run_in_host('service plc start')
311 def stop(self, options):
313 self.run_in_guest('service plc stop')
315 self.run_in_host('service plc stop')
318 # could use a TestKey class
319 def store_keys(self, options):
320 for key_spec in self.plc_spec['keys']:
321 TestKey(self,key_spec).store_key()
324 def clean_keys(self, options):
325 utils.system("rm -rf %s/keys/"%self.path)
327 def sites (self,options):
328 return self.do_sites(options)
330 def clean_sites (self,options):
331 return self.do_sites(options,action="delete")
333 def do_sites (self,options,action="add"):
334 for site_spec in self.plc_spec['sites']:
335 test_site = TestSite (self,site_spec)
336 if (action != "add"):
337 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
338 test_site.delete_site()
339 # deleted with the site
340 #test_site.delete_users()
343 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
344 test_site.create_site()
345 test_site.create_users()
348 def nodes (self, options):
349 return self.do_nodes(options)
350 def clean_nodes (self, options):
351 return self.do_nodes(options,action="delete")
353 def do_nodes (self, options,action="add"):
354 for site_spec in self.plc_spec['sites']:
355 test_site = TestSite (self,site_spec)
357 utils.header("Deleting nodes in site %s"%test_site.name())
358 for node_spec in site_spec['nodes']:
359 test_node=TestNode(self,test_site,node_spec)
360 utils.header("Deleting %s"%test_node.name())
361 test_node.delete_node()
363 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
364 for node_spec in site_spec['nodes']:
365 utils.pprint('Creating node %s'%node_spec,node_spec)
366 test_node = TestNode (self,test_site,node_spec)
367 test_node.create_node ()
370 # create nodegroups if needed, and populate
371 # no need for a clean_nodegroups if we are careful enough
372 def nodegroups (self, options):
373 # 1st pass to scan contents
375 for site_spec in self.plc_spec['sites']:
376 test_site = TestSite (self,site_spec)
377 for node_spec in site_spec['nodes']:
378 test_node=TestNode (self,test_site,node_spec)
379 if node_spec.has_key('nodegroups'):
380 nodegroupnames=node_spec['nodegroups']
381 if isinstance(nodegroupnames,StringTypes):
382 nodegroupnames = [ nodegroupnames ]
383 for nodegroupname in nodegroupnames:
384 if not groups_dict.has_key(nodegroupname):
385 groups_dict[nodegroupname]=[]
386 groups_dict[nodegroupname].append(test_node.name())
387 auth=self.auth_root()
388 for (nodegroupname,group_nodes) in groups_dict.iteritems():
390 self.server.GetNodeGroups(auth,{'name':nodegroupname})[0]
392 self.server.AddNodeGroup(auth,{'name':nodegroupname})
393 for node in group_nodes:
394 self.server.AddNodeToNodeGroup(auth,node,nodegroupname)
397 def all_hostnames (self) :
399 for site_spec in self.plc_spec['sites']:
400 hostnames += [ node_spec['node_fields']['hostname'] \
401 for node_spec in site_spec['nodes'] ]
404 # gracetime : during the first <gracetime> minutes nothing gets printed
405 def do_nodes_booted (self, minutes, gracetime=2):
407 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
408 graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
409 # the nodes that haven't checked yet - start with a full list and shrink over time
410 tocheck = self.all_hostnames()
411 utils.header("checking nodes %r"%tocheck)
412 # create a dict hostname -> status
413 status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
416 tocheck_status=self.server.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
418 for array in tocheck_status:
419 hostname=array['hostname']
420 boot_state=array['boot_state']
421 if boot_state == 'boot':
422 utils.header ("%s has reached the 'boot' state"%hostname)
424 # if it's a real node, never mind
425 (site_spec,node_spec)=self.locate_node(hostname)
426 if TestNode.is_real_model(node_spec['node_fields']['model']):
427 utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
430 if datetime.datetime.now() > graceout:
431 utils.header ("%s still in '%s' state"%(hostname,boot_state))
432 graceout=datetime.datetime.now()+datetime.timedelta(1)
433 status[hostname] = boot_state
435 tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != 'boot' ]
438 if datetime.datetime.now() > timeout:
439 for hostname in tocheck:
440 utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
442 # otherwise, sleep for a while
444 # only useful in empty plcs
447 def nodes_booted(self,options):
448 return self.do_nodes_booted(minutes=5)
450 #to scan and store the nodes's public keys and avoid to ask for confirmation when ssh
451 def scan_publicKeys(self,hostnames):
453 temp_knownhosts="/root/known_hosts"
454 remote_knownhosts="/root/.ssh/known_hosts"
455 self.run_in_host("touch %s"%temp_knownhosts )
456 for hostname in hostnames:
457 utils.header("Scan Public %s key and store it in the known_host file(under the root image) "%hostname)
458 scan=self.run_in_host('ssh-keyscan -t rsa %s >> %s '%(hostname,temp_knownhosts))
459 #Store the public keys in the right root image
460 self.copy_in_guest(temp_knownhosts,remote_knownhosts,True)
461 #clean the temp keys file used
462 self.run_in_host('rm -f %s '%temp_knownhosts )
463 except Exception, err:
466 def do_check_nodesSsh(self,minutes):
468 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
469 tocheck = self.all_hostnames()
470 self.scan_publicKeys(tocheck)
471 utils.header("checking Connectivity on nodes %r"%tocheck)
473 for hostname in tocheck:
474 # try to ssh in nodes
475 access=self.run_in_guest('ssh -i /etc/planetlab/root_ssh_key.rsa root@%s date'%hostname )
477 utils.header('The node %s is sshable -->'%hostname)
479 tocheck.remove(hostname)
481 (site_spec,node_spec)=self.locate_node(hostname)
482 if TestNode.is_real_model(node_spec['node_fields']['model']):
483 utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
484 tocheck.remove(hostname)
487 if datetime.datetime.now() > timeout:
488 for hostname in tocheck:
489 utils.header("FAILURE to ssh into %s"%hostname)
491 # otherwise, sleep for a while
493 # only useful in empty plcs
496 def nodes_ssh(self, options):
497 return self.do_check_nodesSsh(minutes=2)
499 def bootcd (self, options):
500 for site_spec in self.plc_spec['sites']:
501 test_site = TestSite (self,site_spec)
502 for node_spec in site_spec['nodes']:
503 test_node=TestNode (self,test_site,node_spec)
504 test_node.create_boot_cd(options.path)
507 def do_check_intiscripts(self):
508 for site_spec in self.plc_spec['sites']:
509 test_site = TestSite (self,site_spec)
510 test_node = TestNode (self,test_site,site_spec['nodes'])
511 for slice_spec in self.plc_spec['slices']:
512 test_slice=TestSlice (self,test_site,slice_spec)
513 test_sliver=TestSliver(self,test_node,test_slice)
514 init_status=test_sliver.get_initscript(slice_spec)
515 if (not init_status):
519 def check_initscripts(self, options):
520 return self.do_check_intiscripts()
522 def initscripts (self, options):
523 for initscript in self.plc_spec['initscripts']:
524 utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
525 self.server.AddInitScript(self.auth_root(),initscript['initscript_fields'])
528 def slices (self, options):
529 return self.do_slices()
531 def clean_slices (self, options):
532 return self.do_slices("delete")
534 def do_slices (self, action="add"):
535 for slice in self.plc_spec['slices']:
536 site_spec = self.locate_site (slice['sitename'])
537 test_site = TestSite(self,site_spec)
538 test_slice=TestSlice(self,test_site,slice)
540 utils.header("Deleting slices in site %s"%test_site.name())
541 test_slice.delete_slice()
543 utils.pprint("Creating slice",slice)
544 test_slice.create_slice()
545 utils.header('Created Slice %s'%slice['slice_fields']['name'])
548 def check_slices(self, options):
549 for slice_spec in self.plc_spec['slices']:
550 site_spec = self.locate_site (slice_spec['sitename'])
551 test_site = TestSite(self,site_spec)
552 test_slice=TestSlice(self,test_site,slice_spec)
553 status=test_slice.do_check_slice(options)
558 def start_nodes (self, options):
559 utils.header("Starting nodes")
560 for site_spec in self.plc_spec['sites']:
561 TestSite(self,site_spec).start_nodes (options)
564 def stop_nodes (self, options):
565 self.kill_all_qemus(options)
568 def check_tcp (self, options):
569 #we just need to create a sliver object nothing else
570 test_sliver=TestSliver(self,
571 TestNode(self, TestSite(self,self.plc_spec['sites'][0]),
572 self.plc_spec['sites'][0]['nodes'][0]),
573 TestSlice(self,TestSite(self,self.plc_spec['sites'][0]),
574 self.plc_spec['slices']))
575 return test_sliver.do_check_tcp(self.plc_spec['tcp_param'],options)
577 # returns the filename to use for sql dump/restore, using options.dbname if set
578 def dbfile (self, database, options):
579 # uses options.dbname if it is found
582 if not isinstance(name,StringTypes):
585 t=datetime.datetime.now()
588 return "/root/%s-%s.sql"%(database,name)
590 def db_dump(self, options):
592 dump=self.dbfile("planetab4",options)
593 self.run_in_guest('pg_dump -U pgsqluser planetlab4 -f '+ dump)
594 utils.header('Dumped planetlab4 database in %s'%dump)
597 def db_restore(self, options):
598 dump=self.dbfile("planetab4",options)
600 self.run_in_guest('service httpd stop')
601 # xxx - need another wrapper
602 self.run_in_guest_piped('echo drop database planetlab4','psql --user=pgsqluser template1')
603 self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab4')
604 self.run_in_guest('psql -U pgsqluser planetlab4 -f '+dump)
605 ##starting httpd service
606 self.run_in_guest('service httpd start')
608 utils.header('Database restored from ' + dump)
611 def standby_1(): pass
613 def standby_2(): pass
615 def standby_3(): pass
617 def standby_4(): pass
619 def standby_5(): pass
621 def standby_6(): pass
623 def standby_7(): pass
625 def standby_8(): pass
627 def standby_9(): pass
629 def standby_10(): pass
631 def standby_11(): pass
633 def standby_12(): pass
635 def standby_13(): pass
637 def standby_14(): pass
639 def standby_15(): pass
641 def standby_16(): pass
643 def standby_17(): pass
645 def standby_18(): pass
647 def standby_19(): pass
649 def standby_20(): pass