1 # Thierry Parmentelat <thierry.parmentelat@inria.fr>
2 # Copyright (C) 2010 INRIA
9 from datetime import datetime, timedelta
10 from types import StringTypes
13 from TestSite import TestSite
14 from TestNode import TestNode
15 from TestUser import TestUser
16 from TestKey import TestKey
17 from TestSlice import TestSlice
18 from TestSliver import TestSliver
19 from TestBoxQemu import TestBoxQemu
20 from TestSsh import TestSsh
21 from TestApiserver import TestApiserver
22 from TestAuthSfa import TestAuthSfa
23 from PlcapiUrlScanner import PlcapiUrlScanner
24 from Completer import Completer, CompleterTask
26 # step methods must take (self) and return a boolean (options is a member of the class)
28 def standby(minutes,dry_run):
29 utils.header('Entering StandBy for %d mn'%minutes)
33 time.sleep(60*minutes)
36 def standby_generic (func):
38 minutes=int(func.__name__.split("_")[1])
39 return standby(minutes,self.options.dry_run)
42 def node_mapper (method):
43 def actual(self,*args, **kwds):
45 node_method = TestNode.__dict__[method.__name__]
46 for test_node in self.all_nodes():
47 if not node_method(test_node, *args, **kwds): overall=False
49 # restore the doc text
50 actual.__doc__=TestNode.__dict__[method.__name__].__doc__
53 def slice_mapper (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__=TestSlice.__dict__[method.__name__].__doc__
67 def auth_sfa_mapper (method):
70 auth_method = TestAuthSfa.__dict__[method.__name__]
71 for auth_spec in self.plc_spec['sfa']['auth_sfa_specs']:
72 test_auth=TestAuthSfa(self,auth_spec)
73 if not auth_method(test_auth,self.options): overall=False
75 # restore the doc text
76 actual.__doc__=TestAuthSfa.__dict__[method.__name__].__doc__
86 'vs_delete','timestamp_vs','vs_create', SEP,
87 'plc_install', 'plc_configure', 'plc_start', SEP,
88 'keys_fetch', 'keys_store', 'keys_clear_known_hosts', SEP,
89 'plcapi_urls','speed_up_slices', SEP,
90 'initscripts', 'sites', 'nodes', 'slices', 'nodegroups', 'leases', SEP,
91 # slices created under plcsh interactively seem to be fine but these ones don't have the tags
92 # keep this our of the way for now
93 # 'check_vsys_defaults', SEP,
94 'nodestate_reinstall', 'qemu_local_init','bootcd', 'qemu_local_config', SEP,
95 'qemu_export', 'qemu_kill_mine', 'qemu_start', 'timestamp_qemu', SEP,
96 'sfa_install_all', 'sfa_configure', 'cross_sfa_configure', 'sfa_start', 'sfa_import', SEPSFA,
97 'sfi_configure@1', 'sfa_add_site@1','sfa_add_pi@1', SEPSFA,
98 'sfa_add_user@1', 'sfa_update_user@1', 'sfa_add_slice@1', 'sfa_renew_slice@1', SEPSFA,
99 'sfa_discover@1', 'sfa_create_slice@1', 'sfa_check_slice_plc@1', 'sfa_update_slice@1', SEPSFA,
100 'sfi_list@1', 'sfi_show@1', 'sfi_slices@1', 'sfa_utest@1', SEPSFA,
101 # we used to run plcsh_stress_test, and then ssh_node_debug and ssh_node_boot
102 # but as the stress test might take a while, we sometimes missed the debug mode..
103 'ssh_node_debug@1', 'plcsh_stress_test@1', SEP,
104 'ssh_node_boot@1', 'node_bmlogs@1', 'ssh_slice', 'ssh_slice_basics', 'check_initscripts', SEP,
105 'ssh_slice_sfa@1', 'sfa_delete_slice@1', 'sfa_delete_user@1', SEPSFA,
106 'cross_check_tcp@1', 'check_system_slice', SEP,
107 'empty_slices', 'ssh_slice_off', 'fill_slices', SEP,
108 'force_gather_logs', SEP,
111 'export', 'show_boxes', SEP,
112 'check_hooks', 'plc_stop', 'vs_start', 'vs_stop', SEP,
113 'delete_initscripts', 'delete_nodegroups','delete_all_sites', SEP,
114 'delete_sites', 'delete_nodes', 'delete_slices', 'keys_clean', SEP,
115 'delete_leases', 'list_leases', SEP,
117 'nodestate_show','nodestate_safeboot','nodestate_boot', SEP,
118 'qemu_list_all', 'qemu_list_mine', 'qemu_kill_all', 'qemu_clean_mine', SEP,
119 'sfa_install_core', 'sfa_install_sfatables', 'sfa_install_plc', 'sfa_install_client', SEPSFA,
120 'sfa_plcclean', 'sfa_dbclean', 'sfa_stop','sfa_uninstall', 'sfi_clean', SEPSFA,
121 'plc_db_dump' , 'plc_db_restore', SEP,
122 'check_netflow','check_drl', SEP,
123 'debug_nodemanager', SEP,
124 'standby_1_through_20',SEP,
128 def printable_steps (list):
129 single_line=" ".join(list)+" "
130 return single_line.replace(" "+SEP+" "," \\\n").replace(" "+SEPSFA+" "," \\\n")
132 def valid_step (step):
133 return step != SEP and step != SEPSFA
135 # turn off the sfa-related steps when build has skipped SFA
136 # this was originally for centos5 but is still valid
137 # for up to f12 as recent SFAs with sqlalchemy won't build before f14
139 def check_whether_build_has_sfa (rpms_url):
140 utils.header ("Checking if build provides SFA package...")
141 # warning, we're now building 'sface' so let's be a bit more picky
142 retcod=os.system ("curl --silent %s/ | grep -q sfa-"%rpms_url)
143 # full builds are expected to return with 0 here
145 utils.header("build does provide SFA")
147 # move all steps containing 'sfa' from default_steps to other_steps
148 utils.header("SFA package not found - removing steps with sfa or sfi")
149 sfa_steps= [ step for step in TestPlc.default_steps if step.find('sfa')>=0 or step.find("sfi")>=0 ]
150 TestPlc.other_steps += sfa_steps
151 for step in sfa_steps: TestPlc.default_steps.remove(step)
153 def __init__ (self,plc_spec,options):
154 self.plc_spec=plc_spec
156 self.test_ssh=TestSsh(self.plc_spec['host_box'],self.options.buildname)
157 self.vserverip=plc_spec['vserverip']
158 self.vservername=plc_spec['vservername']
159 self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
160 self.apiserver=TestApiserver(self.url,options.dry_run)
161 (self.ssh_node_boot_timeout,self.ssh_node_boot_silent)=plc_spec['ssh_node_boot_timers']
162 (self.ssh_node_debug_timeout,self.ssh_node_debug_silent)=plc_spec['ssh_node_debug_timers']
164 def has_addresses_api (self):
165 return self.apiserver.has_method('AddIpAddress')
168 name=self.plc_spec['name']
169 return "%s.%s"%(name,self.vservername)
172 return self.plc_spec['host_box']
175 return self.test_ssh.is_local()
177 # define the API methods on this object through xmlrpc
178 # would help, but not strictly necessary
182 def actual_command_in_guest (self,command):
183 return self.test_ssh.actual_command(self.host_to_guest(command),dry_run=self.options.dry_run)
185 def start_guest (self):
186 return utils.system(self.test_ssh.actual_command(self.start_guest_in_host(),dry_run=self.options.dry_run))
188 def stop_guest (self):
189 return utils.system(self.test_ssh.actual_command(self.stop_guest_in_host(),dry_run=self.options.dry_run))
191 def run_in_guest (self,command):
192 return utils.system(self.actual_command_in_guest(command))
194 def run_in_host (self,command):
195 return self.test_ssh.run_in_buildname(command, dry_run=self.options.dry_run)
197 #command gets run in the plc's vm
198 def host_to_guest(self,command):
199 if self.options.plcs_use_lxc:
200 return "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null %s %s"%(self.vserverip,command)
202 return "vserver %s exec %s"%(self.vservername,command)
204 def vm_root_in_host(self):
205 if self.options.plcs_use_lxc:
206 return "/vservers/%s/rootfs/"%(self.vservername)
208 return "/vservers/%s"%(self.vservername)
210 def vm_timestamp_path (self):
211 if self.options.plcs_use_lxc:
212 return "/vservers/%s/%s.timestamp"%(self.vservername,self.vservername)
214 return "/vservers/%s.timestamp"%(self.vservername)
216 #start/stop the vserver
217 def start_guest_in_host(self):
218 if self.options.plcs_use_lxc:
219 return "virsh -c lxc:// start %s"%(self.vservername)
221 return "vserver %s start"%(self.vservername)
223 def stop_guest_in_host(self):
224 if self.options.plcs_use_lxc:
225 return "virsh -c lxc:// destroy %s"%(self.vservername)
227 return "vserver %s stop"%(self.vservername)
230 def run_in_guest_piped (self,local,remote):
231 return utils.system(local+" | "+self.test_ssh.actual_command(self.host_to_guest(remote),keep_stdin=True))
233 def yum_check_installed (self, rpms):
234 if isinstance (rpms, list):
236 return self.run_in_guest("rpm -q %s"%rpms)==0
238 # does a yum install in the vs, ignore yum retcod, check with rpm
239 def yum_install (self, rpms):
240 if isinstance (rpms, list):
242 self.run_in_guest("yum -y install %s"%rpms)
243 # yum-complete-transaction comes with yum-utils, that is in vtest.pkgs
244 self.run_in_guest("yum-complete-transaction -y")
245 return self.yum_check_installed (rpms)
247 def auth_root (self):
248 return {'Username':self.plc_spec['PLC_ROOT_USER'],
249 'AuthMethod':'password',
250 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
251 'Role' : self.plc_spec['role']
253 def locate_site (self,sitename):
254 for site in self.plc_spec['sites']:
255 if site['site_fields']['name'] == sitename:
257 if site['site_fields']['login_base'] == sitename:
259 raise Exception,"Cannot locate site %s"%sitename
261 def locate_node (self,nodename):
262 for site in self.plc_spec['sites']:
263 for node in site['nodes']:
264 if node['name'] == nodename:
266 raise Exception,"Cannot locate node %s"%nodename
268 def locate_hostname (self,hostname):
269 for site in self.plc_spec['sites']:
270 for node in site['nodes']:
271 if node['node_fields']['hostname'] == hostname:
273 raise Exception,"Cannot locate hostname %s"%hostname
275 def locate_key (self,key_name):
276 for key in self.plc_spec['keys']:
277 if key['key_name'] == key_name:
279 raise Exception,"Cannot locate key %s"%key_name
281 def locate_private_key_from_key_names (self, key_names):
282 # locate the first avail. key
284 for key_name in key_names:
285 key_spec=self.locate_key(key_name)
286 test_key=TestKey(self,key_spec)
287 publickey=test_key.publicpath()
288 privatekey=test_key.privatepath()
289 if os.path.isfile(publickey) and os.path.isfile(privatekey):
291 if found: return privatekey
294 def locate_slice (self, slicename):
295 for slice in self.plc_spec['slices']:
296 if slice['slice_fields']['name'] == slicename:
298 raise Exception,"Cannot locate slice %s"%slicename
300 def all_sliver_objs (self):
302 for slice_spec in self.plc_spec['slices']:
303 slicename = slice_spec['slice_fields']['name']
304 for nodename in slice_spec['nodenames']:
305 result.append(self.locate_sliver_obj (nodename,slicename))
308 def locate_sliver_obj (self,nodename,slicename):
309 (site,node) = self.locate_node(nodename)
310 slice = self.locate_slice (slicename)
312 test_site = TestSite (self, site)
313 test_node = TestNode (self, test_site,node)
314 # xxx the slice site is assumed to be the node site - mhh - probably harmless
315 test_slice = TestSlice (self, test_site, slice)
316 return TestSliver (self, test_node, test_slice)
318 def locate_first_node(self):
319 nodename=self.plc_spec['slices'][0]['nodenames'][0]
320 (site,node) = self.locate_node(nodename)
321 test_site = TestSite (self, site)
322 test_node = TestNode (self, test_site,node)
325 def locate_first_sliver (self):
326 slice_spec=self.plc_spec['slices'][0]
327 slicename=slice_spec['slice_fields']['name']
328 nodename=slice_spec['nodenames'][0]
329 return self.locate_sliver_obj(nodename,slicename)
331 # all different hostboxes used in this plc
332 def get_BoxNodes(self):
333 # maps on sites and nodes, return [ (host_box,test_node) ]
335 for site_spec in self.plc_spec['sites']:
336 test_site = TestSite (self,site_spec)
337 for node_spec in site_spec['nodes']:
338 test_node = TestNode (self, test_site, node_spec)
339 if not test_node.is_real():
340 tuples.append( (test_node.host_box(),test_node) )
341 # transform into a dict { 'host_box' -> [ test_node .. ] }
343 for (box,node) in tuples:
344 if not result.has_key(box):
347 result[box].append(node)
350 # a step for checking this stuff
351 def show_boxes (self):
352 'print summary of nodes location'
353 for (box,nodes) in self.get_BoxNodes().iteritems():
354 print box,":"," + ".join( [ node.name() for node in nodes ] )
357 # make this a valid step
358 def qemu_kill_all(self):
359 'kill all qemu instances on the qemu boxes involved by this setup'
360 # this is the brute force version, kill all qemus on that host box
361 for (box,nodes) in self.get_BoxNodes().iteritems():
362 # pass the first nodename, as we don't push template-qemu on testboxes
363 nodedir=nodes[0].nodedir()
364 TestBoxQemu(box,self.options.buildname).qemu_kill_all(nodedir)
367 # make this a valid step
368 def qemu_list_all(self):
369 'list all qemu instances on the qemu boxes involved by this setup'
370 for (box,nodes) in self.get_BoxNodes().iteritems():
371 # this is the brute force version, kill all qemus on that host box
372 TestBoxQemu(box,self.options.buildname).qemu_list_all()
375 # kill only the qemus related to this test
376 def qemu_list_mine(self):
377 'list qemu instances for our nodes'
378 for (box,nodes) in self.get_BoxNodes().iteritems():
379 # the fine-grain version
384 # kill only the qemus related to this test
385 def qemu_clean_mine(self):
386 'cleanup (rm -rf) qemu instances for our nodes'
387 for (box,nodes) in self.get_BoxNodes().iteritems():
388 # the fine-grain version
393 # kill only the right qemus
394 def qemu_kill_mine(self):
395 'kill the qemu instances for our nodes'
396 for (box,nodes) in self.get_BoxNodes().iteritems():
397 # the fine-grain version
402 #################### display config
404 "show test configuration after localization"
409 # uggly hack to make sure 'run export' only reports about the 1st plc
410 # to avoid confusion - also we use 'inri_slice1' in various aliases..
413 "print cut'n paste-able stuff to export env variables to your shell"
414 # guess local domain from hostname
415 if TestPlc.exported_id>1:
416 print "export GUESTHOSTNAME%d=%s"%(TestPlc.exported_id,self.plc_spec['vservername'])
418 TestPlc.exported_id+=1
419 domain=socket.gethostname().split('.',1)[1]
420 fqdn="%s.%s"%(self.plc_spec['host_box'],domain)
421 print "export BUILD=%s"%self.options.buildname
422 if self.options.plcs_use_lxc:
423 print "export PLCHOSTLXC=%s"%fqdn
425 print "export PLCHOSTVS=%s"%fqdn
426 print "export GUESTNAME=%s"%self.plc_spec['vservername']
427 vplcname=self.plc_spec['vservername'].split('-')[-1]
428 print "export GUESTHOSTNAME=%s.%s"%(vplcname,domain)
429 # find hostname of first node
430 (hostname,qemubox) = self.all_node_infos()[0]
431 print "export KVMHOST=%s.%s"%(qemubox,domain)
432 print "export NODE=%s"%(hostname)
436 always_display_keys=['PLC_WWW_HOST','nodes','sites',]
437 def show_pass (self,passno):
438 for (key,val) in self.plc_spec.iteritems():
439 if not self.options.verbose and key not in TestPlc.always_display_keys: continue
443 self.display_site_spec(site)
444 for node in site['nodes']:
445 self.display_node_spec(node)
446 elif key=='initscripts':
447 for initscript in val:
448 self.display_initscript_spec (initscript)
451 self.display_slice_spec (slice)
454 self.display_key_spec (key)
456 if key not in ['sites','initscripts','slices','keys', 'sfa']:
457 print '+ ',key,':',val
459 def display_site_spec (self,site):
460 print '+ ======== site',site['site_fields']['name']
461 for (k,v) in site.iteritems():
462 if not self.options.verbose and k not in TestPlc.always_display_keys: continue
465 print '+ ','nodes : ',
467 print node['node_fields']['hostname'],'',
473 print user['name'],'',
475 elif k == 'site_fields':
476 print '+ login_base',':',v['login_base']
477 elif k == 'address_fields':
483 def display_initscript_spec (self,initscript):
484 print '+ ======== initscript',initscript['initscript_fields']['name']
486 def display_key_spec (self,key):
487 print '+ ======== key',key['key_name']
489 def display_slice_spec (self,slice):
490 print '+ ======== slice',slice['slice_fields']['name']
491 for (k,v) in slice.iteritems():
504 elif k=='slice_fields':
505 print '+ fields',':',
506 print 'max_nodes=',v['max_nodes'],
511 def display_node_spec (self,node):
512 print "+ node=%s host_box=%s"%(node['name'],node['host_box']),
513 print "hostname=",node['node_fields']['hostname'],
514 print "ip=",node['interface_fields']['ip']
515 if self.options.verbose:
516 utils.pprint("node details",node,depth=3)
518 # another entry point for just showing the boxes involved
519 def display_mapping (self):
520 TestPlc.display_mapping_plc(self.plc_spec)
524 def display_mapping_plc (plc_spec):
525 print '+ MyPLC',plc_spec['name']
526 # WARNING this would not be right for lxc-based PLC's - should be harmless though
527 print '+\tvserver address = root@%s:/vservers/%s'%(plc_spec['host_box'],plc_spec['vservername'])
528 print '+\tIP = %s/%s'%(plc_spec['PLC_API_HOST'],plc_spec['vserverip'])
529 for site_spec in plc_spec['sites']:
530 for node_spec in site_spec['nodes']:
531 TestPlc.display_mapping_node(node_spec)
534 def display_mapping_node (node_spec):
535 print '+ NODE %s'%(node_spec['name'])
536 print '+\tqemu box %s'%node_spec['host_box']
537 print '+\thostname=%s'%node_spec['node_fields']['hostname']
539 # write a timestamp in /vservers/<>.timestamp
540 # cannot be inside the vserver, that causes vserver .. build to cough
541 def timestamp_vs (self):
542 "Create a timestamp to remember creation date for this plc"
544 # TODO-lxc check this one
545 # a first approx. is to store the timestamp close to the VM root like vs does
546 stamp_path=self.vm_timestamp_path ()
547 stamp_dir = os.path.dirname (stamp_path)
548 utils.system(self.test_ssh.actual_command("mkdir -p %s"%stamp_dir))
549 return utils.system(self.test_ssh.actual_command("echo %d > %s"%(now,stamp_path)))==0
551 # this is called inconditionnally at the beginning of the test sequence
552 # just in case this is a rerun, so if the vm is not running it's fine
554 "vserver delete the test myplc"
555 stamp_path=self.vm_timestamp_path()
556 self.run_in_host("rm -f %s"%stamp_path)
557 if self.options.plcs_use_lxc:
558 self.run_in_host("virsh -c lxc:// destroy %s"%self.vservername)
559 self.run_in_host("virsh -c lxc:// undefine %s"%self.vservername)
560 self.run_in_host("rm -fr /vservers/%s"%self.vservername)
563 self.run_in_host("vserver --silent %s delete"%self.vservername)
567 # historically the build was being fetched by the tests
568 # now the build pushes itself as a subdir of the tests workdir
569 # so that the tests do not have to worry about extracting the build (svn, git, or whatever)
570 def vs_create (self):
571 "vserver creation (no install done)"
572 # push the local build/ dir to the testplc box
574 # a full path for the local calls
575 build_dir=os.path.dirname(sys.argv[0])
576 # sometimes this is empty - set to "." in such a case
577 if not build_dir: build_dir="."
578 build_dir += "/build"
580 # use a standard name - will be relative to remote buildname
582 # remove for safety; do *not* mkdir first, otherwise we end up with build/build/
583 self.test_ssh.rmdir(build_dir)
584 self.test_ssh.copy(build_dir,recursive=True)
585 # the repo url is taken from arch-rpms-url
586 # with the last step (i386) removed
587 repo_url = self.options.arch_rpms_url
588 for level in [ 'arch' ]:
589 repo_url = os.path.dirname(repo_url)
590 # pass the vbuild-nightly options to vtest-init-vserver
592 test_env_options += " -p %s"%self.options.personality
593 test_env_options += " -d %s"%self.options.pldistro
594 test_env_options += " -f %s"%self.options.fcdistro
595 if self.options.plcs_use_lxc:
596 script="vtest-init-lxc.sh"
598 script="vtest-init-vserver.sh"
599 vserver_name = self.vservername
600 vserver_options="--netdev eth0 --interface %s"%self.vserverip
602 vserver_hostname=socket.gethostbyaddr(self.vserverip)[0]
603 vserver_options += " --hostname %s"%vserver_hostname
605 print "Cannot reverse lookup %s"%self.vserverip
606 print "This is considered fatal, as this might pollute the test results"
608 create_vserver="%(build_dir)s/%(script)s %(test_env_options)s %(vserver_name)s %(repo_url)s -- %(vserver_options)s"%locals()
609 return self.run_in_host(create_vserver) == 0
612 def plc_install(self):
613 "yum install myplc, noderepo, and the plain bootstrapfs"
615 # workaround for getting pgsql8.2 on centos5
616 if self.options.fcdistro == "centos5":
617 self.run_in_guest("rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm")
620 if self.options.personality == "linux32":
622 elif self.options.personality == "linux64":
625 raise Exception, "Unsupported personality %r"%self.options.personality
626 nodefamily="%s-%s-%s"%(self.options.pldistro,self.options.fcdistro,arch)
629 pkgs_list.append ("slicerepo-%s"%nodefamily)
630 pkgs_list.append ("myplc")
631 pkgs_list.append ("noderepo-%s"%nodefamily)
632 pkgs_list.append ("nodeimage-%s-plain"%nodefamily)
633 pkgs_string=" ".join(pkgs_list)
634 return self.yum_install (pkgs_list)
637 def plc_configure(self):
639 tmpname='%s.plc-config-tty'%(self.name())
640 fileconf=open(tmpname,'w')
641 for var in [ 'PLC_NAME',
646 'PLC_MAIL_SUPPORT_ADDRESS',
649 # Above line was added for integrating SFA Testing
655 'PLC_RESERVATION_GRANULARITY',
657 'PLC_OMF_XMPP_SERVER',
660 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
661 fileconf.write('w\n')
662 fileconf.write('q\n')
664 utils.system('cat %s'%tmpname)
665 self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
666 utils.system('rm %s'%tmpname)
671 self.run_in_guest('service plc start')
676 self.run_in_guest('service plc stop')
680 "start the PLC vserver"
685 "stop the PLC vserver"
689 # stores the keys from the config for further use
690 def keys_store(self):
691 "stores test users ssh keys in keys/"
692 for key_spec in self.plc_spec['keys']:
693 TestKey(self,key_spec).store_key()
696 def keys_clean(self):
697 "removes keys cached in keys/"
698 utils.system("rm -rf ./keys")
701 # fetches the ssh keys in the plc's /etc/planetlab and stores them in keys/
702 # for later direct access to the nodes
703 def keys_fetch(self):
704 "gets ssh keys in /etc/planetlab/ and stores them locally in keys/"
706 if not os.path.isdir(dir):
708 vservername=self.vservername
709 vm_root=self.vm_root_in_host()
711 prefix = 'debug_ssh_key'
712 for ext in [ 'pub', 'rsa' ] :
713 src="%(vm_root)s/etc/planetlab/%(prefix)s.%(ext)s"%locals()
714 dst="keys/%(vservername)s-debug.%(ext)s"%locals()
715 if self.test_ssh.fetch(src,dst) != 0: overall=False
719 "create sites with PLCAPI"
720 return self.do_sites()
722 def delete_sites (self):
723 "delete sites with PLCAPI"
724 return self.do_sites(action="delete")
726 def do_sites (self,action="add"):
727 for site_spec in self.plc_spec['sites']:
728 test_site = TestSite (self,site_spec)
729 if (action != "add"):
730 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
731 test_site.delete_site()
732 # deleted with the site
733 #test_site.delete_users()
736 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
737 test_site.create_site()
738 test_site.create_users()
741 def delete_all_sites (self):
742 "Delete all sites in PLC, and related objects"
743 print 'auth_root',self.auth_root()
744 sites = self.apiserver.GetSites(self.auth_root(), {}, ['site_id','login_base'])
746 # keep automatic site - otherwise we shoot in our own foot, root_auth is not valid anymore
747 if site['login_base']==self.plc_spec['PLC_SLICE_PREFIX']: continue
748 site_id=site['site_id']
749 print 'Deleting site_id',site_id
750 self.apiserver.DeleteSite(self.auth_root(),site_id)
754 "create nodes with PLCAPI"
755 return self.do_nodes()
756 def delete_nodes (self):
757 "delete nodes with PLCAPI"
758 return self.do_nodes(action="delete")
760 def do_nodes (self,action="add"):
761 for site_spec in self.plc_spec['sites']:
762 test_site = TestSite (self,site_spec)
764 utils.header("Deleting nodes in site %s"%test_site.name())
765 for node_spec in site_spec['nodes']:
766 test_node=TestNode(self,test_site,node_spec)
767 utils.header("Deleting %s"%test_node.name())
768 test_node.delete_node()
770 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
771 for node_spec in site_spec['nodes']:
772 utils.pprint('Creating node %s'%node_spec,node_spec)
773 test_node = TestNode (self,test_site,node_spec)
774 test_node.create_node ()
777 def nodegroups (self):
778 "create nodegroups with PLCAPI"
779 return self.do_nodegroups("add")
780 def delete_nodegroups (self):
781 "delete nodegroups with PLCAPI"
782 return self.do_nodegroups("delete")
786 def translate_timestamp (start,grain,timestamp):
787 if timestamp < TestPlc.YEAR: return start+timestamp*grain
788 else: return timestamp
791 def timestamp_printable (timestamp):
792 return time.strftime('%m-%d %H:%M:%S UTC',time.gmtime(timestamp))
795 "create leases (on reservable nodes only, use e.g. run -c default -c resa)"
797 grain=self.apiserver.GetLeaseGranularity(self.auth_root())
798 print 'API answered grain=',grain
799 start=(now/grain)*grain
801 # find out all nodes that are reservable
802 nodes=self.all_reservable_nodenames()
804 utils.header ("No reservable node found - proceeding without leases")
807 # attach them to the leases as specified in plc_specs
808 # this is where the 'leases' field gets interpreted as relative of absolute
809 for lease_spec in self.plc_spec['leases']:
810 # skip the ones that come with a null slice id
811 if not lease_spec['slice']: continue
812 lease_spec['t_from']=TestPlc.translate_timestamp(start,grain,lease_spec['t_from'])
813 lease_spec['t_until']=TestPlc.translate_timestamp(start,grain,lease_spec['t_until'])
814 lease_addition=self.apiserver.AddLeases(self.auth_root(),nodes,
815 lease_spec['slice'],lease_spec['t_from'],lease_spec['t_until'])
816 if lease_addition['errors']:
817 utils.header("Cannot create leases, %s"%lease_addition['errors'])
820 utils.header('Leases on nodes %r for %s from %d (%s) until %d (%s)'%\
821 (nodes,lease_spec['slice'],
822 lease_spec['t_from'],TestPlc.timestamp_printable(lease_spec['t_from']),
823 lease_spec['t_until'],TestPlc.timestamp_printable(lease_spec['t_until'])))
827 def delete_leases (self):
828 "remove all leases in the myplc side"
829 lease_ids= [ l['lease_id'] for l in self.apiserver.GetLeases(self.auth_root())]
830 utils.header("Cleaning leases %r"%lease_ids)
831 self.apiserver.DeleteLeases(self.auth_root(),lease_ids)
834 def list_leases (self):
835 "list all leases known to the myplc"
836 leases = self.apiserver.GetLeases(self.auth_root())
839 current=l['t_until']>=now
840 if self.options.verbose or current:
841 utils.header("%s %s from %s until %s"%(l['hostname'],l['name'],
842 TestPlc.timestamp_printable(l['t_from']),
843 TestPlc.timestamp_printable(l['t_until'])))
846 # create nodegroups if needed, and populate
847 def do_nodegroups (self, action="add"):
848 # 1st pass to scan contents
850 for site_spec in self.plc_spec['sites']:
851 test_site = TestSite (self,site_spec)
852 for node_spec in site_spec['nodes']:
853 test_node=TestNode (self,test_site,node_spec)
854 if node_spec.has_key('nodegroups'):
855 nodegroupnames=node_spec['nodegroups']
856 if isinstance(nodegroupnames,StringTypes):
857 nodegroupnames = [ nodegroupnames ]
858 for nodegroupname in nodegroupnames:
859 if not groups_dict.has_key(nodegroupname):
860 groups_dict[nodegroupname]=[]
861 groups_dict[nodegroupname].append(test_node.name())
862 auth=self.auth_root()
864 for (nodegroupname,group_nodes) in groups_dict.iteritems():
866 print 'nodegroups:','dealing with nodegroup',nodegroupname,'on nodes',group_nodes
867 # first, check if the nodetagtype is here
868 tag_types = self.apiserver.GetTagTypes(auth,{'tagname':nodegroupname})
870 tag_type_id = tag_types[0]['tag_type_id']
872 tag_type_id = self.apiserver.AddTagType(auth,
873 {'tagname':nodegroupname,
874 'description': 'for nodegroup %s'%nodegroupname,
876 print 'located tag (type)',nodegroupname,'as',tag_type_id
878 nodegroups = self.apiserver.GetNodeGroups (auth, {'groupname':nodegroupname})
880 self.apiserver.AddNodeGroup(auth, nodegroupname, tag_type_id, 'yes')
881 print 'created nodegroup',nodegroupname,'from tagname',nodegroupname,'and value','yes'
882 # set node tag on all nodes, value='yes'
883 for nodename in group_nodes:
885 self.apiserver.AddNodeTag(auth, nodename, nodegroupname, "yes")
887 traceback.print_exc()
888 print 'node',nodename,'seems to already have tag',nodegroupname
891 expect_yes = self.apiserver.GetNodeTags(auth,
892 {'hostname':nodename,
893 'tagname':nodegroupname},
894 ['value'])[0]['value']
895 if expect_yes != "yes":
896 print 'Mismatch node tag on node',nodename,'got',expect_yes
899 if not self.options.dry_run:
900 print 'Cannot find tag',nodegroupname,'on node',nodename
904 print 'cleaning nodegroup',nodegroupname
905 self.apiserver.DeleteNodeGroup(auth,nodegroupname)
907 traceback.print_exc()
911 # a list of TestNode objs
912 def all_nodes (self):
914 for site_spec in self.plc_spec['sites']:
915 test_site = TestSite (self,site_spec)
916 for node_spec in site_spec['nodes']:
917 nodes.append(TestNode (self,test_site,node_spec))
920 # return a list of tuples (nodename,qemuname)
921 def all_node_infos (self) :
923 for site_spec in self.plc_spec['sites']:
924 node_infos += [ (node_spec['node_fields']['hostname'],node_spec['host_box']) \
925 for node_spec in site_spec['nodes'] ]
928 def all_nodenames (self): return [ x[0] for x in self.all_node_infos() ]
929 def all_reservable_nodenames (self):
931 for site_spec in self.plc_spec['sites']:
932 for node_spec in site_spec['nodes']:
933 node_fields=node_spec['node_fields']
934 if 'node_type' in node_fields and node_fields['node_type']=='reservable':
935 res.append(node_fields['hostname'])
938 # silent_minutes : during the first <silent_minutes> minutes nothing gets printed
939 def nodes_check_boot_state (self, target_boot_state, timeout_minutes, silent_minutes,period_seconds=15):
940 if self.options.dry_run:
944 class CompleterTaskBootState (CompleterTask):
945 def __init__ (self, test_plc,hostname):
946 self.test_plc=test_plc
947 self.hostname=hostname
948 self.last_boot_state='undef'
949 def actual_run (self):
951 node = self.test_plc.apiserver.GetNodes(self.test_plc.auth_root(), [ self.hostname ],
953 self.last_boot_state = node['boot_state']
954 return self.last_boot_state == target_boot_state
958 return "CompleterTaskBootState with node %s"%self.hostname
959 def failure_message (self):
960 return "node %s in state %s - expected %s"%(self.hostname,self.last_boot_state,target_boot_state)
962 timeout = timedelta(minutes=timeout_minutes)
963 graceout = timedelta(minutes=silent_minutes)
964 period = timedelta(seconds=period_seconds)
965 # the nodes that haven't checked yet - start with a full list and shrink over time
966 utils.header("checking nodes boot state (expected %s)"%target_boot_state)
967 tasks = [ CompleterTaskBootState (self,hostname) \
968 for (hostname,_) in self.all_node_infos() ]
969 return Completer (tasks).run (timeout, graceout, period)
971 def nodes_booted(self):
972 return self.nodes_check_boot_state('boot',timeout_minutes=30,silent_minutes=28)
974 def check_nodes_ssh(self,debug,timeout_minutes,silent_minutes,period_seconds=15):
975 class CompleterTaskNodeSsh (CompleterTask):
976 def __init__ (self, hostname, qemuname, boot_state, local_key):
977 self.hostname=hostname
978 self.qemuname=qemuname
979 self.boot_state=boot_state
980 self.local_key=local_key
981 def run (self, silent):
982 command = TestSsh (self.hostname,key=self.local_key).actual_command("hostname;uname -a")
983 return utils.system (command, silent=silent)==0
984 def failure_message (self):
985 return "Cannot reach %s @ %s in %s mode"%(self.hostname, self.qemuname, self.boot_state)
988 timeout = timedelta(minutes=timeout_minutes)
989 graceout = timedelta(minutes=silent_minutes)
990 period = timedelta(seconds=period_seconds)
991 vservername=self.vservername
994 local_key = "keys/%(vservername)s-debug.rsa"%locals()
997 local_key = "keys/key_admin.rsa"
998 utils.header("checking ssh access to nodes (expected in %s mode)"%message)
999 node_infos = self.all_node_infos()
1000 tasks = [ CompleterTaskNodeSsh (nodename, qemuname, message, local_key) \
1001 for (nodename,qemuname) in node_infos ]
1002 return Completer (tasks).run (timeout, graceout, period)
1004 def ssh_node_debug(self):
1005 "Tries to ssh into nodes in debug mode with the debug ssh key"
1006 return self.check_nodes_ssh(debug=True,
1007 timeout_minutes=self.ssh_node_debug_timeout,
1008 silent_minutes=self.ssh_node_debug_silent)
1010 def ssh_node_boot(self):
1011 "Tries to ssh into nodes in production mode with the root ssh key"
1012 return self.check_nodes_ssh(debug=False,
1013 timeout_minutes=self.ssh_node_boot_timeout,
1014 silent_minutes=self.ssh_node_boot_silent)
1016 def node_bmlogs(self):
1017 "Checks that there's a non-empty dir. /var/log/bm/raw"
1018 return utils.system(self.actual_command_in_guest("ls /var/log/bm/raw"))==0
1021 def qemu_local_init (self): pass
1023 def bootcd (self): pass
1025 def qemu_local_config (self): pass
1027 def nodestate_reinstall (self): pass
1029 def nodestate_safeboot (self): pass
1031 def nodestate_boot (self): pass
1033 def nodestate_show (self): pass
1035 def qemu_export (self): pass
1037 ### check hooks : invoke scripts from hooks/{node,slice}
1038 def check_hooks_node (self):
1039 return self.locate_first_node().check_hooks()
1040 def check_hooks_sliver (self) :
1041 return self.locate_first_sliver().check_hooks()
1043 def check_hooks (self):
1044 "runs unit tests in the node and slice contexts - see hooks/{node,slice}"
1045 return self.check_hooks_node() and self.check_hooks_sliver()
1048 def do_check_initscripts(self):
1049 class CompleterTaskInitscript (CompleterTask):
1050 def __init__ (self, test_sliver, stamp):
1051 self.test_sliver=test_sliver
1053 def actual_run (self):
1054 return self.test_sliver.check_initscript_stamp (self.stamp)
1056 return "initscript checker for %s"%self.test_sliver.name()
1057 def failure_message (self):
1058 return "initscript stamp %s not found in sliver %s"%(self.stamp,self.test_sliver.name())
1061 for slice_spec in self.plc_spec['slices']:
1062 if not slice_spec.has_key('initscriptstamp'):
1064 stamp=slice_spec['initscriptstamp']
1065 slicename=slice_spec['slice_fields']['name']
1066 for nodename in slice_spec['nodenames']:
1067 print 'nodename',nodename,'slicename',slicename,'stamp',stamp
1068 (site,node) = self.locate_node (nodename)
1069 # xxx - passing the wrong site - probably harmless
1070 test_site = TestSite (self,site)
1071 test_slice = TestSlice (self,test_site,slice_spec)
1072 test_node = TestNode (self,test_site,node)
1073 test_sliver = TestSliver (self, test_node, test_slice)
1074 tasks.append ( CompleterTaskInitscript (test_sliver, stamp))
1075 return Completer (tasks).run (timedelta(minutes=5), timedelta(minutes=4), timedelta(seconds=10))
1077 def check_initscripts(self):
1078 "check that the initscripts have triggered"
1079 return self.do_check_initscripts()
1081 def initscripts (self):
1082 "create initscripts with PLCAPI"
1083 for initscript in self.plc_spec['initscripts']:
1084 utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
1085 self.apiserver.AddInitScript(self.auth_root(),initscript['initscript_fields'])
1088 def delete_initscripts (self):
1089 "delete initscripts with PLCAPI"
1090 for initscript in self.plc_spec['initscripts']:
1091 initscript_name = initscript['initscript_fields']['name']
1092 print('Attempting to delete %s in plc %s'%(initscript_name,self.plc_spec['name']))
1094 self.apiserver.DeleteInitScript(self.auth_root(),initscript_name)
1095 print initscript_name,'deleted'
1097 print 'deletion went wrong - probably did not exist'
1102 "create slices with PLCAPI"
1103 return self.do_slices(action="add")
1105 def delete_slices (self):
1106 "delete slices with PLCAPI"
1107 return self.do_slices(action="delete")
1109 def fill_slices (self):
1110 "add nodes in slices with PLCAPI"
1111 return self.do_slices(action="fill")
1113 def empty_slices (self):
1114 "remove nodes from slices with PLCAPI"
1115 return self.do_slices(action="empty")
1117 def do_slices (self, action="add"):
1118 for slice in self.plc_spec['slices']:
1119 site_spec = self.locate_site (slice['sitename'])
1120 test_site = TestSite(self,site_spec)
1121 test_slice=TestSlice(self,test_site,slice)
1122 if action == "delete":
1123 test_slice.delete_slice()
1124 elif action=="fill":
1125 test_slice.add_nodes()
1126 elif action=="empty":
1127 test_slice.delete_nodes()
1129 test_slice.create_slice()
1133 def ssh_slice(self): pass
1135 def ssh_slice_off (self): pass
1137 def ssh_slice_basics(self): pass
1140 def check_vsys_defaults(self): pass
1143 def keys_clear_known_hosts (self): pass
1145 def plcapi_urls (self):
1146 return PlcapiUrlScanner (self.auth_root(),ip=self.vserverip).scan()
1148 def speed_up_slices (self):
1149 "tweak nodemanager settings on all nodes using a conf file"
1150 # create the template on the server-side
1151 template="%s.nodemanager"%self.name()
1152 template_file = open (template,"w")
1153 template_file.write('OPTIONS="-p 30 -r 11 -d"\n')
1154 template_file.close()
1155 in_vm="/var/www/html/PlanetLabConf/nodemanager"
1156 remote="%s/%s"%(self.vm_root_in_host(),in_vm)
1157 self.test_ssh.copy_abs(template,remote)
1159 self.apiserver.AddConfFile (self.auth_root(),
1160 {'dest':'/etc/sysconfig/nodemanager',
1161 'source':'PlanetLabConf/nodemanager',
1162 'postinstall_cmd':'service nm restart',})
1165 def debug_nodemanager (self):
1166 "sets verbose mode for nodemanager, and speeds up cycle even more (needs speed_up_slices first)"
1167 template="%s.nodemanager"%self.name()
1168 template_file = open (template,"w")
1169 template_file.write('OPTIONS="-p 10 -r 6 -v -d"\n')
1170 template_file.close()
1171 in_vm="/var/www/html/PlanetLabConf/nodemanager"
1172 remote="%s/%s"%(self.vm_root_in_host(),in_vm)
1173 self.test_ssh.copy_abs(template,remote)
1177 def qemu_start (self) : pass
1180 def timestamp_qemu (self) : pass
1182 # when a spec refers to a node possibly on another plc
1183 def locate_sliver_obj_cross (self, nodename, slicename, other_plcs):
1184 for plc in [ self ] + other_plcs:
1186 return plc.locate_sliver_obj (nodename, slicename)
1189 raise Exception, "Cannot locate sliver %s@%s among all PLCs"%(nodename,slicename)
1191 # implement this one as a cross step so that we can take advantage of different nodes
1192 # in multi-plcs mode
1193 def cross_check_tcp (self, other_plcs):
1194 "check TCP connectivity between 2 slices (or in loopback if only one is defined)"
1195 if 'tcp_specs' not in self.plc_spec or not self.plc_spec['tcp_specs']:
1196 utils.header ("check_tcp: no/empty config found")
1198 specs = self.plc_spec['tcp_specs']
1203 s_test_sliver = self.locate_sliver_obj_cross (spec['server_node'],spec['server_slice'],other_plcs)
1204 if not s_test_sliver.run_tcp_server(port,timeout=20):
1208 # idem for the client side
1209 c_test_sliver = self.locate_sliver_obj_cross (spec['client_node'],spec['client_slice'],other_plcs)
1210 # use nodename from locatesd sliver, unless 'client_connect' is set
1211 if 'client_connect' in spec:
1212 destination = spec['client_connect']
1214 destination=s_test_sliver.test_node.name()
1215 if not c_test_sliver.run_tcp_client(destination,port):
1219 # painfully enough, we need to allow for some time as netflow might show up last
1220 def check_system_slice (self):
1221 "all nodes: check that a system slice is alive"
1222 # netflow currently not working in the lxc distro
1223 # drl not built at all in the wtx distro
1224 # if we find either of them we're happy
1225 return self.check_netflow() or self.check_drl()
1228 def check_netflow (self): return self._check_system_slice ('netflow')
1229 def check_drl (self): return self._check_system_slice ('drl')
1231 # we have the slices up already here, so it should not take too long
1232 def _check_system_slice (self, slicename, timeout_minutes=5, period_seconds=15):
1233 class CompleterTaskSystemSlice (CompleterTask):
1234 def __init__ (self, test_node, dry_run):
1235 self.test_node=test_node
1236 self.dry_run=dry_run
1237 def actual_run (self):
1238 return self.test_node._check_system_slice (slicename, dry_run=self.dry_run)
1240 return "System slice %s @ %s"%(slicename, self.test_node.name())
1241 def failure_message (self):
1242 return "COULD not find system slice %s @ %s"%(slicename, self.test_node.name())
1243 timeout = timedelta(minutes=timeout_minutes)
1244 silent = timedelta (0)
1245 period = timedelta (seconds=period_seconds)
1246 tasks = [ CompleterTaskSystemSlice (test_node, self.options.dry_run) \
1247 for test_node in self.all_nodes() ]
1248 return Completer (tasks) . run (timeout, silent, period)
1250 def plcsh_stress_test (self):
1251 "runs PLCAPI stress test, that checks Add/Update/Delete on all types - preserves contents"
1252 # install the stress-test in the plc image
1253 location = "/usr/share/plc_api/plcsh_stress_test.py"
1254 remote="%s/%s"%(self.vm_root_in_host(),location)
1255 self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1257 command += " -- --check"
1258 if self.options.size == 1:
1259 command += " --tiny"
1260 return ( self.run_in_guest(command) == 0)
1262 # populate runs the same utility without slightly different options
1263 # in particular runs with --preserve (dont cleanup) and without --check
1264 # also it gets run twice, once with the --foreign option for creating fake foreign entries
1266 def sfa_install_all (self):
1267 "yum install sfa sfa-plc sfa-sfatables sfa-client"
1268 return self.yum_install ("sfa sfa-plc sfa-sfatables sfa-client")
1270 def sfa_install_core(self):
1272 return self.yum_install ("sfa")
1274 def sfa_install_plc(self):
1275 "yum install sfa-plc"
1276 return self.yum_install("sfa-plc")
1278 def sfa_install_sfatables(self):
1279 "yum install sfa-sfatables"
1280 return self.yum_install ("sfa-sfatables")
1282 # for some very odd reason, this sometimes fails with the following symptom
1283 # # yum install sfa-client
1284 # Setting up Install Process
1286 # Downloading Packages:
1287 # Running rpm_check_debug
1288 # Running Transaction Test
1289 # Transaction Test Succeeded
1290 # Running Transaction
1291 # Transaction couldn't start:
1292 # installing package sfa-client-2.1-7.onelab.2012.05.23.i686 needs 68KB on the / filesystem
1293 # [('installing package sfa-client-2.1-7.onelab.2012.05.23.i686 needs 68KB on the / filesystem', (9, '/', 69632L))]
1294 # even though in the same context I have
1295 # [2012.05.23--f14-32-sfastd1-1-vplc07] / # df -h
1296 # Filesystem Size Used Avail Use% Mounted on
1297 # /dev/hdv1 806G 264G 501G 35% /
1298 # none 16M 36K 16M 1% /tmp
1300 # so as a workaround, we first try yum install, and then invoke rpm on the cached rpm...
1301 def sfa_install_client(self):
1302 "yum install sfa-client"
1303 first_try=self.yum_install("sfa-client")
1304 if first_try: return True
1305 utils.header ("********** Regular yum failed - special workaround in place, 2nd chance")
1306 (code,cached_rpm_path)=utils.output_of(self.actual_command_in_guest('find /var/cache/yum -name sfa-client\*.rpm'))
1307 utils.header("rpm_path=<<%s>>"%rpm_path)
1309 self.run_in_guest("rpm -i %s"%cached_rpm_path)
1310 return self.yum_check_installed ("sfa-client")
1312 def sfa_dbclean(self):
1313 "thoroughly wipes off the SFA database"
1314 return self.run_in_guest("sfaadmin reg nuke")==0 or \
1315 self.run_in_guest("sfa-nuke.py")==0 or \
1316 self.run_in_guest("sfa-nuke-plc.py")==0
1318 def sfa_fsclean(self):
1319 "cleanup /etc/sfa/trusted_roots and /var/lib/sfa"
1320 self.run_in_guest("rm -rf /etc/sfa/trusted_roots /var/lib/sfa/authorities")
1323 def sfa_plcclean(self):
1324 "cleans the PLC entries that were created as a side effect of running the script"
1326 sfa_spec=self.plc_spec['sfa']
1328 for auth_sfa_spec in sfa_spec['auth_sfa_specs']:
1329 login_base=auth_sfa_spec['login_base']
1330 try: self.apiserver.DeleteSite (self.auth_root(),login_base)
1331 except: print "Site %s already absent from PLC db"%login_base
1333 for spec_name in ['pi_spec','user_spec']:
1334 user_spec=auth_sfa_spec[spec_name]
1335 username=user_spec['email']
1336 try: self.apiserver.DeletePerson(self.auth_root(),username)
1338 # this in fact is expected as sites delete their members
1339 #print "User %s already absent from PLC db"%username
1342 print "REMEMBER TO RUN sfa_import AGAIN"
1345 def sfa_uninstall(self):
1346 "uses rpm to uninstall sfa - ignore result"
1347 self.run_in_guest("rpm -e sfa sfa-sfatables sfa-client sfa-plc")
1348 self.run_in_guest("rm -rf /var/lib/sfa")
1349 self.run_in_guest("rm -rf /etc/sfa")
1350 self.run_in_guest("rm -rf /var/log/sfa_access.log /var/log/sfa_import_plc.log /var/log/sfa.daemon")
1352 self.run_in_guest("rpm -e --noscripts sfa-plc")
1355 ### run unit tests for SFA
1356 # NOTE: for some reason on f14/i386, yum install sfa-tests fails for no reason
1357 # Running Transaction
1358 # Transaction couldn't start:
1359 # installing package sfa-tests-1.0-21.onelab.i686 needs 204KB on the / filesystem
1360 # [('installing package sfa-tests-1.0-21.onelab.i686 needs 204KB on the / filesystem', (9, '/', 208896L))]
1361 # no matter how many Gbs are available on the testplc
1362 # could not figure out what's wrong, so...
1363 # if the yum install phase fails, consider the test is successful
1364 # other combinations will eventually run it hopefully
1365 def sfa_utest(self):
1366 "yum install sfa-tests and run SFA unittests"
1367 self.run_in_guest("yum -y install sfa-tests")
1368 # failed to install - forget it
1369 if self.run_in_guest("rpm -q sfa-tests")!=0:
1370 utils.header("WARNING: SFA unit tests failed to install, ignoring")
1372 return self.run_in_guest("/usr/share/sfa/tests/testAll.py")==0
1376 dirname="conf.%s"%self.plc_spec['name']
1377 if not os.path.isdir(dirname):
1378 utils.system("mkdir -p %s"%dirname)
1379 if not os.path.isdir(dirname):
1380 raise Exception,"Cannot create config dir for plc %s"%self.name()
1383 def conffile(self,filename):
1384 return "%s/%s"%(self.confdir(),filename)
1385 def confsubdir(self,dirname,clean,dry_run=False):
1386 subdirname="%s/%s"%(self.confdir(),dirname)
1388 utils.system("rm -rf %s"%subdirname)
1389 if not os.path.isdir(subdirname):
1390 utils.system("mkdir -p %s"%subdirname)
1391 if not dry_run and not os.path.isdir(subdirname):
1392 raise "Cannot create config subdir %s for plc %s"%(dirname,self.name())
1395 def conffile_clean (self,filename):
1396 filename=self.conffile(filename)
1397 return utils.system("rm -rf %s"%filename)==0
1400 def sfa_configure(self):
1401 "run sfa-config-tty"
1402 tmpname=self.conffile("sfa-config-tty")
1403 fileconf=open(tmpname,'w')
1404 for var in [ 'SFA_REGISTRY_ROOT_AUTH',
1405 'SFA_INTERFACE_HRN',
1406 'SFA_REGISTRY_LEVEL1_AUTH',
1407 'SFA_REGISTRY_HOST',
1408 'SFA_AGGREGATE_HOST',
1418 'SFA_GENERIC_FLAVOUR',
1419 'SFA_AGGREGATE_ENABLED',
1421 if self.plc_spec['sfa'].has_key(var):
1422 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec['sfa'][var]))
1423 # the way plc_config handles booleans just sucks..
1426 if self.plc_spec['sfa'][var]: val='true'
1427 fileconf.write ('e %s\n%s\n'%(var,val))
1428 fileconf.write('w\n')
1429 fileconf.write('R\n')
1430 fileconf.write('q\n')
1432 utils.system('cat %s'%tmpname)
1433 self.run_in_guest_piped('cat %s'%tmpname,'sfa-config-tty')
1436 def aggregate_xml_line(self):
1437 port=self.plc_spec['sfa']['neighbours-port']
1438 return '<aggregate addr="%s" hrn="%s" port="%r"/>' % \
1439 (self.vserverip,self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH'],port)
1441 def registry_xml_line(self):
1442 return '<registry addr="%s" hrn="%s" port="12345"/>' % \
1443 (self.vserverip,self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH'])
1446 # a cross step that takes all other plcs in argument
1447 def cross_sfa_configure(self, other_plcs):
1448 "writes aggregates.xml and registries.xml that point to all other PLCs in the test"
1449 # of course with a single plc, other_plcs is an empty list
1452 agg_fname=self.conffile("agg.xml")
1453 file(agg_fname,"w").write("<aggregates>%s</aggregates>\n" % \
1454 " ".join([ plc.aggregate_xml_line() for plc in other_plcs ]))
1455 utils.header ("(Over)wrote %s"%agg_fname)
1456 reg_fname=self.conffile("reg.xml")
1457 file(reg_fname,"w").write("<registries>%s</registries>\n" % \
1458 " ".join([ plc.registry_xml_line() for plc in other_plcs ]))
1459 utils.header ("(Over)wrote %s"%reg_fname)
1460 return self.test_ssh.copy_abs(agg_fname,'/%s/etc/sfa/aggregates.xml'%self.vm_root_in_host())==0 \
1461 and self.test_ssh.copy_abs(reg_fname,'/%s/etc/sfa/registries.xml'%self.vm_root_in_host())==0
1463 def sfa_import(self):
1464 "use sfaadmin to import from plc"
1465 auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1467 self.run_in_guest('sfaadmin reg import_registry')==0
1468 # not needed anymore
1469 # self.run_in_guest('cp /etc/sfa/authorities/%s/%s.pkey /etc/sfa/authorities/server.key'%(auth,auth))
1471 def sfa_start(self):
1473 return self.run_in_guest('service sfa start')==0
1475 def sfi_configure(self):
1476 "Create /root/sfi on the plc side for sfi client configuration"
1477 if self.options.dry_run:
1478 utils.header("DRY RUN - skipping step")
1480 sfa_spec=self.plc_spec['sfa']
1481 # cannot use auth_sfa_mapper to pass dir_name
1482 for slice_spec in self.plc_spec['sfa']['auth_sfa_specs']:
1483 test_slice=TestAuthSfa(self,slice_spec)
1484 dir_basename=os.path.basename(test_slice.sfi_path())
1485 dir_name=self.confsubdir("dot-sfi/%s"%dir_basename,clean=True,dry_run=self.options.dry_run)
1486 test_slice.sfi_configure(dir_name)
1487 # push into the remote /root/sfi area
1488 location = test_slice.sfi_path()
1489 remote="%s/%s"%(self.vm_root_in_host(),location)
1490 self.test_ssh.mkdir(remote,abs=True)
1491 # need to strip last level or remote otherwise we get an extra dir level
1492 self.test_ssh.copy_abs(dir_name, os.path.dirname(remote), recursive=True)
1496 def sfi_clean (self):
1497 "clean up /root/sfi on the plc side"
1498 self.run_in_guest("rm -rf /root/sfi")
1502 def sfa_add_site (self): pass
1504 def sfa_add_pi (self): pass
1506 def sfa_add_user(self): pass
1508 def sfa_update_user(self): pass
1510 def sfa_add_slice(self): pass
1512 def sfa_renew_slice(self): pass
1514 def sfa_discover(self): pass
1516 def sfa_create_slice(self): pass
1518 def sfa_check_slice_plc(self): pass
1520 def sfa_update_slice(self): pass
1522 def sfi_list(self): pass
1524 def sfi_show(self): pass
1526 def sfi_slices(self): pass
1528 def ssh_slice_sfa(self): pass
1530 def sfa_delete_user(self): pass
1532 def sfa_delete_slice(self): pass
1536 self.run_in_guest('service sfa stop')==0
1539 def populate (self):
1540 "creates random entries in the PLCAPI"
1541 # install the stress-test in the plc image
1542 location = "/usr/share/plc_api/plcsh_stress_test.py"
1543 remote="%s/%s"%(self.vm_root_in_host(),location)
1544 self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1546 command += " -- --preserve --short-names"
1547 local = (self.run_in_guest(command) == 0);
1548 # second run with --foreign
1549 command += ' --foreign'
1550 remote = (self.run_in_guest(command) == 0);
1551 return ( local and remote)
1553 def gather_logs (self):
1554 "gets all possible logs from plc's/qemu node's/slice's for future reference"
1555 # (1.a) get the plc's /var/log/ and store it locally in logs/myplc.var-log.<plcname>/*
1556 # (1.b) get the plc's /var/lib/pgsql/data/pg_log/ -> logs/myplc.pgsql-log.<plcname>/*
1557 # (1.c) get the plc's /root/sfi -> logs/sfi.<plcname>/
1558 # (2) get all the nodes qemu log and store it as logs/node.qemu.<node>.log
1559 # (3) get the nodes /var/log and store is as logs/node.var-log.<node>/*
1560 # (4) as far as possible get the slice's /var/log as logs/sliver.var-log.<sliver>/*
1562 print "-------------------- TestPlc.gather_logs : PLC's /var/log"
1563 self.gather_var_logs ()
1565 print "-------------------- TestPlc.gather_logs : PLC's /var/lib/psql/data/pg_log/"
1566 self.gather_pgsql_logs ()
1568 print "-------------------- TestPlc.gather_logs : PLC's /root/sfi/"
1569 self.gather_root_sfi ()
1571 print "-------------------- TestPlc.gather_logs : nodes's QEMU logs"
1572 for site_spec in self.plc_spec['sites']:
1573 test_site = TestSite (self,site_spec)
1574 for node_spec in site_spec['nodes']:
1575 test_node=TestNode(self,test_site,node_spec)
1576 test_node.gather_qemu_logs()
1578 print "-------------------- TestPlc.gather_logs : nodes's /var/log"
1579 self.gather_nodes_var_logs()
1581 print "-------------------- TestPlc.gather_logs : sample sliver's /var/log"
1582 self.gather_slivers_var_logs()
1585 def gather_slivers_var_logs(self):
1586 for test_sliver in self.all_sliver_objs():
1587 remote = test_sliver.tar_var_logs()
1588 utils.system("mkdir -p logs/sliver.var-log.%s"%test_sliver.name())
1589 command = remote + " | tar -C logs/sliver.var-log.%s -xf -"%test_sliver.name()
1590 utils.system(command)
1593 def gather_var_logs (self):
1594 utils.system("mkdir -p logs/myplc.var-log.%s"%self.name())
1595 to_plc = self.actual_command_in_guest("tar -C /var/log/ -cf - .")
1596 command = to_plc + "| tar -C logs/myplc.var-log.%s -xf -"%self.name()
1597 utils.system(command)
1598 command = "chmod a+r,a+x logs/myplc.var-log.%s/httpd"%self.name()
1599 utils.system(command)
1601 def gather_pgsql_logs (self):
1602 utils.system("mkdir -p logs/myplc.pgsql-log.%s"%self.name())
1603 to_plc = self.actual_command_in_guest("tar -C /var/lib/pgsql/data/pg_log/ -cf - .")
1604 command = to_plc + "| tar -C logs/myplc.pgsql-log.%s -xf -"%self.name()
1605 utils.system(command)
1607 def gather_root_sfi (self):
1608 utils.system("mkdir -p logs/sfi.%s"%self.name())
1609 to_plc = self.actual_command_in_guest("tar -C /root/sfi/ -cf - .")
1610 command = to_plc + "| tar -C logs/sfi.%s -xf -"%self.name()
1611 utils.system(command)
1613 def gather_nodes_var_logs (self):
1614 for site_spec in self.plc_spec['sites']:
1615 test_site = TestSite (self,site_spec)
1616 for node_spec in site_spec['nodes']:
1617 test_node=TestNode(self,test_site,node_spec)
1618 test_ssh = TestSsh (test_node.name(),key="keys/key_admin.rsa")
1619 command = test_ssh.actual_command("tar -C /var/log -cf - .")
1620 command = command + "| tar -C logs/node.var-log.%s -xf -"%test_node.name()
1621 utils.system("mkdir -p logs/node.var-log.%s"%test_node.name())
1622 utils.system(command)
1625 # returns the filename to use for sql dump/restore, using options.dbname if set
1626 def dbfile (self, database):
1627 # uses options.dbname if it is found
1629 name=self.options.dbname
1630 if not isinstance(name,StringTypes):
1636 return "/root/%s-%s.sql"%(database,name)
1638 def plc_db_dump(self):
1639 'dump the planetlab5 DB in /root in the PLC - filename has time'
1640 dump=self.dbfile("planetab5")
1641 self.run_in_guest('pg_dump -U pgsqluser planetlab5 -f '+ dump)
1642 utils.header('Dumped planetlab5 database in %s'%dump)
1645 def plc_db_restore(self):
1646 'restore the planetlab5 DB - looks broken, but run -n might help'
1647 dump=self.dbfile("planetab5")
1648 ##stop httpd service
1649 self.run_in_guest('service httpd stop')
1650 # xxx - need another wrapper
1651 self.run_in_guest_piped('echo drop database planetlab5','psql --user=pgsqluser template1')
1652 self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab5')
1653 self.run_in_guest('psql -U pgsqluser planetlab5 -f '+dump)
1654 ##starting httpd service
1655 self.run_in_guest('service httpd start')
1657 utils.header('Database restored from ' + dump)
1659 def standby_1_through_20(self):
1660 """convenience function to wait for a specified number of minutes"""
1663 def standby_1(): pass
1665 def standby_2(): pass
1667 def standby_3(): pass
1669 def standby_4(): pass
1671 def standby_5(): pass
1673 def standby_6(): pass
1675 def standby_7(): pass
1677 def standby_8(): pass
1679 def standby_9(): pass
1681 def standby_10(): pass
1683 def standby_11(): pass
1685 def standby_12(): pass
1687 def standby_13(): pass
1689 def standby_14(): pass
1691 def standby_15(): pass
1693 def standby_16(): pass
1695 def standby_17(): pass
1697 def standby_18(): pass
1699 def standby_19(): pass
1701 def standby_20(): pass