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