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 # kill only the right qemus
167 def kill_qemus(self,options):
168 for (box,nodes) in self.gather_hostBoxes().iteritems():
169 # the fine-grain version
174 def clear_ssh_config (self,options):
175 # install local ssh_config file as root's .ssh/config - ssh should be quiet
176 # dir might need creation first
177 self.run_in_guest("mkdir /root/.ssh")
178 self.run_in_guest("chmod 700 /root/.ssh")
179 # this does not work - > redirection somehow makes it until an argument to cat
180 #self.run_in_guest_piped("cat ssh_config","cat > /root/.ssh/config")
181 self.copy_in_guest("ssh_config","/root/.ssh/config",True)
184 #################### step methods
187 def uninstall_chroot(self,options):
188 self.run_in_host('service plc safestop')
189 #####detecting the last myplc version installed and remove it
190 self.run_in_host('rpm -e myplc')
191 ##### Clean up the /plc directory
192 self.run_in_host('rm -rf /plc/data')
193 ##### stop any running vservers
194 self.run_in_host('for vserver in $(ls /vservers/* | sed -e s,/vservers/,,) ; do vserver $vserver stop ; done')
197 def uninstall_vserver(self,options):
198 self.run_in_host("vserver --silent %s delete"%self.vservername)
201 def uninstall(self,options):
202 # if there's a chroot-based myplc running, and then a native-based myplc is being deployed
203 # it sounds safer to have the former uninstalled too
204 # now the vserver method cannot be invoked for chroot instances as vservername is required
206 self.uninstall_vserver(options)
207 self.uninstall_chroot(options)
209 self.uninstall_chroot(options)
213 def install_chroot(self,options):
217 # xxx this would not work with hostname != localhost as mylc-init-vserver was extracted locally
218 def install_vserver(self,options):
219 # we need build dir for vtest-init-vserver
221 # a full path for the local calls
222 build_dir=self.path+"/build"
224 # use a standard name - will be relative to HOME
225 build_dir="tests-system-build"
226 build_checkout = "svn checkout %s %s"%(options.build_url,build_dir)
227 if self.run_in_host(build_checkout) != 0:
228 raise Exception,"Cannot checkout build dir"
229 # the repo url is taken from myplc-url
230 # with the last two steps (i386/myplc...) removed
231 repo_url = options.myplc_url
232 repo_url = os.path.dirname(repo_url)
233 repo_url = os.path.dirname(repo_url)
234 create_vserver="%s/vtest-init-vserver.sh %s %s -- --interface eth0:%s"%\
235 (build_dir,self.vservername,repo_url,self.vserverip)
236 if self.run_in_host(create_vserver) != 0:
237 raise Exception,"Could not create vserver for %s"%self.vservername
240 def install(self,options):
242 return self.install_vserver(options)
244 return self.install_chroot(options)
247 def install_rpm_chroot(self,options):
248 utils.header('Installing from %s'%options.myplc_url)
249 url=options.myplc_url
250 self.run_in_host('rpm -Uvh '+url)
251 self.run_in_host('service plc mount')
254 def install_rpm_vserver(self,options):
255 self.run_in_guest("yum -y install myplc-native")
258 def install_rpm(self,options):
260 return self.install_rpm_vserver(options)
262 return self.install_rpm_chroot(options)
265 def configure(self,options):
266 tmpname='%s/%s.plc-config-tty'%(options.path,self.name())
267 fileconf=open(tmpname,'w')
268 for var in [ 'PLC_NAME',
272 'PLC_MAIL_SUPPORT_ADDRESS',
279 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
280 fileconf.write('w\n')
281 fileconf.write('q\n')
283 utils.system('cat %s'%tmpname)
284 self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
285 utils.system('rm %s'%tmpname)
288 # the chroot install is slightly different to this respect
289 def start(self, options):
291 self.run_in_guest('service plc start')
293 self.run_in_host('service plc start')
296 def stop(self, options):
298 self.run_in_guest('service plc stop')
300 self.run_in_host('service plc stop')
303 # could use a TestKey class
304 def store_keys(self, options):
305 for key_spec in self.plc_spec['keys']:
306 TestKey(self,key_spec).store_key()
309 def clean_keys(self, options):
310 utils.system("rm -rf %s/keys/"%self.path)
312 def sites (self,options):
313 return self.do_sites(options)
315 def clean_sites (self,options):
316 return self.do_sites(options,action="delete")
318 def do_sites (self,options,action="add"):
319 for site_spec in self.plc_spec['sites']:
320 test_site = TestSite (self,site_spec)
321 if (action != "add"):
322 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
323 test_site.delete_site()
324 # deleted with the site
325 #test_site.delete_users()
328 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
329 test_site.create_site()
330 test_site.create_users()
333 def nodes (self, options):
334 return self.do_nodes(options)
335 def clean_nodes (self, options):
336 return self.do_nodes(options,action="delete")
338 def do_nodes (self, options,action="add"):
339 for site_spec in self.plc_spec['sites']:
340 test_site = TestSite (self,site_spec)
342 utils.header("Deleting nodes in site %s"%test_site.name())
343 for node_spec in site_spec['nodes']:
344 test_node=TestNode(self,test_site,node_spec)
345 utils.header("Deleting %s"%test_node.name())
346 test_node.delete_node()
348 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
349 for node_spec in site_spec['nodes']:
350 utils.show_spec('Creating node %s'%node_spec,node_spec)
351 test_node = TestNode (self,test_site,node_spec)
352 test_node.create_node ()
355 # create nodegroups if needed, and populate
356 # no need for a clean_nodegroups if we are careful enough
357 def nodegroups (self, options):
358 # 1st pass to scan contents
360 for site_spec in self.plc_spec['sites']:
361 test_site = TestSite (self,site_spec)
362 for node_spec in site_spec['nodes']:
363 test_node=TestNode (self,test_site,node_spec)
364 if node_spec.has_key('nodegroups'):
365 nodegroupnames=node_spec['nodegroups']
366 if isinstance(nodegroupnames,StringTypes):
367 nodegroupnames = [ nodegroupnames ]
368 for nodegroupname in nodegroupnames:
369 if not groups_dict.has_key(nodegroupname):
370 groups_dict[nodegroupname]=[]
371 groups_dict[nodegroupname].append(test_node.name())
372 auth=self.auth_root()
373 for (nodegroupname,group_nodes) in groups_dict.iteritems():
375 self.server.GetNodeGroups(auth,{'name':nodegroupname})[0]
377 self.server.AddNodeGroup(auth,{'name':nodegroupname})
378 for node in group_nodes:
379 self.server.AddNodeToNodeGroup(auth,node,nodegroupname)
382 def all_hostnames (self) :
384 for site_spec in self.plc_spec['sites']:
385 hostnames += [ node_spec['node_fields']['hostname'] \
386 for node_spec in site_spec['nodes'] ]
389 # gracetime : during the first <gracetime> minutes nothing gets printed
390 def do_nodes_booted (self, minutes, gracetime=2):
392 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
393 graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
394 # the nodes that haven't checked yet - start with a full list and shrink over time
395 tocheck = self.all_hostnames()
396 utils.header("checking nodes %r"%tocheck)
397 # create a dict hostname -> status
398 status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
401 tocheck_status=self.server.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
403 for array in tocheck_status:
404 hostname=array['hostname']
405 boot_state=array['boot_state']
406 if boot_state == 'boot':
407 utils.header ("%s has reached the 'boot' state"%hostname)
409 # if it's a real node, never mind
410 (site_spec,node_spec)=self.locate_node(hostname)
411 if TestNode.is_real_model(node_spec['node_fields']['model']):
412 utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
415 if datetime.datetime.now() > graceout:
416 utils.header ("%s still in '%s' state"%(hostname,boot_state))
417 graceout=datetime.datetime.now()+datetime.timedelta(1)
418 status[hostname] = boot_state
420 tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != 'boot' ]
423 if datetime.datetime.now() > timeout:
424 for hostname in tocheck:
425 utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
427 # otherwise, sleep for a while
429 # only useful in empty plcs
432 def nodes_booted(self,options):
433 return self.do_nodes_booted(minutes=5)
435 #to scan and store the nodes's public keys and avoid to ask for confirmation when ssh
436 def scan_publicKeys(self,hostnames):
438 temp_knownhosts="/root/known_hosts"
439 remote_knownhosts="/root/.ssh/known_hosts"
440 self.run_in_host("touch %s"%temp_knownhosts )
441 for hostname in hostnames:
442 utils.header("Scan Public %s key and store it in the known_host file(under the root image) "%hostname)
443 scan=self.run_in_host('ssh-keyscan -t rsa %s >> %s '%(hostname,temp_knownhosts))
444 #Store the public keys in the right root image
445 self.copy_in_guest(temp_knownhosts,remote_knownhosts,True)
446 #clean the temp keys file used
447 self.run_in_host('rm -f %s '%temp_knownhosts )
448 except Exception, err:
451 def do_check_nodesSsh(self,minutes):
453 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
454 #graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
455 tocheck = self.all_hostnames()
456 self.scan_publicKeys(tocheck)
457 utils.header("checking Connectivity on nodes %r"%tocheck)
459 for hostname in tocheck:
460 # try to ssh in nodes
461 access=self.run_in_guest('ssh -i /etc/planetlab/root_ssh_key.rsa root@%s date'%hostname )
463 utils.header('The node %s is sshable -->'%hostname)
465 tocheck.remove(hostname)
467 (site_spec,node_spec)=self.locate_node(hostname)
468 if TestNode.is_real_model(node_spec['node_fields']['model']):
469 utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
470 tocheck.remove(hostname)
473 if datetime.datetime.now() > timeout:
474 for hostname in tocheck:
475 utils.header("FAILURE to ssh into %s"%hostname)
477 # otherwise, sleep for a while
479 # only useful in empty plcs
482 def nodes_ssh(self, options):
483 return self.do_check_nodesSsh(minutes=2)
485 def bootcd (self, options):
486 for site_spec in self.plc_spec['sites']:
487 test_site = TestSite (self,site_spec)
488 for node_spec in site_spec['nodes']:
489 test_node=TestNode (self,test_site,node_spec)
490 test_node.create_boot_cd(options.path)
493 def initscripts (self, options):
494 for initscript in self.plc_spec['initscripts']:
495 utils.show_spec('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
496 self.server.AddInitScript(self.auth_root(),initscript['initscript_fields'])
499 def slices (self, options):
500 return self.do_slices()
502 def clean_slices (self, options):
503 return self.do_slices("delete")
505 def do_slices (self, action="add"):
506 for slice in self.plc_spec['slices']:
507 site_spec = self.locate_site (slice['sitename'])
508 test_site = TestSite(self,site_spec)
509 test_slice=TestSlice(self,test_site,slice)
511 utils.header("Deleting slices in site %s"%test_site.name())
512 test_slice.delete_slice()
514 utils.show_spec("Creating slice",slice)
515 test_slice.create_slice()
516 utils.header('Created Slice %s'%slice['slice_fields']['name'])
519 def check_slices(self, options):
520 for slice_spec in self.plc_spec['slices']:
521 site_spec = self.locate_site (slice_spec['sitename'])
522 test_site = TestSite(self,site_spec)
523 test_slice=TestSlice(self,test_site,slice_spec)
524 status=test_slice.do_check_slice(options)
529 def start_nodes (self, options):
530 utils.header("Starting nodes")
531 for site_spec in self.plc_spec['sites']:
532 TestSite(self,site_spec).start_nodes (options)
535 def stop_nodes (self, options):
536 self.kill_all_qemus()
539 def check_tcp (self, options):
540 print 'check_tcp not yet implemented'
543 # returns the filename to use for sql dump/restore, using options.dbname if set
544 def dbfile (self, database, options):
545 # uses options.dbname if it is found
548 if not isinstance(name,StringTypes):
551 t=datetime.datetime.now()
554 return "/root/%s-%s.sql"%(database,name)
556 def db_dump(self, options):
558 dump=self.dbfile("planetab4",options)
559 self.run_in_guest('pg_dump -U pgsqluser planetlab4 -f '+ dump)
560 utils.header('Dumped planetlab4 database in %s'%dump)
563 def db_restore(self, options):
564 dump=self.dbfile("planetab4",options)
566 self.run_in_guest('service httpd stop')
567 # xxx - need another wrapper
568 self.run_in_guest_piped('echo drop database planetlab4','psql --user=pgsqluser template1')
569 self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab4')
570 self.run_in_guest('psql -U pgsqluser planetlab4 -f '+dump)
571 ##starting httpd service
572 self.run_in_guest('service httpd start')
574 utils.header('Database restored from ' + dump)
577 def standby_1(): pass
579 def standby_2(): pass
581 def standby_3(): pass
583 def standby_4(): pass
585 def standby_5(): pass
587 def standby_6(): pass
589 def standby_7(): pass
591 def standby_8(): pass
593 def standby_9(): pass
595 def standby_10(): pass
597 def standby_11(): pass
599 def standby_12(): pass
601 def standby_13(): pass
603 def standby_14(): pass
605 def standby_15(): pass
607 def standby_16(): pass
609 def standby_17(): pass
611 def standby_18(): pass
613 def standby_19(): pass
615 def standby_20(): pass