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):
121 return step != SEP and step != SEPSFA
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 'PLC_RESERVATION_GRANULARITY',
553 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
554 fileconf.write('w\n')
555 fileconf.write('q\n')
557 utils.system('cat %s'%tmpname)
558 self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
559 utils.system('rm %s'%tmpname)
564 self.run_in_guest('service plc start')
569 self.run_in_guest('service plc stop')
573 "start the PLC vserver"
577 # stores the keys from the config for further use
578 def store_keys(self):
579 "stores test users ssh keys in keys/"
580 for key_spec in self.plc_spec['keys']:
581 TestKey(self,key_spec).store_key()
584 def clean_keys(self):
585 "removes keys cached in keys/"
586 utils.system("rm -rf %s/keys/"%os.path(sys.argv[0]))
588 # fetches the ssh keys in the plc's /etc/planetlab and stores them in keys/
589 # for later direct access to the nodes
590 def fetch_keys(self):
591 "gets ssh keys in /etc/planetlab/ and stores them locally in keys/"
593 if not os.path.isdir(dir):
595 vservername=self.vservername
597 prefix = 'debug_ssh_key'
598 for ext in [ 'pub', 'rsa' ] :
599 src="/vservers/%(vservername)s/etc/planetlab/%(prefix)s.%(ext)s"%locals()
600 dst="keys/%(vservername)s-debug.%(ext)s"%locals()
601 if self.test_ssh.fetch(src,dst) != 0: overall=False
605 "create sites with PLCAPI"
606 return self.do_sites()
608 def clean_sites (self):
609 "delete sites with PLCAPI"
610 return self.do_sites(action="delete")
612 def do_sites (self,action="add"):
613 for site_spec in self.plc_spec['sites']:
614 test_site = TestSite (self,site_spec)
615 if (action != "add"):
616 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
617 test_site.delete_site()
618 # deleted with the site
619 #test_site.delete_users()
622 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
623 test_site.create_site()
624 test_site.create_users()
627 def clean_all_sites (self):
628 "Delete all sites in PLC, and related objects"
629 print 'auth_root',self.auth_root()
630 site_ids = [s['site_id'] for s in self.apiserver.GetSites(self.auth_root(), {}, ['site_id'])]
631 for site_id in site_ids:
632 print 'Deleting site_id',site_id
633 self.apiserver.DeleteSite(self.auth_root(),site_id)
636 "create nodes with PLCAPI"
637 return self.do_nodes()
638 def clean_nodes (self):
639 "delete nodes with PLCAPI"
640 return self.do_nodes(action="delete")
642 def do_nodes (self,action="add"):
643 for site_spec in self.plc_spec['sites']:
644 test_site = TestSite (self,site_spec)
646 utils.header("Deleting nodes in site %s"%test_site.name())
647 for node_spec in site_spec['nodes']:
648 test_node=TestNode(self,test_site,node_spec)
649 utils.header("Deleting %s"%test_node.name())
650 test_node.delete_node()
652 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
653 for node_spec in site_spec['nodes']:
654 utils.pprint('Creating node %s'%node_spec,node_spec)
655 test_node = TestNode (self,test_site,node_spec)
656 test_node.create_node ()
659 def nodegroups (self):
660 "create nodegroups with PLCAPI"
661 return self.do_nodegroups("add")
662 def clean_nodegroups (self):
663 "delete nodegroups with PLCAPI"
664 return self.do_nodegroups("delete")
668 def translate_timestamp (start,grain,timestamp):
669 if timestamp < TestPlc.YEAR: return start+timestamp*grain
670 else: return timestamp
673 def timestamp_printable (timestamp):
674 return time.strftime('%m-%d %H:%M:%S UTC',time.gmtime(timestamp))
677 "create leases (on reservable nodes only, use e.g. run -c default -c resa)"
679 grain=self.apiserver.GetLeaseGranularity(self.auth_root())
680 print 'API answered grain=',grain
681 start=(now/grain)*grain
683 # find out all nodes that are reservable
684 nodes=self.all_reservable_nodenames()
686 utils.header ("No reservable node found - proceeding without leases")
689 # attach them to the leases as specified in plc_specs
690 # this is where the 'leases' field gets interpreted as relative of absolute
691 for lease_spec in self.plc_spec['leases']:
692 # skip the ones that come with a null slice id
693 if not lease_spec['slice']: continue
694 lease_spec['t_from']=TestPlc.translate_timestamp(start,grain,lease_spec['t_from'])
695 lease_spec['t_until']=TestPlc.translate_timestamp(start,grain,lease_spec['t_until'])
696 lease_addition=self.apiserver.AddLeases(self.auth_root(),nodes,
697 lease_spec['slice'],lease_spec['t_from'],lease_spec['t_until'])
698 if lease_addition['errors']:
699 utils.header("Cannot create leases, %s"%lease_addition['errors'])
702 utils.header('Leases on nodes %r for %s from %d (%s) until %d (%s)'%\
703 (nodes,lease_spec['slice'],
704 lease_spec['t_from'],TestPlc.timestamp_printable(lease_spec['t_from']),
705 lease_spec['t_until'],TestPlc.timestamp_printable(lease_spec['t_until'])))
709 def clean_leases (self):
710 "remove all leases in the myplc side"
711 lease_ids= [ l['lease_id'] for l in self.apiserver.GetLeases(self.auth_root())]
712 utils.header("Cleaning leases %r"%lease_ids)
713 self.apiserver.DeleteLeases(self.auth_root(),lease_ids)
716 def list_leases (self):
717 "list all leases known to the myplc"
718 leases = self.apiserver.GetLeases(self.auth_root())
721 current=l['t_until']>=now
722 if self.options.verbose or current:
723 utils.header("%s %s from %s until %s"%(l['hostname'],l['name'],
724 TestPlc.timestamp_printable(l['t_from']),
725 TestPlc.timestamp_printable(l['t_until'])))
728 # create nodegroups if needed, and populate
729 def do_nodegroups (self, action="add"):
730 # 1st pass to scan contents
732 for site_spec in self.plc_spec['sites']:
733 test_site = TestSite (self,site_spec)
734 for node_spec in site_spec['nodes']:
735 test_node=TestNode (self,test_site,node_spec)
736 if node_spec.has_key('nodegroups'):
737 nodegroupnames=node_spec['nodegroups']
738 if isinstance(nodegroupnames,StringTypes):
739 nodegroupnames = [ nodegroupnames ]
740 for nodegroupname in nodegroupnames:
741 if not groups_dict.has_key(nodegroupname):
742 groups_dict[nodegroupname]=[]
743 groups_dict[nodegroupname].append(test_node.name())
744 auth=self.auth_root()
746 for (nodegroupname,group_nodes) in groups_dict.iteritems():
748 print 'nodegroups:','dealing with nodegroup',nodegroupname,'on nodes',group_nodes
749 # first, check if the nodetagtype is here
750 tag_types = self.apiserver.GetTagTypes(auth,{'tagname':nodegroupname})
752 tag_type_id = tag_types[0]['tag_type_id']
754 tag_type_id = self.apiserver.AddTagType(auth,
755 {'tagname':nodegroupname,
756 'description': 'for nodegroup %s'%nodegroupname,
759 print 'located tag (type)',nodegroupname,'as',tag_type_id
761 nodegroups = self.apiserver.GetNodeGroups (auth, {'groupname':nodegroupname})
763 self.apiserver.AddNodeGroup(auth, nodegroupname, tag_type_id, 'yes')
764 print 'created nodegroup',nodegroupname,'from tagname',nodegroupname,'and value','yes'
765 # set node tag on all nodes, value='yes'
766 for nodename in group_nodes:
768 self.apiserver.AddNodeTag(auth, nodename, nodegroupname, "yes")
770 traceback.print_exc()
771 print 'node',nodename,'seems to already have tag',nodegroupname
774 expect_yes = self.apiserver.GetNodeTags(auth,
775 {'hostname':nodename,
776 'tagname':nodegroupname},
777 ['value'])[0]['value']
778 if expect_yes != "yes":
779 print 'Mismatch node tag on node',nodename,'got',expect_yes
782 if not self.options.dry_run:
783 print 'Cannot find tag',nodegroupname,'on node',nodename
787 print 'cleaning nodegroup',nodegroupname
788 self.apiserver.DeleteNodeGroup(auth,nodegroupname)
790 traceback.print_exc()
794 # return a list of tuples (nodename,qemuname)
795 def all_node_infos (self) :
797 for site_spec in self.plc_spec['sites']:
798 node_infos += [ (node_spec['node_fields']['hostname'],node_spec['host_box']) \
799 for node_spec in site_spec['nodes'] ]
802 def all_nodenames (self): return [ x[0] for x in self.all_node_infos() ]
803 def all_reservable_nodenames (self):
805 for site_spec in self.plc_spec['sites']:
806 for node_spec in site_spec['nodes']:
807 node_fields=node_spec['node_fields']
808 if 'node_type' in node_fields and node_fields['node_type']=='reservable':
809 res.append(node_fields['hostname'])
812 # silent_minutes : during the first <silent_minutes> minutes nothing gets printed
813 def nodes_check_boot_state (self, target_boot_state, timeout_minutes, silent_minutes,period=15):
814 if self.options.dry_run:
818 timeout = datetime.datetime.now()+datetime.timedelta(minutes=timeout_minutes)
819 graceout = datetime.datetime.now()+datetime.timedelta(minutes=silent_minutes)
820 # the nodes that haven't checked yet - start with a full list and shrink over time
821 tocheck = self.all_hostnames()
822 utils.header("checking nodes %r"%tocheck)
823 # create a dict hostname -> status
824 status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
827 tocheck_status=self.apiserver.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
829 for array in tocheck_status:
830 hostname=array['hostname']
831 boot_state=array['boot_state']
832 if boot_state == target_boot_state:
833 utils.header ("%s has reached the %s state"%(hostname,target_boot_state))
835 # if it's a real node, never mind
836 (site_spec,node_spec)=self.locate_hostname(hostname)
837 if TestNode.is_real_model(node_spec['node_fields']['model']):
838 utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
840 boot_state = target_boot_state
841 elif datetime.datetime.now() > graceout:
842 utils.header ("%s still in '%s' state"%(hostname,boot_state))
843 graceout=datetime.datetime.now()+datetime.timedelta(1)
844 status[hostname] = boot_state
846 tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != target_boot_state ]
849 if datetime.datetime.now() > timeout:
850 for hostname in tocheck:
851 utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
853 # otherwise, sleep for a while
855 # only useful in empty plcs
858 def nodes_booted(self):
859 return self.nodes_check_boot_state('boot',timeout_minutes=30,silent_minutes=20)
861 def check_nodes_ssh(self,debug,timeout_minutes,silent_minutes,period=15):
863 timeout = datetime.datetime.now()+datetime.timedelta(minutes=timeout_minutes)
864 graceout = datetime.datetime.now()+datetime.timedelta(minutes=silent_minutes)
865 vservername=self.vservername
868 local_key = "keys/%(vservername)s-debug.rsa"%locals()
871 local_key = "keys/key1.rsa"
872 node_infos = self.all_node_infos()
873 utils.header("checking ssh access (expected in %s mode) to nodes:"%message)
874 for (nodename,qemuname) in node_infos:
875 utils.header("hostname=%s -- qemubox=%s"%(nodename,qemuname))
876 utils.header("max timeout is %d minutes, silent for %d minutes (period is %s)"%\
877 (timeout_minutes,silent_minutes,period))
879 for node_info in node_infos:
880 (hostname,qemuname) = node_info
881 # try to run 'hostname' in the node
882 command = TestSsh (hostname,key=local_key).actual_command("hostname;uname -a")
883 # don't spam logs - show the command only after the grace period
884 success = utils.system ( command, silent=datetime.datetime.now() < graceout)
886 utils.header('Successfully entered root@%s (%s)'%(hostname,message))
888 node_infos.remove(node_info)
890 # we will have tried real nodes once, in case they're up - but if not, just skip
891 (site_spec,node_spec)=self.locate_hostname(hostname)
892 if TestNode.is_real_model(node_spec['node_fields']['model']):
893 utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
894 node_infos.remove(node_info)
897 if datetime.datetime.now() > timeout:
898 for (hostname,qemuname) in node_infos:
899 utils.header("FAILURE to ssh into %s (on %s)"%(hostname,qemuname))
901 # otherwise, sleep for a while
903 # only useful in empty plcs
906 def nodes_ssh_debug(self):
907 "Tries to ssh into nodes in debug mode with the debug ssh key"
908 return self.check_nodes_ssh(debug=True,timeout_minutes=10,silent_minutes=5)
910 def nodes_ssh_boot(self):
911 "Tries to ssh into nodes in production mode with the root ssh key"
912 return self.check_nodes_ssh(debug=False,timeout_minutes=40,silent_minutes=15)
915 def init_node (self):
916 "all nodes : init a clean local directory for holding node-dep stuff like iso image..."
920 "all nodes: invoke GetBootMedium and store result locally"
923 def configure_qemu (self):
924 "all nodes: compute qemu config qemu.conf and store it locally"
927 def reinstall_node (self):
928 "all nodes: mark PLCAPI boot_state as reinstall"
931 def export_qemu (self):
932 "all nodes: push local node-dep directory on the qemu box"
935 ### check hooks : invoke scripts from hooks/{node,slice}
936 def check_hooks_node (self):
937 return self.locate_first_node().check_hooks()
938 def check_hooks_sliver (self) :
939 return self.locate_first_sliver().check_hooks()
941 def check_hooks (self):
942 "runs unit tests in the node and slice contexts - see hooks/{node,slice}"
943 return self.check_hooks_node() and self.check_hooks_sliver()
946 def do_check_initscripts(self):
948 for slice_spec in self.plc_spec['slices']:
949 if not slice_spec.has_key('initscriptname'):
951 initscript=slice_spec['initscriptname']
952 for nodename in slice_spec['nodenames']:
953 (site,node) = self.locate_node (nodename)
954 # xxx - passing the wrong site - probably harmless
955 test_site = TestSite (self,site)
956 test_slice = TestSlice (self,test_site,slice_spec)
957 test_node = TestNode (self,test_site,node)
958 test_sliver = TestSliver (self, test_node, test_slice)
959 if not test_sliver.check_initscript(initscript):
963 def check_initscripts(self):
964 "check that the initscripts have triggered"
965 return self.do_check_initscripts()
967 def initscripts (self):
968 "create initscripts with PLCAPI"
969 for initscript in self.plc_spec['initscripts']:
970 utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
971 self.apiserver.AddInitScript(self.auth_root(),initscript['initscript_fields'])
974 def clean_initscripts (self):
975 "delete initscripts with PLCAPI"
976 for initscript in self.plc_spec['initscripts']:
977 initscript_name = initscript['initscript_fields']['name']
978 print('Attempting to delete %s in plc %s'%(initscript_name,self.plc_spec['name']))
980 self.apiserver.DeleteInitScript(self.auth_root(),initscript_name)
981 print initscript_name,'deleted'
983 print 'deletion went wrong - probably did not exist'
988 "create slices with PLCAPI"
989 return self.do_slices()
991 def clean_slices (self):
992 "delete slices with PLCAPI"
993 return self.do_slices("delete")
995 def do_slices (self, action="add"):
996 for slice in self.plc_spec['slices']:
997 site_spec = self.locate_site (slice['sitename'])
998 test_site = TestSite(self,site_spec)
999 test_slice=TestSlice(self,test_site,slice)
1001 utils.header("Deleting slices in site %s"%test_site.name())
1002 test_slice.delete_slice()
1004 utils.pprint("Creating slice",slice)
1005 test_slice.create_slice()
1006 utils.header('Created Slice %s'%slice['slice_fields']['name'])
1009 @slice_mapper_options
1010 def check_slice(self):
1011 "tries to ssh-enter the slice with the user key, to ensure slice creation"
1015 def clear_known_hosts (self):
1016 "remove test nodes entries from the local known_hosts file"
1020 def start_node (self) :
1021 "all nodes: start the qemu instance (also runs qemu-bridge-init start)"
1024 def check_tcp (self):
1025 "check TCP connectivity between 2 slices (or in loopback if only one is defined)"
1026 specs = self.plc_spec['tcp_test']
1031 s_test_sliver = self.locate_sliver_obj (spec['server_node'],spec['server_slice'])
1032 if not s_test_sliver.run_tcp_server(port,timeout=10):
1036 # idem for the client side
1037 c_test_sliver = self.locate_sliver_obj(spec['server_node'],spec['server_slice'])
1038 if not c_test_sliver.run_tcp_client(s_test_sliver.test_node.name(),port):
1042 def plcsh_stress_test (self):
1043 "runs PLCAPI stress test, that checks Add/Update/Delete on all types - preserves contents"
1044 # install the stress-test in the plc image
1045 location = "/usr/share/plc_api/plcsh_stress_test.py"
1046 remote="/vservers/%s/%s"%(self.vservername,location)
1047 self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1049 command += " -- --check"
1050 if self.options.size == 1:
1051 command += " --tiny"
1052 return ( self.run_in_guest(command) == 0)
1054 # populate runs the same utility without slightly different options
1055 # in particular runs with --preserve (dont cleanup) and without --check
1056 # also it gets run twice, once with the --foreign option for creating fake foreign entries
1059 def install_sfa(self):
1060 "yum install sfa, sfa-plc and sfa-client"
1061 if self.options.personality == "linux32":
1063 elif self.options.personality == "linux64":
1066 raise Exception, "Unsupported personality %r"%self.options.personality
1067 return self.run_in_guest("yum -y install sfa sfa-client sfa-plc sfa-sfatables")==0
1070 def configure_sfa(self):
1071 "run sfa-config-tty"
1072 tmpname='%s.sfa-config-tty'%(self.name())
1073 fileconf=open(tmpname,'w')
1074 for var in [ 'SFA_REGISTRY_ROOT_AUTH',
1075 'SFA_REGISTRY_LEVEL1_AUTH',
1076 'SFA_REGISTRY_HOST',
1077 'SFA_AGGREGATE_HOST',
1083 'SFA_PLC_DB_PASSWORD',
1085 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec['sfa'][var]))
1086 fileconf.write('w\n')
1087 fileconf.write('R\n')
1088 fileconf.write('q\n')
1090 utils.system('cat %s'%tmpname)
1091 self.run_in_guest_piped('cat %s'%tmpname,'sfa-config-tty')
1092 utils.system('rm %s'%tmpname)
1095 def import_sfa(self):
1097 auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1098 return self.run_in_guest('sfa-import-plc.py')==0
1099 # not needed anymore
1100 # self.run_in_guest('cp /etc/sfa/authorities/%s/%s.pkey /etc/sfa/authorities/server.key'%(auth,auth))
1102 def start_sfa(self):
1104 return self.run_in_guest('service sfa start')==0
1106 def setup_sfa(self):
1107 "sfi client configuration"
1109 if os.path.exists(dir_name):
1110 utils.system('rm -rf %s'%dir_name)
1111 utils.system('mkdir %s'%dir_name)
1112 file_name=dir_name + os.sep + 'fake-pi1.pkey'
1113 fileconf=open(file_name,'w')
1114 fileconf.write (self.plc_spec['keys'][0]['private'])
1117 file_name=dir_name + os.sep + 'sfi_config'
1118 fileconf=open(file_name,'w')
1119 SFI_AUTH=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']+".main"
1120 fileconf.write ("SFI_AUTH='%s'"%SFI_AUTH)
1121 fileconf.write('\n')
1122 SFI_USER=SFI_AUTH+'.fake-pi1'
1123 fileconf.write ("SFI_USER='%s'"%SFI_USER)
1124 fileconf.write('\n')
1125 SFI_REGISTRY='http://' + self.plc_spec['sfa']['SFA_PLC_DB_HOST'] + ':12345/'
1126 fileconf.write ("SFI_REGISTRY='%s'"%SFI_REGISTRY)
1127 fileconf.write('\n')
1128 SFI_SM='http://' + self.plc_spec['sfa']['SFA_PLC_DB_HOST'] + ':12347/'
1129 fileconf.write ("SFI_SM='%s'"%SFI_SM)
1130 fileconf.write('\n')
1133 file_name=dir_name + os.sep + 'person.xml'
1134 fileconf=open(file_name,'w')
1135 for record in self.plc_spec['sfa']['sfa_person_xml']:
1136 person_record=record
1137 fileconf.write(person_record)
1138 fileconf.write('\n')
1141 file_name=dir_name + os.sep + 'slice.xml'
1142 fileconf=open(file_name,'w')
1143 for record in self.plc_spec['sfa']['sfa_slice_xml']:
1145 #slice_record=self.plc_spec['sfa']['sfa_slice_xml']
1146 fileconf.write(slice_record)
1147 fileconf.write('\n')
1150 file_name=dir_name + os.sep + 'slice.rspec'
1151 fileconf=open(file_name,'w')
1153 for (key, value) in self.plc_spec['sfa']['sfa_slice_rspec'].items():
1155 fileconf.write(slice_rspec)
1156 fileconf.write('\n')
1159 remote="/vservers/%s/%s"%(self.vservername,location)
1160 self.test_ssh.copy_abs(dir_name, remote, recursive=True)
1162 #utils.system('cat %s'%tmpname)
1163 utils.system('rm -rf %s'%dir_name)
1167 "run sfi.py add (on Registry) and sfi.py create (on SM) to form new objects"
1169 test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1170 success=test_user_sfa.add_user()
1172 for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1173 site_spec = self.locate_site (slice_spec['sitename'])
1174 test_site = TestSite(self,site_spec)
1175 test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1176 success1=test_slice_sfa.add_slice()
1177 success2=test_slice_sfa.create_slice()
1178 return success and success1 and success2
1180 def update_sfa(self):
1181 "run sfi.py update (on Registry) and sfi.py create (on SM) on existing objects"
1183 test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1184 success1=test_user_sfa.update_user()
1186 for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1187 site_spec = self.locate_site (slice_spec['sitename'])
1188 test_site = TestSite(self,site_spec)
1189 test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1190 success2=test_slice_sfa.update_slice()
1191 return success1 and success2
1194 "run sfi.py list and sfi.py show (both on Registry) and sfi.py slices and sfi.py resources (both on SM)"
1195 auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1197 self.run_in_guest("sfi.py -d /root/.sfi/ list %s.main"%auth)==0 and \
1198 self.run_in_guest("sfi.py -d /root/.sfi/ show %s.main"%auth)==0 and \
1199 self.run_in_guest("sfi.py -d /root/.sfi/ slices")==0 and \
1200 self.run_in_guest("sfi.py -d /root/.sfi/ resources -o resources")==0
1202 @slice_mapper_options_sfa
1203 def check_slice_sfa(self):
1204 "tries to ssh-enter the SFA slice"
1207 def delete_sfa(self):
1208 "run sfi.py delete (on SM), sfi.py remove (on Registry)"
1210 test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1211 success1=test_user_sfa.delete_user()
1212 for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1213 site_spec = self.locate_site (slice_spec['sitename'])
1214 test_site = TestSite(self,site_spec)
1215 test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1216 success2=test_slice_sfa.delete_slice()
1218 return success1 and success2
1222 return self.run_in_guest('service sfa stop')==0
1224 def populate (self):
1225 "creates random entries in the PLCAPI"
1226 # install the stress-test in the plc image
1227 location = "/usr/share/plc_api/plcsh_stress_test.py"
1228 remote="/vservers/%s/%s"%(self.vservername,location)
1229 self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1231 command += " -- --preserve --short-names"
1232 local = (self.run_in_guest(command) == 0);
1233 # second run with --foreign
1234 command += ' --foreign'
1235 remote = (self.run_in_guest(command) == 0);
1236 return ( local and remote)
1238 def gather_logs (self):
1239 "gets all possible logs from plc's/qemu node's/slice's for future reference"
1240 # (1.a) get the plc's /var/log/ and store it locally in logs/myplc.var-log.<plcname>/*
1241 # (1.b) get the plc's /var/lib/pgsql/data/pg_log/ -> logs/myplc.pgsql-log.<plcname>/*
1242 # (2) get all the nodes qemu log and store it as logs/node.qemu.<node>.log
1243 # (3) get the nodes /var/log and store is as logs/node.var-log.<node>/*
1244 # (4) as far as possible get the slice's /var/log as logs/sliver.var-log.<sliver>/*
1246 print "-------------------- TestPlc.gather_logs : PLC's /var/log"
1247 self.gather_var_logs ()
1249 print "-------------------- TestPlc.gather_logs : PLC's /var/lib/psql/data/pg_log/"
1250 self.gather_pgsql_logs ()
1252 print "-------------------- TestPlc.gather_logs : nodes's QEMU logs"
1253 for site_spec in self.plc_spec['sites']:
1254 test_site = TestSite (self,site_spec)
1255 for node_spec in site_spec['nodes']:
1256 test_node=TestNode(self,test_site,node_spec)
1257 test_node.gather_qemu_logs()
1259 print "-------------------- TestPlc.gather_logs : nodes's /var/log"
1260 self.gather_nodes_var_logs()
1262 print "-------------------- TestPlc.gather_logs : sample sliver's /var/log"
1263 self.gather_slivers_var_logs()
1266 def gather_slivers_var_logs(self):
1267 for test_sliver in self.all_sliver_objs():
1268 remote = test_sliver.tar_var_logs()
1269 utils.system("mkdir -p logs/sliver.var-log.%s"%test_sliver.name())
1270 command = remote + " | tar -C logs/sliver.var-log.%s -xf -"%test_sliver.name()
1271 utils.system(command)
1274 def gather_var_logs (self):
1275 utils.system("mkdir -p logs/myplc.var-log.%s"%self.name())
1276 to_plc = self.actual_command_in_guest("tar -C /var/log/ -cf - .")
1277 command = to_plc + "| tar -C logs/myplc.var-log.%s -xf -"%self.name()
1278 utils.system(command)
1279 command = "chmod a+r,a+x logs/myplc.var-log.%s/httpd"%self.name()
1280 utils.system(command)
1282 def gather_pgsql_logs (self):
1283 utils.system("mkdir -p logs/myplc.pgsql-log.%s"%self.name())
1284 to_plc = self.actual_command_in_guest("tar -C /var/lib/pgsql/data/pg_log/ -cf - .")
1285 command = to_plc + "| tar -C logs/myplc.pgsql-log.%s -xf -"%self.name()
1286 utils.system(command)
1288 def gather_nodes_var_logs (self):
1289 for site_spec in self.plc_spec['sites']:
1290 test_site = TestSite (self,site_spec)
1291 for node_spec in site_spec['nodes']:
1292 test_node=TestNode(self,test_site,node_spec)
1293 test_ssh = TestSsh (test_node.name(),key="keys/key1.rsa")
1294 command = test_ssh.actual_command("tar -C /var/log -cf - .")
1295 command = command + "| tar -C logs/node.var-log.%s -xf -"%test_node.name()
1296 utils.system("mkdir -p logs/node.var-log.%s"%test_node.name())
1297 utils.system(command)
1300 # returns the filename to use for sql dump/restore, using options.dbname if set
1301 def dbfile (self, database):
1302 # uses options.dbname if it is found
1304 name=self.options.dbname
1305 if not isinstance(name,StringTypes):
1308 t=datetime.datetime.now()
1311 return "/root/%s-%s.sql"%(database,name)
1314 'dump the planetlab5 DB in /root in the PLC - filename has time'
1315 dump=self.dbfile("planetab5")
1316 self.run_in_guest('pg_dump -U pgsqluser planetlab5 -f '+ dump)
1317 utils.header('Dumped planetlab5 database in %s'%dump)
1320 def db_restore(self):
1321 'restore the planetlab5 DB - looks broken, but run -n might help'
1322 dump=self.dbfile("planetab5")
1323 ##stop httpd service
1324 self.run_in_guest('service httpd stop')
1325 # xxx - need another wrapper
1326 self.run_in_guest_piped('echo drop database planetlab5','psql --user=pgsqluser template1')
1327 self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab5')
1328 self.run_in_guest('psql -U pgsqluser planetlab5 -f '+dump)
1329 ##starting httpd service
1330 self.run_in_guest('service httpd start')
1332 utils.header('Database restored from ' + dump)
1335 def standby_1(): pass
1337 def standby_2(): pass
1339 def standby_3(): pass
1341 def standby_4(): pass
1343 def standby_5(): pass
1345 def standby_6(): pass
1347 def standby_7(): pass
1349 def standby_8(): pass
1351 def standby_9(): pass
1353 def standby_10(): pass
1355 def standby_11(): pass
1357 def standby_12(): pass
1359 def standby_13(): pass
1361 def standby_14(): pass
1363 def standby_15(): pass
1365 def standby_16(): pass
1367 def standby_17(): pass
1369 def standby_18(): pass
1371 def standby_19(): pass
1373 def standby_20(): pass