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__
88 'display', 'resources_pre', SEP,
89 'delete_vs','create_vs','install', 'configure', 'start', SEP,
90 'fetch_keys', 'store_keys', 'clear_known_hosts', SEP,
91 'initscripts', 'sites', 'nodes', 'slices', 'nodegroups', 'leases', SEP,
92 'reinstall_node', 'init_node','bootcd', 'configure_qemu', 'export_qemu',
93 'kill_all_qemus', 'start_node', SEP,
94 # better use of time: do this now that the nodes are taking off
95 'plcsh_stress_test', SEP,
96 'install_sfa', 'configure_sfa', 'import_sfa', 'start_sfa', SEPSFA,
97 'setup_sfa', 'add_sfa', 'update_sfa', 'view_sfa', SEPSFA,
98 'nodes_ssh_debug', 'nodes_ssh_boot', 'check_slice', 'check_initscripts', SEPSFA,
99 'check_slice_sfa', 'delete_sfa', 'stop_sfa', SEPSFA,
100 'check_tcp', 'check_hooks', SEP,
101 'force_gather_logs', 'force_resources_post', SEP,
104 'show_boxes', 'resources_list','resources_release','resources_release_plc','resources_release_qemu',SEP,
105 'stop', 'vs_start', SEP,
106 'clean_initscripts', 'clean_nodegroups','clean_all_sites', SEP,
107 'clean_sites', 'clean_nodes', 'clean_slices', 'clean_keys', SEP,
108 'clean_leases', 'list_leases', SEP,
110 'list_all_qemus', 'list_qemus', 'kill_qemus', SEP,
111 'db_dump' , 'db_restore', SEP,
112 'standby_1 through 20',SEP,
116 def printable_steps (list):
117 single_line=" ".join(list)+" "
118 return single_line.replace(" "+SEP+" "," \\\n").replace(" "+SEPSFA+" "," \\\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 # move all steps containing 'sfa' from default_steps to other_steps
131 sfa_steps= [ step for step in TestPlc.default_steps if step.find('sfa')>=0 ]
132 TestPlc.other_steps += sfa_steps
133 for step in sfa_steps: TestPlc.default_steps.remove(step)
135 def __init__ (self,plc_spec,options):
136 self.plc_spec=plc_spec
138 self.test_ssh=TestSsh(self.plc_spec['hostname'],self.options.buildname)
140 self.vserverip=plc_spec['vserverip']
141 self.vservername=plc_spec['vservername']
142 self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
145 raise Exception,'chroot-based myplc testing is deprecated'
146 self.apiserver=TestApiserver(self.url,options.dry_run)
149 name=self.plc_spec['name']
150 return "%s.%s"%(name,self.vservername)
153 return self.plc_spec['hostname']
156 return self.test_ssh.is_local()
158 # define the API methods on this object through xmlrpc
159 # would help, but not strictly necessary
163 def actual_command_in_guest (self,command):
164 return self.test_ssh.actual_command(self.host_to_guest(command))
166 def start_guest (self):
167 return utils.system(self.test_ssh.actual_command(self.start_guest_in_host()))
169 def run_in_guest (self,command):
170 return utils.system(self.actual_command_in_guest(command))
172 def run_in_host (self,command):
173 return self.test_ssh.run_in_buildname(command)
175 #command gets run in the vserver
176 def host_to_guest(self,command):
177 return "vserver %s exec %s"%(self.vservername,command)
179 #command gets run in the vserver
180 def start_guest_in_host(self):
181 return "vserver %s start"%(self.vservername)
184 def run_in_guest_piped (self,local,remote):
185 return utils.system(local+" | "+self.test_ssh.actual_command(self.host_to_guest(remote),keep_stdin=True))
187 def auth_root (self):
188 return {'Username':self.plc_spec['PLC_ROOT_USER'],
189 'AuthMethod':'password',
190 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
191 'Role' : self.plc_spec['role']
193 def locate_site (self,sitename):
194 for site in self.plc_spec['sites']:
195 if site['site_fields']['name'] == sitename:
197 if site['site_fields']['login_base'] == sitename:
199 raise Exception,"Cannot locate site %s"%sitename
201 def locate_node (self,nodename):
202 for site in self.plc_spec['sites']:
203 for node in site['nodes']:
204 if node['name'] == nodename:
206 raise Exception,"Cannot locate node %s"%nodename
208 def locate_hostname (self,hostname):
209 for site in self.plc_spec['sites']:
210 for node in site['nodes']:
211 if node['node_fields']['hostname'] == hostname:
213 raise Exception,"Cannot locate hostname %s"%hostname
215 def locate_key (self,keyname):
216 for key in self.plc_spec['keys']:
217 if key['name'] == keyname:
219 raise Exception,"Cannot locate key %s"%keyname
221 def locate_slice (self, slicename):
222 for slice in self.plc_spec['slices']:
223 if slice['slice_fields']['name'] == slicename:
225 raise Exception,"Cannot locate slice %s"%slicename
227 def all_sliver_objs (self):
229 for slice_spec in self.plc_spec['slices']:
230 slicename = slice_spec['slice_fields']['name']
231 for nodename in slice_spec['nodenames']:
232 result.append(self.locate_sliver_obj (nodename,slicename))
235 def locate_sliver_obj (self,nodename,slicename):
236 (site,node) = self.locate_node(nodename)
237 slice = self.locate_slice (slicename)
239 test_site = TestSite (self, site)
240 test_node = TestNode (self, test_site,node)
241 # xxx the slice site is assumed to be the node site - mhh - probably harmless
242 test_slice = TestSlice (self, test_site, slice)
243 return TestSliver (self, test_node, test_slice)
245 def locate_first_node(self):
246 nodename=self.plc_spec['slices'][0]['nodenames'][0]
247 (site,node) = self.locate_node(nodename)
248 test_site = TestSite (self, site)
249 test_node = TestNode (self, test_site,node)
252 def locate_first_sliver (self):
253 slice_spec=self.plc_spec['slices'][0]
254 slicename=slice_spec['slice_fields']['name']
255 nodename=slice_spec['nodenames'][0]
256 return self.locate_sliver_obj(nodename,slicename)
258 # all different hostboxes used in this plc
259 def gather_hostBoxes(self):
260 # maps on sites and nodes, return [ (host_box,test_node) ]
262 for site_spec in self.plc_spec['sites']:
263 test_site = TestSite (self,site_spec)
264 for node_spec in site_spec['nodes']:
265 test_node = TestNode (self, test_site, node_spec)
266 if not test_node.is_real():
267 tuples.append( (test_node.host_box(),test_node) )
268 # transform into a dict { 'host_box' -> [ test_node .. ] }
270 for (box,node) in tuples:
271 if not result.has_key(box):
274 result[box].append(node)
277 # a step for checking this stuff
278 def show_boxes (self):
279 'print summary of nodes location'
280 for (box,nodes) in self.gather_hostBoxes().iteritems():
281 print box,":"," + ".join( [ node.name() for node in nodes ] )
284 # make this a valid step
285 def kill_all_qemus(self):
286 'kill all qemu instances on the qemu boxes involved by this setup'
287 # this is the brute force version, kill all qemus on that host box
288 for (box,nodes) in self.gather_hostBoxes().iteritems():
289 # pass the first nodename, as we don't push template-qemu on testboxes
290 nodedir=nodes[0].nodedir()
291 TestBox(box,self.options.buildname).kill_all_qemus(nodedir)
294 # make this a valid step
295 def list_all_qemus(self):
296 'list all qemu instances on the qemu boxes involved by this setup'
297 for (box,nodes) in self.gather_hostBoxes().iteritems():
298 # this is the brute force version, kill all qemus on that host box
299 TestBox(box,self.options.buildname).list_all_qemus()
302 # kill only the right qemus
303 def list_qemus(self):
304 'list qemu instances for our nodes'
305 for (box,nodes) in self.gather_hostBoxes().iteritems():
306 # the fine-grain version
311 # kill only the right qemus
312 def kill_qemus(self):
313 'kill the qemu instances for our nodes'
314 for (box,nodes) in self.gather_hostBoxes().iteritems():
315 # the fine-grain version
320 #################### display config
322 "show test configuration after localization"
323 self.display_pass (1)
324 self.display_pass (2)
328 always_display_keys=['PLC_WWW_HOST','nodes','sites',]
329 def display_pass (self,passno):
330 for (key,val) in self.plc_spec.iteritems():
331 if not self.options.verbose and key not in TestPlc.always_display_keys: continue
335 self.display_site_spec(site)
336 for node in site['nodes']:
337 self.display_node_spec(node)
338 elif key=='initscripts':
339 for initscript in val:
340 self.display_initscript_spec (initscript)
343 self.display_slice_spec (slice)
346 self.display_key_spec (key)
348 if key not in ['sites','initscripts','slices','keys', 'sfa']:
349 print '+ ',key,':',val
351 def display_site_spec (self,site):
352 print '+ ======== site',site['site_fields']['name']
353 for (k,v) in site.iteritems():
354 if not self.options.verbose and k not in TestPlc.always_display_keys: continue
357 print '+ ','nodes : ',
359 print node['node_fields']['hostname'],'',
365 print user['name'],'',
367 elif k == 'site_fields':
368 print '+ login_base',':',v['login_base']
369 elif k == 'address_fields':
375 def display_initscript_spec (self,initscript):
376 print '+ ======== initscript',initscript['initscript_fields']['name']
378 def display_key_spec (self,key):
379 print '+ ======== key',key['name']
381 def display_slice_spec (self,slice):
382 print '+ ======== slice',slice['slice_fields']['name']
383 for (k,v) in slice.iteritems():
396 elif k=='slice_fields':
397 print '+ fields',':',
398 print 'max_nodes=',v['max_nodes'],
403 def display_node_spec (self,node):
404 print "+ node=%s host_box=%s"%(node['name'],node['host_box']),
405 print "hostname=",node['node_fields']['hostname'],
406 print "ip=",node['interface_fields']['ip']
407 if self.options.verbose:
408 utils.pprint("node details",node,depth=3)
410 # another entry point for just showing the boxes involved
411 def display_mapping (self):
412 TestPlc.display_mapping_plc(self.plc_spec)
416 def display_mapping_plc (plc_spec):
417 print '+ MyPLC',plc_spec['name']
418 print '+\tvserver address = root@%s:/vservers/%s'%(plc_spec['hostname'],plc_spec['vservername'])
419 print '+\tIP = %s/%s'%(plc_spec['PLC_API_HOST'],plc_spec['vserverip'])
420 for site_spec in plc_spec['sites']:
421 for node_spec in site_spec['nodes']:
422 TestPlc.display_mapping_node(node_spec)
425 def display_mapping_node (node_spec):
426 print '+ NODE %s'%(node_spec['name'])
427 print '+\tqemu box %s'%node_spec['host_box']
428 print '+\thostname=%s'%node_spec['node_fields']['hostname']
430 def resources_pre (self):
431 "run site-dependant pre-test script as defined in LocalTestResources"
432 from LocalTestResources import local_resources
433 return local_resources.step_pre(self)
435 def resources_post (self):
436 "run site-dependant post-test script as defined in LocalTestResources"
437 from LocalTestResources import local_resources
438 return local_resources.step_post(self)
440 def resources_list (self):
441 "run site-dependant list script as defined in LocalTestResources"
442 from LocalTestResources import local_resources
443 return local_resources.step_list(self)
445 def resources_release (self):
446 "run site-dependant release script as defined in LocalTestResources"
447 from LocalTestResources import local_resources
448 return local_resources.step_release(self)
450 def resources_release_plc (self):
451 "run site-dependant release script as defined in LocalTestResources"
452 from LocalTestResources import local_resources
453 return local_resources.step_release_plc(self)
455 def resources_release_qemu (self):
456 "run site-dependant release script as defined in LocalTestResources"
457 from LocalTestResources import local_resources
458 return local_resources.step_release_qemu(self)
461 "vserver delete the test myplc"
462 self.run_in_host("vserver --silent %s delete"%self.vservername)
466 # historically the build was being fetched by the tests
467 # now the build pushes itself as a subdir of the tests workdir
468 # so that the tests do not have to worry about extracting the build (svn, git, or whatever)
469 def create_vs (self):
470 "vserver creation (no install done)"
471 # push the local build/ dir to the testplc box
473 # a full path for the local calls
474 build_dir=os.path.dirname(sys.argv[0])
475 # sometimes this is empty - set to "." in such a case
476 if not build_dir: build_dir="."
477 build_dir += "/build"
479 # use a standard name - will be relative to remote buildname
481 # remove for safety; do *not* mkdir first, otherwise we end up with build/build/
482 self.test_ssh.rmdir(build_dir)
483 self.test_ssh.copy(build_dir,recursive=True)
484 # the repo url is taken from arch-rpms-url
485 # with the last step (i386) removed
486 repo_url = self.options.arch_rpms_url
487 for level in [ 'arch' ]:
488 repo_url = os.path.dirname(repo_url)
489 # pass the vbuild-nightly options to vtest-init-vserver
491 test_env_options += " -p %s"%self.options.personality
492 test_env_options += " -d %s"%self.options.pldistro
493 test_env_options += " -f %s"%self.options.fcdistro
494 script="vtest-init-vserver.sh"
495 vserver_name = self.vservername
496 vserver_options="--netdev eth0 --interface %s"%self.vserverip
498 vserver_hostname=socket.gethostbyaddr(self.vserverip)[0]
499 vserver_options += " --hostname %s"%vserver_hostname
501 print "Cannot reverse lookup %s"%self.vserverip
502 print "This is considered fatal, as this might pollute the test results"
504 create_vserver="%(build_dir)s/%(script)s %(test_env_options)s %(vserver_name)s %(repo_url)s -- %(vserver_options)s"%locals()
505 return self.run_in_host(create_vserver) == 0
509 "yum install myplc, noderepo, and the plain bootstrapfs"
511 # workaround for getting pgsql8.2 on centos5
512 if self.options.fcdistro == "centos5":
513 self.run_in_guest("rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm")
515 if self.options.personality == "linux32":
517 elif self.options.personality == "linux64":
520 raise Exception, "Unsupported personality %r"%self.options.personality
522 nodefamily="%s-%s-%s"%(self.options.pldistro,self.options.fcdistro,arch)
524 # try to install slicerepo - not fatal yet
525 self.run_in_guest("yum -y install slicerepo-%s"%nodefamily)
528 self.run_in_guest("yum -y install myplc")==0 and \
529 self.run_in_guest("yum -y install noderepo-%s"%nodefamily)==0 and \
530 self.run_in_guest("yum -y install bootstrapfs-%s-plain"%nodefamily)==0
535 tmpname='%s.plc-config-tty'%(self.name())
536 fileconf=open(tmpname,'w')
537 for var in [ 'PLC_NAME',
541 'PLC_MAIL_SUPPORT_ADDRESS',
544 # Above line was added for integrating SFA Testing
550 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
551 fileconf.write('w\n')
552 fileconf.write('q\n')
554 utils.system('cat %s'%tmpname)
555 self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
556 utils.system('rm %s'%tmpname)
561 self.run_in_guest('service plc start')
566 self.run_in_guest('service plc stop')
570 "start the PLC vserver"
574 # stores the keys from the config for further use
575 def store_keys(self):
576 "stores test users ssh keys in keys/"
577 for key_spec in self.plc_spec['keys']:
578 TestKey(self,key_spec).store_key()
581 def clean_keys(self):
582 "removes keys cached in keys/"
583 utils.system("rm -rf %s/keys/"%os.path(sys.argv[0]))
585 # fetches the ssh keys in the plc's /etc/planetlab and stores them in keys/
586 # for later direct access to the nodes
587 def fetch_keys(self):
588 "gets ssh keys in /etc/planetlab/ and stores them locally in keys/"
590 if not os.path.isdir(dir):
592 vservername=self.vservername
594 prefix = 'debug_ssh_key'
595 for ext in [ 'pub', 'rsa' ] :
596 src="/vservers/%(vservername)s/etc/planetlab/%(prefix)s.%(ext)s"%locals()
597 dst="keys/%(vservername)s-debug.%(ext)s"%locals()
598 if self.test_ssh.fetch(src,dst) != 0: overall=False
602 "create sites with PLCAPI"
603 return self.do_sites()
605 def clean_sites (self):
606 "delete sites with PLCAPI"
607 return self.do_sites(action="delete")
609 def do_sites (self,action="add"):
610 for site_spec in self.plc_spec['sites']:
611 test_site = TestSite (self,site_spec)
612 if (action != "add"):
613 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
614 test_site.delete_site()
615 # deleted with the site
616 #test_site.delete_users()
619 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
620 test_site.create_site()
621 test_site.create_users()
624 def clean_all_sites (self):
625 "Delete all sites in PLC, and related objects"
626 print 'auth_root',self.auth_root()
627 site_ids = [s['site_id'] for s in self.apiserver.GetSites(self.auth_root(), {}, ['site_id'])]
628 for site_id in site_ids:
629 print 'Deleting site_id',site_id
630 self.apiserver.DeleteSite(self.auth_root(),site_id)
633 "create nodes with PLCAPI"
634 return self.do_nodes()
635 def clean_nodes (self):
636 "delete nodes with PLCAPI"
637 return self.do_nodes(action="delete")
639 def do_nodes (self,action="add"):
640 for site_spec in self.plc_spec['sites']:
641 test_site = TestSite (self,site_spec)
643 utils.header("Deleting nodes in site %s"%test_site.name())
644 for node_spec in site_spec['nodes']:
645 test_node=TestNode(self,test_site,node_spec)
646 utils.header("Deleting %s"%test_node.name())
647 test_node.delete_node()
649 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
650 for node_spec in site_spec['nodes']:
651 utils.pprint('Creating node %s'%node_spec,node_spec)
652 test_node = TestNode (self,test_site,node_spec)
653 test_node.create_node ()
656 def nodegroups (self):
657 "create nodegroups with PLCAPI"
658 return self.do_nodegroups("add")
659 def clean_nodegroups (self):
660 "delete nodegroups with PLCAPI"
661 return self.do_nodegroups("delete")
665 def translate_timestamp (start,timestamp):
666 if timestamp < TestPlc.YEAR: return start+timestamp
667 else: return timestamp
670 def timestamp_printable (timestamp):
671 return time.strftime('%m-%d %H:%M:%S UTC',time.gmtime(timestamp))
674 "create leases (on reservable nodes only, use e.g. run -c default -c resa)"
676 grain=self.apiserver.GetLeaseGranularity(self.auth_root())
677 round_time=(now/grain)*grain
678 start=round_time+grain
679 # find out all nodes that are reservable
680 nodes=self.all_reservable_nodenames()
682 utils.header ("No reservable node found - proceeding without leases")
685 # attach them to the leases as specified in plc_specs
686 # this is where the 'leases' field gets interpreted as relative of absolute
687 for lease_spec in self.plc_spec['leases']:
688 # skip the ones that come with a null slice id
689 if not lease_spec['slice']: continue
690 lease_spec['t_from']=TestPlc.translate_timestamp(start,lease_spec['t_from'])
691 lease_spec['t_until']=TestPlc.translate_timestamp(start,lease_spec['t_until'])
692 lease_addition=self.apiserver.AddLeases(self.auth_root(),nodes,
693 lease_spec['slice'],lease_spec['t_from'],lease_spec['t_until'])
694 if lease_addition['errors']:
695 utils.header("Cannot create leases, %s"%lease_addition['errors'])
698 utils.header('Leases on nodes %r for %s from %d (%s) until %d (%s)'%\
699 (nodes,lease_spec['slice'],
700 lease_spec['t_from'],TestPlc.timestamp_printable(lease_spec['t_from']),
701 lease_spec['t_until'],TestPlc.timestamp_printable(lease_spec['t_until'])))
705 def clean_leases (self):
706 "remove all leases in the myplc side"
707 lease_ids= [ l['lease_id'] for l in self.apiserver.GetLeases(self.auth_root())]
708 utils.header("Cleaning leases %r"%lease_ids)
709 self.apiserver.DeleteLeases(self.auth_root(),lease_ids)
712 def list_leases (self):
713 "list all leases known to the myplc"
714 leases = self.apiserver.GetLeases(self.auth_root())
716 utils.header("%s %s from %s until %s"%(l['hostname'],l['name'],
717 TestPlc.timestamp_printable(l['t_from']),
718 TestPlc.timestamp_printable(l['t_until'])))
721 # create nodegroups if needed, and populate
722 def do_nodegroups (self, action="add"):
723 # 1st pass to scan contents
725 for site_spec in self.plc_spec['sites']:
726 test_site = TestSite (self,site_spec)
727 for node_spec in site_spec['nodes']:
728 test_node=TestNode (self,test_site,node_spec)
729 if node_spec.has_key('nodegroups'):
730 nodegroupnames=node_spec['nodegroups']
731 if isinstance(nodegroupnames,StringTypes):
732 nodegroupnames = [ nodegroupnames ]
733 for nodegroupname in nodegroupnames:
734 if not groups_dict.has_key(nodegroupname):
735 groups_dict[nodegroupname]=[]
736 groups_dict[nodegroupname].append(test_node.name())
737 auth=self.auth_root()
739 for (nodegroupname,group_nodes) in groups_dict.iteritems():
741 print 'nodegroups:','dealing with nodegroup',nodegroupname,'on nodes',group_nodes
742 # first, check if the nodetagtype is here
743 tag_types = self.apiserver.GetTagTypes(auth,{'tagname':nodegroupname})
745 tag_type_id = tag_types[0]['tag_type_id']
747 tag_type_id = self.apiserver.AddTagType(auth,
748 {'tagname':nodegroupname,
749 'description': 'for nodegroup %s'%nodegroupname,
752 print 'located tag (type)',nodegroupname,'as',tag_type_id
754 nodegroups = self.apiserver.GetNodeGroups (auth, {'groupname':nodegroupname})
756 self.apiserver.AddNodeGroup(auth, nodegroupname, tag_type_id, 'yes')
757 print 'created nodegroup',nodegroupname,'from tagname',nodegroupname,'and value','yes'
758 # set node tag on all nodes, value='yes'
759 for nodename in group_nodes:
761 self.apiserver.AddNodeTag(auth, nodename, nodegroupname, "yes")
763 traceback.print_exc()
764 print 'node',nodename,'seems to already have tag',nodegroupname
767 expect_yes = self.apiserver.GetNodeTags(auth,
768 {'hostname':nodename,
769 'tagname':nodegroupname},
770 ['value'])[0]['value']
771 if expect_yes != "yes":
772 print 'Mismatch node tag on node',nodename,'got',expect_yes
775 if not self.options.dry_run:
776 print 'Cannot find tag',nodegroupname,'on node',nodename
780 print 'cleaning nodegroup',nodegroupname
781 self.apiserver.DeleteNodeGroup(auth,nodegroupname)
783 traceback.print_exc()
787 # return a list of tuples (nodename,qemuname)
788 def all_node_infos (self) :
790 for site_spec in self.plc_spec['sites']:
791 node_infos += [ (node_spec['node_fields']['hostname'],node_spec['host_box']) \
792 for node_spec in site_spec['nodes'] ]
795 def all_nodenames (self): return [ x[0] for x in self.all_node_infos() ]
796 def all_reservable_nodenames (self):
798 for site_spec in self.plc_spec['sites']:
799 for node_spec in site_spec['nodes']:
800 node_fields=node_spec['node_fields']
801 if 'node_type' in node_fields and node_fields['node_type']=='reservable':
802 res.append(node_fields['hostname'])
805 # silent_minutes : during the first <silent_minutes> minutes nothing gets printed
806 def nodes_check_boot_state (self, target_boot_state, timeout_minutes, silent_minutes,period=15):
807 if self.options.dry_run:
811 timeout = datetime.datetime.now()+datetime.timedelta(minutes=timeout_minutes)
812 graceout = datetime.datetime.now()+datetime.timedelta(minutes=silent_minutes)
813 # the nodes that haven't checked yet - start with a full list and shrink over time
814 tocheck = self.all_hostnames()
815 utils.header("checking nodes %r"%tocheck)
816 # create a dict hostname -> status
817 status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
820 tocheck_status=self.apiserver.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
822 for array in tocheck_status:
823 hostname=array['hostname']
824 boot_state=array['boot_state']
825 if boot_state == target_boot_state:
826 utils.header ("%s has reached the %s state"%(hostname,target_boot_state))
828 # if it's a real node, never mind
829 (site_spec,node_spec)=self.locate_hostname(hostname)
830 if TestNode.is_real_model(node_spec['node_fields']['model']):
831 utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
833 boot_state = target_boot_state
834 elif datetime.datetime.now() > graceout:
835 utils.header ("%s still in '%s' state"%(hostname,boot_state))
836 graceout=datetime.datetime.now()+datetime.timedelta(1)
837 status[hostname] = boot_state
839 tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != target_boot_state ]
842 if datetime.datetime.now() > timeout:
843 for hostname in tocheck:
844 utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
846 # otherwise, sleep for a while
848 # only useful in empty plcs
851 def nodes_booted(self):
852 return self.nodes_check_boot_state('boot',timeout_minutes=30,silent_minutes=20)
854 def check_nodes_ssh(self,debug,timeout_minutes,silent_minutes,period=15):
856 timeout = datetime.datetime.now()+datetime.timedelta(minutes=timeout_minutes)
857 graceout = datetime.datetime.now()+datetime.timedelta(minutes=silent_minutes)
858 vservername=self.vservername
861 local_key = "keys/%(vservername)s-debug.rsa"%locals()
864 local_key = "keys/key1.rsa"
865 node_infos = self.all_node_infos()
866 utils.header("checking ssh access (expected in %s mode) to nodes:"%message)
867 for (nodename,qemuname) in node_infos:
868 utils.header("hostname=%s -- qemubox=%s"%(nodename,qemuname))
869 utils.header("max timeout is %d minutes, silent for %d minutes (period is %s)"%\
870 (timeout_minutes,silent_minutes,period))
872 for node_info in node_infos:
873 (hostname,qemuname) = node_info
874 # try to run 'hostname' in the node
875 command = TestSsh (hostname,key=local_key).actual_command("hostname;uname -a")
876 # don't spam logs - show the command only after the grace period
877 success = utils.system ( command, silent=datetime.datetime.now() < graceout)
879 utils.header('Successfully entered root@%s (%s)'%(hostname,message))
881 node_infos.remove(node_info)
883 # we will have tried real nodes once, in case they're up - but if not, just skip
884 (site_spec,node_spec)=self.locate_hostname(hostname)
885 if TestNode.is_real_model(node_spec['node_fields']['model']):
886 utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
887 node_infos.remove(node_info)
890 if datetime.datetime.now() > timeout:
891 for (hostname,qemuname) in node_infos:
892 utils.header("FAILURE to ssh into %s (on %s)"%(hostname,qemuname))
894 # otherwise, sleep for a while
896 # only useful in empty plcs
899 def nodes_ssh_debug(self):
900 "Tries to ssh into nodes in debug mode with the debug ssh key"
901 return self.check_nodes_ssh(debug=True,timeout_minutes=30,silent_minutes=5)
903 def nodes_ssh_boot(self):
904 "Tries to ssh into nodes in production mode with the root ssh key"
905 return self.check_nodes_ssh(debug=False,timeout_minutes=30,silent_minutes=15)
908 def init_node (self):
909 "all nodes : init a clean local directory for holding node-dep stuff like iso image..."
913 "all nodes: invoke GetBootMedium and store result locally"
916 def configure_qemu (self):
917 "all nodes: compute qemu config qemu.conf and store it locally"
920 def reinstall_node (self):
921 "all nodes: mark PLCAPI boot_state as reinstall"
924 def export_qemu (self):
925 "all nodes: push local node-dep directory on the qemu box"
928 ### check hooks : invoke scripts from hooks/{node,slice}
929 def check_hooks_node (self):
930 return self.locate_first_node().check_hooks()
931 def check_hooks_sliver (self) :
932 return self.locate_first_sliver().check_hooks()
934 def check_hooks (self):
935 "runs unit tests in the node and slice contexts - see hooks/{node,slice}"
936 return self.check_hooks_node() and self.check_hooks_sliver()
939 def do_check_initscripts(self):
941 for slice_spec in self.plc_spec['slices']:
942 if not slice_spec.has_key('initscriptname'):
944 initscript=slice_spec['initscriptname']
945 for nodename in slice_spec['nodenames']:
946 (site,node) = self.locate_node (nodename)
947 # xxx - passing the wrong site - probably harmless
948 test_site = TestSite (self,site)
949 test_slice = TestSlice (self,test_site,slice_spec)
950 test_node = TestNode (self,test_site,node)
951 test_sliver = TestSliver (self, test_node, test_slice)
952 if not test_sliver.check_initscript(initscript):
956 def check_initscripts(self):
957 "check that the initscripts have triggered"
958 return self.do_check_initscripts()
960 def initscripts (self):
961 "create initscripts with PLCAPI"
962 for initscript in self.plc_spec['initscripts']:
963 utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
964 self.apiserver.AddInitScript(self.auth_root(),initscript['initscript_fields'])
967 def clean_initscripts (self):
968 "delete initscripts with PLCAPI"
969 for initscript in self.plc_spec['initscripts']:
970 initscript_name = initscript['initscript_fields']['name']
971 print('Attempting to delete %s in plc %s'%(initscript_name,self.plc_spec['name']))
973 self.apiserver.DeleteInitScript(self.auth_root(),initscript_name)
974 print initscript_name,'deleted'
976 print 'deletion went wrong - probably did not exist'
981 "create slices with PLCAPI"
982 return self.do_slices()
984 def clean_slices (self):
985 "delete slices with PLCAPI"
986 return self.do_slices("delete")
988 def do_slices (self, action="add"):
989 for slice in self.plc_spec['slices']:
990 site_spec = self.locate_site (slice['sitename'])
991 test_site = TestSite(self,site_spec)
992 test_slice=TestSlice(self,test_site,slice)
994 utils.header("Deleting slices in site %s"%test_site.name())
995 test_slice.delete_slice()
997 utils.pprint("Creating slice",slice)
998 test_slice.create_slice()
999 utils.header('Created Slice %s'%slice['slice_fields']['name'])
1002 @slice_mapper_options
1003 def check_slice(self):
1004 "tries to ssh-enter the slice with the user key, to ensure slice creation"
1008 def clear_known_hosts (self):
1009 "remove test nodes entries from the local known_hosts file"
1013 def start_node (self) :
1014 "all nodes: start the qemu instance (also runs qemu-bridge-init start)"
1017 def check_tcp (self):
1018 "check TCP connectivity between 2 slices (or in loopback if only one is defined)"
1019 specs = self.plc_spec['tcp_test']
1024 s_test_sliver = self.locate_sliver_obj (spec['server_node'],spec['server_slice'])
1025 if not s_test_sliver.run_tcp_server(port,timeout=10):
1029 # idem for the client side
1030 c_test_sliver = self.locate_sliver_obj(spec['server_node'],spec['server_slice'])
1031 if not c_test_sliver.run_tcp_client(s_test_sliver.test_node.name(),port):
1035 def plcsh_stress_test (self):
1036 "runs PLCAPI stress test, that checks Add/Update/Delete on all types - preserves contents"
1037 # install the stress-test in the plc image
1038 location = "/usr/share/plc_api/plcsh_stress_test.py"
1039 remote="/vservers/%s/%s"%(self.vservername,location)
1040 self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1042 command += " -- --check"
1043 if self.options.size == 1:
1044 command += " --tiny"
1045 return ( self.run_in_guest(command) == 0)
1047 # populate runs the same utility without slightly different options
1048 # in particular runs with --preserve (dont cleanup) and without --check
1049 # also it gets run twice, once with the --foreign option for creating fake foreign entries
1052 def install_sfa(self):
1053 "yum install sfa, sfa-plc and sfa-client"
1054 if self.options.personality == "linux32":
1056 elif self.options.personality == "linux64":
1059 raise Exception, "Unsupported personality %r"%self.options.personality
1060 return self.run_in_guest("yum -y install sfa sfa-client sfa-plc sfa-sfatables")==0
1063 def configure_sfa(self):
1064 "run sfa-config-tty"
1065 tmpname='%s.sfa-config-tty'%(self.name())
1066 fileconf=open(tmpname,'w')
1067 for var in [ 'SFA_REGISTRY_ROOT_AUTH',
1068 'SFA_REGISTRY_LEVEL1_AUTH',
1069 'SFA_REGISTRY_HOST',
1070 'SFA_AGGREGATE_HOST',
1076 'SFA_PLC_DB_PASSWORD',
1078 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec['sfa'][var]))
1079 fileconf.write('w\n')
1080 fileconf.write('R\n')
1081 fileconf.write('q\n')
1083 utils.system('cat %s'%tmpname)
1084 self.run_in_guest_piped('cat %s'%tmpname,'sfa-config-tty')
1085 utils.system('rm %s'%tmpname)
1088 def import_sfa(self):
1090 auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1091 return self.run_in_guest('sfa-import-plc.py')==0
1092 # not needed anymore
1093 # self.run_in_guest('cp /etc/sfa/authorities/%s/%s.pkey /etc/sfa/authorities/server.key'%(auth,auth))
1095 def start_sfa(self):
1097 return self.run_in_guest('service sfa start')==0
1099 def setup_sfa(self):
1100 "sfi client configuration"
1102 if os.path.exists(dir_name):
1103 utils.system('rm -rf %s'%dir_name)
1104 utils.system('mkdir %s'%dir_name)
1105 file_name=dir_name + os.sep + 'fake-pi1.pkey'
1106 fileconf=open(file_name,'w')
1107 fileconf.write (self.plc_spec['keys'][0]['private'])
1110 file_name=dir_name + os.sep + 'sfi_config'
1111 fileconf=open(file_name,'w')
1112 SFI_AUTH=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']+".main"
1113 fileconf.write ("SFI_AUTH='%s'"%SFI_AUTH)
1114 fileconf.write('\n')
1115 SFI_USER=SFI_AUTH+'.fake-pi1'
1116 fileconf.write ("SFI_USER='%s'"%SFI_USER)
1117 fileconf.write('\n')
1118 SFI_REGISTRY='http://' + self.plc_spec['sfa']['SFA_PLC_DB_HOST'] + ':12345/'
1119 fileconf.write ("SFI_REGISTRY='%s'"%SFI_REGISTRY)
1120 fileconf.write('\n')
1121 SFI_SM='http://' + self.plc_spec['sfa']['SFA_PLC_DB_HOST'] + ':12347/'
1122 fileconf.write ("SFI_SM='%s'"%SFI_SM)
1123 fileconf.write('\n')
1126 file_name=dir_name + os.sep + 'person.xml'
1127 fileconf=open(file_name,'w')
1128 for record in self.plc_spec['sfa']['sfa_person_xml']:
1129 person_record=record
1130 fileconf.write(person_record)
1131 fileconf.write('\n')
1134 file_name=dir_name + os.sep + 'slice.xml'
1135 fileconf=open(file_name,'w')
1136 for record in self.plc_spec['sfa']['sfa_slice_xml']:
1138 #slice_record=self.plc_spec['sfa']['sfa_slice_xml']
1139 fileconf.write(slice_record)
1140 fileconf.write('\n')
1143 file_name=dir_name + os.sep + 'slice.rspec'
1144 fileconf=open(file_name,'w')
1146 for (key, value) in self.plc_spec['sfa']['sfa_slice_rspec'].items():
1148 fileconf.write(slice_rspec)
1149 fileconf.write('\n')
1152 remote="/vservers/%s/%s"%(self.vservername,location)
1153 self.test_ssh.copy_abs(dir_name, remote, recursive=True)
1155 #utils.system('cat %s'%tmpname)
1156 utils.system('rm -rf %s'%dir_name)
1160 "run sfi.py add (on Registry) and sfi.py create (on SM) to form new objects"
1162 test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1163 success=test_user_sfa.add_user()
1165 for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1166 site_spec = self.locate_site (slice_spec['sitename'])
1167 test_site = TestSite(self,site_spec)
1168 test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1169 success1=test_slice_sfa.add_slice()
1170 success2=test_slice_sfa.create_slice()
1171 return success and success1 and success2
1173 def update_sfa(self):
1174 "run sfi.py update (on Registry) and sfi.py create (on SM) on existing objects"
1176 test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1177 success1=test_user_sfa.update_user()
1179 for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1180 site_spec = self.locate_site (slice_spec['sitename'])
1181 test_site = TestSite(self,site_spec)
1182 test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1183 success2=test_slice_sfa.update_slice()
1184 return success1 and success2
1187 "run sfi.py list and sfi.py show (both on Registry) and sfi.py slices and sfi.py resources (both on SM)"
1188 auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1190 self.run_in_guest("sfi.py -d /root/.sfi/ list %s.main"%auth)==0 and \
1191 self.run_in_guest("sfi.py -d /root/.sfi/ show %s.main"%auth)==0 and \
1192 self.run_in_guest("sfi.py -d /root/.sfi/ slices")==0 and \
1193 self.run_in_guest("sfi.py -d /root/.sfi/ resources -o resources")==0
1195 @slice_mapper_options_sfa
1196 def check_slice_sfa(self):
1197 "tries to ssh-enter the SFA slice"
1200 def delete_sfa(self):
1201 "run sfi.py delete (on SM), sfi.py remove (on Registry)"
1203 test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1204 success1=test_user_sfa.delete_user()
1205 for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1206 site_spec = self.locate_site (slice_spec['sitename'])
1207 test_site = TestSite(self,site_spec)
1208 test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1209 success2=test_slice_sfa.delete_slice()
1211 return success1 and success2
1215 return self.run_in_guest('service sfa stop')==0
1217 def populate (self):
1218 "creates random entries in the PLCAPI"
1219 # install the stress-test in the plc image
1220 location = "/usr/share/plc_api/plcsh_stress_test.py"
1221 remote="/vservers/%s/%s"%(self.vservername,location)
1222 self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1224 command += " -- --preserve --short-names"
1225 local = (self.run_in_guest(command) == 0);
1226 # second run with --foreign
1227 command += ' --foreign'
1228 remote = (self.run_in_guest(command) == 0);
1229 return ( local and remote)
1231 def gather_logs (self):
1232 "gets all possible logs from plc's/qemu node's/slice's for future reference"
1233 # (1.a) get the plc's /var/log/ and store it locally in logs/myplc.var-log.<plcname>/*
1234 # (1.b) get the plc's /var/lib/pgsql/data/pg_log/ -> logs/myplc.pgsql-log.<plcname>/*
1235 # (2) get all the nodes qemu log and store it as logs/node.qemu.<node>.log
1236 # (3) get the nodes /var/log and store is as logs/node.var-log.<node>/*
1237 # (4) as far as possible get the slice's /var/log as logs/sliver.var-log.<sliver>/*
1239 print "-------------------- TestPlc.gather_logs : PLC's /var/log"
1240 self.gather_var_logs ()
1242 print "-------------------- TestPlc.gather_logs : PLC's /var/lib/psql/data/pg_log/"
1243 self.gather_pgsql_logs ()
1245 print "-------------------- TestPlc.gather_logs : nodes's QEMU logs"
1246 for site_spec in self.plc_spec['sites']:
1247 test_site = TestSite (self,site_spec)
1248 for node_spec in site_spec['nodes']:
1249 test_node=TestNode(self,test_site,node_spec)
1250 test_node.gather_qemu_logs()
1252 print "-------------------- TestPlc.gather_logs : nodes's /var/log"
1253 self.gather_nodes_var_logs()
1255 print "-------------------- TestPlc.gather_logs : sample sliver's /var/log"
1256 self.gather_slivers_var_logs()
1259 def gather_slivers_var_logs(self):
1260 for test_sliver in self.all_sliver_objs():
1261 remote = test_sliver.tar_var_logs()
1262 utils.system("mkdir -p logs/sliver.var-log.%s"%test_sliver.name())
1263 command = remote + " | tar -C logs/sliver.var-log.%s -xf -"%test_sliver.name()
1264 utils.system(command)
1267 def gather_var_logs (self):
1268 utils.system("mkdir -p logs/myplc.var-log.%s"%self.name())
1269 to_plc = self.actual_command_in_guest("tar -C /var/log/ -cf - .")
1270 command = to_plc + "| tar -C logs/myplc.var-log.%s -xf -"%self.name()
1271 utils.system(command)
1272 command = "chmod a+r,a+x logs/myplc.var-log.%s/httpd"%self.name()
1273 utils.system(command)
1275 def gather_pgsql_logs (self):
1276 utils.system("mkdir -p logs/myplc.pgsql-log.%s"%self.name())
1277 to_plc = self.actual_command_in_guest("tar -C /var/lib/pgsql/data/pg_log/ -cf - .")
1278 command = to_plc + "| tar -C logs/myplc.pgsql-log.%s -xf -"%self.name()
1279 utils.system(command)
1281 def gather_nodes_var_logs (self):
1282 for site_spec in self.plc_spec['sites']:
1283 test_site = TestSite (self,site_spec)
1284 for node_spec in site_spec['nodes']:
1285 test_node=TestNode(self,test_site,node_spec)
1286 test_ssh = TestSsh (test_node.name(),key="keys/key1.rsa")
1287 command = test_ssh.actual_command("tar -C /var/log -cf - .")
1288 command = command + "| tar -C logs/node.var-log.%s -xf -"%test_node.name()
1289 utils.system("mkdir -p logs/node.var-log.%s"%test_node.name())
1290 utils.system(command)
1293 # returns the filename to use for sql dump/restore, using options.dbname if set
1294 def dbfile (self, database):
1295 # uses options.dbname if it is found
1297 name=self.options.dbname
1298 if not isinstance(name,StringTypes):
1301 t=datetime.datetime.now()
1304 return "/root/%s-%s.sql"%(database,name)
1307 'dump the planetlab5 DB in /root in the PLC - filename has time'
1308 dump=self.dbfile("planetab5")
1309 self.run_in_guest('pg_dump -U pgsqluser planetlab5 -f '+ dump)
1310 utils.header('Dumped planetlab5 database in %s'%dump)
1313 def db_restore(self):
1314 'restore the planetlab5 DB - looks broken, but run -n might help'
1315 dump=self.dbfile("planetab5")
1316 ##stop httpd service
1317 self.run_in_guest('service httpd stop')
1318 # xxx - need another wrapper
1319 self.run_in_guest_piped('echo drop database planetlab5','psql --user=pgsqluser template1')
1320 self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab5')
1321 self.run_in_guest('psql -U pgsqluser planetlab5 -f '+dump)
1322 ##starting httpd service
1323 self.run_in_guest('service httpd start')
1325 utils.header('Database restored from ' + dump)
1328 def standby_1(): pass
1330 def standby_2(): pass
1332 def standby_3(): pass
1334 def standby_4(): pass
1336 def standby_5(): pass
1338 def standby_6(): pass
1340 def standby_7(): pass
1342 def standby_8(): pass
1344 def standby_9(): pass
1346 def standby_10(): pass
1348 def standby_11(): pass
1350 def standby_12(): pass
1352 def standby_13(): pass
1354 def standby_14(): pass
1356 def standby_15(): pass
1358 def standby_16(): pass
1360 def standby_17(): pass
1362 def standby_18(): pass
1364 def standby_19(): pass
1366 def standby_20(): pass