checking for sfa steps was buggy on centos - fixed
[tests.git] / system / TestPlc.py
1 # $Id$
2 import os, os.path
3 import datetime
4 import time
5 import sys
6 import traceback
7 from types import StringTypes
8 import socket
9
10 import utils
11 from TestSite import TestSite
12 from TestNode import TestNode
13 from TestUser import TestUser
14 from TestKey import TestKey
15 from TestSlice import TestSlice
16 from TestSliver import TestSliver
17 from TestBox import TestBox
18 from TestSsh import TestSsh
19 from TestApiserver import TestApiserver
20 from TestSliceSfa import TestSliceSfa
21 from TestUserSfa import TestUserSfa
22
23 # step methods must take (self) and return a boolean (options is a member of the class)
24
25 def standby(minutes,dry_run):
26     utils.header('Entering StandBy for %d mn'%minutes)
27     if dry_run:
28         print 'dry_run'
29     else:
30         time.sleep(60*minutes)
31     return True
32
33 def standby_generic (func):
34     def actual(self):
35         minutes=int(func.__name__.split("_")[1])
36         return standby(minutes,self.options.dry_run)
37     return actual
38
39 def node_mapper (method):
40     def actual(self):
41         overall=True
42         node_method = TestNode.__dict__[method.__name__]
43         for site_spec in self.plc_spec['sites']:
44             test_site = TestSite (self,site_spec)
45             for node_spec in site_spec['nodes']:
46                 test_node = TestNode (self,test_site,node_spec)
47                 if not node_method(test_node): overall=False
48         return overall
49     # restore the doc text
50     actual.__doc__=method.__doc__
51     return actual
52
53 def slice_mapper_options (method):
54     def actual(self):
55         overall=True
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
62         return overall
63     # restore the doc text
64     actual.__doc__=method.__doc__
65     return actual
66
67 def slice_mapper_options_sfa (method):
68     def actual(self):
69         test_plc=self
70         overall=True
71         slice_method = TestSliceSfa.__dict__[method.__name__]
72         for slice_spec in self.plc_spec['sfa']['slices_sfa']:
73             site_spec = self.locate_site (slice_spec['sitename'])
74             test_site = TestSite(self,site_spec)
75             test_slice=TestSliceSfa(test_plc,test_site,slice_spec)
76             if not slice_method(test_slice,self.options): overall=False
77         return overall
78     # restore the doc text
79     actual.__doc__=method.__doc__
80     return actual
81
82 SEP='<sep>'
83 SEPSFA='<sep_sfa>'
84
85 class TestPlc:
86
87     default_steps = [
88         'display', 'resources_pre', SEP,
89         'delete_vs','create_vs','install', 'configure', 'start', SEP,
90         'fetch_keys', 'store_keys', 'clear_known_hosts', SEP,
91         'initscripts', 'sites', 'nodes', 'slices', 'nodegroups', 'leases', SEP,
92         'reinstall_node', 'init_node','bootcd', 'configure_qemu', 'export_qemu',
93         'kill_all_qemus', 'start_node', SEP,
94         # better use of time: do this now that the nodes are taking off
95         'plcsh_stress_test', SEP,
96         'install_sfa', 'configure_sfa', 'import_sfa', 'start_sfa', SEPSFA,
97         'setup_sfa', 'add_sfa', 'update_sfa', 'view_sfa', SEPSFA,
98         'nodes_ssh_debug', 'nodes_ssh_boot', 'check_slice', 'check_initscripts', SEPSFA,
99         'check_slice_sfa', 'delete_sfa', 'stop_sfa', SEPSFA,
100         'check_tcp',  'check_hooks',  SEP,
101         'force_gather_logs', 'force_resources_post', SEP,
102         ]
103     other_steps = [ 
104         'show_boxes', 'resources_list','resources_release','resources_release_plc','resources_release_qemu',SEP,
105         'stop', 'vs_start', SEP,
106         'clean_initscripts', 'clean_nodegroups','clean_all_sites', SEP,
107         'clean_sites', 'clean_nodes', 'clean_slices', 'clean_keys', SEP,
108         'clean_leases', 'list_leases', SEP,
109         'populate' , SEP,
110         'list_all_qemus', 'list_qemus', 'kill_qemus', SEP,
111         'db_dump' , 'db_restore', SEP,
112         'standby_1 through 20',SEP,
113         ]
114
115     @staticmethod
116     def printable_steps (list):
117         single_line=" ".join(list)+" "
118         return single_line.replace(" "+SEP+" "," \\\n").replace(" "+SEPSFA+" "," \\\n")
119     @staticmethod
120     def valid_step (step):
121         return step != SEP
122
123     # turn off the sfa-related steps when build has skipped SFA
124     # this is originally for centos5 as recent SFAs won't build on this platformb
125     @staticmethod
126     def check_whether_build_has_sfa (rpms_url):
127         retcod=os.system ("curl --silent %s/ | grep -q sfa"%rpms_url)
128         # full builds are expected to return with 0 here
129         if retcod!=0:
130             # move all steps containing 'sfa' from default_steps to other_steps
131             sfa_steps= [ step for step in TestPlc.default_steps if step.find('sfa')>=0 ]
132             TestPlc.other_steps += sfa_steps
133             for step in sfa_steps: TestPlc.default_steps.remove(step)
134
135     def __init__ (self,plc_spec,options):
136         self.plc_spec=plc_spec
137         self.options=options
138         self.test_ssh=TestSsh(self.plc_spec['hostname'],self.options.buildname)
139         try:
140             self.vserverip=plc_spec['vserverip']
141             self.vservername=plc_spec['vservername']
142             self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
143             self.vserver=True
144         except:
145             raise Exception,'chroot-based myplc testing is deprecated'
146         self.apiserver=TestApiserver(self.url,options.dry_run)
147         
148     def name(self):
149         name=self.plc_spec['name']
150         return "%s.%s"%(name,self.vservername)
151
152     def hostname(self):
153         return self.plc_spec['hostname']
154
155     def is_local (self):
156         return self.test_ssh.is_local()
157
158     # define the API methods on this object through xmlrpc
159     # would help, but not strictly necessary
160     def connect (self):
161         pass
162
163     def actual_command_in_guest (self,command):
164         return self.test_ssh.actual_command(self.host_to_guest(command))
165     
166     def start_guest (self):
167       return utils.system(self.test_ssh.actual_command(self.start_guest_in_host()))
168     
169     def run_in_guest (self,command):
170         return utils.system(self.actual_command_in_guest(command))
171     
172     def run_in_host (self,command):
173         return self.test_ssh.run_in_buildname(command)
174
175     #command gets run in the vserver
176     def host_to_guest(self,command):
177         return "vserver %s exec %s"%(self.vservername,command)
178     
179     #command gets run in the vserver
180     def start_guest_in_host(self):
181         return "vserver %s start"%(self.vservername)
182     
183     # xxx quick n dirty
184     def run_in_guest_piped (self,local,remote):
185         return utils.system(local+" | "+self.test_ssh.actual_command(self.host_to_guest(remote),keep_stdin=True))
186
187     def auth_root (self):
188         return {'Username':self.plc_spec['PLC_ROOT_USER'],
189                 'AuthMethod':'password',
190                 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
191                 'Role' : self.plc_spec['role']
192                 }
193     def locate_site (self,sitename):
194         for site in self.plc_spec['sites']:
195             if site['site_fields']['name'] == sitename:
196                 return site
197             if site['site_fields']['login_base'] == sitename:
198                 return site
199         raise Exception,"Cannot locate site %s"%sitename
200         
201     def locate_node (self,nodename):
202         for site in self.plc_spec['sites']:
203             for node in site['nodes']:
204                 if node['name'] == nodename:
205                     return (site,node)
206         raise Exception,"Cannot locate node %s"%nodename
207         
208     def locate_hostname (self,hostname):
209         for site in self.plc_spec['sites']:
210             for node in site['nodes']:
211                 if node['node_fields']['hostname'] == hostname:
212                     return (site,node)
213         raise Exception,"Cannot locate hostname %s"%hostname
214         
215     def locate_key (self,keyname):
216         for key in self.plc_spec['keys']:
217             if key['name'] == keyname:
218                 return key
219         raise Exception,"Cannot locate key %s"%keyname
220
221     def locate_slice (self, slicename):
222         for slice in self.plc_spec['slices']:
223             if slice['slice_fields']['name'] == slicename:
224                 return slice
225         raise Exception,"Cannot locate slice %s"%slicename
226
227     def all_sliver_objs (self):
228         result=[]
229         for slice_spec in self.plc_spec['slices']:
230             slicename = slice_spec['slice_fields']['name']
231             for nodename in slice_spec['nodenames']:
232                 result.append(self.locate_sliver_obj (nodename,slicename))
233         return result
234
235     def locate_sliver_obj (self,nodename,slicename):
236         (site,node) = self.locate_node(nodename)
237         slice = self.locate_slice (slicename)
238         # build objects
239         test_site = TestSite (self, site)
240         test_node = TestNode (self, test_site,node)
241         # xxx the slice site is assumed to be the node site - mhh - probably harmless
242         test_slice = TestSlice (self, test_site, slice)
243         return TestSliver (self, test_node, test_slice)
244
245     def locate_first_node(self):
246         nodename=self.plc_spec['slices'][0]['nodenames'][0]
247         (site,node) = self.locate_node(nodename)
248         test_site = TestSite (self, site)
249         test_node = TestNode (self, test_site,node)
250         return test_node
251
252     def locate_first_sliver (self):
253         slice_spec=self.plc_spec['slices'][0]
254         slicename=slice_spec['slice_fields']['name']
255         nodename=slice_spec['nodenames'][0]
256         return self.locate_sliver_obj(nodename,slicename)
257
258     # all different hostboxes used in this plc
259     def gather_hostBoxes(self):
260         # maps on sites and nodes, return [ (host_box,test_node) ]
261         tuples=[]
262         for site_spec in self.plc_spec['sites']:
263             test_site = TestSite (self,site_spec)
264             for node_spec in site_spec['nodes']:
265                 test_node = TestNode (self, test_site, node_spec)
266                 if not test_node.is_real():
267                     tuples.append( (test_node.host_box(),test_node) )
268         # transform into a dict { 'host_box' -> [ test_node .. ] }
269         result = {}
270         for (box,node) in tuples:
271             if not result.has_key(box):
272                 result[box]=[node]
273             else:
274                 result[box].append(node)
275         return result
276                     
277     # a step for checking this stuff
278     def show_boxes (self):
279         'print summary of nodes location'
280         for (box,nodes) in self.gather_hostBoxes().iteritems():
281             print box,":"," + ".join( [ node.name() for node in nodes ] )
282         return True
283
284     # make this a valid step
285     def kill_all_qemus(self):
286         'kill all qemu instances on the qemu boxes involved by this setup'
287         # this is the brute force version, kill all qemus on that host box
288         for (box,nodes) in self.gather_hostBoxes().iteritems():
289             # pass the first nodename, as we don't push template-qemu on testboxes
290             nodedir=nodes[0].nodedir()
291             TestBox(box,self.options.buildname).kill_all_qemus(nodedir)
292         return True
293
294     # make this a valid step
295     def list_all_qemus(self):
296         'list all qemu instances on the qemu boxes involved by this setup'
297         for (box,nodes) in self.gather_hostBoxes().iteritems():
298             # this is the brute force version, kill all qemus on that host box
299             TestBox(box,self.options.buildname).list_all_qemus()
300         return True
301
302     # kill only the right qemus
303     def list_qemus(self):
304         'list qemu instances for our nodes'
305         for (box,nodes) in self.gather_hostBoxes().iteritems():
306             # the fine-grain version
307             for node in nodes:
308                 node.list_qemu()
309         return True
310
311     # kill only the right qemus
312     def kill_qemus(self):
313         'kill the qemu instances for our nodes'
314         for (box,nodes) in self.gather_hostBoxes().iteritems():
315             # the fine-grain version
316             for node in nodes:
317                 node.kill_qemu()
318         return True
319
320     #################### display config
321     def display (self):
322         "show test configuration after localization"
323         self.display_pass (1)
324         self.display_pass (2)
325         return True
326
327     # entry point
328     always_display_keys=['PLC_WWW_HOST','nodes','sites',]
329     def display_pass (self,passno):
330         for (key,val) in self.plc_spec.iteritems():
331             if not self.options.verbose and key not in TestPlc.always_display_keys: continue
332             if passno == 2:
333                 if key == 'sites':
334                     for site in val:
335                         self.display_site_spec(site)
336                         for node in site['nodes']:
337                             self.display_node_spec(node)
338                 elif key=='initscripts':
339                     for initscript in val:
340                         self.display_initscript_spec (initscript)
341                 elif key=='slices':
342                     for slice in val:
343                         self.display_slice_spec (slice)
344                 elif key=='keys':
345                     for key in val:
346                         self.display_key_spec (key)
347             elif passno == 1:
348                 if key not in ['sites','initscripts','slices','keys', 'sfa']:
349                     print '+   ',key,':',val
350
351     def display_site_spec (self,site):
352         print '+ ======== site',site['site_fields']['name']
353         for (k,v) in site.iteritems():
354             if not self.options.verbose and k not in TestPlc.always_display_keys: continue
355             if k=='nodes':
356                 if v: 
357                     print '+       ','nodes : ',
358                     for node in v:  
359                         print node['node_fields']['hostname'],'',
360                     print ''
361             elif k=='users':
362                 if v: 
363                     print '+       users : ',
364                     for user in v:  
365                         print user['name'],'',
366                     print ''
367             elif k == 'site_fields':
368                 print '+       login_base',':',v['login_base']
369             elif k == 'address_fields':
370                 pass
371             else:
372                 print '+       ',
373                 utils.pprint(k,v)
374         
375     def display_initscript_spec (self,initscript):
376         print '+ ======== initscript',initscript['initscript_fields']['name']
377
378     def display_key_spec (self,key):
379         print '+ ======== key',key['name']
380
381     def display_slice_spec (self,slice):
382         print '+ ======== slice',slice['slice_fields']['name']
383         for (k,v) in slice.iteritems():
384             if k=='nodenames':
385                 if v: 
386                     print '+       nodes : ',
387                     for nodename in v:  
388                         print nodename,'',
389                     print ''
390             elif k=='usernames':
391                 if v: 
392                     print '+       users : ',
393                     for username in v:  
394                         print username,'',
395                     print ''
396             elif k=='slice_fields':
397                 print '+       fields',':',
398                 print 'max_nodes=',v['max_nodes'],
399                 print ''
400             else:
401                 print '+       ',k,v
402
403     def display_node_spec (self,node):
404         print "+           node=%s host_box=%s"%(node['name'],node['host_box']),
405         print "hostname=",node['node_fields']['hostname'],
406         print "ip=",node['interface_fields']['ip']
407         if self.options.verbose:
408             utils.pprint("node details",node,depth=3)
409
410     # another entry point for just showing the boxes involved
411     def display_mapping (self):
412         TestPlc.display_mapping_plc(self.plc_spec)
413         return True
414
415     @staticmethod
416     def display_mapping_plc (plc_spec):
417         print '+ MyPLC',plc_spec['name']
418         print '+\tvserver address = root@%s:/vservers/%s'%(plc_spec['hostname'],plc_spec['vservername'])
419         print '+\tIP = %s/%s'%(plc_spec['PLC_API_HOST'],plc_spec['vserverip'])
420         for site_spec in plc_spec['sites']:
421             for node_spec in site_spec['nodes']:
422                 TestPlc.display_mapping_node(node_spec)
423
424     @staticmethod
425     def display_mapping_node (node_spec):
426         print '+   NODE %s'%(node_spec['name'])
427         print '+\tqemu box %s'%node_spec['host_box']
428         print '+\thostname=%s'%node_spec['node_fields']['hostname']
429
430     def resources_pre (self):
431         "run site-dependant pre-test script as defined in LocalTestResources"
432         from LocalTestResources import local_resources
433         return local_resources.step_pre(self)
434  
435     def resources_post (self):
436         "run site-dependant post-test script as defined in LocalTestResources"
437         from LocalTestResources import local_resources
438         return local_resources.step_post(self)
439  
440     def resources_list (self):
441         "run site-dependant list script as defined in LocalTestResources"
442         from LocalTestResources import local_resources
443         return local_resources.step_list(self)
444  
445     def resources_release (self):
446         "run site-dependant release script as defined in LocalTestResources"
447         from LocalTestResources import local_resources
448         return local_resources.step_release(self)
449  
450     def resources_release_plc (self):
451         "run site-dependant release script as defined in LocalTestResources"
452         from LocalTestResources import local_resources
453         return local_resources.step_release_plc(self)
454  
455     def resources_release_qemu (self):
456         "run site-dependant release script as defined in LocalTestResources"
457         from LocalTestResources import local_resources
458         return local_resources.step_release_qemu(self)
459  
460     def delete_vs(self):
461         "vserver delete the test myplc"
462         self.run_in_host("vserver --silent %s delete"%self.vservername)
463         return True
464
465     ### install
466     # historically the build was being fetched by the tests
467     # now the build pushes itself as a subdir of the tests workdir
468     # so that the tests do not have to worry about extracting the build (svn, git, or whatever)
469     def create_vs (self):
470         "vserver creation (no install done)"
471         # push the local build/ dir to the testplc box 
472         if self.is_local():
473             # a full path for the local calls
474             build_dir=os.path.dirname(sys.argv[0])
475             # sometimes this is empty - set to "." in such a case
476             if not build_dir: build_dir="."
477             build_dir += "/build"
478         else:
479             # use a standard name - will be relative to remote buildname
480             build_dir="build"
481             # remove for safety; do *not* mkdir first, otherwise we end up with build/build/
482             self.test_ssh.rmdir(build_dir)
483             self.test_ssh.copy(build_dir,recursive=True)
484         # the repo url is taken from arch-rpms-url 
485         # with the last step (i386) removed
486         repo_url = self.options.arch_rpms_url
487         for level in [ 'arch' ]:
488             repo_url = os.path.dirname(repo_url)
489         # pass the vbuild-nightly options to vtest-init-vserver
490         test_env_options=""
491         test_env_options += " -p %s"%self.options.personality
492         test_env_options += " -d %s"%self.options.pldistro
493         test_env_options += " -f %s"%self.options.fcdistro
494         script="vtest-init-vserver.sh"
495         vserver_name = self.vservername
496         vserver_options="--netdev eth0 --interface %s"%self.vserverip
497         try:
498             vserver_hostname=socket.gethostbyaddr(self.vserverip)[0]
499             vserver_options += " --hostname %s"%vserver_hostname
500         except:
501             print "Cannot reverse lookup %s"%self.vserverip
502             print "This is considered fatal, as this might pollute the test results"
503             return False
504         create_vserver="%(build_dir)s/%(script)s %(test_env_options)s %(vserver_name)s %(repo_url)s -- %(vserver_options)s"%locals()
505         return self.run_in_host(create_vserver) == 0
506
507     ### install_rpm 
508     def install(self):
509         "yum install myplc, noderepo, and the plain bootstrapfs"
510
511         # workaround for getting pgsql8.2 on centos5
512         if self.options.fcdistro == "centos5":
513             self.run_in_guest("rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm")
514
515         if self.options.personality == "linux32":
516             arch = "i386"
517         elif self.options.personality == "linux64":
518             arch = "x86_64"
519         else:
520             raise Exception, "Unsupported personality %r"%self.options.personality
521         
522         nodefamily="%s-%s-%s"%(self.options.pldistro,self.options.fcdistro,arch)
523
524         # try to install slicerepo - not fatal yet
525         self.run_in_guest("yum -y install slicerepo-%s"%nodefamily)
526         
527         return \
528             self.run_in_guest("yum -y install myplc")==0 and \
529             self.run_in_guest("yum -y install noderepo-%s"%nodefamily)==0 and \
530             self.run_in_guest("yum -y install bootstrapfs-%s-plain"%nodefamily)==0 
531
532     ### 
533     def configure(self):
534         "run plc-config-tty"
535         tmpname='%s.plc-config-tty'%(self.name())
536         fileconf=open(tmpname,'w')
537         for var in [ 'PLC_NAME',
538                      'PLC_ROOT_PASSWORD',
539                      'PLC_ROOT_USER',
540                      'PLC_MAIL_ENABLED',
541                      'PLC_MAIL_SUPPORT_ADDRESS',
542                      'PLC_DB_HOST',
543                      'PLC_DB_PASSWORD',
544                      # Above line was added for integrating SFA Testing
545                      'PLC_API_HOST',
546                      'PLC_WWW_HOST',
547                      'PLC_BOOT_HOST',
548                      'PLC_NET_DNS1',
549                      'PLC_NET_DNS2']:
550             fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
551         fileconf.write('w\n')
552         fileconf.write('q\n')
553         fileconf.close()
554         utils.system('cat %s'%tmpname)
555         self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
556         utils.system('rm %s'%tmpname)
557         return True
558
559     def start(self):
560         "service plc start"
561         self.run_in_guest('service plc start')
562         return True
563
564     def stop(self):
565         "service plc stop"
566         self.run_in_guest('service plc stop')
567         return True
568         
569     def vs_start (self):
570         "start the PLC vserver"
571         self.start_guest()
572         return True
573
574     # stores the keys from the config for further use
575     def store_keys(self):
576         "stores test users ssh keys in keys/"
577         for key_spec in self.plc_spec['keys']:
578                 TestKey(self,key_spec).store_key()
579         return True
580
581     def clean_keys(self):
582         "removes keys cached in keys/"
583         utils.system("rm -rf %s/keys/"%os.path(sys.argv[0]))
584
585     # fetches the ssh keys in the plc's /etc/planetlab and stores them in keys/
586     # for later direct access to the nodes
587     def fetch_keys(self):
588         "gets ssh keys in /etc/planetlab/ and stores them locally in keys/"
589         dir="./keys"
590         if not os.path.isdir(dir):
591             os.mkdir(dir)
592         vservername=self.vservername
593         overall=True
594         prefix = 'debug_ssh_key'
595         for ext in [ 'pub', 'rsa' ] :
596             src="/vservers/%(vservername)s/etc/planetlab/%(prefix)s.%(ext)s"%locals()
597             dst="keys/%(vservername)s-debug.%(ext)s"%locals()
598             if self.test_ssh.fetch(src,dst) != 0: overall=False
599         return overall
600
601     def sites (self):
602         "create sites with PLCAPI"
603         return self.do_sites()
604     
605     def clean_sites (self):
606         "delete sites with PLCAPI"
607         return self.do_sites(action="delete")
608     
609     def do_sites (self,action="add"):
610         for site_spec in self.plc_spec['sites']:
611             test_site = TestSite (self,site_spec)
612             if (action != "add"):
613                 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
614                 test_site.delete_site()
615                 # deleted with the site
616                 #test_site.delete_users()
617                 continue
618             else:
619                 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
620                 test_site.create_site()
621                 test_site.create_users()
622         return True
623
624     def clean_all_sites (self):
625         "Delete all sites in PLC, and related objects"
626         print 'auth_root',self.auth_root()
627         site_ids = [s['site_id'] for s in self.apiserver.GetSites(self.auth_root(), {}, ['site_id'])]
628         for site_id in site_ids:
629             print 'Deleting site_id',site_id
630             self.apiserver.DeleteSite(self.auth_root(),site_id)
631
632     def nodes (self):
633         "create nodes with PLCAPI"
634         return self.do_nodes()
635     def clean_nodes (self):
636         "delete nodes with PLCAPI"
637         return self.do_nodes(action="delete")
638
639     def do_nodes (self,action="add"):
640         for site_spec in self.plc_spec['sites']:
641             test_site = TestSite (self,site_spec)
642             if action != "add":
643                 utils.header("Deleting nodes in site %s"%test_site.name())
644                 for node_spec in site_spec['nodes']:
645                     test_node=TestNode(self,test_site,node_spec)
646                     utils.header("Deleting %s"%test_node.name())
647                     test_node.delete_node()
648             else:
649                 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
650                 for node_spec in site_spec['nodes']:
651                     utils.pprint('Creating node %s'%node_spec,node_spec)
652                     test_node = TestNode (self,test_site,node_spec)
653                     test_node.create_node ()
654         return True
655
656     def nodegroups (self):
657         "create nodegroups with PLCAPI"
658         return self.do_nodegroups("add")
659     def clean_nodegroups (self):
660         "delete nodegroups with PLCAPI"
661         return self.do_nodegroups("delete")
662
663     YEAR = 365*24*3600
664     @staticmethod
665     def translate_timestamp (start,timestamp):
666         if timestamp < TestPlc.YEAR:    return start+timestamp
667         else:                           return timestamp
668
669     @staticmethod
670     def timestamp_printable (timestamp):
671         return time.strftime('%m-%d %H:%M:%S UTC',time.gmtime(timestamp))
672
673     def leases(self):
674         "create leases (on reservable nodes only, use e.g. run -c default -c resa)"
675         now=int(time.time())
676         grain=self.apiserver.GetLeaseGranularity(self.auth_root())
677         round_time=(now/grain)*grain
678         start=round_time+grain
679         # find out all nodes that are reservable
680         nodes=self.all_reservable_nodenames()
681         if not nodes: 
682             utils.header ("No reservable node found - proceeding without leases")
683             return True
684         ok=True
685         # attach them to the leases as specified in plc_specs
686         # this is where the 'leases' field gets interpreted as relative of absolute
687         for lease_spec in self.plc_spec['leases']:
688             # skip the ones that come with a null slice id
689             if not lease_spec['slice']: continue
690             lease_spec['t_from']=TestPlc.translate_timestamp(start,lease_spec['t_from'])
691             lease_spec['t_until']=TestPlc.translate_timestamp(start,lease_spec['t_until'])
692             lease_addition=self.apiserver.AddLeases(self.auth_root(),nodes,
693                                                     lease_spec['slice'],lease_spec['t_from'],lease_spec['t_until'])
694             if lease_addition['errors']:
695                 utils.header("Cannot create leases, %s"%lease_addition['errors'])
696                 ok=False
697             else:
698                 utils.header('Leases on nodes %r for %s from %d (%s) until %d (%s)'%\
699                               (nodes,lease_spec['slice'],
700                                lease_spec['t_from'],TestPlc.timestamp_printable(lease_spec['t_from']),
701                                lease_spec['t_until'],TestPlc.timestamp_printable(lease_spec['t_until'])))
702                 
703         return ok
704
705     def clean_leases (self):
706         "remove all leases in the myplc side"
707         lease_ids= [ l['lease_id'] for l in self.apiserver.GetLeases(self.auth_root())]
708         utils.header("Cleaning leases %r"%lease_ids)
709         self.apiserver.DeleteLeases(self.auth_root(),lease_ids)
710         return True
711
712     def list_leases (self):
713         "list all leases known to the myplc"
714         leases = self.apiserver.GetLeases(self.auth_root())
715         for l in leases:
716             utils.header("%s %s from %s until %s"%(l['hostname'],l['name'],
717                                                    TestPlc.timestamp_printable(l['t_from']), 
718                                                    TestPlc.timestamp_printable(l['t_until'])))
719         return True
720
721     # create nodegroups if needed, and populate
722     def do_nodegroups (self, action="add"):
723         # 1st pass to scan contents
724         groups_dict = {}
725         for site_spec in self.plc_spec['sites']:
726             test_site = TestSite (self,site_spec)
727             for node_spec in site_spec['nodes']:
728                 test_node=TestNode (self,test_site,node_spec)
729                 if node_spec.has_key('nodegroups'):
730                     nodegroupnames=node_spec['nodegroups']
731                     if isinstance(nodegroupnames,StringTypes):
732                         nodegroupnames = [ nodegroupnames ]
733                     for nodegroupname in nodegroupnames:
734                         if not groups_dict.has_key(nodegroupname):
735                             groups_dict[nodegroupname]=[]
736                         groups_dict[nodegroupname].append(test_node.name())
737         auth=self.auth_root()
738         overall = True
739         for (nodegroupname,group_nodes) in groups_dict.iteritems():
740             if action == "add":
741                 print 'nodegroups:','dealing with nodegroup',nodegroupname,'on nodes',group_nodes
742                 # first, check if the nodetagtype is here
743                 tag_types = self.apiserver.GetTagTypes(auth,{'tagname':nodegroupname})
744                 if tag_types:
745                     tag_type_id = tag_types[0]['tag_type_id']
746                 else:
747                     tag_type_id = self.apiserver.AddTagType(auth,
748                                                             {'tagname':nodegroupname,
749                                                              'description': 'for nodegroup %s'%nodegroupname,
750                                                              'category':'test',
751                                                              'min_role_id':10})
752                 print 'located tag (type)',nodegroupname,'as',tag_type_id
753                 # create nodegroup
754                 nodegroups = self.apiserver.GetNodeGroups (auth, {'groupname':nodegroupname})
755                 if not nodegroups:
756                     self.apiserver.AddNodeGroup(auth, nodegroupname, tag_type_id, 'yes')
757                     print 'created nodegroup',nodegroupname,'from tagname',nodegroupname,'and value','yes'
758                 # set node tag on all nodes, value='yes'
759                 for nodename in group_nodes:
760                     try:
761                         self.apiserver.AddNodeTag(auth, nodename, nodegroupname, "yes")
762                     except:
763                         traceback.print_exc()
764                         print 'node',nodename,'seems to already have tag',nodegroupname
765                     # check anyway
766                     try:
767                         expect_yes = self.apiserver.GetNodeTags(auth,
768                                                                 {'hostname':nodename,
769                                                                  'tagname':nodegroupname},
770                                                                 ['value'])[0]['value']
771                         if expect_yes != "yes":
772                             print 'Mismatch node tag on node',nodename,'got',expect_yes
773                             overall=False
774                     except:
775                         if not self.options.dry_run:
776                             print 'Cannot find tag',nodegroupname,'on node',nodename
777                             overall = False
778             else:
779                 try:
780                     print 'cleaning nodegroup',nodegroupname
781                     self.apiserver.DeleteNodeGroup(auth,nodegroupname)
782                 except:
783                     traceback.print_exc()
784                     overall=False
785         return overall
786
787     # return a list of tuples (nodename,qemuname)
788     def all_node_infos (self) :
789         node_infos = []
790         for site_spec in self.plc_spec['sites']:
791             node_infos += [ (node_spec['node_fields']['hostname'],node_spec['host_box']) \
792                                 for node_spec in site_spec['nodes'] ]
793         return node_infos
794     
795     def all_nodenames (self): return [ x[0] for x in self.all_node_infos() ]
796     def all_reservable_nodenames (self): 
797         res=[]
798         for site_spec in self.plc_spec['sites']:
799             for node_spec in site_spec['nodes']:
800                 node_fields=node_spec['node_fields']
801                 if 'node_type' in node_fields and node_fields['node_type']=='reservable':
802                     res.append(node_fields['hostname'])
803         return res
804
805     # silent_minutes : during the first <silent_minutes> minutes nothing gets printed
806     def nodes_check_boot_state (self, target_boot_state, timeout_minutes, silent_minutes,period=15):
807         if self.options.dry_run:
808             print 'dry_run'
809             return True
810         # compute timeout
811         timeout = datetime.datetime.now()+datetime.timedelta(minutes=timeout_minutes)
812         graceout = datetime.datetime.now()+datetime.timedelta(minutes=silent_minutes)
813         # the nodes that haven't checked yet - start with a full list and shrink over time
814         tocheck = self.all_hostnames()
815         utils.header("checking nodes %r"%tocheck)
816         # create a dict hostname -> status
817         status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
818         while tocheck:
819             # get their status
820             tocheck_status=self.apiserver.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
821             # update status
822             for array in tocheck_status:
823                 hostname=array['hostname']
824                 boot_state=array['boot_state']
825                 if boot_state == target_boot_state:
826                     utils.header ("%s has reached the %s state"%(hostname,target_boot_state))
827                 else:
828                     # if it's a real node, never mind
829                     (site_spec,node_spec)=self.locate_hostname(hostname)
830                     if TestNode.is_real_model(node_spec['node_fields']['model']):
831                         utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
832                         # let's cheat
833                         boot_state = target_boot_state
834                     elif datetime.datetime.now() > graceout:
835                         utils.header ("%s still in '%s' state"%(hostname,boot_state))
836                         graceout=datetime.datetime.now()+datetime.timedelta(1)
837                 status[hostname] = boot_state
838             # refresh tocheck
839             tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != target_boot_state ]
840             if not tocheck:
841                 return True
842             if datetime.datetime.now() > timeout:
843                 for hostname in tocheck:
844                     utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
845                 return False
846             # otherwise, sleep for a while
847             time.sleep(period)
848         # only useful in empty plcs
849         return True
850
851     def nodes_booted(self):
852         return self.nodes_check_boot_state('boot',timeout_minutes=30,silent_minutes=20)
853
854     def check_nodes_ssh(self,debug,timeout_minutes,silent_minutes,period=15):
855         # compute timeout
856         timeout = datetime.datetime.now()+datetime.timedelta(minutes=timeout_minutes)
857         graceout = datetime.datetime.now()+datetime.timedelta(minutes=silent_minutes)
858         vservername=self.vservername
859         if debug: 
860             message="debug"
861             local_key = "keys/%(vservername)s-debug.rsa"%locals()
862         else: 
863             message="boot"
864             local_key = "keys/key1.rsa"
865         node_infos = self.all_node_infos()
866         utils.header("checking ssh access (expected in %s mode) to nodes:"%message)
867         for (nodename,qemuname) in node_infos:
868             utils.header("hostname=%s -- qemubox=%s"%(nodename,qemuname))
869         utils.header("max timeout is %d minutes, silent for %d minutes (period is %s)"%\
870                          (timeout_minutes,silent_minutes,period))
871         while node_infos:
872             for node_info in node_infos:
873                 (hostname,qemuname) = node_info
874                 # try to run 'hostname' in the node
875                 command = TestSsh (hostname,key=local_key).actual_command("hostname;uname -a")
876                 # don't spam logs - show the command only after the grace period 
877                 success = utils.system ( command, silent=datetime.datetime.now() < graceout)
878                 if success==0:
879                     utils.header('Successfully entered root@%s (%s)'%(hostname,message))
880                     # refresh node_infos
881                     node_infos.remove(node_info)
882                 else:
883                     # we will have tried real nodes once, in case they're up - but if not, just skip
884                     (site_spec,node_spec)=self.locate_hostname(hostname)
885                     if TestNode.is_real_model(node_spec['node_fields']['model']):
886                         utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
887                         node_infos.remove(node_info)
888             if  not node_infos:
889                 return True
890             if datetime.datetime.now() > timeout:
891                 for (hostname,qemuname) in node_infos:
892                     utils.header("FAILURE to ssh into %s (on %s)"%(hostname,qemuname))
893                 return False
894             # otherwise, sleep for a while
895             time.sleep(period)
896         # only useful in empty plcs
897         return True
898         
899     def nodes_ssh_debug(self):
900         "Tries to ssh into nodes in debug mode with the debug ssh key"
901         return self.check_nodes_ssh(debug=True,timeout_minutes=30,silent_minutes=5)
902     
903     def nodes_ssh_boot(self):
904         "Tries to ssh into nodes in production mode with the root ssh key"
905         return self.check_nodes_ssh(debug=False,timeout_minutes=30,silent_minutes=15)
906     
907     @node_mapper
908     def init_node (self): 
909         "all nodes : init a clean local directory for holding node-dep stuff like iso image..."
910         pass
911     @node_mapper
912     def bootcd (self): 
913         "all nodes: invoke GetBootMedium and store result locally"
914         pass
915     @node_mapper
916     def configure_qemu (self): 
917         "all nodes: compute qemu config qemu.conf and store it locally"
918         pass
919     @node_mapper
920     def reinstall_node (self): 
921         "all nodes: mark PLCAPI boot_state as reinstall"
922         pass
923     @node_mapper
924     def export_qemu (self): 
925         "all nodes: push local node-dep directory on the qemu box"
926         pass
927         
928     ### check hooks : invoke scripts from hooks/{node,slice}
929     def check_hooks_node (self): 
930         return self.locate_first_node().check_hooks()
931     def check_hooks_sliver (self) : 
932         return self.locate_first_sliver().check_hooks()
933     
934     def check_hooks (self):
935         "runs unit tests in the node and slice contexts - see hooks/{node,slice}"
936         return self.check_hooks_node() and self.check_hooks_sliver()
937
938     ### initscripts
939     def do_check_initscripts(self):
940         overall = True
941         for slice_spec in self.plc_spec['slices']:
942             if not slice_spec.has_key('initscriptname'):
943                 continue
944             initscript=slice_spec['initscriptname']
945             for nodename in slice_spec['nodenames']:
946                 (site,node) = self.locate_node (nodename)
947                 # xxx - passing the wrong site - probably harmless
948                 test_site = TestSite (self,site)
949                 test_slice = TestSlice (self,test_site,slice_spec)
950                 test_node = TestNode (self,test_site,node)
951                 test_sliver = TestSliver (self, test_node, test_slice)
952                 if not test_sliver.check_initscript(initscript):
953                     overall = False
954         return overall
955             
956     def check_initscripts(self):
957         "check that the initscripts have triggered"
958         return self.do_check_initscripts()
959     
960     def initscripts (self):
961         "create initscripts with PLCAPI"
962         for initscript in self.plc_spec['initscripts']:
963             utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
964             self.apiserver.AddInitScript(self.auth_root(),initscript['initscript_fields'])
965         return True
966
967     def clean_initscripts (self):
968         "delete initscripts with PLCAPI"
969         for initscript in self.plc_spec['initscripts']:
970             initscript_name = initscript['initscript_fields']['name']
971             print('Attempting to delete %s in plc %s'%(initscript_name,self.plc_spec['name']))
972             try:
973                 self.apiserver.DeleteInitScript(self.auth_root(),initscript_name)
974                 print initscript_name,'deleted'
975             except:
976                 print 'deletion went wrong - probably did not exist'
977         return True
978
979     ### manage slices
980     def slices (self):
981         "create slices with PLCAPI"
982         return self.do_slices()
983
984     def clean_slices (self):
985         "delete slices with PLCAPI"
986         return self.do_slices("delete")
987
988     def do_slices (self,  action="add"):
989         for slice in self.plc_spec['slices']:
990             site_spec = self.locate_site (slice['sitename'])
991             test_site = TestSite(self,site_spec)
992             test_slice=TestSlice(self,test_site,slice)
993             if action != "add":
994                 utils.header("Deleting slices in site %s"%test_site.name())
995                 test_slice.delete_slice()
996             else:    
997                 utils.pprint("Creating slice",slice)
998                 test_slice.create_slice()
999                 utils.header('Created Slice %s'%slice['slice_fields']['name'])
1000         return True
1001         
1002     @slice_mapper_options
1003     def check_slice(self): 
1004         "tries to ssh-enter the slice with the user key, to ensure slice creation"
1005         pass
1006
1007     @node_mapper
1008     def clear_known_hosts (self): 
1009         "remove test nodes entries from the local known_hosts file"
1010         pass
1011     
1012     @node_mapper
1013     def start_node (self) : 
1014         "all nodes: start the qemu instance (also runs qemu-bridge-init start)"
1015         pass
1016
1017     def check_tcp (self):
1018         "check TCP connectivity between 2 slices (or in loopback if only one is defined)"
1019         specs = self.plc_spec['tcp_test']
1020         overall=True
1021         for spec in specs:
1022             port = spec['port']
1023             # server side
1024             s_test_sliver = self.locate_sliver_obj (spec['server_node'],spec['server_slice'])
1025             if not s_test_sliver.run_tcp_server(port,timeout=10):
1026                 overall=False
1027                 break
1028
1029             # idem for the client side
1030             c_test_sliver = self.locate_sliver_obj(spec['server_node'],spec['server_slice'])
1031             if not c_test_sliver.run_tcp_client(s_test_sliver.test_node.name(),port):
1032                 overall=False
1033         return overall
1034
1035     def plcsh_stress_test (self):
1036         "runs PLCAPI stress test, that checks Add/Update/Delete on all types - preserves contents"
1037         # install the stress-test in the plc image
1038         location = "/usr/share/plc_api/plcsh_stress_test.py"
1039         remote="/vservers/%s/%s"%(self.vservername,location)
1040         self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1041         command = location
1042         command += " -- --check"
1043         if self.options.size == 1:
1044             command +=  " --tiny"
1045         return ( self.run_in_guest(command) == 0)
1046
1047     # populate runs the same utility without slightly different options
1048     # in particular runs with --preserve (dont cleanup) and without --check
1049     # also it gets run twice, once with the --foreign option for creating fake foreign entries
1050
1051     ### install_sfa_rpm
1052     def install_sfa(self):
1053         "yum install sfa, sfa-plc and sfa-client"
1054         if self.options.personality == "linux32":
1055             arch = "i386"
1056         elif self.options.personality == "linux64":
1057             arch = "x86_64"
1058         else:
1059             raise Exception, "Unsupported personality %r"%self.options.personality
1060         return self.run_in_guest("yum -y install sfa sfa-client sfa-plc sfa-sfatables")==0
1061
1062     ###
1063     def configure_sfa(self):
1064         "run sfa-config-tty"
1065         tmpname='%s.sfa-config-tty'%(self.name())
1066         fileconf=open(tmpname,'w')
1067         for var in [ 'SFA_REGISTRY_ROOT_AUTH',
1068                      'SFA_REGISTRY_LEVEL1_AUTH',
1069                      'SFA_REGISTRY_HOST',
1070                      'SFA_AGGREGATE_HOST',
1071                      'SFA_SM_HOST',
1072                      'SFA_PLC_USER',
1073                      'SFA_PLC_PASSWORD',
1074                      'SFA_PLC_DB_HOST',
1075                      'SFA_PLC_DB_USER',
1076                      'SFA_PLC_DB_PASSWORD',
1077                      'SFA_PLC_URL']:
1078             fileconf.write ('e %s\n%s\n'%(var,self.plc_spec['sfa'][var]))
1079         fileconf.write('w\n')
1080         fileconf.write('R\n')
1081         fileconf.write('q\n')
1082         fileconf.close()
1083         utils.system('cat %s'%tmpname)
1084         self.run_in_guest_piped('cat %s'%tmpname,'sfa-config-tty')
1085         utils.system('rm %s'%tmpname)
1086         return True
1087
1088     def import_sfa(self):
1089         "sfa-import-plc"
1090         auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1091         return self.run_in_guest('sfa-import-plc.py')==0
1092 # not needed anymore
1093 #        self.run_in_guest('cp /etc/sfa/authorities/%s/%s.pkey /etc/sfa/authorities/server.key'%(auth,auth))
1094
1095     def start_sfa(self):
1096         "service sfa start"
1097         return self.run_in_guest('service sfa start')==0
1098
1099     def setup_sfa(self):
1100         "sfi client configuration"
1101         dir_name=".sfi"
1102         if os.path.exists(dir_name):
1103            utils.system('rm -rf %s'%dir_name)
1104         utils.system('mkdir %s'%dir_name)
1105         file_name=dir_name + os.sep + 'fake-pi1.pkey'
1106         fileconf=open(file_name,'w')
1107         fileconf.write (self.plc_spec['keys'][0]['private'])
1108         fileconf.close()
1109
1110         file_name=dir_name + os.sep + 'sfi_config'
1111         fileconf=open(file_name,'w')
1112         SFI_AUTH=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']+".main"
1113         fileconf.write ("SFI_AUTH='%s'"%SFI_AUTH)
1114         fileconf.write('\n')
1115         SFI_USER=SFI_AUTH+'.fake-pi1'
1116         fileconf.write ("SFI_USER='%s'"%SFI_USER)
1117         fileconf.write('\n')
1118         SFI_REGISTRY='http://' + self.plc_spec['sfa']['SFA_PLC_DB_HOST'] + ':12345/'
1119         fileconf.write ("SFI_REGISTRY='%s'"%SFI_REGISTRY)
1120         fileconf.write('\n')
1121         SFI_SM='http://' + self.plc_spec['sfa']['SFA_PLC_DB_HOST'] + ':12347/'
1122         fileconf.write ("SFI_SM='%s'"%SFI_SM)
1123         fileconf.write('\n')
1124         fileconf.close()
1125
1126         file_name=dir_name + os.sep + 'person.xml'
1127         fileconf=open(file_name,'w')
1128         for record in self.plc_spec['sfa']['sfa_person_xml']:
1129            person_record=record
1130         fileconf.write(person_record)
1131         fileconf.write('\n')
1132         fileconf.close()
1133
1134         file_name=dir_name + os.sep + 'slice.xml'
1135         fileconf=open(file_name,'w')
1136         for record in self.plc_spec['sfa']['sfa_slice_xml']:
1137             slice_record=record
1138         #slice_record=self.plc_spec['sfa']['sfa_slice_xml']
1139         fileconf.write(slice_record)
1140         fileconf.write('\n')
1141         fileconf.close()
1142
1143         file_name=dir_name + os.sep + 'slice.rspec'
1144         fileconf=open(file_name,'w')
1145         slice_rspec=''
1146         for (key, value) in self.plc_spec['sfa']['sfa_slice_rspec'].items():
1147             slice_rspec +=value 
1148         fileconf.write(slice_rspec)
1149         fileconf.write('\n')
1150         fileconf.close()
1151         location = "root/"
1152         remote="/vservers/%s/%s"%(self.vservername,location)
1153         self.test_ssh.copy_abs(dir_name, remote, recursive=True)
1154
1155         #utils.system('cat %s'%tmpname)
1156         utils.system('rm -rf %s'%dir_name)
1157         return True
1158
1159     def add_sfa(self):
1160         "run sfi.py add (on Registry) and sfi.py create (on SM) to form new objects"
1161         test_plc=self
1162         test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1163         success=test_user_sfa.add_user()
1164
1165         for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1166             site_spec = self.locate_site (slice_spec['sitename'])
1167             test_site = TestSite(self,site_spec)
1168             test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1169             success1=test_slice_sfa.add_slice()
1170             success2=test_slice_sfa.create_slice()
1171         return success and success1 and success2
1172
1173     def update_sfa(self):
1174         "run sfi.py update (on Registry) and sfi.py create (on SM) on existing objects"
1175         test_plc=self
1176         test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1177         success1=test_user_sfa.update_user()
1178         
1179         for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1180             site_spec = self.locate_site (slice_spec['sitename'])
1181             test_site = TestSite(self,site_spec)
1182             test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1183             success2=test_slice_sfa.update_slice()
1184         return success1 and success2
1185
1186     def view_sfa(self):
1187         "run sfi.py list and sfi.py show (both on Registry) and sfi.py slices and sfi.py resources (both on SM)"
1188         auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1189         return \
1190         self.run_in_guest("sfi.py -d /root/.sfi/ list %s.main"%auth)==0 and \
1191         self.run_in_guest("sfi.py -d /root/.sfi/ show %s.main"%auth)==0 and \
1192         self.run_in_guest("sfi.py -d /root/.sfi/ slices")==0 and \
1193         self.run_in_guest("sfi.py -d /root/.sfi/ resources -o resources")==0
1194
1195     @slice_mapper_options_sfa
1196     def check_slice_sfa(self): 
1197         "tries to ssh-enter the SFA slice"
1198         pass
1199
1200     def delete_sfa(self):
1201         "run sfi.py delete (on SM), sfi.py remove (on Registry)"
1202         test_plc=self
1203         test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1204         success1=test_user_sfa.delete_user()
1205         for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1206             site_spec = self.locate_site (slice_spec['sitename'])
1207             test_site = TestSite(self,site_spec)
1208             test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1209             success2=test_slice_sfa.delete_slice()
1210
1211         return success1 and success2
1212
1213     def stop_sfa(self):
1214         "service sfa stop"
1215         return self.run_in_guest('service sfa stop')==0
1216
1217     def populate (self):
1218         "creates random entries in the PLCAPI"
1219         # install the stress-test in the plc image
1220         location = "/usr/share/plc_api/plcsh_stress_test.py"
1221         remote="/vservers/%s/%s"%(self.vservername,location)
1222         self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1223         command = location
1224         command += " -- --preserve --short-names"
1225         local = (self.run_in_guest(command) == 0);
1226         # second run with --foreign
1227         command += ' --foreign'
1228         remote = (self.run_in_guest(command) == 0);
1229         return ( local and remote)
1230
1231     def gather_logs (self):
1232         "gets all possible logs from plc's/qemu node's/slice's for future reference"
1233         # (1.a) get the plc's /var/log/ and store it locally in logs/myplc.var-log.<plcname>/*
1234         # (1.b) get the plc's  /var/lib/pgsql/data/pg_log/ -> logs/myplc.pgsql-log.<plcname>/*
1235         # (2) get all the nodes qemu log and store it as logs/node.qemu.<node>.log
1236         # (3) get the nodes /var/log and store is as logs/node.var-log.<node>/*
1237         # (4) as far as possible get the slice's /var/log as logs/sliver.var-log.<sliver>/*
1238         # (1.a)
1239         print "-------------------- TestPlc.gather_logs : PLC's /var/log"
1240         self.gather_var_logs ()
1241         # (1.b)
1242         print "-------------------- TestPlc.gather_logs : PLC's /var/lib/psql/data/pg_log/"
1243         self.gather_pgsql_logs ()
1244         # (2) 
1245         print "-------------------- TestPlc.gather_logs : nodes's QEMU logs"
1246         for site_spec in self.plc_spec['sites']:
1247             test_site = TestSite (self,site_spec)
1248             for node_spec in site_spec['nodes']:
1249                 test_node=TestNode(self,test_site,node_spec)
1250                 test_node.gather_qemu_logs()
1251         # (3)
1252         print "-------------------- TestPlc.gather_logs : nodes's /var/log"
1253         self.gather_nodes_var_logs()
1254         # (4)
1255         print "-------------------- TestPlc.gather_logs : sample sliver's /var/log"
1256         self.gather_slivers_var_logs()
1257         return True
1258
1259     def gather_slivers_var_logs(self):
1260         for test_sliver in self.all_sliver_objs():
1261             remote = test_sliver.tar_var_logs()
1262             utils.system("mkdir -p logs/sliver.var-log.%s"%test_sliver.name())
1263             command = remote + " | tar -C logs/sliver.var-log.%s -xf -"%test_sliver.name()
1264             utils.system(command)
1265         return True
1266
1267     def gather_var_logs (self):
1268         utils.system("mkdir -p logs/myplc.var-log.%s"%self.name())
1269         to_plc = self.actual_command_in_guest("tar -C /var/log/ -cf - .")        
1270         command = to_plc + "| tar -C logs/myplc.var-log.%s -xf -"%self.name()
1271         utils.system(command)
1272         command = "chmod a+r,a+x logs/myplc.var-log.%s/httpd"%self.name()
1273         utils.system(command)
1274
1275     def gather_pgsql_logs (self):
1276         utils.system("mkdir -p logs/myplc.pgsql-log.%s"%self.name())
1277         to_plc = self.actual_command_in_guest("tar -C /var/lib/pgsql/data/pg_log/ -cf - .")        
1278         command = to_plc + "| tar -C logs/myplc.pgsql-log.%s -xf -"%self.name()
1279         utils.system(command)
1280
1281     def gather_nodes_var_logs (self):
1282         for site_spec in self.plc_spec['sites']:
1283             test_site = TestSite (self,site_spec)
1284             for node_spec in site_spec['nodes']:
1285                 test_node=TestNode(self,test_site,node_spec)
1286                 test_ssh = TestSsh (test_node.name(),key="keys/key1.rsa")
1287                 command = test_ssh.actual_command("tar -C /var/log -cf - .")
1288                 command = command + "| tar -C logs/node.var-log.%s -xf -"%test_node.name()
1289                 utils.system("mkdir -p logs/node.var-log.%s"%test_node.name())
1290                 utils.system(command)
1291
1292
1293     # returns the filename to use for sql dump/restore, using options.dbname if set
1294     def dbfile (self, database):
1295         # uses options.dbname if it is found
1296         try:
1297             name=self.options.dbname
1298             if not isinstance(name,StringTypes):
1299                 raise Exception
1300         except:
1301             t=datetime.datetime.now()
1302             d=t.date()
1303             name=str(d)
1304         return "/root/%s-%s.sql"%(database,name)
1305
1306     def db_dump(self):
1307         'dump the planetlab5 DB in /root in the PLC - filename has time'
1308         dump=self.dbfile("planetab5")
1309         self.run_in_guest('pg_dump -U pgsqluser planetlab5 -f '+ dump)
1310         utils.header('Dumped planetlab5 database in %s'%dump)
1311         return True
1312
1313     def db_restore(self):
1314         'restore the planetlab5 DB - looks broken, but run -n might help'
1315         dump=self.dbfile("planetab5")
1316         ##stop httpd service
1317         self.run_in_guest('service httpd stop')
1318         # xxx - need another wrapper
1319         self.run_in_guest_piped('echo drop database planetlab5','psql --user=pgsqluser template1')
1320         self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab5')
1321         self.run_in_guest('psql -U pgsqluser planetlab5 -f '+dump)
1322         ##starting httpd service
1323         self.run_in_guest('service httpd start')
1324
1325         utils.header('Database restored from ' + dump)
1326
1327     @standby_generic 
1328     def standby_1(): pass
1329     @standby_generic 
1330     def standby_2(): pass
1331     @standby_generic 
1332     def standby_3(): pass
1333     @standby_generic 
1334     def standby_4(): pass
1335     @standby_generic 
1336     def standby_5(): pass
1337     @standby_generic 
1338     def standby_6(): pass
1339     @standby_generic 
1340     def standby_7(): pass
1341     @standby_generic 
1342     def standby_8(): pass
1343     @standby_generic 
1344     def standby_9(): pass
1345     @standby_generic 
1346     def standby_10(): pass
1347     @standby_generic 
1348     def standby_11(): pass
1349     @standby_generic 
1350     def standby_12(): pass
1351     @standby_generic 
1352     def standby_13(): pass
1353     @standby_generic 
1354     def standby_14(): pass
1355     @standby_generic 
1356     def standby_15(): pass
1357     @standby_generic 
1358     def standby_16(): pass
1359     @standby_generic 
1360     def standby_17(): pass
1361     @standby_generic 
1362     def standby_18(): pass
1363     @standby_generic 
1364     def standby_19(): pass
1365     @standby_generic 
1366     def standby_20(): pass