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'] == 'localhost'
59 # define the API methods on this object through xmlrpc
60 # would help, but not strictly necessary
64 # command gets run in the chroot/vserver
65 def host_to_guest(self,command):
67 return "vserver %s exec %s"%(self.vservername,command)
69 return "chroot /plc/root %s"%utils.backslash_shell_specials(command)
71 # command gets run on the right box
72 def to_host(self,command):
76 return "ssh %s %s"%(self.plc_spec['hostname'],utils.backslash_shell_specials(command))
78 def full_command(self,command):
79 return self.to_host(self.host_to_guest(command))
81 def run_in_guest (self,command):
82 return utils.system(self.full_command(command))
83 def run_in_host (self,command):
84 return utils.system(self.to_host(command))
87 def run_in_guest_piped (self,local,remote):
88 return utils.system(local+" | "+self.full_command(remote))
90 # copy a file to the myplc root image - pass in_data=True if the file must go in /plc/data
91 def copy_in_guest (self, localfile, remotefile, in_data=False):
93 chroot_dest="/plc/data"
95 chroot_dest="/plc/root"
98 utils.system("cp %s %s/%s"%(localfile,chroot_dest,remotefile))
100 utils.system("cp %s /vservers/%s/%s"%(localfile,self.vservername,remotefile))
103 utils.system("scp %s %s:%s/%s"%(localfile,self.plc_spec['hostname'],chroot_dest,remotefile))
105 utils.system("scp %s %s@/vservers/%s/%s"%(localfile,self.plc_spec['hostname'],self.vservername,remotefile))
107 def auth_root (self):
108 return {'Username':self.plc_spec['PLC_ROOT_USER'],
109 'AuthMethod':'password',
110 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
111 'Role' : self.plc_spec['role']
113 def locate_site (self,sitename):
114 for site in self.plc_spec['sites']:
115 if site['site_fields']['name'] == sitename:
117 if site['site_fields']['login_base'] == sitename:
119 raise Exception,"Cannot locate site %s"%sitename
121 def locate_node (self,nodename):
122 for site in self.plc_spec['sites']:
123 for node in site['nodes']:
124 if node['node_fields']['hostname'] == nodename:
126 raise Exception,"Cannot locate node %s"%nodename
128 def locate_key (self,keyname):
129 for key in self.plc_spec['keys']:
130 if key['name'] == keyname:
132 raise Exception,"Cannot locate key %s"%keyname
134 # all different hostboxes used in this plc
135 def gather_hostBoxes(self):
136 # maps on sites and nodes, return [ (host_box,test_node) ]
138 for site_spec in self.plc_spec['sites']:
139 test_site = TestSite (self,site_spec)
140 for node_spec in site_spec['nodes']:
141 test_node = TestNode (self, test_site, node_spec)
142 if not test_node.is_real():
143 tuples.append( (test_node.host_box(),test_node) )
144 # transform into a dict { 'host_box' -> [ hostnames .. ] }
146 for (box,node) in tuples:
147 if not result.has_key(box):
150 result[box].append(node)
153 # a step for checking this stuff
154 def showboxes (self,options):
156 for (box,nodes) in self.gather_hostBoxes().iteritems():
157 print box,":"," + ".join( [ node.name() for node in nodes ] )
160 # make this a valid step
161 def kill_all_qemus(self,options):
162 for (box,nodes) in self.gather_hostBoxes().iteritems():
163 # this is the brute force version, kill all qemus on that host box
164 TestBox(box).kill_all_qemus()
167 # make this a valid step
168 def list_all_qemus(self,options):
169 for (box,nodes) in self.gather_hostBoxes().iteritems():
171 TestBox(box).copy("qemu_kill.sh")
172 # this is the brute force version, kill all qemus on that host box
173 TestBox(box).run("./qemu_kill.sh -l")
176 # kill only the right qemus
177 def kill_qemus(self,options):
178 for (box,nodes) in self.gather_hostBoxes().iteritems():
180 TestBox(box).copy("qemu_kill.sh")
181 # the fine-grain version
186 def clear_ssh_config (self,options):
187 # install local ssh_config file as root's .ssh/config - ssh should be quiet
188 # dir might need creation first
189 self.run_in_guest("mkdir /root/.ssh")
190 self.run_in_guest("chmod 700 /root/.ssh")
191 # this does not work - > redirection somehow makes it until an argument to cat
192 #self.run_in_guest_piped("cat ssh_config","cat > /root/.ssh/config")
193 self.copy_in_guest("ssh_config","/root/.ssh/config",True)
196 #################### step methods
199 def uninstall_chroot(self,options):
200 self.run_in_host('service plc safestop')
201 #####detecting the last myplc version installed and remove it
202 self.run_in_host('rpm -e myplc')
203 ##### Clean up the /plc directory
204 self.run_in_host('rm -rf /plc/data')
205 ##### stop any running vservers
206 self.run_in_host('for vserver in $(ls /vservers/* | sed -e s,/vservers/,,) ; do vserver $vserver stop ; done')
209 def uninstall_vserver(self,options):
210 self.run_in_host("vserver --silent %s delete"%self.vservername)
213 def uninstall(self,options):
214 # if there's a chroot-based myplc running, and then a native-based myplc is being deployed
215 # it sounds safer to have the former uninstalled too
216 # now the vserver method cannot be invoked for chroot instances as vservername is required
218 self.uninstall_vserver(options)
219 self.uninstall_chroot(options)
221 self.uninstall_chroot(options)
225 def install_chroot(self,options):
229 # xxx this would not work with hostname != localhost as mylc-init-vserver was extracted locally
230 def install_vserver(self,options):
231 # we need build dir for vtest-init-vserver
233 # a full path for the local calls
234 build_dir=self.path+"/build"
236 # use a standard name - will be relative to HOME
237 build_dir="tests-system-build"
238 build_checkout = "svn checkout %s %s"%(options.build_url,build_dir)
239 if self.run_in_host(build_checkout) != 0:
240 raise Exception,"Cannot checkout build dir"
241 # the repo url is taken from myplc-url
242 # with the last two steps (i386/myplc...) removed
243 repo_url = options.myplc_url
244 repo_url = os.path.dirname(repo_url)
245 repo_url = os.path.dirname(repo_url)
246 create_vserver="%s/vtest-init-vserver.sh %s %s -- --interface eth0:%s"%\
247 (build_dir,self.vservername,repo_url,self.vserverip)
248 if self.run_in_host(create_vserver) != 0:
249 raise Exception,"Could not create vserver for %s"%self.vservername
252 def install(self,options):
254 return self.install_vserver(options)
256 return self.install_chroot(options)
259 def install_rpm_chroot(self,options):
260 utils.header('Installing from %s'%options.myplc_url)
261 url=options.myplc_url
262 self.run_in_host('rpm -Uvh '+url)
263 self.run_in_host('service plc mount')
266 def install_rpm_vserver(self,options):
267 self.run_in_guest("yum -y install myplc-native")
270 def install_rpm(self,options):
272 return self.install_rpm_vserver(options)
274 return self.install_rpm_chroot(options)
277 def configure(self,options):
278 tmpname='%s/%s.plc-config-tty'%(options.path,self.name())
279 fileconf=open(tmpname,'w')
280 for var in [ 'PLC_NAME',
284 'PLC_MAIL_SUPPORT_ADDRESS',
291 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
292 fileconf.write('w\n')
293 fileconf.write('q\n')
295 utils.system('cat %s'%tmpname)
296 self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
297 utils.system('rm %s'%tmpname)
300 # the chroot install is slightly different to this respect
301 def start(self, options):
303 self.run_in_guest('service plc start')
305 self.run_in_host('service plc start')
308 def stop(self, options):
310 self.run_in_guest('service plc stop')
312 self.run_in_host('service plc stop')
315 # could use a TestKey class
316 def store_keys(self, options):
317 for key_spec in self.plc_spec['keys']:
318 TestKey(self,key_spec).store_key()
321 def clean_keys(self, options):
322 utils.system("rm -rf %s/keys/"%self.path)
324 def sites (self,options):
325 return self.do_sites(options)
327 def clean_sites (self,options):
328 return self.do_sites(options,action="delete")
330 def do_sites (self,options,action="add"):
331 for site_spec in self.plc_spec['sites']:
332 test_site = TestSite (self,site_spec)
333 if (action != "add"):
334 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
335 test_site.delete_site()
336 # deleted with the site
337 #test_site.delete_users()
340 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
341 test_site.create_site()
342 test_site.create_users()
345 def nodes (self, options):
346 return self.do_nodes(options)
347 def clean_nodes (self, options):
348 return self.do_nodes(options,action="delete")
350 def do_nodes (self, options,action="add"):
351 for site_spec in self.plc_spec['sites']:
352 test_site = TestSite (self,site_spec)
354 utils.header("Deleting nodes in site %s"%test_site.name())
355 for node_spec in site_spec['nodes']:
356 test_node=TestNode(self,test_site,node_spec)
357 utils.header("Deleting %s"%test_node.name())
358 test_node.delete_node()
360 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
361 for node_spec in site_spec['nodes']:
362 utils.show_spec('Creating node %s'%node_spec,node_spec)
363 test_node = TestNode (self,test_site,node_spec)
364 test_node.create_node ()
367 # create nodegroups if needed, and populate
368 # no need for a clean_nodegroups if we are careful enough
369 def nodegroups (self, options):
370 # 1st pass to scan contents
372 for site_spec in self.plc_spec['sites']:
373 test_site = TestSite (self,site_spec)
374 for node_spec in site_spec['nodes']:
375 test_node=TestNode (self,test_site,node_spec)
376 if node_spec.has_key('nodegroups'):
377 nodegroupnames=node_spec['nodegroups']
378 if isinstance(nodegroupnames,StringTypes):
379 nodegroupnames = [ nodegroupnames ]
380 for nodegroupname in nodegroupnames:
381 if not groups_dict.has_key(nodegroupname):
382 groups_dict[nodegroupname]=[]
383 groups_dict[nodegroupname].append(test_node.name())
384 auth=self.auth_root()
385 for (nodegroupname,group_nodes) in groups_dict.iteritems():
387 self.server.GetNodeGroups(auth,{'name':nodegroupname})[0]
389 self.server.AddNodeGroup(auth,{'name':nodegroupname})
390 for node in group_nodes:
391 self.server.AddNodeToNodeGroup(auth,node,nodegroupname)
394 def all_hostnames (self) :
396 for site_spec in self.plc_spec['sites']:
397 hostnames += [ node_spec['node_fields']['hostname'] \
398 for node_spec in site_spec['nodes'] ]
401 # gracetime : during the first <gracetime> minutes nothing gets printed
402 def do_nodes_booted (self, minutes, gracetime=2):
404 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
405 graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
406 # the nodes that haven't checked yet - start with a full list and shrink over time
407 tocheck = self.all_hostnames()
408 utils.header("checking nodes %r"%tocheck)
409 # create a dict hostname -> status
410 status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
413 tocheck_status=self.server.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
415 for array in tocheck_status:
416 hostname=array['hostname']
417 boot_state=array['boot_state']
418 if boot_state == 'boot':
419 utils.header ("%s has reached the 'boot' state"%hostname)
421 # if it's a real node, never mind
422 (site_spec,node_spec)=self.locate_node(hostname)
423 if TestNode.is_real_model(node_spec['node_fields']['model']):
424 utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
427 if datetime.datetime.now() > graceout:
428 utils.header ("%s still in '%s' state"%(hostname,boot_state))
429 graceout=datetime.datetime.now()+datetime.timedelta(1)
430 status[hostname] = boot_state
432 tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != 'boot' ]
435 if datetime.datetime.now() > timeout:
436 for hostname in tocheck:
437 utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
439 # otherwise, sleep for a while
441 # only useful in empty plcs
444 def nodes_booted(self,options):
445 return self.do_nodes_booted(minutes=5)
447 #to scan and store the nodes's public keys and avoid to ask for confirmation when ssh
448 def scan_publicKeys(self,hostnames):
450 temp_knownhosts="/root/known_hosts"
451 remote_knownhosts="/root/.ssh/known_hosts"
452 self.run_in_host("touch %s"%temp_knownhosts )
453 for hostname in hostnames:
454 utils.header("Scan Public %s key and store it in the known_host file(under the root image) "%hostname)
455 scan=self.run_in_host('ssh-keyscan -t rsa %s >> %s '%(hostname,temp_knownhosts))
456 #Store the public keys in the right root image
457 self.copy_in_guest(temp_knownhosts,remote_knownhosts,True)
458 #clean the temp keys file used
459 self.run_in_host('rm -f %s '%temp_knownhosts )
460 except Exception, err:
463 def do_check_nodesSsh(self,minutes):
465 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
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 test_node = TestNode (self,test_site,site_spec['nodes'])
508 for slice_spec in self.plc_spec['slices']:
509 test_slice=TestSlice (self,test_site,slice_spec)
510 test_sliver=TestSliver(self,test_node,test_slice)
511 init_status=test_sliver.get_initscript(slice_spec)
512 if (not init_status):
516 def check_initscripts(self, options):
517 return self.do_check_intiscripts()
519 def initscripts (self, options):
520 for initscript in self.plc_spec['initscripts']:
521 utils.show_spec('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
522 self.server.AddInitScript(self.auth_root(),initscript['initscript_fields'])
525 def slices (self, options):
526 return self.do_slices()
528 def clean_slices (self, options):
529 return self.do_slices("delete")
531 def do_slices (self, action="add"):
532 for slice in self.plc_spec['slices']:
533 site_spec = self.locate_site (slice['sitename'])
534 test_site = TestSite(self,site_spec)
535 test_slice=TestSlice(self,test_site,slice)
537 utils.header("Deleting slices in site %s"%test_site.name())
538 test_slice.delete_slice()
540 utils.show_spec("Creating slice",slice)
541 test_slice.create_slice()
542 utils.header('Created Slice %s'%slice['slice_fields']['name'])
545 def check_slices(self, options):
546 for slice_spec in self.plc_spec['slices']:
547 site_spec = self.locate_site (slice_spec['sitename'])
548 test_site = TestSite(self,site_spec)
549 test_slice=TestSlice(self,test_site,slice_spec)
550 status=test_slice.do_check_slice(options)
555 def start_nodes (self, options):
556 utils.header("Starting nodes")
557 for site_spec in self.plc_spec['sites']:
558 TestSite(self,site_spec).start_nodes (options)
561 def stop_nodes (self, options):
562 self.kill_all_qemus()
565 def check_tcp (self, options):
566 #we just need to create a sliver object nothing else
567 test_sliver=TestSliver(self,
568 TestNode(self, TestSite(self,self.plc_spec['sites'][0]),
569 self.plc_spec['sites'][0]['nodes'][0]),
570 TestSlice(self,TestSite(self,self.plc_spec['sites'][0]),
571 self.plc_spec['slices']))
572 return test_sliver.do_check_tcp(self.plc_spec['tcp_param'],options)
574 # returns the filename to use for sql dump/restore, using options.dbname if set
575 def dbfile (self, database, options):
576 # uses options.dbname if it is found
579 if not isinstance(name,StringTypes):
582 t=datetime.datetime.now()
585 return "/root/%s-%s.sql"%(database,name)
587 def db_dump(self, options):
589 dump=self.dbfile("planetab4",options)
590 self.run_in_guest('pg_dump -U pgsqluser planetlab4 -f '+ dump)
591 utils.header('Dumped planetlab4 database in %s'%dump)
594 def db_restore(self, options):
595 dump=self.dbfile("planetab4",options)
597 self.run_in_guest('service httpd stop')
598 # xxx - need another wrapper
599 self.run_in_guest_piped('echo drop database planetlab4','psql --user=pgsqluser template1')
600 self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab4')
601 self.run_in_guest('psql -U pgsqluser planetlab4 -f '+dump)
602 ##starting httpd service
603 self.run_in_guest('service httpd start')
605 utils.header('Database restored from ' + dump)
608 def standby_1(): pass
610 def standby_2(): pass
612 def standby_3(): pass
614 def standby_4(): pass
616 def standby_5(): pass
618 def standby_6(): pass
620 def standby_7(): pass
622 def standby_8(): pass
624 def standby_9(): pass
626 def standby_10(): pass
628 def standby_11(): pass
630 def standby_12(): pass
632 def standby_13(): pass
634 def standby_14(): pass
636 def standby_15(): pass
638 def standby_16(): pass
640 def standby_17(): pass
642 def standby_18(): pass
644 def standby_19(): pass
646 def standby_20(): pass