7 from types import StringTypes
11 from TestSite import TestSite
12 from TestNode import TestNode
13 from TestUser import TestUser
14 from TestKey import TestKey
15 from TestSlice import TestSlice
16 from TestSliver import TestSliver
17 from TestBox import TestBox
18 from TestSsh import TestSsh
19 from TestApiserver import TestApiserver
20 from TestSliceSfa import TestSliceSfa
21 from TestUserSfa import TestUserSfa
23 # step methods must take (self) and return a boolean (options is a member of the class)
25 def standby(minutes,dry_run):
26 utils.header('Entering StandBy for %d mn'%minutes)
30 time.sleep(60*minutes)
33 def standby_generic (func):
35 minutes=int(func.__name__.split("_")[1])
36 return standby(minutes,self.options.dry_run)
39 def node_mapper (method):
42 node_method = TestNode.__dict__[method.__name__]
43 for site_spec in self.plc_spec['sites']:
44 test_site = TestSite (self,site_spec)
45 for node_spec in site_spec['nodes']:
46 test_node = TestNode (self,test_site,node_spec)
47 if not node_method(test_node): overall=False
49 # restore the doc text
50 actual.__doc__=method.__doc__
53 def slice_mapper_options (method):
56 slice_method = TestSlice.__dict__[method.__name__]
57 for slice_spec in self.plc_spec['slices']:
58 site_spec = self.locate_site (slice_spec['sitename'])
59 test_site = TestSite(self,site_spec)
60 test_slice=TestSlice(self,test_site,slice_spec)
61 if not slice_method(test_slice,self.options): overall=False
63 # restore the doc text
64 actual.__doc__=method.__doc__
67 def slice_mapper_options_sfa (method):
71 slice_method = TestSliceSfa.__dict__[method.__name__]
72 for slice_spec in self.plc_spec['sfa']['slices_sfa']:
73 site_spec = self.locate_site (slice_spec['sitename'])
74 test_site = TestSite(self,site_spec)
75 test_slice=TestSliceSfa(test_plc,test_site,slice_spec)
76 if not slice_method(test_slice,self.options): overall=False
78 # restore the doc text
79 actual.__doc__=method.__doc__
87 'display', 'resources_pre', SEP,
88 'delete_vs','create_vs','install', 'configure', 'start', SEP,
89 'fetch_keys', 'store_keys', 'clear_known_hosts', SEP,
90 'initscripts', 'sites', 'nodes', 'slices', 'nodegroups', 'leases', SEP,
91 'reinstall_node', 'init_node','bootcd', 'configure_qemu', 'export_qemu',
92 'kill_all_qemus', 'start_node', SEP,
93 # better use of time: do this now that the nodes are taking off
94 'plcsh_stress_test', SEP,
95 'install_sfa', 'configure_sfa', 'import_sfa', 'start_sfa', SEP,
96 'setup_sfa', 'add_sfa', 'update_sfa', 'view_sfa', SEP,
97 'nodes_ssh_debug', 'nodes_ssh_boot', 'check_slice', 'check_initscripts', SEP,
98 # optionally run sfa later; takes longer, but checks more about nm
99 # 'install_sfa', 'configure_sfa', 'import_sfa', 'start_sfa', SEP,
100 # 'setup_sfa', 'add_sfa', 'update_sfa', 'view_sfa', SEP,
101 'check_slice_sfa', 'delete_sfa', 'stop_sfa', SEP,
102 'check_tcp', 'check_hooks', SEP,
103 'force_gather_logs', 'force_resources_post',
106 'show_boxes', 'resources_list','resources_release','resources_release_plc','resources_release_qemu',SEP,
107 'stop', 'vs_start', SEP,
108 'clean_initscripts', 'clean_nodegroups','clean_all_sites', SEP,
109 'clean_sites', 'clean_nodes', 'clean_slices', 'clean_keys', SEP,
111 'list_all_qemus', 'list_qemus', 'kill_qemus', SEP,
112 'db_dump' , 'db_restore', SEP,
113 'standby_1 through 20',
117 def printable_steps (list):
118 return " ".join(list).replace(" "+SEP+" "," \\\n")
120 def valid_step (step):
123 # turn off the sfa-related steps when build has skipped SFA
124 # this is originally for centos5 as recent SFAs won't build on this platformb
126 def check_whether_build_has_sfa (rpms_url):
127 retcod=os.system ("curl --silent %s/ | grep -q sfa"%rpms_url)
128 # full builds are expected to return with 0 here
130 TestPlc.default_steps = [ step for step in TestPlc.default_steps
131 if step.find('sfa') < 0 ]
133 def __init__ (self,plc_spec,options):
134 self.plc_spec=plc_spec
136 self.test_ssh=TestSsh(self.plc_spec['hostname'],self.options.buildname)
138 self.vserverip=plc_spec['vserverip']
139 self.vservername=plc_spec['vservername']
140 self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
143 raise Exception,'chroot-based myplc testing is deprecated'
144 self.apiserver=TestApiserver(self.url,options.dry_run)
147 name=self.plc_spec['name']
148 return "%s.%s"%(name,self.vservername)
151 return self.plc_spec['hostname']
154 return self.test_ssh.is_local()
156 # define the API methods on this object through xmlrpc
157 # would help, but not strictly necessary
161 def actual_command_in_guest (self,command):
162 return self.test_ssh.actual_command(self.host_to_guest(command))
164 def start_guest (self):
165 return utils.system(self.test_ssh.actual_command(self.start_guest_in_host()))
167 def run_in_guest (self,command):
168 return utils.system(self.actual_command_in_guest(command))
170 def run_in_host (self,command):
171 return self.test_ssh.run_in_buildname(command)
173 #command gets run in the vserver
174 def host_to_guest(self,command):
175 return "vserver %s exec %s"%(self.vservername,command)
177 #command gets run in the vserver
178 def start_guest_in_host(self):
179 return "vserver %s start"%(self.vservername)
182 def run_in_guest_piped (self,local,remote):
183 return utils.system(local+" | "+self.test_ssh.actual_command(self.host_to_guest(remote),keep_stdin=True))
185 def auth_root (self):
186 return {'Username':self.plc_spec['PLC_ROOT_USER'],
187 'AuthMethod':'password',
188 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
189 'Role' : self.plc_spec['role']
191 def locate_site (self,sitename):
192 for site in self.plc_spec['sites']:
193 if site['site_fields']['name'] == sitename:
195 if site['site_fields']['login_base'] == sitename:
197 raise Exception,"Cannot locate site %s"%sitename
199 def locate_node (self,nodename):
200 for site in self.plc_spec['sites']:
201 for node in site['nodes']:
202 if node['name'] == nodename:
204 raise Exception,"Cannot locate node %s"%nodename
206 def locate_hostname (self,hostname):
207 for site in self.plc_spec['sites']:
208 for node in site['nodes']:
209 if node['node_fields']['hostname'] == hostname:
211 raise Exception,"Cannot locate hostname %s"%hostname
213 def locate_key (self,keyname):
214 for key in self.plc_spec['keys']:
215 if key['name'] == keyname:
217 raise Exception,"Cannot locate key %s"%keyname
219 def locate_slice (self, slicename):
220 for slice in self.plc_spec['slices']:
221 if slice['slice_fields']['name'] == slicename:
223 raise Exception,"Cannot locate slice %s"%slicename
225 def all_sliver_objs (self):
227 for slice_spec in self.plc_spec['slices']:
228 slicename = slice_spec['slice_fields']['name']
229 for nodename in slice_spec['nodenames']:
230 result.append(self.locate_sliver_obj (nodename,slicename))
233 def locate_sliver_obj (self,nodename,slicename):
234 (site,node) = self.locate_node(nodename)
235 slice = self.locate_slice (slicename)
237 test_site = TestSite (self, site)
238 test_node = TestNode (self, test_site,node)
239 # xxx the slice site is assumed to be the node site - mhh - probably harmless
240 test_slice = TestSlice (self, test_site, slice)
241 return TestSliver (self, test_node, test_slice)
243 def locate_first_node(self):
244 nodename=self.plc_spec['slices'][0]['nodenames'][0]
245 (site,node) = self.locate_node(nodename)
246 test_site = TestSite (self, site)
247 test_node = TestNode (self, test_site,node)
250 def locate_first_sliver (self):
251 slice_spec=self.plc_spec['slices'][0]
252 slicename=slice_spec['slice_fields']['name']
253 nodename=slice_spec['nodenames'][0]
254 return self.locate_sliver_obj(nodename,slicename)
256 # all different hostboxes used in this plc
257 def gather_hostBoxes(self):
258 # maps on sites and nodes, return [ (host_box,test_node) ]
260 for site_spec in self.plc_spec['sites']:
261 test_site = TestSite (self,site_spec)
262 for node_spec in site_spec['nodes']:
263 test_node = TestNode (self, test_site, node_spec)
264 if not test_node.is_real():
265 tuples.append( (test_node.host_box(),test_node) )
266 # transform into a dict { 'host_box' -> [ test_node .. ] }
268 for (box,node) in tuples:
269 if not result.has_key(box):
272 result[box].append(node)
275 # a step for checking this stuff
276 def show_boxes (self):
277 'print summary of nodes location'
278 for (box,nodes) in self.gather_hostBoxes().iteritems():
279 print box,":"," + ".join( [ node.name() for node in nodes ] )
282 # make this a valid step
283 def kill_all_qemus(self):
284 'kill all qemu instances on the qemu boxes involved by this setup'
285 # this is the brute force version, kill all qemus on that host box
286 for (box,nodes) in self.gather_hostBoxes().iteritems():
287 # pass the first nodename, as we don't push template-qemu on testboxes
288 nodedir=nodes[0].nodedir()
289 TestBox(box,self.options.buildname).kill_all_qemus(nodedir)
292 # make this a valid step
293 def list_all_qemus(self):
294 'list all qemu instances on the qemu boxes involved by this setup'
295 for (box,nodes) in self.gather_hostBoxes().iteritems():
296 # this is the brute force version, kill all qemus on that host box
297 TestBox(box,self.options.buildname).list_all_qemus()
300 # kill only the right qemus
301 def list_qemus(self):
302 'list qemu instances for our nodes'
303 for (box,nodes) in self.gather_hostBoxes().iteritems():
304 # the fine-grain version
309 # kill only the right qemus
310 def kill_qemus(self):
311 'kill the qemu instances for our nodes'
312 for (box,nodes) in self.gather_hostBoxes().iteritems():
313 # the fine-grain version
318 #################### display config
320 "show test configuration after localization"
321 self.display_pass (1)
322 self.display_pass (2)
326 always_display_keys=['PLC_WWW_HOST','nodes','sites',]
327 def display_pass (self,passno):
328 for (key,val) in self.plc_spec.iteritems():
329 if not self.options.verbose and key not in TestPlc.always_display_keys: continue
333 self.display_site_spec(site)
334 for node in site['nodes']:
335 self.display_node_spec(node)
336 elif key=='initscripts':
337 for initscript in val:
338 self.display_initscript_spec (initscript)
341 self.display_slice_spec (slice)
344 self.display_key_spec (key)
346 if key not in ['sites','initscripts','slices','keys', 'sfa']:
347 print '+ ',key,':',val
349 def display_site_spec (self,site):
350 print '+ ======== site',site['site_fields']['name']
351 for (k,v) in site.iteritems():
352 if not self.options.verbose and k not in TestPlc.always_display_keys: continue
355 print '+ ','nodes : ',
357 print node['node_fields']['hostname'],'',
363 print user['name'],'',
365 elif k == 'site_fields':
366 print '+ login_base',':',v['login_base']
367 elif k == 'address_fields':
371 PrettyPrinter(indent=8,depth=2).pprint(v)
373 def display_initscript_spec (self,initscript):
374 print '+ ======== initscript',initscript['initscript_fields']['name']
376 def display_key_spec (self,key):
377 print '+ ======== key',key['name']
379 def display_slice_spec (self,slice):
380 print '+ ======== slice',slice['slice_fields']['name']
381 for (k,v) in slice.iteritems():
394 elif k=='slice_fields':
395 print '+ fields',':',
396 print 'max_nodes=',v['max_nodes'],
401 def display_node_spec (self,node):
402 print "+ node",node['name'],"host_box=",node['host_box'],
403 print "hostname=",node['node_fields']['hostname'],
404 print "ip=",node['interface_fields']['ip']
407 # another entry point for just showing the boxes involved
408 def display_mapping (self):
409 TestPlc.display_mapping_plc(self.plc_spec)
413 def display_mapping_plc (plc_spec):
414 print '+ MyPLC',plc_spec['name']
415 print '+\tvserver address = root@%s:/vservers/%s'%(plc_spec['hostname'],plc_spec['vservername'])
416 print '+\tIP = %s/%s'%(plc_spec['PLC_API_HOST'],plc_spec['vserverip'])
417 for site_spec in plc_spec['sites']:
418 for node_spec in site_spec['nodes']:
419 TestPlc.display_mapping_node(node_spec)
422 def display_mapping_node (node_spec):
423 print '+ NODE %s'%(node_spec['name'])
424 print '+\tqemu box %s'%node_spec['host_box']
425 print '+\thostname=%s'%node_spec['node_fields']['hostname']
427 def resources_pre (self):
428 "run site-dependant pre-test script as defined in LocalTestResources"
429 from LocalTestResources import local_resources
430 return local_resources.step_pre(self)
432 def resources_post (self):
433 "run site-dependant post-test script as defined in LocalTestResources"
434 from LocalTestResources import local_resources
435 return local_resources.step_post(self)
437 def resources_list (self):
438 "run site-dependant list script as defined in LocalTestResources"
439 from LocalTestResources import local_resources
440 return local_resources.step_list(self)
442 def resources_release (self):
443 "run site-dependant release script as defined in LocalTestResources"
444 from LocalTestResources import local_resources
445 return local_resources.step_release(self)
447 def resources_release_plc (self):
448 "run site-dependant release script as defined in LocalTestResources"
449 from LocalTestResources import local_resources
450 return local_resources.step_release_plc(self)
452 def resources_release_qemu (self):
453 "run site-dependant release script as defined in LocalTestResources"
454 from LocalTestResources import local_resources
455 return local_resources.step_release_qemu(self)
458 "vserver delete the test myplc"
459 self.run_in_host("vserver --silent %s delete"%self.vservername)
463 # historically the build was being fetched by the tests
464 # now the build pushes itself as a subdir of the tests workdir
465 # so that the tests do not have to worry about extracting the build (svn, git, or whatever)
466 def create_vs (self):
467 "vserver creation (no install done)"
468 # push the local build/ dir to the testplc box
470 # a full path for the local calls
471 build_dir=os.path.dirname(sys.argv[0])
472 # sometimes this is empty - set to "." in such a case
473 if not build_dir: build_dir="."
474 build_dir += "/build"
476 # use a standard name - will be relative to remote buildname
478 # remove for safety; do *not* mkdir first, otherwise we end up with build/build/
479 self.test_ssh.rmdir(build_dir)
480 self.test_ssh.copy(build_dir,recursive=True)
481 # the repo url is taken from arch-rpms-url
482 # with the last step (i386) removed
483 repo_url = self.options.arch_rpms_url
484 for level in [ 'arch' ]:
485 repo_url = os.path.dirname(repo_url)
486 # pass the vbuild-nightly options to vtest-init-vserver
488 test_env_options += " -p %s"%self.options.personality
489 test_env_options += " -d %s"%self.options.pldistro
490 test_env_options += " -f %s"%self.options.fcdistro
491 script="vtest-init-vserver.sh"
492 vserver_name = self.vservername
493 vserver_options="--netdev eth0 --interface %s"%self.vserverip
495 vserver_hostname=socket.gethostbyaddr(self.vserverip)[0]
496 vserver_options += " --hostname %s"%vserver_hostname
498 print "Cannot reverse lookup %s"%self.vserverip
499 print "This is considered fatal, as this might pollute the test results"
501 create_vserver="%(build_dir)s/%(script)s %(test_env_options)s %(vserver_name)s %(repo_url)s -- %(vserver_options)s"%locals()
502 return self.run_in_host(create_vserver) == 0
506 "yum install myplc, noderepo, and the plain bootstrapfs"
508 # workaround for getting pgsql8.2 on centos5
509 if self.options.fcdistro == "centos5":
510 self.run_in_guest("rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm")
512 if self.options.personality == "linux32":
514 elif self.options.personality == "linux64":
517 raise Exception, "Unsupported personality %r"%self.options.personality
519 nodefamily="%s-%s-%s"%(self.options.pldistro,self.options.fcdistro,arch)
521 # try to install slicerepo - not fatal yet
522 self.run_in_guest("yum -y install slicerepo-%s"%nodefamily)
525 self.run_in_guest("yum -y install myplc")==0 and \
526 self.run_in_guest("yum -y install noderepo-%s"%nodefamily)==0 and \
527 self.run_in_guest("yum -y install bootstrapfs-%s-plain"%nodefamily)==0
532 tmpname='%s.plc-config-tty'%(self.name())
533 fileconf=open(tmpname,'w')
534 for var in [ 'PLC_NAME',
538 'PLC_MAIL_SUPPORT_ADDRESS',
541 # Above line was added for integrating SFA Testing
547 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
548 fileconf.write('w\n')
549 fileconf.write('q\n')
551 utils.system('cat %s'%tmpname)
552 self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
553 utils.system('rm %s'%tmpname)
558 self.run_in_guest('service plc start')
563 self.run_in_guest('service plc stop')
567 "start the PLC vserver"
571 # stores the keys from the config for further use
572 def store_keys(self):
573 "stores test users ssh keys in keys/"
574 for key_spec in self.plc_spec['keys']:
575 TestKey(self,key_spec).store_key()
578 def clean_keys(self):
579 "removes keys cached in keys/"
580 utils.system("rm -rf %s/keys/"%os.path(sys.argv[0]))
582 # fetches the ssh keys in the plc's /etc/planetlab and stores them in keys/
583 # for later direct access to the nodes
584 def fetch_keys(self):
585 "gets ssh keys in /etc/planetlab/ and stores them locally in keys/"
587 if not os.path.isdir(dir):
589 vservername=self.vservername
591 prefix = 'debug_ssh_key'
592 for ext in [ 'pub', 'rsa' ] :
593 src="/vservers/%(vservername)s/etc/planetlab/%(prefix)s.%(ext)s"%locals()
594 dst="keys/%(vservername)s-debug.%(ext)s"%locals()
595 if self.test_ssh.fetch(src,dst) != 0: overall=False
599 "create sites with PLCAPI"
600 return self.do_sites()
602 def clean_sites (self):
603 "delete sites with PLCAPI"
604 return self.do_sites(action="delete")
606 def do_sites (self,action="add"):
607 for site_spec in self.plc_spec['sites']:
608 test_site = TestSite (self,site_spec)
609 if (action != "add"):
610 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
611 test_site.delete_site()
612 # deleted with the site
613 #test_site.delete_users()
616 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
617 test_site.create_site()
618 test_site.create_users()
621 def clean_all_sites (self):
622 "Delete all sites in PLC, and related objects"
623 print 'auth_root',self.auth_root()
624 site_ids = [s['site_id'] for s in self.apiserver.GetSites(self.auth_root(), {}, ['site_id'])]
625 for site_id in site_ids:
626 print 'Deleting site_id',site_id
627 self.apiserver.DeleteSite(self.auth_root(),site_id)
630 "create nodes with PLCAPI"
631 return self.do_nodes()
632 def clean_nodes (self):
633 "delete nodes with PLCAPI"
634 return self.do_nodes(action="delete")
636 def do_nodes (self,action="add"):
637 for site_spec in self.plc_spec['sites']:
638 test_site = TestSite (self,site_spec)
640 utils.header("Deleting nodes in site %s"%test_site.name())
641 for node_spec in site_spec['nodes']:
642 test_node=TestNode(self,test_site,node_spec)
643 utils.header("Deleting %s"%test_node.name())
644 test_node.delete_node()
646 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
647 for node_spec in site_spec['nodes']:
648 utils.pprint('Creating node %s'%node_spec,node_spec)
649 test_node = TestNode (self,test_site,node_spec)
650 test_node.create_node ()
653 def nodegroups (self):
654 "create nodegroups with PLCAPI"
655 return self.do_nodegroups("add")
656 def clean_nodegroups (self):
657 "delete nodegroups with PLCAPI"
658 return self.do_nodegroups("delete")
662 def translate_timestamp (start,timestamp):
663 if timestamp < TestPlc.YEAR: return start+timestamp
664 else: return timestamp
667 def timestamp_printable (timestamp):
668 return time.strftime('%m-%d %H:%M UTC',time.gmtime(timestamp))
672 grain=self.apiserver.GetLeaseGranularity(self.auth_root())
673 round=(int(now)/grain)*grain
675 # find out all nodes that are reservable
676 nodes=self.all_reservable_nodenames()
678 utils.header ("No reservable node found - proceeding without leases")
681 # attach them to the leases as specified in plc_specs
682 # this is where the 'leases' field gets interpreted as relative of absolute
683 for lease_spec in self.plc_spec['leases']:
684 lease_spec['t_from']=TestPlc.translate_timestamp(start,lease_spec['t_from'])
685 lease_spec['t_until']=TestPlc.translate_timestamp(start,lease_spec['t_until'])
686 if self.apiserver.AddLeases(self.auth_root(),nodes,
687 lease_spec['slice'],lease_spec['t_from'],lease_spec['t_until']):
688 utils.header('Leases on nodes %r from %s until %s'%(nodes,lease_spec['t_from'],lease_spec['t_until']))
693 # create nodegroups if needed, and populate
694 def do_nodegroups (self, action="add"):
695 # 1st pass to scan contents
697 for site_spec in self.plc_spec['sites']:
698 test_site = TestSite (self,site_spec)
699 for node_spec in site_spec['nodes']:
700 test_node=TestNode (self,test_site,node_spec)
701 if node_spec.has_key('nodegroups'):
702 nodegroupnames=node_spec['nodegroups']
703 if isinstance(nodegroupnames,StringTypes):
704 nodegroupnames = [ nodegroupnames ]
705 for nodegroupname in nodegroupnames:
706 if not groups_dict.has_key(nodegroupname):
707 groups_dict[nodegroupname]=[]
708 groups_dict[nodegroupname].append(test_node.name())
709 auth=self.auth_root()
711 for (nodegroupname,group_nodes) in groups_dict.iteritems():
713 print 'nodegroups:','dealing with nodegroup',nodegroupname,'on nodes',group_nodes
714 # first, check if the nodetagtype is here
715 tag_types = self.apiserver.GetTagTypes(auth,{'tagname':nodegroupname})
717 tag_type_id = tag_types[0]['tag_type_id']
719 tag_type_id = self.apiserver.AddTagType(auth,
720 {'tagname':nodegroupname,
721 'description': 'for nodegroup %s'%nodegroupname,
724 print 'located tag (type)',nodegroupname,'as',tag_type_id
726 nodegroups = self.apiserver.GetNodeGroups (auth, {'groupname':nodegroupname})
728 self.apiserver.AddNodeGroup(auth, nodegroupname, tag_type_id, 'yes')
729 print 'created nodegroup',nodegroupname,'from tagname',nodegroupname,'and value','yes'
730 # set node tag on all nodes, value='yes'
731 for nodename in group_nodes:
733 self.apiserver.AddNodeTag(auth, nodename, nodegroupname, "yes")
735 traceback.print_exc()
736 print 'node',nodename,'seems to already have tag',nodegroupname
739 expect_yes = self.apiserver.GetNodeTags(auth,
740 {'hostname':nodename,
741 'tagname':nodegroupname},
742 ['value'])[0]['value']
743 if expect_yes != "yes":
744 print 'Mismatch node tag on node',nodename,'got',expect_yes
747 if not self.options.dry_run:
748 print 'Cannot find tag',nodegroupname,'on node',nodename
752 print 'cleaning nodegroup',nodegroupname
753 self.apiserver.DeleteNodeGroup(auth,nodegroupname)
755 traceback.print_exc()
759 # return a list of tuples (nodename,qemuname)
760 def all_node_infos (self) :
762 for site_spec in self.plc_spec['sites']:
763 node_infos += [ (node_spec['node_fields']['hostname'],node_spec['host_box']) \
764 for node_spec in site_spec['nodes'] ]
767 def all_nodenames (self): return [ x[0] for x in self.all_node_infos() ]
768 def all_reservable_nodenames (self):
770 for site_spec in self.plc_spec['sites']:
771 res += [ node_spec['hostname'] for node_spec in site_spec['nodes']
772 if 'node_type' in node_spec and node_spec['node_type'] == 'reservable' ]
775 # silent_minutes : during the first <silent_minutes> minutes nothing gets printed
776 def nodes_check_boot_state (self, target_boot_state, timeout_minutes, silent_minutes,period=15):
777 if self.options.dry_run:
781 timeout = datetime.datetime.now()+datetime.timedelta(minutes=timeout_minutes)
782 graceout = datetime.datetime.now()+datetime.timedelta(minutes=silent_minutes)
783 # the nodes that haven't checked yet - start with a full list and shrink over time
784 tocheck = self.all_hostnames()
785 utils.header("checking nodes %r"%tocheck)
786 # create a dict hostname -> status
787 status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
790 tocheck_status=self.apiserver.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
792 for array in tocheck_status:
793 hostname=array['hostname']
794 boot_state=array['boot_state']
795 if boot_state == target_boot_state:
796 utils.header ("%s has reached the %s state"%(hostname,target_boot_state))
798 # if it's a real node, never mind
799 (site_spec,node_spec)=self.locate_hostname(hostname)
800 if TestNode.is_real_model(node_spec['node_fields']['model']):
801 utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
803 boot_state = target_boot_state
804 elif datetime.datetime.now() > graceout:
805 utils.header ("%s still in '%s' state"%(hostname,boot_state))
806 graceout=datetime.datetime.now()+datetime.timedelta(1)
807 status[hostname] = boot_state
809 tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != target_boot_state ]
812 if datetime.datetime.now() > timeout:
813 for hostname in tocheck:
814 utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
816 # otherwise, sleep for a while
818 # only useful in empty plcs
821 def nodes_booted(self):
822 return self.nodes_check_boot_state('boot',timeout_minutes=30,silent_minutes=20)
824 def check_nodes_ssh(self,debug,timeout_minutes,silent_minutes,period=15):
826 timeout = datetime.datetime.now()+datetime.timedelta(minutes=timeout_minutes)
827 graceout = datetime.datetime.now()+datetime.timedelta(minutes=silent_minutes)
828 vservername=self.vservername
831 local_key = "keys/%(vservername)s-debug.rsa"%locals()
834 local_key = "keys/key1.rsa"
835 node_infos = self.all_node_infos()
836 utils.header("checking ssh access (expected in %s mode) to nodes:"%message)
837 for (nodename,qemuname) in node_infos:
838 utils.header("hostname=%s -- qemubox=%s"%(nodename,qemuname))
839 utils.header("max timeout is %d minutes, silent for %d minutes (period is %s)"%\
840 (timeout_minutes,silent_minutes,period))
842 for node_info in node_infos:
843 (hostname,qemuname) = node_info
844 # try to run 'hostname' in the node
845 command = TestSsh (hostname,key=local_key).actual_command("hostname;uname -a")
846 # don't spam logs - show the command only after the grace period
847 success = utils.system ( command, silent=datetime.datetime.now() < graceout)
849 utils.header('Successfully entered root@%s (%s)'%(hostname,message))
851 node_infos.remove(node_info)
853 # we will have tried real nodes once, in case they're up - but if not, just skip
854 (site_spec,node_spec)=self.locate_hostname(hostname)
855 if TestNode.is_real_model(node_spec['node_fields']['model']):
856 utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
857 node_infos.remove(node_info)
860 if datetime.datetime.now() > timeout:
861 for (hostname,qemuname) in node_infos:
862 utils.header("FAILURE to ssh into %s (on %s)"%(hostname,qemuname))
864 # otherwise, sleep for a while
866 # only useful in empty plcs
869 def nodes_ssh_debug(self):
870 "Tries to ssh into nodes in debug mode with the debug ssh key"
871 return self.check_nodes_ssh(debug=True,timeout_minutes=30,silent_minutes=5)
873 def nodes_ssh_boot(self):
874 "Tries to ssh into nodes in production mode with the root ssh key"
875 return self.check_nodes_ssh(debug=False,timeout_minutes=30,silent_minutes=15)
878 def init_node (self):
879 "all nodes : init a clean local directory for holding node-dep stuff like iso image..."
883 "all nodes: invoke GetBootMedium and store result locally"
886 def configure_qemu (self):
887 "all nodes: compute qemu config qemu.conf and store it locally"
890 def reinstall_node (self):
891 "all nodes: mark PLCAPI boot_state as reinstall"
894 def export_qemu (self):
895 "all nodes: push local node-dep directory on the qemu box"
898 ### check hooks : invoke scripts from hooks/{node,slice}
899 def check_hooks_node (self):
900 return self.locate_first_node().check_hooks()
901 def check_hooks_sliver (self) :
902 return self.locate_first_sliver().check_hooks()
904 def check_hooks (self):
905 "runs unit tests in the node and slice contexts - see hooks/{node,slice}"
906 return self.check_hooks_node() and self.check_hooks_sliver()
909 def do_check_initscripts(self):
911 for slice_spec in self.plc_spec['slices']:
912 if not slice_spec.has_key('initscriptname'):
914 initscript=slice_spec['initscriptname']
915 for nodename in slice_spec['nodenames']:
916 (site,node) = self.locate_node (nodename)
917 # xxx - passing the wrong site - probably harmless
918 test_site = TestSite (self,site)
919 test_slice = TestSlice (self,test_site,slice_spec)
920 test_node = TestNode (self,test_site,node)
921 test_sliver = TestSliver (self, test_node, test_slice)
922 if not test_sliver.check_initscript(initscript):
926 def check_initscripts(self):
927 "check that the initscripts have triggered"
928 return self.do_check_initscripts()
930 def initscripts (self):
931 "create initscripts with PLCAPI"
932 for initscript in self.plc_spec['initscripts']:
933 utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
934 self.apiserver.AddInitScript(self.auth_root(),initscript['initscript_fields'])
937 def clean_initscripts (self):
938 "delete initscripts with PLCAPI"
939 for initscript in self.plc_spec['initscripts']:
940 initscript_name = initscript['initscript_fields']['name']
941 print('Attempting to delete %s in plc %s'%(initscript_name,self.plc_spec['name']))
943 self.apiserver.DeleteInitScript(self.auth_root(),initscript_name)
944 print initscript_name,'deleted'
946 print 'deletion went wrong - probably did not exist'
951 "create slices with PLCAPI"
952 return self.do_slices()
954 def clean_slices (self):
955 "delete slices with PLCAPI"
956 return self.do_slices("delete")
958 def do_slices (self, action="add"):
959 for slice in self.plc_spec['slices']:
960 site_spec = self.locate_site (slice['sitename'])
961 test_site = TestSite(self,site_spec)
962 test_slice=TestSlice(self,test_site,slice)
964 utils.header("Deleting slices in site %s"%test_site.name())
965 test_slice.delete_slice()
967 utils.pprint("Creating slice",slice)
968 test_slice.create_slice()
969 utils.header('Created Slice %s'%slice['slice_fields']['name'])
972 @slice_mapper_options
973 def check_slice(self):
974 "tries to ssh-enter the slice with the user key, to ensure slice creation"
978 def clear_known_hosts (self):
979 "remove test nodes entries from the local known_hosts file"
983 def start_node (self) :
984 "all nodes: start the qemu instance (also runs qemu-bridge-init start)"
987 def check_tcp (self):
988 "check TCP connectivity between 2 slices (or in loopback if only one is defined)"
989 specs = self.plc_spec['tcp_test']
994 s_test_sliver = self.locate_sliver_obj (spec['server_node'],spec['server_slice'])
995 if not s_test_sliver.run_tcp_server(port,timeout=10):
999 # idem for the client side
1000 c_test_sliver = self.locate_sliver_obj(spec['server_node'],spec['server_slice'])
1001 if not c_test_sliver.run_tcp_client(s_test_sliver.test_node.name(),port):
1005 def plcsh_stress_test (self):
1006 "runs PLCAPI stress test, that checks Add/Update/Delete on all types - preserves contents"
1007 # install the stress-test in the plc image
1008 location = "/usr/share/plc_api/plcsh_stress_test.py"
1009 remote="/vservers/%s/%s"%(self.vservername,location)
1010 self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1012 command += " -- --check"
1013 if self.options.size == 1:
1014 command += " --tiny"
1015 return ( self.run_in_guest(command) == 0)
1017 # populate runs the same utility without slightly different options
1018 # in particular runs with --preserve (dont cleanup) and without --check
1019 # also it gets run twice, once with the --foreign option for creating fake foreign entries
1022 def install_sfa(self):
1023 "yum install sfa, sfa-plc and sfa-client"
1024 if self.options.personality == "linux32":
1026 elif self.options.personality == "linux64":
1029 raise Exception, "Unsupported personality %r"%self.options.personality
1030 return self.run_in_guest("yum -y install sfa sfa-client sfa-plc sfa-sfatables")==0
1033 def configure_sfa(self):
1034 "run sfa-config-tty"
1035 tmpname='%s.sfa-config-tty'%(self.name())
1036 fileconf=open(tmpname,'w')
1037 for var in [ 'SFA_REGISTRY_ROOT_AUTH',
1038 'SFA_REGISTRY_LEVEL1_AUTH',
1039 'SFA_REGISTRY_HOST',
1040 'SFA_AGGREGATE_HOST',
1046 'SFA_PLC_DB_PASSWORD',
1048 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec['sfa'][var]))
1049 fileconf.write('w\n')
1050 fileconf.write('R\n')
1051 fileconf.write('q\n')
1053 utils.system('cat %s'%tmpname)
1054 self.run_in_guest_piped('cat %s'%tmpname,'sfa-config-tty')
1055 utils.system('rm %s'%tmpname)
1058 def import_sfa(self):
1060 auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1061 return self.run_in_guest('sfa-import-plc.py')==0
1062 # not needed anymore
1063 # self.run_in_guest('cp /etc/sfa/authorities/%s/%s.pkey /etc/sfa/authorities/server.key'%(auth,auth))
1065 def start_sfa(self):
1067 return self.run_in_guest('service sfa start')==0
1069 def setup_sfa(self):
1070 "sfi client configuration"
1072 if os.path.exists(dir_name):
1073 utils.system('rm -rf %s'%dir_name)
1074 utils.system('mkdir %s'%dir_name)
1075 file_name=dir_name + os.sep + 'fake-pi1.pkey'
1076 fileconf=open(file_name,'w')
1077 fileconf.write (self.plc_spec['keys'][0]['private'])
1080 file_name=dir_name + os.sep + 'sfi_config'
1081 fileconf=open(file_name,'w')
1082 SFI_AUTH=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']+".main"
1083 fileconf.write ("SFI_AUTH='%s'"%SFI_AUTH)
1084 fileconf.write('\n')
1085 SFI_USER=SFI_AUTH+'.fake-pi1'
1086 fileconf.write ("SFI_USER='%s'"%SFI_USER)
1087 fileconf.write('\n')
1088 SFI_REGISTRY='http://' + self.plc_spec['sfa']['SFA_PLC_DB_HOST'] + ':12345/'
1089 fileconf.write ("SFI_REGISTRY='%s'"%SFI_REGISTRY)
1090 fileconf.write('\n')
1091 SFI_SM='http://' + self.plc_spec['sfa']['SFA_PLC_DB_HOST'] + ':12347/'
1092 fileconf.write ("SFI_SM='%s'"%SFI_SM)
1093 fileconf.write('\n')
1096 file_name=dir_name + os.sep + 'person.xml'
1097 fileconf=open(file_name,'w')
1098 for record in self.plc_spec['sfa']['sfa_person_xml']:
1099 person_record=record
1100 fileconf.write(person_record)
1101 fileconf.write('\n')
1104 file_name=dir_name + os.sep + 'slice.xml'
1105 fileconf=open(file_name,'w')
1106 for record in self.plc_spec['sfa']['sfa_slice_xml']:
1108 #slice_record=self.plc_spec['sfa']['sfa_slice_xml']
1109 fileconf.write(slice_record)
1110 fileconf.write('\n')
1113 file_name=dir_name + os.sep + 'slice.rspec'
1114 fileconf=open(file_name,'w')
1116 for (key, value) in self.plc_spec['sfa']['sfa_slice_rspec'].items():
1118 fileconf.write(slice_rspec)
1119 fileconf.write('\n')
1122 remote="/vservers/%s/%s"%(self.vservername,location)
1123 self.test_ssh.copy_abs(dir_name, remote, recursive=True)
1125 #utils.system('cat %s'%tmpname)
1126 utils.system('rm -rf %s'%dir_name)
1130 "run sfi.py add (on Registry) and sfi.py create (on SM) to form new objects"
1132 test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1133 success=test_user_sfa.add_user()
1135 for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1136 site_spec = self.locate_site (slice_spec['sitename'])
1137 test_site = TestSite(self,site_spec)
1138 test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1139 success1=test_slice_sfa.add_slice()
1140 success2=test_slice_sfa.create_slice()
1141 return success and success1 and success2
1143 def update_sfa(self):
1144 "run sfi.py update (on Registry) and sfi.py create (on SM) on existing objects"
1146 test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1147 success1=test_user_sfa.update_user()
1149 for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1150 site_spec = self.locate_site (slice_spec['sitename'])
1151 test_site = TestSite(self,site_spec)
1152 test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1153 success2=test_slice_sfa.update_slice()
1154 return success1 and success2
1157 "run sfi.py list and sfi.py show (both on Registry) and sfi.py slices and sfi.py resources (both on SM)"
1158 auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1160 self.run_in_guest("sfi.py -d /root/.sfi/ list %s.main"%auth)==0 and \
1161 self.run_in_guest("sfi.py -d /root/.sfi/ show %s.main"%auth)==0 and \
1162 self.run_in_guest("sfi.py -d /root/.sfi/ slices")==0 and \
1163 self.run_in_guest("sfi.py -d /root/.sfi/ resources -o resources")==0
1165 @slice_mapper_options_sfa
1166 def check_slice_sfa(self):
1167 "tries to ssh-enter the SFA slice"
1170 def delete_sfa(self):
1171 "run sfi.py delete (on SM), sfi.py remove (on Registry)"
1173 test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1174 success1=test_user_sfa.delete_user()
1175 for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1176 site_spec = self.locate_site (slice_spec['sitename'])
1177 test_site = TestSite(self,site_spec)
1178 test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1179 success2=test_slice_sfa.delete_slice()
1181 return success1 and success2
1185 return self.run_in_guest('service sfa stop')==0
1187 def populate (self):
1188 "creates random entries in the PLCAPI"
1189 # install the stress-test in the plc image
1190 location = "/usr/share/plc_api/plcsh_stress_test.py"
1191 remote="/vservers/%s/%s"%(self.vservername,location)
1192 self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1194 command += " -- --preserve --short-names"
1195 local = (self.run_in_guest(command) == 0);
1196 # second run with --foreign
1197 command += ' --foreign'
1198 remote = (self.run_in_guest(command) == 0);
1199 return ( local and remote)
1201 def gather_logs (self):
1202 "gets all possible logs from plc's/qemu node's/slice's for future reference"
1203 # (1.a) get the plc's /var/log/ and store it locally in logs/myplc.var-log.<plcname>/*
1204 # (1.b) get the plc's /var/lib/pgsql/data/pg_log/ -> logs/myplc.pgsql-log.<plcname>/*
1205 # (2) get all the nodes qemu log and store it as logs/node.qemu.<node>.log
1206 # (3) get the nodes /var/log and store is as logs/node.var-log.<node>/*
1207 # (4) as far as possible get the slice's /var/log as logs/sliver.var-log.<sliver>/*
1209 print "-------------------- TestPlc.gather_logs : PLC's /var/log"
1210 self.gather_var_logs ()
1212 print "-------------------- TestPlc.gather_logs : PLC's /var/lib/psql/data/pg_log/"
1213 self.gather_pgsql_logs ()
1215 print "-------------------- TestPlc.gather_logs : nodes's QEMU logs"
1216 for site_spec in self.plc_spec['sites']:
1217 test_site = TestSite (self,site_spec)
1218 for node_spec in site_spec['nodes']:
1219 test_node=TestNode(self,test_site,node_spec)
1220 test_node.gather_qemu_logs()
1222 print "-------------------- TestPlc.gather_logs : nodes's /var/log"
1223 self.gather_nodes_var_logs()
1225 print "-------------------- TestPlc.gather_logs : sample sliver's /var/log"
1226 self.gather_slivers_var_logs()
1229 def gather_slivers_var_logs(self):
1230 for test_sliver in self.all_sliver_objs():
1231 remote = test_sliver.tar_var_logs()
1232 utils.system("mkdir -p logs/sliver.var-log.%s"%test_sliver.name())
1233 command = remote + " | tar -C logs/sliver.var-log.%s -xf -"%test_sliver.name()
1234 utils.system(command)
1237 def gather_var_logs (self):
1238 utils.system("mkdir -p logs/myplc.var-log.%s"%self.name())
1239 to_plc = self.actual_command_in_guest("tar -C /var/log/ -cf - .")
1240 command = to_plc + "| tar -C logs/myplc.var-log.%s -xf -"%self.name()
1241 utils.system(command)
1242 command = "chmod a+r,a+x logs/myplc.var-log.%s/httpd"%self.name()
1243 utils.system(command)
1245 def gather_pgsql_logs (self):
1246 utils.system("mkdir -p logs/myplc.pgsql-log.%s"%self.name())
1247 to_plc = self.actual_command_in_guest("tar -C /var/lib/pgsql/data/pg_log/ -cf - .")
1248 command = to_plc + "| tar -C logs/myplc.pgsql-log.%s -xf -"%self.name()
1249 utils.system(command)
1251 def gather_nodes_var_logs (self):
1252 for site_spec in self.plc_spec['sites']:
1253 test_site = TestSite (self,site_spec)
1254 for node_spec in site_spec['nodes']:
1255 test_node=TestNode(self,test_site,node_spec)
1256 test_ssh = TestSsh (test_node.name(),key="keys/key1.rsa")
1257 command = test_ssh.actual_command("tar -C /var/log -cf - .")
1258 command = command + "| tar -C logs/node.var-log.%s -xf -"%test_node.name()
1259 utils.system("mkdir -p logs/node.var-log.%s"%test_node.name())
1260 utils.system(command)
1263 # returns the filename to use for sql dump/restore, using options.dbname if set
1264 def dbfile (self, database):
1265 # uses options.dbname if it is found
1267 name=self.options.dbname
1268 if not isinstance(name,StringTypes):
1271 t=datetime.datetime.now()
1274 return "/root/%s-%s.sql"%(database,name)
1277 'dump the planetlab5 DB in /root in the PLC - filename has time'
1278 dump=self.dbfile("planetab5")
1279 self.run_in_guest('pg_dump -U pgsqluser planetlab5 -f '+ dump)
1280 utils.header('Dumped planetlab5 database in %s'%dump)
1283 def db_restore(self):
1284 'restore the planetlab5 DB - looks broken, but run -n might help'
1285 dump=self.dbfile("planetab5")
1286 ##stop httpd service
1287 self.run_in_guest('service httpd stop')
1288 # xxx - need another wrapper
1289 self.run_in_guest_piped('echo drop database planetlab5','psql --user=pgsqluser template1')
1290 self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab5')
1291 self.run_in_guest('psql -U pgsqluser planetlab5 -f '+dump)
1292 ##starting httpd service
1293 self.run_in_guest('service httpd start')
1295 utils.header('Database restored from ' + dump)
1298 def standby_1(): pass
1300 def standby_2(): pass
1302 def standby_3(): pass
1304 def standby_4(): pass
1306 def standby_5(): pass
1308 def standby_6(): pass
1310 def standby_7(): pass
1312 def standby_8(): pass
1314 def standby_9(): pass
1316 def standby_10(): pass
1318 def standby_11(): pass
1320 def standby_12(): pass
1322 def standby_13(): pass
1324 def standby_14(): pass
1326 def standby_15(): pass
1328 def standby_16(): pass
1330 def standby_17(): pass
1332 def standby_18(): pass
1334 def standby_19(): pass
1336 def standby_20(): pass