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