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