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