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 # run a step but return True so that we can go on
68 def ignore_result (method):
70 # ssh_slice_ignore->ssh_slice
71 ref_name=method.__name__.replace('_ignore','').replace('force_','')
72 ref_method=TestPlc.__dict__[ref_name]
73 result=ref_method(self)
74 print "Actual (but ignored) result for %(ref_name)s is %(result)s"%locals()
75 return Ignored (result)
76 wrappee.__doc__="ignored version of " + method.__name__.replace('_ignore','').replace('ignore_','')
79 # a variant that expects the TestSlice method to return a list of CompleterTasks that
80 # are then merged into a single Completer run to avoid wating for all the slices
81 # esp. useful when a test fails of course
82 # because we need to pass arguments we use a class instead..
83 class slice_mapper__tasks (object):
84 # could not get this to work with named arguments
85 def __init__ (self,timeout_minutes,silent_minutes,period_seconds):
86 self.timeout=timedelta(minutes=timeout_minutes)
87 self.silent=timedelta(minutes=silent_minutes)
88 self.period=timedelta(seconds=period_seconds)
89 def __call__ (self, method):
91 # compute augmented method name
92 method_name = method.__name__ + "__tasks"
94 slice_method = TestSlice.__dict__[ method_name ]
97 for slice_spec in self.plc_spec['slices']:
98 site_spec = self.locate_site (slice_spec['sitename'])
99 test_site = TestSite(self,site_spec)
100 test_slice=TestSlice(self,test_site,slice_spec)
101 tasks += slice_method (test_slice, self.options)
102 return Completer (tasks).run (decorator_self.timeout, decorator_self.silent, decorator_self.period)
103 # restore the doc text from the TestSlice method even if a bit odd
104 wrappee.__doc__ = slice_method.__doc__
107 def auth_sfa_mapper (method):
110 auth_method = TestAuthSfa.__dict__[method.__name__]
111 for auth_spec in self.plc_spec['sfa']['auth_sfa_specs']:
112 test_auth=TestAuthSfa(self,auth_spec)
113 if not auth_method(test_auth,self.options): overall=False
115 # restore the doc text
116 actual.__doc__=TestAuthSfa.__dict__[method.__name__].__doc__
120 def __init__ (self,result):
130 'vs_delete','timestamp_vs','vs_create', SEP,
131 # 'plc_install', 'mod_python', 'plc_configure', 'plc_start', SEP,
132 'plc_install', 'plc_configure', 'plc_start', SEP,
133 'keys_fetch', 'keys_store', 'keys_clear_known_hosts', SEP,
134 'plcapi_urls','speed_up_slices', SEP,
135 'initscripts', 'sites', 'nodes', 'slices', 'nodegroups', 'leases', SEP,
136 # slices created under plcsh interactively seem to be fine but these ones don't have the tags
137 # keep this our of the way for now
138 # 'check_vsys_defaults', SEP,
139 'nodestate_reinstall', 'qemu_local_init','bootcd', 'qemu_local_config', SEP,
140 'qemu_kill_mine','qemu_clean_mine', 'qemu_export', 'qemu_start', 'timestamp_qemu', SEP,
141 'sfa_install_all', 'sfa_configure', 'cross_sfa_configure', 'sfa_start', 'sfa_import', SEPSFA,
142 'sfi_configure@1', 'sfa_add_site@1','sfa_add_pi@1', SEPSFA,
143 'sfa_add_user@1', 'sfa_update_user@1', 'sfa_add_slice@1', 'sfa_renew_slice@1', SEPSFA,
144 'sfa_discover@1', 'sfa_create_slice@1', 'sfa_check_slice_plc@1', 'sfa_update_slice@1', SEPSFA,
145 'sfi_list@1', 'sfi_show@1', 'sfa_utest@1', SEPSFA,
146 # we used to run plcsh_stress_test, and then ssh_node_debug and ssh_node_boot
147 # but as the stress test might take a while, we sometimes missed the debug mode..
148 'ssh_node_debug@1', 'plcsh_stress_test@1', SEP,
149 'ssh_node_boot@1', 'node_bmlogs@1', 'ssh_slice', 'ssh_slice_basics', 'check_initscripts_ignore', SEP,
150 'ssh_slice_sfa@1', 'sfa_delete_slice@1', 'sfa_delete_user@1', SEPSFA,
151 'cross_check_tcp@1', 'check_system_slice', SEP,
152 # check slices are turned off properly
153 'empty_slices', 'ssh_slice_off', SEP,
154 # check they are properly re-created with the same name
155 'fill_slices', 'ssh_slice_again_ignore', SEP,
156 'gather_logs_force', SEP,
159 'export', 'show_boxes', SEP,
160 'check_hooks', 'plc_stop', 'vs_start', 'vs_stop', SEP,
161 'delete_initscripts', 'delete_nodegroups','delete_all_sites', SEP,
162 'delete_sites', 'delete_nodes', 'delete_slices', 'keys_clean', SEP,
163 'delete_leases', 'list_leases', SEP,
165 'nodestate_show','nodestate_safeboot','nodestate_boot', SEP,
166 'qemu_list_all', 'qemu_list_mine', 'qemu_kill_all', SEP,
167 'sfa_install_core', 'sfa_install_sfatables', 'sfa_install_plc', 'sfa_install_client', SEPSFA,
168 'sfa_plcclean', 'sfa_dbclean', 'sfa_stop','sfa_uninstall', 'sfi_clean', SEPSFA,
169 'plc_db_dump' , 'plc_db_restore', SEP,
170 'check_netflow','check_drl', SEP,
171 'debug_nodemanager', SEP,
172 'standby_1_through_20','yes','no',SEP,
176 def printable_steps (list):
177 single_line=" ".join(list)+" "
178 return single_line.replace(" "+SEP+" "," \\\n").replace(" "+SEPSFA+" "," \\\n")
180 def valid_step (step):
181 return step != SEP and step != SEPSFA
183 # turn off the sfa-related steps when build has skipped SFA
184 # this was originally for centos5 but is still valid
185 # for up to f12 as recent SFAs with sqlalchemy won't build before f14
187 def check_whether_build_has_sfa (rpms_url):
188 utils.header ("Checking if build provides SFA package...")
189 # warning, we're now building 'sface' so let's be a bit more picky
190 retcod=os.system ("curl --silent %s/ | grep -q sfa-"%rpms_url)
191 # full builds are expected to return with 0 here
193 utils.header("build does provide SFA")
195 # move all steps containing 'sfa' from default_steps to other_steps
196 utils.header("SFA package not found - removing steps with sfa or sfi")
197 sfa_steps= [ step for step in TestPlc.default_steps if step.find('sfa')>=0 or step.find("sfi")>=0 ]
198 TestPlc.other_steps += sfa_steps
199 for step in sfa_steps: TestPlc.default_steps.remove(step)
201 def __init__ (self,plc_spec,options):
202 self.plc_spec=plc_spec
204 self.test_ssh=TestSsh(self.plc_spec['host_box'],self.options.buildname)
205 self.vserverip=plc_spec['vserverip']
206 self.vservername=plc_spec['vservername']
207 self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
208 self.apiserver=TestApiserver(self.url,options.dry_run)
209 (self.ssh_node_boot_timeout,self.ssh_node_boot_silent)=plc_spec['ssh_node_boot_timers']
210 (self.ssh_node_debug_timeout,self.ssh_node_debug_silent)=plc_spec['ssh_node_debug_timers']
212 def has_addresses_api (self):
213 return self.apiserver.has_method('AddIpAddress')
216 name=self.plc_spec['name']
217 return "%s.%s"%(name,self.vservername)
220 return self.plc_spec['host_box']
223 return self.test_ssh.is_local()
225 # define the API methods on this object through xmlrpc
226 # would help, but not strictly necessary
230 def actual_command_in_guest (self,command, backslash=False):
231 raw1=self.host_to_guest(command)
232 raw2=self.test_ssh.actual_command(raw1,dry_run=self.options.dry_run, backslash=backslash)
235 def start_guest (self):
236 return utils.system(self.test_ssh.actual_command(self.start_guest_in_host(),dry_run=self.options.dry_run))
238 def stop_guest (self):
239 return utils.system(self.test_ssh.actual_command(self.stop_guest_in_host(),dry_run=self.options.dry_run))
241 def run_in_guest (self,command,backslash=False):
242 raw=self.actual_command_in_guest(command,backslash)
243 return utils.system(raw)
245 def run_in_host (self,command):
246 return self.test_ssh.run_in_buildname(command, dry_run=self.options.dry_run)
248 #command gets run in the plc's vm
249 def host_to_guest(self,command):
250 # need to backslash stuff here and not later on
251 backslashed_command=TestSsh.backslash_shell_specials(command)
252 # f14 still needs some extra help
253 if self.options.fcdistro == 'f14':
254 raw="virsh -c lxc:/// lxc-enter-namespace %s /bin/bash -c \\'PATH=/bin:/sbin:/usr/bin:/usr/sbin %s\\'" %(self.vservername,backslashed_command)
256 raw="virsh -c lxc:/// lxc-enter-namespace %s /bin/bash -c \\'%s\\'" %(self.vservername,backslashed_command)
259 # this /vservers thing is legacy...
260 def vm_root_in_host(self):
261 return "/vservers/%s/"%(self.vservername)
263 def vm_timestamp_path (self):
264 return "/vservers/%s/%s.timestamp"%(self.vservername,self.vservername)
266 #start/stop the vserver
267 def start_guest_in_host(self):
268 return "virsh -c lxc:/// start %s"%(self.vservername)
270 def stop_guest_in_host(self):
271 return "virsh -c lxc:/// destroy %s"%(self.vservername)
274 def run_in_guest_piped (self,local,remote):
275 return utils.system(local+" | "+self.test_ssh.actual_command(self.host_to_guest(remote),keep_stdin=True))
277 def yum_check_installed (self, rpms):
278 if isinstance (rpms, list):
280 return self.run_in_guest("rpm -q %s"%rpms)==0
282 # does a yum install in the vs, ignore yum retcod, check with rpm
283 def yum_install (self, rpms):
284 if isinstance (rpms, list):
286 self.run_in_guest("yum -y install %s"%rpms)
287 # yum-complete-transaction comes with yum-utils, that is in vtest.pkgs
288 self.run_in_guest("yum-complete-transaction -y")
289 return self.yum_check_installed (rpms)
291 def auth_root (self):
292 return {'Username':self.plc_spec['PLC_ROOT_USER'],
293 'AuthMethod':'password',
294 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
295 'Role' : self.plc_spec['role']
297 def locate_site (self,sitename):
298 for site in self.plc_spec['sites']:
299 if site['site_fields']['name'] == sitename:
301 if site['site_fields']['login_base'] == sitename:
303 raise Exception,"Cannot locate site %s"%sitename
305 def locate_node (self,nodename):
306 for site in self.plc_spec['sites']:
307 for node in site['nodes']:
308 if node['name'] == nodename:
310 raise Exception,"Cannot locate node %s"%nodename
312 def locate_hostname (self,hostname):
313 for site in self.plc_spec['sites']:
314 for node in site['nodes']:
315 if node['node_fields']['hostname'] == hostname:
317 raise Exception,"Cannot locate hostname %s"%hostname
319 def locate_key (self,key_name):
320 for key in self.plc_spec['keys']:
321 if key['key_name'] == key_name:
323 raise Exception,"Cannot locate key %s"%key_name
325 def locate_private_key_from_key_names (self, key_names):
326 # locate the first avail. key
328 for key_name in key_names:
329 key_spec=self.locate_key(key_name)
330 test_key=TestKey(self,key_spec)
331 publickey=test_key.publicpath()
332 privatekey=test_key.privatepath()
333 if os.path.isfile(publickey) and os.path.isfile(privatekey):
335 if found: return privatekey
338 def locate_slice (self, slicename):
339 for slice in self.plc_spec['slices']:
340 if slice['slice_fields']['name'] == slicename:
342 raise Exception,"Cannot locate slice %s"%slicename
344 def all_sliver_objs (self):
346 for slice_spec in self.plc_spec['slices']:
347 slicename = slice_spec['slice_fields']['name']
348 for nodename in slice_spec['nodenames']:
349 result.append(self.locate_sliver_obj (nodename,slicename))
352 def locate_sliver_obj (self,nodename,slicename):
353 (site,node) = self.locate_node(nodename)
354 slice = self.locate_slice (slicename)
356 test_site = TestSite (self, site)
357 test_node = TestNode (self, test_site,node)
358 # xxx the slice site is assumed to be the node site - mhh - probably harmless
359 test_slice = TestSlice (self, test_site, slice)
360 return TestSliver (self, test_node, test_slice)
362 def locate_first_node(self):
363 nodename=self.plc_spec['slices'][0]['nodenames'][0]
364 (site,node) = self.locate_node(nodename)
365 test_site = TestSite (self, site)
366 test_node = TestNode (self, test_site,node)
369 def locate_first_sliver (self):
370 slice_spec=self.plc_spec['slices'][0]
371 slicename=slice_spec['slice_fields']['name']
372 nodename=slice_spec['nodenames'][0]
373 return self.locate_sliver_obj(nodename,slicename)
375 # all different hostboxes used in this plc
376 def get_BoxNodes(self):
377 # maps on sites and nodes, return [ (host_box,test_node) ]
379 for site_spec in self.plc_spec['sites']:
380 test_site = TestSite (self,site_spec)
381 for node_spec in site_spec['nodes']:
382 test_node = TestNode (self, test_site, node_spec)
383 if not test_node.is_real():
384 tuples.append( (test_node.host_box(),test_node) )
385 # transform into a dict { 'host_box' -> [ test_node .. ] }
387 for (box,node) in tuples:
388 if not result.has_key(box):
391 result[box].append(node)
394 # a step for checking this stuff
395 def show_boxes (self):
396 'print summary of nodes location'
397 for (box,nodes) in self.get_BoxNodes().iteritems():
398 print box,":"," + ".join( [ node.name() for node in nodes ] )
401 # make this a valid step
402 def qemu_kill_all(self):
403 'kill all qemu instances on the qemu boxes involved by this setup'
404 # this is the brute force version, kill all qemus on that host box
405 for (box,nodes) in self.get_BoxNodes().iteritems():
406 # pass the first nodename, as we don't push template-qemu on testboxes
407 nodedir=nodes[0].nodedir()
408 TestBoxQemu(box,self.options.buildname).qemu_kill_all(nodedir)
411 # make this a valid step
412 def qemu_list_all(self):
413 'list all qemu instances on the qemu boxes involved by this setup'
414 for (box,nodes) in self.get_BoxNodes().iteritems():
415 # this is the brute force version, kill all qemus on that host box
416 TestBoxQemu(box,self.options.buildname).qemu_list_all()
419 # kill only the qemus related to this test
420 def qemu_list_mine(self):
421 'list qemu instances for our nodes'
422 for (box,nodes) in self.get_BoxNodes().iteritems():
423 # the fine-grain version
428 # kill only the qemus related to this test
429 def qemu_clean_mine(self):
430 'cleanup (rm -rf) qemu instances for our nodes'
431 for (box,nodes) in self.get_BoxNodes().iteritems():
432 # the fine-grain version
437 # kill only the right qemus
438 def qemu_kill_mine(self):
439 'kill the qemu instances for our nodes'
440 for (box,nodes) in self.get_BoxNodes().iteritems():
441 # the fine-grain version
446 #################### display config
448 "show test configuration after localization"
453 # uggly hack to make sure 'run export' only reports about the 1st plc
454 # to avoid confusion - also we use 'inri_slice1' in various aliases..
457 "print cut'n paste-able stuff to export env variables to your shell"
458 # guess local domain from hostname
459 if TestPlc.exported_id>1:
460 print "export GUESTHOSTNAME%d=%s"%(TestPlc.exported_id,self.plc_spec['vservername'])
462 TestPlc.exported_id+=1
463 domain=socket.gethostname().split('.',1)[1]
464 fqdn="%s.%s"%(self.plc_spec['host_box'],domain)
465 print "export BUILD=%s"%self.options.buildname
466 print "export PLCHOSTLXC=%s"%fqdn
467 print "export GUESTNAME=%s"%self.plc_spec['vservername']
468 vplcname=self.plc_spec['vservername'].split('-')[-1]
469 print "export GUESTHOSTNAME=%s.%s"%(vplcname,domain)
470 # find hostname of first node
471 (hostname,qemubox) = self.all_node_infos()[0]
472 print "export KVMHOST=%s.%s"%(qemubox,domain)
473 print "export NODE=%s"%(hostname)
477 always_display_keys=['PLC_WWW_HOST','nodes','sites',]
478 def show_pass (self,passno):
479 for (key,val) in self.plc_spec.iteritems():
480 if not self.options.verbose and key not in TestPlc.always_display_keys: continue
484 self.display_site_spec(site)
485 for node in site['nodes']:
486 self.display_node_spec(node)
487 elif key=='initscripts':
488 for initscript in val:
489 self.display_initscript_spec (initscript)
492 self.display_slice_spec (slice)
495 self.display_key_spec (key)
497 if key not in ['sites','initscripts','slices','keys', 'sfa']:
498 print '+ ',key,':',val
500 def display_site_spec (self,site):
501 print '+ ======== site',site['site_fields']['name']
502 for (k,v) in site.iteritems():
503 if not self.options.verbose and k not in TestPlc.always_display_keys: continue
506 print '+ ','nodes : ',
508 print node['node_fields']['hostname'],'',
514 print user['name'],'',
516 elif k == 'site_fields':
517 print '+ login_base',':',v['login_base']
518 elif k == 'address_fields':
524 def display_initscript_spec (self,initscript):
525 print '+ ======== initscript',initscript['initscript_fields']['name']
527 def display_key_spec (self,key):
528 print '+ ======== key',key['key_name']
530 def display_slice_spec (self,slice):
531 print '+ ======== slice',slice['slice_fields']['name']
532 for (k,v) in slice.iteritems():
545 elif k=='slice_fields':
546 print '+ fields',':',
547 print 'max_nodes=',v['max_nodes'],
552 def display_node_spec (self,node):
553 print "+ node=%s host_box=%s"%(node['name'],node['host_box']),
554 print "hostname=",node['node_fields']['hostname'],
555 print "ip=",node['interface_fields']['ip']
556 if self.options.verbose:
557 utils.pprint("node details",node,depth=3)
559 # another entry point for just showing the boxes involved
560 def display_mapping (self):
561 TestPlc.display_mapping_plc(self.plc_spec)
565 def display_mapping_plc (plc_spec):
566 print '+ MyPLC',plc_spec['name']
567 # WARNING this would not be right for lxc-based PLC's - should be harmless though
568 print '+\tvserver address = root@%s:/vservers/%s'%(plc_spec['host_box'],plc_spec['vservername'])
569 print '+\tIP = %s/%s'%(plc_spec['PLC_API_HOST'],plc_spec['vserverip'])
570 for site_spec in plc_spec['sites']:
571 for node_spec in site_spec['nodes']:
572 TestPlc.display_mapping_node(node_spec)
575 def display_mapping_node (node_spec):
576 print '+ NODE %s'%(node_spec['name'])
577 print '+\tqemu box %s'%node_spec['host_box']
578 print '+\thostname=%s'%node_spec['node_fields']['hostname']
580 # write a timestamp in /vservers/<>.timestamp
581 # cannot be inside the vserver, that causes vserver .. build to cough
582 def timestamp_vs (self):
583 "Create a timestamp to remember creation date for this plc"
585 # TODO-lxc check this one
586 # a first approx. is to store the timestamp close to the VM root like vs does
587 stamp_path=self.vm_timestamp_path ()
588 stamp_dir = os.path.dirname (stamp_path)
589 utils.system(self.test_ssh.actual_command("mkdir -p %s"%stamp_dir))
590 return utils.system(self.test_ssh.actual_command("echo %d > %s"%(now,stamp_path)))==0
592 # this is called inconditionnally at the beginning of the test sequence
593 # just in case this is a rerun, so if the vm is not running it's fine
595 "vserver delete the test myplc"
596 stamp_path=self.vm_timestamp_path()
597 self.run_in_host("rm -f %s"%stamp_path)
598 self.run_in_host("virsh -c lxc:// destroy %s"%self.vservername)
599 self.run_in_host("virsh -c lxc:// undefine %s"%self.vservername)
600 self.run_in_host("rm -fr /vservers/%s"%self.vservername)
604 # historically the build was being fetched by the tests
605 # now the build pushes itself as a subdir of the tests workdir
606 # so that the tests do not have to worry about extracting the build (svn, git, or whatever)
607 def vs_create (self):
608 "vserver creation (no install done)"
609 # push the local build/ dir to the testplc box
611 # a full path for the local calls
612 build_dir=os.path.dirname(sys.argv[0])
613 # sometimes this is empty - set to "." in such a case
614 if not build_dir: build_dir="."
615 build_dir += "/build"
617 # use a standard name - will be relative to remote buildname
619 # remove for safety; do *not* mkdir first, otherwise we end up with build/build/
620 self.test_ssh.rmdir(build_dir)
621 self.test_ssh.copy(build_dir,recursive=True)
622 # the repo url is taken from arch-rpms-url
623 # with the last step (i386) removed
624 repo_url = self.options.arch_rpms_url
625 for level in [ 'arch' ]:
626 repo_url = os.path.dirname(repo_url)
628 # invoke initvm (drop support for vs)
629 script="lbuild-initvm.sh"
631 # pass the vbuild-nightly options to [lv]test-initvm
632 script_options += " -p %s"%self.options.personality
633 script_options += " -d %s"%self.options.pldistro
634 script_options += " -f %s"%self.options.fcdistro
635 script_options += " -r %s"%repo_url
636 vserver_name = self.vservername
638 vserver_hostname=socket.gethostbyaddr(self.vserverip)[0]
639 script_options += " -n %s"%vserver_hostname
641 print "Cannot reverse lookup %s"%self.vserverip
642 print "This is considered fatal, as this might pollute the test results"
644 create_vserver="%(build_dir)s/%(script)s %(script_options)s %(vserver_name)s"%locals()
645 return self.run_in_host(create_vserver) == 0
648 def plc_install(self):
649 "yum install myplc, noderepo, and the plain bootstrapfs"
651 # workaround for getting pgsql8.2 on centos5
652 if self.options.fcdistro == "centos5":
653 self.run_in_guest("rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm")
656 if self.options.personality == "linux32":
658 elif self.options.personality == "linux64":
661 raise Exception, "Unsupported personality %r"%self.options.personality
662 nodefamily="%s-%s-%s"%(self.options.pldistro,self.options.fcdistro,arch)
665 pkgs_list.append ("slicerepo-%s"%nodefamily)
666 pkgs_list.append ("myplc")
667 pkgs_list.append ("noderepo-%s"%nodefamily)
668 pkgs_list.append ("nodeimage-%s-plain"%nodefamily)
669 pkgs_string=" ".join(pkgs_list)
670 return self.yum_install (pkgs_list)
673 def mod_python(self):
674 """yum install mod_python, useful on f18 and above so as to avoid broken wsgi"""
675 return self.yum_install ( [ 'mod_python' ] )
678 def plc_configure(self):
680 tmpname='%s.plc-config-tty'%(self.name())
681 fileconf=open(tmpname,'w')
682 for var in [ 'PLC_NAME',
687 'PLC_MAIL_SUPPORT_ADDRESS',
690 # Above line was added for integrating SFA Testing
696 'PLC_RESERVATION_GRANULARITY',
698 'PLC_OMF_XMPP_SERVER',
701 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
702 fileconf.write('w\n')
703 fileconf.write('q\n')
705 utils.system('cat %s'%tmpname)
706 self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
707 utils.system('rm %s'%tmpname)
710 # f14 is a bit odd in this respect, although this worked fine in guests up to f18
711 # however using a vplc guest under f20 requires this trick
712 # the symptom is this: service plc start
713 # Starting plc (via systemctl): Failed to get D-Bus connection: \
714 # Failed to connect to socket /org/freedesktop/systemd1/private: Connection refused
715 # weird thing is the doc says f14 uses upstart by default and not systemd
716 # so this sounds kind of harmless
718 "service plc start (use a special trick to set SYSTEMCTL_SKIP_REDIRECT on f14)"
719 if self.options.fcdistro != 'f14':
720 self.run_in_guest ("service plc start")
722 # patch /sbin/service so it does not reset environment
723 # this is because our own scripts in turn call service
724 self.run_in_guest ("sed -i -e 's,env -i ,,' /sbin/service")
725 self.run_in_guest("SYSTEMCTL_SKIP_REDIRECT=true /etc/init.d/plc start")
726 # retcod of service is not meaningful
730 "service plc stop (use a special trick to set SYSTEMCTL_SKIP_REDIRECT on f14)"
731 if self.options.fcdistro != 'f14':
732 self.run_in_guest ("service plc stop")
734 # patch /sbin/service so it does not reset environment
735 # this is because our own scripts in turn call service
736 self.run_in_guest ("sed -i -e 's,env -i ,,' /sbin/service")
737 self.run_in_guest("SYSTEMCTL_SKIP_REDIRECT=true /etc/init.d/plc stop")
738 # retcod of service is not meaningful
742 "start the PLC vserver"
747 "stop the PLC vserver"
751 # stores the keys from the config for further use
752 def keys_store(self):
753 "stores test users ssh keys in keys/"
754 for key_spec in self.plc_spec['keys']:
755 TestKey(self,key_spec).store_key()
758 def keys_clean(self):
759 "removes keys cached in keys/"
760 utils.system("rm -rf ./keys")
763 # fetches the ssh keys in the plc's /etc/planetlab and stores them in keys/
764 # for later direct access to the nodes
765 def keys_fetch(self):
766 "gets ssh keys in /etc/planetlab/ and stores them locally in keys/"
768 if not os.path.isdir(dir):
770 vservername=self.vservername
771 vm_root=self.vm_root_in_host()
773 prefix = 'debug_ssh_key'
774 for ext in [ 'pub', 'rsa' ] :
775 src="%(vm_root)s/etc/planetlab/%(prefix)s.%(ext)s"%locals()
776 dst="keys/%(vservername)s-debug.%(ext)s"%locals()
777 if self.test_ssh.fetch(src,dst) != 0: overall=False
781 "create sites with PLCAPI"
782 return self.do_sites()
784 def delete_sites (self):
785 "delete sites with PLCAPI"
786 return self.do_sites(action="delete")
788 def do_sites (self,action="add"):
789 for site_spec in self.plc_spec['sites']:
790 test_site = TestSite (self,site_spec)
791 if (action != "add"):
792 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
793 test_site.delete_site()
794 # deleted with the site
795 #test_site.delete_users()
798 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
799 test_site.create_site()
800 test_site.create_users()
803 def delete_all_sites (self):
804 "Delete all sites in PLC, and related objects"
805 print 'auth_root',self.auth_root()
806 sites = self.apiserver.GetSites(self.auth_root(), {}, ['site_id','login_base'])
808 # keep automatic site - otherwise we shoot in our own foot, root_auth is not valid anymore
809 if site['login_base']==self.plc_spec['PLC_SLICE_PREFIX']: continue
810 site_id=site['site_id']
811 print 'Deleting site_id',site_id
812 self.apiserver.DeleteSite(self.auth_root(),site_id)
816 "create nodes with PLCAPI"
817 return self.do_nodes()
818 def delete_nodes (self):
819 "delete nodes with PLCAPI"
820 return self.do_nodes(action="delete")
822 def do_nodes (self,action="add"):
823 for site_spec in self.plc_spec['sites']:
824 test_site = TestSite (self,site_spec)
826 utils.header("Deleting nodes in site %s"%test_site.name())
827 for node_spec in site_spec['nodes']:
828 test_node=TestNode(self,test_site,node_spec)
829 utils.header("Deleting %s"%test_node.name())
830 test_node.delete_node()
832 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
833 for node_spec in site_spec['nodes']:
834 utils.pprint('Creating node %s'%node_spec,node_spec)
835 test_node = TestNode (self,test_site,node_spec)
836 test_node.create_node ()
839 def nodegroups (self):
840 "create nodegroups with PLCAPI"
841 return self.do_nodegroups("add")
842 def delete_nodegroups (self):
843 "delete nodegroups with PLCAPI"
844 return self.do_nodegroups("delete")
848 def translate_timestamp (start,grain,timestamp):
849 if timestamp < TestPlc.YEAR: return start+timestamp*grain
850 else: return timestamp
853 def timestamp_printable (timestamp):
854 return time.strftime('%m-%d %H:%M:%S UTC',time.gmtime(timestamp))
857 "create leases (on reservable nodes only, use e.g. run -c default -c resa)"
859 grain=self.apiserver.GetLeaseGranularity(self.auth_root())
860 print 'API answered grain=',grain
861 start=(now/grain)*grain
863 # find out all nodes that are reservable
864 nodes=self.all_reservable_nodenames()
866 utils.header ("No reservable node found - proceeding without leases")
869 # attach them to the leases as specified in plc_specs
870 # this is where the 'leases' field gets interpreted as relative of absolute
871 for lease_spec in self.plc_spec['leases']:
872 # skip the ones that come with a null slice id
873 if not lease_spec['slice']: continue
874 lease_spec['t_from']=TestPlc.translate_timestamp(start,grain,lease_spec['t_from'])
875 lease_spec['t_until']=TestPlc.translate_timestamp(start,grain,lease_spec['t_until'])
876 lease_addition=self.apiserver.AddLeases(self.auth_root(),nodes,
877 lease_spec['slice'],lease_spec['t_from'],lease_spec['t_until'])
878 if lease_addition['errors']:
879 utils.header("Cannot create leases, %s"%lease_addition['errors'])
882 utils.header('Leases on nodes %r for %s from %d (%s) until %d (%s)'%\
883 (nodes,lease_spec['slice'],
884 lease_spec['t_from'],TestPlc.timestamp_printable(lease_spec['t_from']),
885 lease_spec['t_until'],TestPlc.timestamp_printable(lease_spec['t_until'])))
889 def delete_leases (self):
890 "remove all leases in the myplc side"
891 lease_ids= [ l['lease_id'] for l in self.apiserver.GetLeases(self.auth_root())]
892 utils.header("Cleaning leases %r"%lease_ids)
893 self.apiserver.DeleteLeases(self.auth_root(),lease_ids)
896 def list_leases (self):
897 "list all leases known to the myplc"
898 leases = self.apiserver.GetLeases(self.auth_root())
901 current=l['t_until']>=now
902 if self.options.verbose or current:
903 utils.header("%s %s from %s until %s"%(l['hostname'],l['name'],
904 TestPlc.timestamp_printable(l['t_from']),
905 TestPlc.timestamp_printable(l['t_until'])))
908 # create nodegroups if needed, and populate
909 def do_nodegroups (self, action="add"):
910 # 1st pass to scan contents
912 for site_spec in self.plc_spec['sites']:
913 test_site = TestSite (self,site_spec)
914 for node_spec in site_spec['nodes']:
915 test_node=TestNode (self,test_site,node_spec)
916 if node_spec.has_key('nodegroups'):
917 nodegroupnames=node_spec['nodegroups']
918 if isinstance(nodegroupnames,StringTypes):
919 nodegroupnames = [ nodegroupnames ]
920 for nodegroupname in nodegroupnames:
921 if not groups_dict.has_key(nodegroupname):
922 groups_dict[nodegroupname]=[]
923 groups_dict[nodegroupname].append(test_node.name())
924 auth=self.auth_root()
926 for (nodegroupname,group_nodes) in groups_dict.iteritems():
928 print 'nodegroups:','dealing with nodegroup',nodegroupname,'on nodes',group_nodes
929 # first, check if the nodetagtype is here
930 tag_types = self.apiserver.GetTagTypes(auth,{'tagname':nodegroupname})
932 tag_type_id = tag_types[0]['tag_type_id']
934 tag_type_id = self.apiserver.AddTagType(auth,
935 {'tagname':nodegroupname,
936 'description': 'for nodegroup %s'%nodegroupname,
938 print 'located tag (type)',nodegroupname,'as',tag_type_id
940 nodegroups = self.apiserver.GetNodeGroups (auth, {'groupname':nodegroupname})
942 self.apiserver.AddNodeGroup(auth, nodegroupname, tag_type_id, 'yes')
943 print 'created nodegroup',nodegroupname,'from tagname',nodegroupname,'and value','yes'
944 # set node tag on all nodes, value='yes'
945 for nodename in group_nodes:
947 self.apiserver.AddNodeTag(auth, nodename, nodegroupname, "yes")
949 traceback.print_exc()
950 print 'node',nodename,'seems to already have tag',nodegroupname
953 expect_yes = self.apiserver.GetNodeTags(auth,
954 {'hostname':nodename,
955 'tagname':nodegroupname},
956 ['value'])[0]['value']
957 if expect_yes != "yes":
958 print 'Mismatch node tag on node',nodename,'got',expect_yes
961 if not self.options.dry_run:
962 print 'Cannot find tag',nodegroupname,'on node',nodename
966 print 'cleaning nodegroup',nodegroupname
967 self.apiserver.DeleteNodeGroup(auth,nodegroupname)
969 traceback.print_exc()
973 # a list of TestNode objs
974 def all_nodes (self):
976 for site_spec in self.plc_spec['sites']:
977 test_site = TestSite (self,site_spec)
978 for node_spec in site_spec['nodes']:
979 nodes.append(TestNode (self,test_site,node_spec))
982 # return a list of tuples (nodename,qemuname)
983 def all_node_infos (self) :
985 for site_spec in self.plc_spec['sites']:
986 node_infos += [ (node_spec['node_fields']['hostname'],node_spec['host_box']) \
987 for node_spec in site_spec['nodes'] ]
990 def all_nodenames (self): return [ x[0] for x in self.all_node_infos() ]
991 def all_reservable_nodenames (self):
993 for site_spec in self.plc_spec['sites']:
994 for node_spec in site_spec['nodes']:
995 node_fields=node_spec['node_fields']
996 if 'node_type' in node_fields and node_fields['node_type']=='reservable':
997 res.append(node_fields['hostname'])
1000 # silent_minutes : during the first <silent_minutes> minutes nothing gets printed
1001 def nodes_check_boot_state (self, target_boot_state, timeout_minutes, silent_minutes,period_seconds=15):
1002 if self.options.dry_run:
1006 class CompleterTaskBootState (CompleterTask):
1007 def __init__ (self, test_plc,hostname):
1008 self.test_plc=test_plc
1009 self.hostname=hostname
1010 self.last_boot_state='undef'
1011 def actual_run (self):
1013 node = self.test_plc.apiserver.GetNodes(self.test_plc.auth_root(), [ self.hostname ],
1015 self.last_boot_state = node['boot_state']
1016 return self.last_boot_state == target_boot_state
1020 return "CompleterTaskBootState with node %s"%self.hostname
1021 def failure_message (self):
1022 return "node %s in state %s - expected %s"%(self.hostname,self.last_boot_state,target_boot_state)
1024 timeout = timedelta(minutes=timeout_minutes)
1025 graceout = timedelta(minutes=silent_minutes)
1026 period = timedelta(seconds=period_seconds)
1027 # the nodes that haven't checked yet - start with a full list and shrink over time
1028 utils.header("checking nodes boot state (expected %s)"%target_boot_state)
1029 tasks = [ CompleterTaskBootState (self,hostname) \
1030 for (hostname,_) in self.all_node_infos() ]
1031 return Completer (tasks).run (timeout, graceout, period)
1033 def nodes_booted(self):
1034 return self.nodes_check_boot_state('boot',timeout_minutes=30,silent_minutes=28)
1036 def check_nodes_ssh(self,debug,timeout_minutes,silent_minutes,period_seconds=15):
1037 class CompleterTaskNodeSsh (CompleterTask):
1038 def __init__ (self, hostname, qemuname, boot_state, local_key):
1039 self.hostname=hostname
1040 self.qemuname=qemuname
1041 self.boot_state=boot_state
1042 self.local_key=local_key
1043 def run (self, silent):
1044 command = TestSsh (self.hostname,key=self.local_key).actual_command("hostname;uname -a")
1045 return utils.system (command, silent=silent)==0
1046 def failure_message (self):
1047 return "Cannot reach %s @ %s in %s mode"%(self.hostname, self.qemuname, self.boot_state)
1050 timeout = timedelta(minutes=timeout_minutes)
1051 graceout = timedelta(minutes=silent_minutes)
1052 period = timedelta(seconds=period_seconds)
1053 vservername=self.vservername
1056 local_key = "keys/%(vservername)s-debug.rsa"%locals()
1059 local_key = "keys/key_admin.rsa"
1060 utils.header("checking ssh access to nodes (expected in %s mode)"%message)
1061 node_infos = self.all_node_infos()
1062 tasks = [ CompleterTaskNodeSsh (nodename, qemuname, message, local_key) \
1063 for (nodename,qemuname) in node_infos ]
1064 return Completer (tasks).run (timeout, graceout, period)
1066 def ssh_node_debug(self):
1067 "Tries to ssh into nodes in debug mode with the debug ssh key"
1068 return self.check_nodes_ssh(debug=True,
1069 timeout_minutes=self.ssh_node_debug_timeout,
1070 silent_minutes=self.ssh_node_debug_silent)
1072 def ssh_node_boot(self):
1073 "Tries to ssh into nodes in production mode with the root ssh key"
1074 return self.check_nodes_ssh(debug=False,
1075 timeout_minutes=self.ssh_node_boot_timeout,
1076 silent_minutes=self.ssh_node_boot_silent)
1078 def node_bmlogs(self):
1079 "Checks that there's a non-empty dir. /var/log/bm/raw"
1080 return utils.system(self.actual_command_in_guest("ls /var/log/bm/raw"))==0
1083 def qemu_local_init (self): pass
1085 def bootcd (self): pass
1087 def qemu_local_config (self): pass
1089 def nodestate_reinstall (self): pass
1091 def nodestate_safeboot (self): pass
1093 def nodestate_boot (self): pass
1095 def nodestate_show (self): pass
1097 def qemu_export (self): pass
1099 ### check hooks : invoke scripts from hooks/{node,slice}
1100 def check_hooks_node (self):
1101 return self.locate_first_node().check_hooks()
1102 def check_hooks_sliver (self) :
1103 return self.locate_first_sliver().check_hooks()
1105 def check_hooks (self):
1106 "runs unit tests in the node and slice contexts - see hooks/{node,slice}"
1107 return self.check_hooks_node() and self.check_hooks_sliver()
1110 def do_check_initscripts(self):
1111 class CompleterTaskInitscript (CompleterTask):
1112 def __init__ (self, test_sliver, stamp):
1113 self.test_sliver=test_sliver
1115 def actual_run (self):
1116 return self.test_sliver.check_initscript_stamp (self.stamp)
1118 return "initscript checker for %s"%self.test_sliver.name()
1119 def failure_message (self):
1120 return "initscript stamp %s not found in sliver %s"%(self.stamp,self.test_sliver.name())
1123 for slice_spec in self.plc_spec['slices']:
1124 if not slice_spec.has_key('initscriptstamp'):
1126 stamp=slice_spec['initscriptstamp']
1127 slicename=slice_spec['slice_fields']['name']
1128 for nodename in slice_spec['nodenames']:
1129 print 'nodename',nodename,'slicename',slicename,'stamp',stamp
1130 (site,node) = self.locate_node (nodename)
1131 # xxx - passing the wrong site - probably harmless
1132 test_site = TestSite (self,site)
1133 test_slice = TestSlice (self,test_site,slice_spec)
1134 test_node = TestNode (self,test_site,node)
1135 test_sliver = TestSliver (self, test_node, test_slice)
1136 tasks.append ( CompleterTaskInitscript (test_sliver, stamp))
1137 return Completer (tasks).run (timedelta(minutes=5), timedelta(minutes=4), timedelta(seconds=10))
1139 def check_initscripts(self):
1140 "check that the initscripts have triggered"
1141 return self.do_check_initscripts()
1143 def initscripts (self):
1144 "create initscripts with PLCAPI"
1145 for initscript in self.plc_spec['initscripts']:
1146 utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
1147 self.apiserver.AddInitScript(self.auth_root(),initscript['initscript_fields'])
1150 def delete_initscripts (self):
1151 "delete initscripts with PLCAPI"
1152 for initscript in self.plc_spec['initscripts']:
1153 initscript_name = initscript['initscript_fields']['name']
1154 print('Attempting to delete %s in plc %s'%(initscript_name,self.plc_spec['name']))
1156 self.apiserver.DeleteInitScript(self.auth_root(),initscript_name)
1157 print initscript_name,'deleted'
1159 print 'deletion went wrong - probably did not exist'
1164 "create slices with PLCAPI"
1165 return self.do_slices(action="add")
1167 def delete_slices (self):
1168 "delete slices with PLCAPI"
1169 return self.do_slices(action="delete")
1171 def fill_slices (self):
1172 "add nodes in slices with PLCAPI"
1173 return self.do_slices(action="fill")
1175 def empty_slices (self):
1176 "remove nodes from slices with PLCAPI"
1177 return self.do_slices(action="empty")
1179 def do_slices (self, action="add"):
1180 for slice in self.plc_spec['slices']:
1181 site_spec = self.locate_site (slice['sitename'])
1182 test_site = TestSite(self,site_spec)
1183 test_slice=TestSlice(self,test_site,slice)
1184 if action == "delete":
1185 test_slice.delete_slice()
1186 elif action=="fill":
1187 test_slice.add_nodes()
1188 elif action=="empty":
1189 test_slice.delete_nodes()
1191 test_slice.create_slice()
1194 @slice_mapper__tasks(20,10,15)
1195 def ssh_slice(self): pass
1196 @slice_mapper__tasks(20,19,15)
1197 def ssh_slice_off (self): pass
1199 # use another name so we can exclude/ignore it from the tests on the nightly command line
1200 def ssh_slice_again(self): return self.ssh_slice()
1201 # note that simply doing ssh_slice_again=ssh_slice would kind od work too
1202 # but for some reason the ignore-wrapping thing would not
1205 def ssh_slice_basics(self): pass
1208 def check_vsys_defaults(self): pass
1211 def keys_clear_known_hosts (self): pass
1213 def plcapi_urls (self):
1214 return PlcapiUrlScanner (self.auth_root(),ip=self.vserverip).scan()
1216 def speed_up_slices (self):
1217 "tweak nodemanager settings on all nodes using a conf file"
1218 # create the template on the server-side
1219 template="%s.nodemanager"%self.name()
1220 template_file = open (template,"w")
1221 template_file.write('OPTIONS="-p 30 -r 11 -d"\n')
1222 template_file.close()
1223 in_vm="/var/www/html/PlanetLabConf/nodemanager"
1224 remote="%s/%s"%(self.vm_root_in_host(),in_vm)
1225 self.test_ssh.copy_abs(template,remote)
1227 self.apiserver.AddConfFile (self.auth_root(),
1228 {'dest':'/etc/sysconfig/nodemanager',
1229 'source':'PlanetLabConf/nodemanager',
1230 'postinstall_cmd':'service nm restart',})
1233 def debug_nodemanager (self):
1234 "sets verbose mode for nodemanager, and speeds up cycle even more (needs speed_up_slices first)"
1235 template="%s.nodemanager"%self.name()
1236 template_file = open (template,"w")
1237 template_file.write('OPTIONS="-p 10 -r 6 -v -d"\n')
1238 template_file.close()
1239 in_vm="/var/www/html/PlanetLabConf/nodemanager"
1240 remote="%s/%s"%(self.vm_root_in_host(),in_vm)
1241 self.test_ssh.copy_abs(template,remote)
1245 def qemu_start (self) : pass
1248 def timestamp_qemu (self) : pass
1250 # when a spec refers to a node possibly on another plc
1251 def locate_sliver_obj_cross (self, nodename, slicename, other_plcs):
1252 for plc in [ self ] + other_plcs:
1254 return plc.locate_sliver_obj (nodename, slicename)
1257 raise Exception, "Cannot locate sliver %s@%s among all PLCs"%(nodename,slicename)
1259 # implement this one as a cross step so that we can take advantage of different nodes
1260 # in multi-plcs mode
1261 def cross_check_tcp (self, other_plcs):
1262 "check TCP connectivity between 2 slices (or in loopback if only one is defined)"
1263 if 'tcp_specs' not in self.plc_spec or not self.plc_spec['tcp_specs']:
1264 utils.header ("check_tcp: no/empty config found")
1266 specs = self.plc_spec['tcp_specs']
1271 s_test_sliver = self.locate_sliver_obj_cross (spec['server_node'],spec['server_slice'],other_plcs)
1272 if not s_test_sliver.run_tcp_server(port,timeout=20):
1276 # idem for the client side
1277 c_test_sliver = self.locate_sliver_obj_cross (spec['client_node'],spec['client_slice'],other_plcs)
1278 # use nodename from locatesd sliver, unless 'client_connect' is set
1279 if 'client_connect' in spec:
1280 destination = spec['client_connect']
1282 destination=s_test_sliver.test_node.name()
1283 if not c_test_sliver.run_tcp_client(destination,port):
1287 # painfully enough, we need to allow for some time as netflow might show up last
1288 def check_system_slice (self):
1289 "all nodes: check that a system slice is alive"
1290 # netflow currently not working in the lxc distro
1291 # drl not built at all in the wtx distro
1292 # if we find either of them we're happy
1293 return self.check_netflow() or self.check_drl()
1296 def check_netflow (self): return self._check_system_slice ('netflow')
1297 def check_drl (self): return self._check_system_slice ('drl')
1299 # we have the slices up already here, so it should not take too long
1300 def _check_system_slice (self, slicename, timeout_minutes=5, period_seconds=15):
1301 class CompleterTaskSystemSlice (CompleterTask):
1302 def __init__ (self, test_node, dry_run):
1303 self.test_node=test_node
1304 self.dry_run=dry_run
1305 def actual_run (self):
1306 return self.test_node._check_system_slice (slicename, dry_run=self.dry_run)
1308 return "System slice %s @ %s"%(slicename, self.test_node.name())
1309 def failure_message (self):
1310 return "COULD not find system slice %s @ %s"%(slicename, self.test_node.name())
1311 timeout = timedelta(minutes=timeout_minutes)
1312 silent = timedelta (0)
1313 period = timedelta (seconds=period_seconds)
1314 tasks = [ CompleterTaskSystemSlice (test_node, self.options.dry_run) \
1315 for test_node in self.all_nodes() ]
1316 return Completer (tasks) . run (timeout, silent, period)
1318 def plcsh_stress_test (self):
1319 "runs PLCAPI stress test, that checks Add/Update/Delete on all types - preserves contents"
1320 # install the stress-test in the plc image
1321 location = "/usr/share/plc_api/plcsh_stress_test.py"
1322 remote="%s/%s"%(self.vm_root_in_host(),location)
1323 self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1325 command += " -- --check"
1326 if self.options.size == 1:
1327 command += " --tiny"
1328 return ( self.run_in_guest(command) == 0)
1330 # populate runs the same utility without slightly different options
1331 # in particular runs with --preserve (dont cleanup) and without --check
1332 # also it gets run twice, once with the --foreign option for creating fake foreign entries
1334 def sfa_install_all (self):
1335 "yum install sfa sfa-plc sfa-sfatables sfa-client"
1336 return self.yum_install ("sfa sfa-plc sfa-sfatables sfa-client")
1338 def sfa_install_core(self):
1340 return self.yum_install ("sfa")
1342 def sfa_install_plc(self):
1343 "yum install sfa-plc"
1344 return self.yum_install("sfa-plc")
1346 def sfa_install_sfatables(self):
1347 "yum install sfa-sfatables"
1348 return self.yum_install ("sfa-sfatables")
1350 # for some very odd reason, this sometimes fails with the following symptom
1351 # # yum install sfa-client
1352 # Setting up Install Process
1354 # Downloading Packages:
1355 # Running rpm_check_debug
1356 # Running Transaction Test
1357 # Transaction Test Succeeded
1358 # Running Transaction
1359 # Transaction couldn't start:
1360 # installing package sfa-client-2.1-7.onelab.2012.05.23.i686 needs 68KB on the / filesystem
1361 # [('installing package sfa-client-2.1-7.onelab.2012.05.23.i686 needs 68KB on the / filesystem', (9, '/', 69632L))]
1362 # even though in the same context I have
1363 # [2012.05.23--f14-32-sfastd1-1-vplc07] / # df -h
1364 # Filesystem Size Used Avail Use% Mounted on
1365 # /dev/hdv1 806G 264G 501G 35% /
1366 # none 16M 36K 16M 1% /tmp
1368 # so as a workaround, we first try yum install, and then invoke rpm on the cached rpm...
1369 def sfa_install_client(self):
1370 "yum install sfa-client"
1371 first_try=self.yum_install("sfa-client")
1372 if first_try: return True
1373 utils.header ("********** Regular yum failed - special workaround in place, 2nd chance")
1374 (code,cached_rpm_path)=utils.output_of(self.actual_command_in_guest('find /var/cache/yum -name sfa-client\*.rpm'))
1375 utils.header("rpm_path=<<%s>>"%rpm_path)
1377 self.run_in_guest("rpm -i %s"%cached_rpm_path)
1378 return self.yum_check_installed ("sfa-client")
1380 def sfa_dbclean(self):
1381 "thoroughly wipes off the SFA database"
1382 return self.run_in_guest("sfaadmin reg nuke")==0 or \
1383 self.run_in_guest("sfa-nuke.py")==0 or \
1384 self.run_in_guest("sfa-nuke-plc.py")==0
1386 def sfa_fsclean(self):
1387 "cleanup /etc/sfa/trusted_roots and /var/lib/sfa"
1388 self.run_in_guest("rm -rf /etc/sfa/trusted_roots /var/lib/sfa/authorities")
1391 def sfa_plcclean(self):
1392 "cleans the PLC entries that were created as a side effect of running the script"
1394 sfa_spec=self.plc_spec['sfa']
1396 for auth_sfa_spec in sfa_spec['auth_sfa_specs']:
1397 login_base=auth_sfa_spec['login_base']
1398 try: self.apiserver.DeleteSite (self.auth_root(),login_base)
1399 except: print "Site %s already absent from PLC db"%login_base
1401 for spec_name in ['pi_spec','user_spec']:
1402 user_spec=auth_sfa_spec[spec_name]
1403 username=user_spec['email']
1404 try: self.apiserver.DeletePerson(self.auth_root(),username)
1406 # this in fact is expected as sites delete their members
1407 #print "User %s already absent from PLC db"%username
1410 print "REMEMBER TO RUN sfa_import AGAIN"
1413 def sfa_uninstall(self):
1414 "uses rpm to uninstall sfa - ignore result"
1415 self.run_in_guest("rpm -e sfa sfa-sfatables sfa-client sfa-plc")
1416 self.run_in_guest("rm -rf /var/lib/sfa")
1417 self.run_in_guest("rm -rf /etc/sfa")
1418 self.run_in_guest("rm -rf /var/log/sfa_access.log /var/log/sfa_import_plc.log /var/log/sfa.daemon")
1420 self.run_in_guest("rpm -e --noscripts sfa-plc")
1423 ### run unit tests for SFA
1424 # NOTE: for some reason on f14/i386, yum install sfa-tests fails for no reason
1425 # Running Transaction
1426 # Transaction couldn't start:
1427 # installing package sfa-tests-1.0-21.onelab.i686 needs 204KB on the / filesystem
1428 # [('installing package sfa-tests-1.0-21.onelab.i686 needs 204KB on the / filesystem', (9, '/', 208896L))]
1429 # no matter how many Gbs are available on the testplc
1430 # could not figure out what's wrong, so...
1431 # if the yum install phase fails, consider the test is successful
1432 # other combinations will eventually run it hopefully
1433 def sfa_utest(self):
1434 "yum install sfa-tests and run SFA unittests"
1435 self.run_in_guest("yum -y install sfa-tests")
1436 # failed to install - forget it
1437 if self.run_in_guest("rpm -q sfa-tests")!=0:
1438 utils.header("WARNING: SFA unit tests failed to install, ignoring")
1440 return self.run_in_guest("/usr/share/sfa/tests/testAll.py")==0
1444 dirname="conf.%s"%self.plc_spec['name']
1445 if not os.path.isdir(dirname):
1446 utils.system("mkdir -p %s"%dirname)
1447 if not os.path.isdir(dirname):
1448 raise Exception,"Cannot create config dir for plc %s"%self.name()
1451 def conffile(self,filename):
1452 return "%s/%s"%(self.confdir(),filename)
1453 def confsubdir(self,dirname,clean,dry_run=False):
1454 subdirname="%s/%s"%(self.confdir(),dirname)
1456 utils.system("rm -rf %s"%subdirname)
1457 if not os.path.isdir(subdirname):
1458 utils.system("mkdir -p %s"%subdirname)
1459 if not dry_run and not os.path.isdir(subdirname):
1460 raise "Cannot create config subdir %s for plc %s"%(dirname,self.name())
1463 def conffile_clean (self,filename):
1464 filename=self.conffile(filename)
1465 return utils.system("rm -rf %s"%filename)==0
1468 def sfa_configure(self):
1469 "run sfa-config-tty"
1470 tmpname=self.conffile("sfa-config-tty")
1471 fileconf=open(tmpname,'w')
1472 for var in [ 'SFA_REGISTRY_ROOT_AUTH',
1473 'SFA_INTERFACE_HRN',
1474 'SFA_REGISTRY_LEVEL1_AUTH',
1475 'SFA_REGISTRY_HOST',
1476 'SFA_AGGREGATE_HOST',
1486 'SFA_GENERIC_FLAVOUR',
1487 'SFA_AGGREGATE_ENABLED',
1489 if self.plc_spec['sfa'].has_key(var):
1490 fileconf.write ('e %s\n%s\n'%(var,self.plc_spec['sfa'][var]))
1491 # the way plc_config handles booleans just sucks..
1494 if self.plc_spec['sfa'][var]: val='true'
1495 fileconf.write ('e %s\n%s\n'%(var,val))
1496 fileconf.write('w\n')
1497 fileconf.write('R\n')
1498 fileconf.write('q\n')
1500 utils.system('cat %s'%tmpname)
1501 self.run_in_guest_piped('cat %s'%tmpname,'sfa-config-tty')
1504 def aggregate_xml_line(self):
1505 port=self.plc_spec['sfa']['neighbours-port']
1506 return '<aggregate addr="%s" hrn="%s" port="%r"/>' % \
1507 (self.vserverip,self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH'],port)
1509 def registry_xml_line(self):
1510 return '<registry addr="%s" hrn="%s" port="12345"/>' % \
1511 (self.vserverip,self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH'])
1514 # a cross step that takes all other plcs in argument
1515 def cross_sfa_configure(self, other_plcs):
1516 "writes aggregates.xml and registries.xml that point to all other PLCs in the test"
1517 # of course with a single plc, other_plcs is an empty list
1520 agg_fname=self.conffile("agg.xml")
1521 file(agg_fname,"w").write("<aggregates>%s</aggregates>\n" % \
1522 " ".join([ plc.aggregate_xml_line() for plc in other_plcs ]))
1523 utils.header ("(Over)wrote %s"%agg_fname)
1524 reg_fname=self.conffile("reg.xml")
1525 file(reg_fname,"w").write("<registries>%s</registries>\n" % \
1526 " ".join([ plc.registry_xml_line() for plc in other_plcs ]))
1527 utils.header ("(Over)wrote %s"%reg_fname)
1528 return self.test_ssh.copy_abs(agg_fname,'/%s/etc/sfa/aggregates.xml'%self.vm_root_in_host())==0 \
1529 and self.test_ssh.copy_abs(reg_fname,'/%s/etc/sfa/registries.xml'%self.vm_root_in_host())==0
1531 def sfa_import(self):
1532 "use sfaadmin to import from plc"
1533 auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1535 self.run_in_guest('sfaadmin reg import_registry')==0
1536 # not needed anymore
1537 # self.run_in_guest('cp /etc/sfa/authorities/%s/%s.pkey /etc/sfa/authorities/server.key'%(auth,auth))
1539 def sfa_start(self):
1541 return self.run_in_guest('service sfa start')==0
1543 def sfi_configure(self):
1544 "Create /root/sfi on the plc side for sfi client configuration"
1545 if self.options.dry_run:
1546 utils.header("DRY RUN - skipping step")
1548 sfa_spec=self.plc_spec['sfa']
1549 # cannot use auth_sfa_mapper to pass dir_name
1550 for slice_spec in self.plc_spec['sfa']['auth_sfa_specs']:
1551 test_slice=TestAuthSfa(self,slice_spec)
1552 dir_basename=os.path.basename(test_slice.sfi_path())
1553 dir_name=self.confsubdir("dot-sfi/%s"%dir_basename,clean=True,dry_run=self.options.dry_run)
1554 test_slice.sfi_configure(dir_name)
1555 # push into the remote /root/sfi area
1556 location = test_slice.sfi_path()
1557 remote="%s/%s"%(self.vm_root_in_host(),location)
1558 self.test_ssh.mkdir(remote,abs=True)
1559 # need to strip last level or remote otherwise we get an extra dir level
1560 self.test_ssh.copy_abs(dir_name, os.path.dirname(remote), recursive=True)
1564 def sfi_clean (self):
1565 "clean up /root/sfi on the plc side"
1566 self.run_in_guest("rm -rf /root/sfi")
1570 def sfa_add_site (self): pass
1572 def sfa_add_pi (self): pass
1574 def sfa_add_user(self): pass
1576 def sfa_update_user(self): pass
1578 def sfa_add_slice(self): pass
1580 def sfa_renew_slice(self): pass
1582 def sfa_discover(self): pass
1584 def sfa_create_slice(self): pass
1586 def sfa_check_slice_plc(self): pass
1588 def sfa_update_slice(self): pass
1590 def sfi_list(self): pass
1592 def sfi_show(self): pass
1594 def ssh_slice_sfa(self): pass
1596 def sfa_delete_user(self): pass
1598 def sfa_delete_slice(self): pass
1602 self.run_in_guest('service sfa stop')==0
1605 def populate (self):
1606 "creates random entries in the PLCAPI"
1607 # install the stress-test in the plc image
1608 location = "/usr/share/plc_api/plcsh_stress_test.py"
1609 remote="%s/%s"%(self.vm_root_in_host(),location)
1610 self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1612 command += " -- --preserve --short-names"
1613 local = (self.run_in_guest(command) == 0);
1614 # second run with --foreign
1615 command += ' --foreign'
1616 remote = (self.run_in_guest(command) == 0);
1617 return ( local and remote)
1619 def gather_logs (self):
1620 "gets all possible logs from plc's/qemu node's/slice's for future reference"
1621 # (1.a) get the plc's /var/log/ and store it locally in logs/myplc.var-log.<plcname>/*
1622 # (1.b) get the plc's /var/lib/pgsql/data/pg_log/ -> logs/myplc.pgsql-log.<plcname>/*
1623 # (1.c) get the plc's /root/sfi -> logs/sfi.<plcname>/
1624 # (2) get all the nodes qemu log and store it as logs/node.qemu.<node>.log
1625 # (3) get the nodes /var/log and store is as logs/node.var-log.<node>/*
1626 # (4) as far as possible get the slice's /var/log as logs/sliver.var-log.<sliver>/*
1628 print "-------------------- TestPlc.gather_logs : PLC's /var/log"
1629 self.gather_var_logs ()
1631 print "-------------------- TestPlc.gather_logs : PLC's /var/lib/psql/data/pg_log/"
1632 self.gather_pgsql_logs ()
1634 print "-------------------- TestPlc.gather_logs : PLC's /root/sfi/"
1635 self.gather_root_sfi ()
1637 print "-------------------- TestPlc.gather_logs : nodes's QEMU logs"
1638 for site_spec in self.plc_spec['sites']:
1639 test_site = TestSite (self,site_spec)
1640 for node_spec in site_spec['nodes']:
1641 test_node=TestNode(self,test_site,node_spec)
1642 test_node.gather_qemu_logs()
1644 print "-------------------- TestPlc.gather_logs : nodes's /var/log"
1645 self.gather_nodes_var_logs()
1647 print "-------------------- TestPlc.gather_logs : sample sliver's /var/log"
1648 self.gather_slivers_var_logs()
1651 def gather_slivers_var_logs(self):
1652 for test_sliver in self.all_sliver_objs():
1653 remote = test_sliver.tar_var_logs()
1654 utils.system("mkdir -p logs/sliver.var-log.%s"%test_sliver.name())
1655 command = remote + " | tar -C logs/sliver.var-log.%s -xf -"%test_sliver.name()
1656 utils.system(command)
1659 def gather_var_logs (self):
1660 utils.system("mkdir -p logs/myplc.var-log.%s"%self.name())
1661 to_plc = self.actual_command_in_guest("tar -C /var/log/ -cf - .")
1662 command = to_plc + "| tar -C logs/myplc.var-log.%s -xf -"%self.name()
1663 utils.system(command)
1664 command = "chmod a+r,a+x logs/myplc.var-log.%s/httpd"%self.name()
1665 utils.system(command)
1667 def gather_pgsql_logs (self):
1668 utils.system("mkdir -p logs/myplc.pgsql-log.%s"%self.name())
1669 to_plc = self.actual_command_in_guest("tar -C /var/lib/pgsql/data/pg_log/ -cf - .")
1670 command = to_plc + "| tar -C logs/myplc.pgsql-log.%s -xf -"%self.name()
1671 utils.system(command)
1673 def gather_root_sfi (self):
1674 utils.system("mkdir -p logs/sfi.%s"%self.name())
1675 to_plc = self.actual_command_in_guest("tar -C /root/sfi/ -cf - .")
1676 command = to_plc + "| tar -C logs/sfi.%s -xf -"%self.name()
1677 utils.system(command)
1679 def gather_nodes_var_logs (self):
1680 for site_spec in self.plc_spec['sites']:
1681 test_site = TestSite (self,site_spec)
1682 for node_spec in site_spec['nodes']:
1683 test_node=TestNode(self,test_site,node_spec)
1684 test_ssh = TestSsh (test_node.name(),key="keys/key_admin.rsa")
1685 command = test_ssh.actual_command("tar -C /var/log -cf - .")
1686 command = command + "| tar -C logs/node.var-log.%s -xf -"%test_node.name()
1687 utils.system("mkdir -p logs/node.var-log.%s"%test_node.name())
1688 utils.system(command)
1691 # returns the filename to use for sql dump/restore, using options.dbname if set
1692 def dbfile (self, database):
1693 # uses options.dbname if it is found
1695 name=self.options.dbname
1696 if not isinstance(name,StringTypes):
1702 return "/root/%s-%s.sql"%(database,name)
1704 def plc_db_dump(self):
1705 'dump the planetlab5 DB in /root in the PLC - filename has time'
1706 dump=self.dbfile("planetab5")
1707 self.run_in_guest('pg_dump -U pgsqluser planetlab5 -f '+ dump)
1708 utils.header('Dumped planetlab5 database in %s'%dump)
1711 def plc_db_restore(self):
1712 'restore the planetlab5 DB - looks broken, but run -n might help'
1713 dump=self.dbfile("planetab5")
1714 ##stop httpd service
1715 self.run_in_guest('service httpd stop')
1716 # xxx - need another wrapper
1717 self.run_in_guest_piped('echo drop database planetlab5','psql --user=pgsqluser template1')
1718 self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab5')
1719 self.run_in_guest('psql -U pgsqluser planetlab5 -f '+dump)
1720 ##starting httpd service
1721 self.run_in_guest('service httpd start')
1723 utils.header('Database restored from ' + dump)
1726 def create_ignore_steps ():
1727 for step in TestPlc.default_steps + TestPlc.other_steps:
1728 # default step can have a plc qualifier
1729 if '@' in step: (step,qualifier)=step.split('@')
1730 # or be defined as forced or ignored by default
1731 for keyword in ['_ignore','_force']:
1732 if step.endswith (keyword): step=step.replace(keyword,'')
1733 if step == SEP or step == SEPSFA : continue
1734 method=getattr(TestPlc,step)
1736 wrapped=ignore_result(method)
1737 # wrapped.__doc__ = method.__doc__ + " (run in ignore-result mode)"
1738 setattr(TestPlc, name, wrapped)
1741 # def ssh_slice_again_ignore (self): pass
1743 # def check_initscripts_ignore (self): pass
1745 def standby_1_through_20(self):
1746 """convenience function to wait for a specified number of minutes"""
1749 def standby_1(): pass
1751 def standby_2(): pass
1753 def standby_3(): pass
1755 def standby_4(): pass
1757 def standby_5(): pass
1759 def standby_6(): pass
1761 def standby_7(): pass
1763 def standby_8(): pass
1765 def standby_9(): pass
1767 def standby_10(): pass
1769 def standby_11(): pass
1771 def standby_12(): pass
1773 def standby_13(): pass
1775 def standby_14(): pass
1777 def standby_15(): pass
1779 def standby_16(): pass
1781 def standby_17(): pass
1783 def standby_18(): pass
1785 def standby_19(): pass
1787 def standby_20(): pass
1789 # convenience for debugging the test logic
1790 def yes (self): return True
1791 def no (self): return False