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 # inserts a backslash before each occurence of the following chars
20 # \ " ' < > & | ; ( ) $ * ~
21 def backslash_shell_specials (command):
24 if char in "\\\"'<>&|;()$*~":
30 # step methods must take (self, options) and return a boolean
33 utils.header('Entering StandBy for %d mn'%minutes)
34 time.sleep(60*minutes)
37 def standby_generic (func):
38 def actual(self,options):
39 minutes=int(func.__name__.split("_")[1])
40 return standby(minutes)
45 def __init__ (self,plc_spec):
46 self.plc_spec=plc_spec
47 self.path=os.path.dirname(sys.argv[0])
49 self.vserverip=plc_spec['vserverip']
50 self.vservername=plc_spec['vservername']
51 self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
55 self.url="https://%s:443/PLCAPI/"%plc_spec['hostname']
56 utils.header('Using API url %s'%self.url)
57 self.server=xmlrpclib.Server(self.url,allow_none=True)
60 name=self.plc_spec['name']
62 return name+"[%s]"%self.vservername
64 return name+"[chroot]"
67 return self.plc_spec['hostname'] == 'localhost'
69 # define the API methods on this object through xmlrpc
70 # would help, but not strictly necessary
74 # command gets run in the chroot/vserver
75 def host_to_guest(self,command):
77 return "vserver %s exec %s"%(self.vservername,command)
79 return "chroot /plc/root %s"%backslash_shell_specials(command)
81 # command gets run on the right box
82 def to_host(self,command):
86 return "ssh %s %s"%(self.plc_spec['hostname'],backslash_shell_specials(command))
88 def full_command(self,command):
89 return self.to_host(self.host_to_guest(command))
91 def run_in_guest (self,command):
92 return utils.system(self.full_command(command))
93 def run_in_host (self,command):
94 return utils.system(self.to_host(command))
97 def run_in_guest_piped (self,local,remote):
98 return utils.system(local+" | "+self.full_command(remote))
100 # copy a file to the myplc root image - pass in_data=True if the file must go in /plc/data
101 def copy_in_guest (self, localfile, remotefile, in_data=False):
103 chroot_dest="/plc/data"
105 chroot_dest="/plc/root"
108 utils.system("cp %s %s/%s"%(localfile,chroot_dest,remotefile))
110 utils.system("cp %s /vservers/%s/%s"%(localfile,self.vservername,remotefile))
113 utils.system("scp %s %s:%s/%s"%(localfile,self.plc_spec['hostname'],chroot_dest,remotefile))
115 utils.system("scp %s %s@/vservers/%s/%s"%(localfile,self.plc_spec['hostname'],self.vservername,remotefile))
117 def auth_root (self):
118 return {'Username':self.plc_spec['PLC_ROOT_USER'],
119 'AuthMethod':'password',
120 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
121 'Role' : self.plc_spec['role']
123 def locate_site (self,sitename):
124 for site in self.plc_spec['sites']:
125 if site['site_fields']['name'] == sitename:
127 if site['site_fields']['login_base'] == sitename:
129 raise Exception,"Cannot locate site %s"%sitename
131 def locate_node (self,nodename):
132 for site in self.plc_spec['sites']:
133 for node in site['nodes']:
134 if node['node_fields']['hostname'] == nodename:
136 raise Exception,"Cannot locate node %s"%nodename
138 def locate_key (self,keyname):
139 for key in self.plc_spec['keys']:
140 if key['name'] == keyname:
142 raise Exception,"Cannot locate key %s"%keyname
144 # all different hostboxes used in this plc
145 def gather_hostBoxes(self):
146 # maps on sites and nodes, return [ (host_box,hostname) ]
148 for site_spec in self.plc_spec['sites']:
149 test_site = TestSite (self,site_spec)
150 for node_spec in site_spec['nodes']:
151 test_node = TestNode (self, test_site, node_spec)
152 if not test_node.is_real():
153 tuples.append( (test_node.host_box(),node_spec['node_fields']['hostname']) )
154 # transform into a dict { 'host_box' -> [ hostnames .. ] }
156 for (box,hostname) in tuples:
157 if not result.has_key(box):
158 result[box]=[hostname]
160 result[box].append(hostname)
163 # a step for checking this stuff
164 def showboxes (self,options):
166 for (box,hosts) in self.gather_hostBoxes().iteritems():
167 print box,":"," + ".join(hosts)
170 def kill_all_qemus(self):
171 for (box,hosts) in self.gather_hostBoxes().iteritems():
172 # this is the brute force version, kill all qemus on that host box
173 TestBox(box).kill_all_qemus()
175 def clear_ssh_config (self,options):
176 # install local ssh_config file as root's .ssh/config - ssh should be quiet
177 # dir might need creation first
178 self.run_in_guest("mkdir /root/.ssh")
179 self.run_in_guest("chmod 700 /root/.ssh")
180 # this does not work - > redirection somehow makes it until an argument to cat
181 #self.run_in_guest_piped("cat ssh_config","cat > /root/.ssh/config")
182 self.copy_in_guest("ssh_config","/root/.ssh/config",True)
185 #################### step methods
188 def uninstall_chroot(self,options):
189 self.run_in_host('service plc safestop')
190 #####detecting the last myplc version installed and remove it
191 self.run_in_host('rpm -e myplc')
192 ##### Clean up the /plc directory
193 self.run_in_host('rm -rf /plc/data')
194 ##### stop any running vservers
195 self.run_in_host('for vserver in $(ls /vservers/* | sed -e s,/vservers/,,) ; do vserver $vserver stop ; done')
198 def uninstall_vserver(self,options):
199 self.run_in_host("vserver --silent %s delete"%self.vservername)
202 def uninstall(self,options):
203 # if there's a chroot-based myplc running, and then a native-based myplc is being deployed
204 # it sounds safer to have the former uninstalled too
205 # now the vserver method cannot be invoked for chroot instances as vservername is required
207 self.uninstall_vserver(options)
208 self.uninstall_chroot(options)
210 self.uninstall_chroot(options)
214 def install_chroot(self,options):
218 # xxx this would not work with hostname != localhost as mylc-init-vserver was extracted locally
219 def install_vserver(self,options):
220 # we need build dir for vtest-init-vserver
222 # a full path for the local calls
223 build_dir=self.path+"/build"
225 # use a standard name - will be relative to HOME
226 build_dir="tests-system-build"
227 build_checkout = "svn checkout %s %s"%(options.build_url,build_dir)
228 if self.run_in_host(build_checkout) != 0:
229 raise Exception,"Cannot checkout build dir"
230 # the repo url is taken from myplc-url
231 # with the last two steps (i386/myplc...) removed
232 repo_url = options.myplc_url
233 repo_url = os.path.dirname(repo_url)
234 repo_url = os.path.dirname(repo_url)
235 create_vserver="%s/vtest-init-vserver.sh %s %s -- --interface eth0:%s"%\
236 (build_dir,self.vservername,repo_url,self.vserverip)
237 if self.run_in_host(create_vserver) != 0:
238 raise Exception,"Could not create vserver for %s"%self.vservername
241 def install(self,options):
243 return self.install_vserver(options)
245 return self.install_chroot(options)
248 def install_rpm_chroot(self,options):
249 utils.header('Installing from %s'%options.myplc_url)
250 url=options.myplc_url
251 self.run_in_host('rpm -Uvh '+url)
252 self.run_in_host('service plc mount')
255 def install_rpm_vserver(self,options):
256 self.run_in_guest("yum -y install myplc-native")
259 def install_rpm(self,options):
261 return self.install_rpm_vserver(options)
263 return self.install_rpm_chroot(options)
266 def configure(self,options):
267 tmpname='%s/%s.plc-config-tty'%(options.path,self.name())
268 fileconf=open(tmpname,'w')
269 for var in [ 'PLC_NAME',
273 'PLC_MAIL_SUPPORT_ADDRESS',
280 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
281 fileconf.write('w\n')
282 fileconf.write('q\n')
284 utils.system('cat %s'%tmpname)
285 self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
286 utils.system('rm %s'%tmpname)
289 # the chroot install is slightly different to this respect
290 def start(self, options):
292 self.run_in_guest('service plc start')
294 self.run_in_host('service plc start')
297 def stop(self, options):
299 self.run_in_guest('service plc stop')
301 self.run_in_host('service plc stop')
304 # could use a TestKey class
305 def store_keys(self, options):
306 for key_spec in self.plc_spec['keys']:
307 TestKey(self,key_spec).store_key()
310 def clean_keys(self, options):
311 utils.system("rm -rf %s/keys/"%self.path)
313 def sites (self,options):
314 return self.do_sites(options)
316 def clean_sites (self,options):
317 return self.do_sites(options,action="delete")
319 def do_sites (self,options,action="add"):
320 for site_spec in self.plc_spec['sites']:
321 test_site = TestSite (self,site_spec)
322 if (action != "add"):
323 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
324 test_site.delete_site()
325 # deleted with the site
326 #test_site.delete_users()
329 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
330 test_site.create_site()
331 test_site.create_users()
334 def nodes (self, options):
335 return self.do_nodes(options)
336 def clean_nodes (self, options):
337 return self.do_nodes(options,action="delete")
339 def do_nodes (self, options,action="add"):
340 for site_spec in self.plc_spec['sites']:
341 test_site = TestSite (self,site_spec)
343 utils.header("Deleting nodes in site %s"%test_site.name())
344 for node_spec in site_spec['nodes']:
345 test_node=TestNode(self,test_site,node_spec)
346 utils.header("Deleting %s"%test_node.name())
347 test_node.delete_node()
349 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
350 for node_spec in site_spec['nodes']:
351 utils.show_spec('Creating node %s'%node_spec,node_spec)
352 test_node = TestNode (self,test_site,node_spec)
353 test_node.create_node ()
356 # create nodegroups if needed, and populate
357 # no need for a clean_nodegroups if we are careful enough
358 def nodegroups (self, options):
359 # 1st pass to scan contents
361 for site_spec in self.plc_spec['sites']:
362 test_site = TestSite (self,site_spec)
363 for node_spec in site_spec['nodes']:
364 test_node=TestNode (self,test_site,node_spec)
365 if node_spec.has_key('nodegroups'):
366 nodegroupnames=node_spec['nodegroups']
367 if isinstance(nodegroupnames,StringTypes):
368 nodegroupnames = [ nodegroupnames ]
369 for nodegroupname in nodegroupnames:
370 if not groups_dict.has_key(nodegroupname):
371 groups_dict[nodegroupname]=[]
372 groups_dict[nodegroupname].append(test_node.name())
373 auth=self.auth_root()
374 for (nodegroupname,group_nodes) in groups_dict.iteritems():
376 self.server.GetNodeGroups(auth,{'name':nodegroupname})[0]
378 self.server.AddNodeGroup(auth,{'name':nodegroupname})
379 for node in group_nodes:
380 self.server.AddNodeToNodeGroup(auth,node,nodegroupname)
383 def all_hostnames (self) :
385 for site_spec in self.plc_spec['sites']:
386 hostnames += [ node_spec['node_fields']['hostname'] \
387 for node_spec in site_spec['nodes'] ]
390 # gracetime : during the first <gracetime> minutes nothing gets printed
391 def do_nodes_booted (self, minutes, gracetime=2):
393 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
394 graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
395 # the nodes that haven't checked yet - start with a full list and shrink over time
396 tocheck = self.all_hostnames()
397 utils.header("checking nodes %r"%tocheck)
398 # create a dict hostname -> status
399 status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
402 tocheck_status=self.server.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
404 for array in tocheck_status:
405 hostname=array['hostname']
406 boot_state=array['boot_state']
407 if boot_state == 'boot':
408 utils.header ("%s has reached the 'boot' state"%hostname)
410 # if it's a real node, never mind
411 (site_spec,node_spec)=self.locate_node(hostname)
412 if TestNode.is_real_model(node_spec['node_fields']['model']):
413 utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
416 if datetime.datetime.now() > graceout:
417 utils.header ("%s still in '%s' state"%(hostname,boot_state))
418 graceout=datetime.datetime.now()+datetime.timedelta(1)
419 status[hostname] = boot_state
421 tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != 'boot' ]
424 if datetime.datetime.now() > timeout:
425 for hostname in tocheck:
426 utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
428 # otherwise, sleep for a while
430 # only useful in empty plcs
433 def nodes_booted(self,options):
434 return self.do_nodes_booted(minutes=5)
436 #to scan and store the nodes's public keys and avoid to ask for confirmation when ssh
437 def scan_publicKeys(self,hostnames):
439 temp_knownhosts="/root/known_hosts"
440 remote_knownhosts="/root/.ssh/known_hosts"
441 self.run_in_host("touch %s"%temp_knownhosts )
442 for hostname in hostnames:
443 utils.header("Scan Public %s key and store it in the known_host file(under the root image) "%hostname)
444 scan=self.run_in_host('ssh-keyscan -t rsa %s >> %s '%(hostname,temp_knownhosts))
445 #Store the public keys in the right root image
446 self.copy_in_guest(temp_knownhosts,remote_knownhosts,True)
447 #clean the temp keys file used
448 self.run_in_host('rm -f %s '%temp_knownhosts )
449 except Exception, err:
452 def do_check_nodesSsh(self,minutes):
454 timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
455 #graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
456 tocheck = self.all_hostnames()
457 self.scan_publicKeys(tocheck)
458 utils.header("checking Connectivity on nodes %r"%tocheck)
460 for hostname in tocheck:
461 # try to ssh in nodes
462 access=self.run_in_guest('ssh -i /etc/planetlab/root_ssh_key.rsa root@%s date'%hostname )
464 utils.header('The node %s is sshable -->'%hostname)
466 tocheck.remove(hostname)
468 (site_spec,node_spec)=self.locate_node(hostname)
469 if TestNode.is_real_model(node_spec['node_fields']['model']):
470 utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
471 tocheck.remove(hostname)
474 if datetime.datetime.now() > timeout:
475 for hostname in tocheck:
476 utils.header("FAILURE to ssh into %s"%hostname)
478 # otherwise, sleep for a while
480 # only useful in empty plcs
483 def nodes_ssh(self, options):
484 return self.do_check_nodesSsh(minutes=2)
486 def bootcd (self, options):
487 for site_spec in self.plc_spec['sites']:
488 test_site = TestSite (self,site_spec)
489 for node_spec in site_spec['nodes']:
490 test_node=TestNode (self,test_site,node_spec)
491 test_node.create_boot_cd(options.path)
494 def initscripts (self, options):
495 for initscript in self.plc_spec['initscripts']:
496 utils.show_spec('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
497 self.server.AddInitScript(self.auth_root(),initscript['initscript_fields'])
500 def slices (self, options):
501 return self.do_slices()
503 def clean_slices (self, options):
504 return self.do_slices("delete")
506 def do_slices (self, action="add"):
507 for slice in self.plc_spec['slices']:
508 site_spec = self.locate_site (slice['sitename'])
509 test_site = TestSite(self,site_spec)
510 test_slice=TestSlice(self,test_site,slice)
512 utils.header("Deleting slices in site %s"%test_site.name())
513 test_slice.delete_slice()
515 utils.show_spec("Creating slice",slice)
516 test_slice.create_slice()
517 utils.header('Created Slice %s'%slice['slice_fields']['name'])
520 def check_slices(self, options):
521 for slice_spec in self.plc_spec['slices']:
522 site_spec = self.locate_site (slice_spec['sitename'])
523 test_site = TestSite(self,site_spec)
524 test_slice=TestSlice(self,test_site,slice_spec)
525 status=test_slice.do_check_slice(options)
530 def start_nodes (self, options):
531 self.kill_all_qemus()
532 utils.header("Starting nodes")
533 for site_spec in self.plc_spec['sites']:
534 TestSite(self,site_spec).start_nodes (options)
537 def stop_nodes (self, options):
538 self.kill_all_qemus()
541 # returns the filename to use for sql dump/restore, using options.dbname if set
542 def dbfile (self, database, options):
543 # uses options.dbname if it is found
546 if not isinstance(name,StringTypes):
549 t=datetime.datetime.now()
552 return "/root/%s-%s.sql"%(database,name)
554 def db_dump(self, options):
556 dump=self.dbfile("planetab4",options)
557 self.run_in_guest('pg_dump -U pgsqluser planetlab4 -f '+ dump)
558 utils.header('Dumped planetlab4 database in %s'%dump)
561 def db_restore(self, options):
562 dump=self.dbfile("planetab4",options)
564 self.run_in_guest('service httpd stop')
565 # xxx - need another wrapper
566 self.run_in_guest_piped('echo drop database planetlab4','psql --user=pgsqluser template1')
567 self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab4')
568 self.run_in_guest('psql -U pgsqluser planetlab4 -f '+dump)
569 ##starting httpd service
570 self.run_in_guest('service httpd start')
572 utils.header('Database restored from ' + dump)
575 def standby_1(): pass
577 def standby_2(): pass
579 def standby_3(): pass
581 def standby_4(): pass
583 def standby_5(): pass
585 def standby_6(): pass
587 def standby_7(): pass
589 def standby_8(): pass
591 def standby_9(): pass
593 def standby_10(): pass
595 def standby_11(): pass
597 def standby_12(): pass
599 def standby_13(): pass
601 def standby_14(): pass
603 def standby_15(): pass
605 def standby_16(): pass
607 def standby_17(): pass
609 def standby_18(): pass
611 def standby_19(): pass
613 def standby_20(): pass