4911647f5906ec4bae36503c042407e726cfcfb1
[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         'safeboot_node','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 safeboot_node (self): 
938         "all nodes: mark PLCAPI boot_state as safeboot"
939         pass
940     @node_mapper
941     def export_qemu (self): 
942         "all nodes: push local node-dep directory on the qemu box"
943         pass
944         
945     ### check hooks : invoke scripts from hooks/{node,slice}
946     def check_hooks_node (self): 
947         return self.locate_first_node().check_hooks()
948     def check_hooks_sliver (self) : 
949         return self.locate_first_sliver().check_hooks()
950     
951     def check_hooks (self):
952         "runs unit tests in the node and slice contexts - see hooks/{node,slice}"
953         return self.check_hooks_node() and self.check_hooks_sliver()
954
955     ### initscripts
956     def do_check_initscripts(self):
957         overall = True
958         for slice_spec in self.plc_spec['slices']:
959             if not slice_spec.has_key('initscriptname'):
960                 continue
961             initscript=slice_spec['initscriptname']
962             for nodename in slice_spec['nodenames']:
963                 (site,node) = self.locate_node (nodename)
964                 # xxx - passing the wrong site - probably harmless
965                 test_site = TestSite (self,site)
966                 test_slice = TestSlice (self,test_site,slice_spec)
967                 test_node = TestNode (self,test_site,node)
968                 test_sliver = TestSliver (self, test_node, test_slice)
969                 if not test_sliver.check_initscript(initscript):
970                     overall = False
971         return overall
972             
973     def check_initscripts(self):
974         "check that the initscripts have triggered"
975         return self.do_check_initscripts()
976     
977     def initscripts (self):
978         "create initscripts with PLCAPI"
979         for initscript in self.plc_spec['initscripts']:
980             utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
981             self.apiserver.AddInitScript(self.auth_root(),initscript['initscript_fields'])
982         return True
983
984     def clean_initscripts (self):
985         "delete initscripts with PLCAPI"
986         for initscript in self.plc_spec['initscripts']:
987             initscript_name = initscript['initscript_fields']['name']
988             print('Attempting to delete %s in plc %s'%(initscript_name,self.plc_spec['name']))
989             try:
990                 self.apiserver.DeleteInitScript(self.auth_root(),initscript_name)
991                 print initscript_name,'deleted'
992             except:
993                 print 'deletion went wrong - probably did not exist'
994         return True
995
996     ### manage slices
997     def slices (self):
998         "create slices with PLCAPI"
999         return self.do_slices()
1000
1001     def clean_slices (self):
1002         "delete slices with PLCAPI"
1003         return self.do_slices("delete")
1004
1005     def do_slices (self,  action="add"):
1006         for slice in self.plc_spec['slices']:
1007             site_spec = self.locate_site (slice['sitename'])
1008             test_site = TestSite(self,site_spec)
1009             test_slice=TestSlice(self,test_site,slice)
1010             if action != "add":
1011                 utils.header("Deleting slices in site %s"%test_site.name())
1012                 test_slice.delete_slice()
1013             else:    
1014                 utils.pprint("Creating slice",slice)
1015                 test_slice.create_slice()
1016                 utils.header('Created Slice %s'%slice['slice_fields']['name'])
1017         return True
1018         
1019     @slice_mapper
1020     def check_slice(self): 
1021         "tries to ssh-enter the slice with the user key, to ensure slice creation"
1022         pass
1023
1024     @node_mapper
1025     def clear_known_hosts (self): 
1026         "remove test nodes entries from the local known_hosts file"
1027         pass
1028     
1029     @node_mapper
1030     def start_node (self) : 
1031         "all nodes: start the qemu instance (also runs qemu-bridge-init start)"
1032         pass
1033
1034     def check_tcp (self):
1035         "check TCP connectivity between 2 slices (or in loopback if only one is defined)"
1036         specs = self.plc_spec['tcp_test']
1037         overall=True
1038         for spec in specs:
1039             port = spec['port']
1040             # server side
1041             s_test_sliver = self.locate_sliver_obj (spec['server_node'],spec['server_slice'])
1042             if not s_test_sliver.run_tcp_server(port,timeout=10):
1043                 overall=False
1044                 break
1045
1046             # idem for the client side
1047             c_test_sliver = self.locate_sliver_obj(spec['server_node'],spec['server_slice'])
1048             if not c_test_sliver.run_tcp_client(s_test_sliver.test_node.name(),port):
1049                 overall=False
1050         return overall
1051
1052     def plcsh_stress_test (self):
1053         "runs PLCAPI stress test, that checks Add/Update/Delete on all types - preserves contents"
1054         # install the stress-test in the plc image
1055         location = "/usr/share/plc_api/plcsh_stress_test.py"
1056         remote="/vservers/%s/%s"%(self.vservername,location)
1057         self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1058         command = location
1059         command += " -- --check"
1060         if self.options.size == 1:
1061             command +=  " --tiny"
1062         return ( self.run_in_guest(command) == 0)
1063
1064     # populate runs the same utility without slightly different options
1065     # in particular runs with --preserve (dont cleanup) and without --check
1066     # also it gets run twice, once with the --foreign option for creating fake foreign entries
1067
1068     ### install_sfa_rpm
1069     def install_sfa(self):
1070         "yum install sfa, sfa-plc and sfa-client"
1071         # ignore yum retcod
1072         self.run_in_guest("yum -y install sfa sfa-client sfa-plc sfa-sfatables")
1073         return  self.run_in_guest("rpm -q sfa sfa-client sfa-plc sfa-sfatables")==0
1074         
1075
1076     def dbclean_sfa(self):
1077         "thoroughly wipes off the SFA database"
1078         self.run_in_guest("sfa-nuke-plc.py")==0
1079         return True
1080
1081     def plcclean_sfa(self):
1082         "cleans the PLC entries that were created as a side effect of running the script"
1083         # ignore result 
1084         sfa_spec=self.plc_spec['sfa']
1085
1086         slicename='%s_%s'%(sfa_spec['login_base'],sfa_spec['slicename'])
1087         try: self.apiserver.DeleteSlice(self.auth_root(),slicename)
1088         except: print "Slice %s already absent from PLC db"%slicename
1089
1090         username="%s@%s"%(sfa_spec['regularuser'],sfa_spec['domain'])
1091         try: self.apiserver.DeletePerson(self.auth_root(),username)
1092         except: print "User %s already absent from PLC db"%username
1093
1094         print "REMEMBER TO RUN import_sfa AGAIN"
1095         return True
1096
1097     def uninstall_sfa(self):
1098         "uses rpm to uninstall sfa - ignore result"
1099         self.run_in_guest("rpm -e sfa sfa-sfatables sfa-client sfa-plc")
1100         self.run_in_guest("rm -rf /var/lib/sfa")
1101         self.run_in_guest("rm -rf /etc/sfa")
1102         self.run_in_guest("rm -rf /var/log/sfa_access.log /var/log/sfa_import_plc.log /var/log/sfa.daemon")
1103         # xxx tmp 
1104         self.run_in_guest("rpm -e --noscripts sfa-plc")
1105         return True
1106
1107     ### install_sfa_rpm
1108     def install_unittest_sfa(self):
1109         "yum install sfa-tests"
1110         # ignore yum retcod
1111         self.run_in_guest("yum -y install sfa-tests")
1112         return  self.run_in_guest("rpm -q sfa-tests")==0
1113
1114     def unittest_sfa(self):
1115         "run SFA unittests"
1116         return self.run_in_guest("/usr/share/sfa/tests/testAll.py")==0
1117
1118     ###
1119     def confdir(self):
1120         dirname="conf.%s"%self.plc_spec['name']
1121         if not os.path.isdir(dirname):
1122             utils.system("mkdir -p %s"%dirname)
1123         if not os.path.isdir(dirname):
1124             raise "Cannot create config dir for plc %s"%self.name()
1125         return dirname
1126
1127     def conffile(self,filename):
1128         return "%s/%s"%(self.confdir(),filename)
1129     def confsubdir(self,dirname,clean):
1130         subdirname="%s/%s"%(self.confdir(),dirname)
1131         if clean:
1132             utils.system("rm -rf %s"%subdirname)
1133         if not os.path.isdir(subdirname): 
1134             utils.system("mkdir -p %s"%subdirname)
1135         if not os.path.isdir(subdirname):
1136             raise "Cannot create config subdir %s for plc %s"%(dirname,self.name())
1137         return subdirname
1138         
1139     def conffile_clean (self,filename):
1140         filename=self.conffile(filename)
1141         return utils.system("rm -rf %s"%filename)==0
1142     
1143     ###
1144     def configure_sfa(self):
1145         "run sfa-config-tty"
1146         tmpname=self.conffile("sfa-config-tty")
1147         fileconf=open(tmpname,'w')
1148         for var in [ 'SFA_REGISTRY_ROOT_AUTH',
1149                      'SFA_INTERFACE_HRN',
1150 #                     'SFA_REGISTRY_LEVEL1_AUTH',
1151                      'SFA_REGISTRY_HOST',
1152                      'SFA_AGGREGATE_HOST',
1153                      'SFA_SM_HOST',
1154                      'SFA_PLC_USER',
1155                      'SFA_PLC_PASSWORD',
1156                      'SFA_PLC_DB_HOST',
1157                      'SFA_PLC_DB_USER',
1158                      'SFA_PLC_DB_PASSWORD',
1159                      'SFA_PLC_URL',
1160                      ]:
1161             fileconf.write ('e %s\n%s\n'%(var,self.plc_spec['sfa'][var]))
1162         # the way plc_config handles booleans just sucks..
1163         for var in ['SFA_API_DEBUG']:
1164             val='false'
1165             if self.plc_spec['sfa'][var]: val='true'
1166             fileconf.write ('e %s\n%s\n'%(var,val))
1167         fileconf.write('w\n')
1168         fileconf.write('R\n')
1169         fileconf.write('q\n')
1170         fileconf.close()
1171         utils.system('cat %s'%tmpname)
1172         self.run_in_guest_piped('cat %s'%tmpname,'sfa-config-tty')
1173         return True
1174
1175     def aggregate_xml_line(self):
1176         return '<aggregate addr="%s" hrn="%s" port="12346"/>' % \
1177             (self.vserverip,self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH'])
1178
1179     def registry_xml_line(self):
1180         return '<registry addr="%s" hrn="%s" port="12345"/>' % \
1181             (self.vserverip,self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH'])
1182
1183
1184     # a cross step that takes all other plcs in argument
1185     def cross_configure_sfa(self, other_plcs):
1186         # of course with a single plc, other_plcs is an empty list
1187         if not other_plcs:
1188             return True
1189         agg_fname=self.conffile("agg.xml")
1190         file(agg_fname,"w").write("<aggregates>%s</aggregates>\n" % \
1191                                      " ".join([ plc.aggregate_xml_line() for plc in other_plcs ]))
1192         utils.header ("(Over)wrote %s"%agg_fname)
1193         reg_fname=self.conffile("reg.xml")
1194         file(reg_fname,"w").write("<registries>%s</registries>\n" % \
1195                                      " ".join([ plc.registry_xml_line() for plc in other_plcs ]))
1196         utils.header ("(Over)wrote %s"%reg_fname)
1197         return self.test_ssh.copy_abs(agg_fname,'/vservers/%s/etc/sfa/aggregates.xml'%self.vservername)==0 \
1198             and  self.test_ssh.copy_abs(reg_fname,'/vservers/%s/etc/sfa/registries.xml'%self.vservername)==0
1199
1200     def import_sfa(self):
1201         "sfa-import-plc"
1202         auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1203         return self.run_in_guest('sfa-import-plc.py')==0
1204 # not needed anymore
1205 #        self.run_in_guest('cp /etc/sfa/authorities/%s/%s.pkey /etc/sfa/authorities/server.key'%(auth,auth))
1206
1207     def start_sfa(self):
1208         "service sfa start"
1209         return self.run_in_guest('service sfa start')==0
1210
1211     def configure_sfi(self):
1212         sfa_spec=self.plc_spec['sfa']
1213         "sfi client configuration"
1214         dir_name=self.confsubdir("dot-sfi",clean=True)
1215         file_name=dir_name + os.sep + sfa_spec['piuser'] + '.pkey'
1216         fileconf=open(file_name,'w')
1217         fileconf.write (self.plc_spec['keys'][0]['private'])
1218         fileconf.close()
1219         utils.header ("(Over)wrote %s"%file_name)
1220
1221         file_name=dir_name + os.sep + 'sfi_config'
1222         fileconf=open(file_name,'w')
1223         SFI_AUTH="%s.%s"%(sfa_spec['SFA_REGISTRY_ROOT_AUTH'],sfa_spec['login_base'])
1224         fileconf.write ("SFI_AUTH='%s'"%SFI_AUTH)
1225         fileconf.write('\n')
1226         SFI_USER=SFI_AUTH + '.' + sfa_spec['piuser']
1227         fileconf.write ("SFI_USER='%s'"%SFI_USER)
1228         fileconf.write('\n')
1229         SFI_REGISTRY='http://' + sfa_spec['SFA_PLC_DB_HOST'] + ':12345/'
1230         fileconf.write ("SFI_REGISTRY='%s'"%SFI_REGISTRY)
1231         fileconf.write('\n')
1232         SFI_SM='http://' + sfa_spec['SFA_PLC_DB_HOST'] + ':12347/'
1233         fileconf.write ("SFI_SM='%s'"%SFI_SM)
1234         fileconf.write('\n')
1235         fileconf.close()
1236         utils.header ("(Over)wrote %s"%file_name)
1237
1238         file_name=dir_name + os.sep + 'person.xml'
1239         fileconf=open(file_name,'w')
1240         for record in sfa_spec['sfa_person_xml']:
1241            person_record=record
1242         fileconf.write(person_record)
1243         fileconf.write('\n')
1244         fileconf.close()
1245         utils.header ("(Over)wrote %s"%file_name)
1246
1247         file_name=dir_name + os.sep + 'slice.xml'
1248         fileconf=open(file_name,'w')
1249         for record in sfa_spec['sfa_slice_xml']:
1250             slice_record=record
1251         #slice_record=sfa_spec['sfa_slice_xml']
1252         fileconf.write(slice_record)
1253         fileconf.write('\n')
1254         utils.header ("(Over)wrote %s"%file_name)
1255         fileconf.close()
1256
1257         file_name=dir_name + os.sep + 'slice.rspec'
1258         fileconf=open(file_name,'w')
1259         slice_rspec=''
1260         for (key, value) in sfa_spec['sfa_slice_rspec'].items():
1261             slice_rspec +=value 
1262         fileconf.write(slice_rspec)
1263         fileconf.write('\n')
1264         fileconf.close()
1265         utils.header ("(Over)wrote %s"%file_name)
1266         
1267         # push to the remote root's .sfi
1268         location = "root/.sfi"
1269         remote="/vservers/%s/%s"%(self.vservername,location)
1270         self.test_ssh.copy_abs(dir_name, remote, recursive=True)
1271
1272         return True
1273
1274     def clean_sfi (self):
1275         self.run_in_guest("rm -rf /root/.sfi")
1276         return True
1277
1278     def add_user_sfa(self):
1279         return TestUserSfa(self).add_user()
1280
1281     @slice_sfa_mapper
1282     def add_sfa(self):
1283         "run sfi.py add (on Registry)"
1284         pass
1285
1286     @slice_sfa_mapper
1287     def create_sfa(self):
1288         "run sfi.py create (on SM) for 1st-time creation"
1289         pass
1290
1291     def update_user_sfa(self):
1292         return TestUserSfa(self).update_user()
1293
1294     @slice_sfa_mapper
1295     def update_sfa(self):
1296         "run sfi.py create (on SM) on existing object"
1297         pass
1298
1299     def view_sfa(self):
1300         "run sfi.py list and sfi.py show (both on Registry) and sfi.py slices and sfi.py resources (both on SM)"
1301         sfa_spec=self.plc_spec['sfa']
1302         auth=sfa_spec['SFA_REGISTRY_ROOT_AUTH']
1303         return \
1304         self.run_in_guest("sfi.py -d /root/.sfi/ list %s.%s"%(auth,sfa_spec['login_base']))==0 and \
1305         self.run_in_guest("sfi.py -d /root/.sfi/ show %s.%s"%(auth,sfa_spec['login_base']))==0 and \
1306         self.run_in_guest("sfi.py -d /root/.sfi/ slices")==0 and \
1307         self.run_in_guest("sfi.py -d /root/.sfi/ resources -o resources")==0
1308
1309     @slice_sfa_mapper
1310     def check_slice_sfa(self): 
1311         "tries to ssh-enter the SFA slice"
1312         pass
1313
1314     def delete_user_sfa(self):
1315         "run sfi.py delete (on SM) for user"
1316         test_user_sfa=TestUserSfa(self)
1317         return test_user_sfa.delete_user()
1318
1319     @slice_sfa_mapper
1320     def delete_slice_sfa(self):
1321         "run sfi.py delete (on SM), sfi.py remove (on Registry) to clean slices"
1322         pass
1323
1324     def stop_sfa(self):
1325         "service sfa stop"
1326         self.run_in_guest('service sfa stop')==0
1327         return True
1328
1329     def populate (self):
1330         "creates random entries in the PLCAPI"
1331         # install the stress-test in the plc image
1332         location = "/usr/share/plc_api/plcsh_stress_test.py"
1333         remote="/vservers/%s/%s"%(self.vservername,location)
1334         self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1335         command = location
1336         command += " -- --preserve --short-names"
1337         local = (self.run_in_guest(command) == 0);
1338         # second run with --foreign
1339         command += ' --foreign'
1340         remote = (self.run_in_guest(command) == 0);
1341         return ( local and remote)
1342
1343     def gather_logs (self):
1344         "gets all possible logs from plc's/qemu node's/slice's for future reference"
1345         # (1.a) get the plc's /var/log/ and store it locally in logs/myplc.var-log.<plcname>/*
1346         # (1.b) get the plc's  /var/lib/pgsql/data/pg_log/ -> logs/myplc.pgsql-log.<plcname>/*
1347         # (2) get all the nodes qemu log and store it as logs/node.qemu.<node>.log
1348         # (3) get the nodes /var/log and store is as logs/node.var-log.<node>/*
1349         # (4) as far as possible get the slice's /var/log as logs/sliver.var-log.<sliver>/*
1350         # (1.a)
1351         print "-------------------- TestPlc.gather_logs : PLC's /var/log"
1352         self.gather_var_logs ()
1353         # (1.b)
1354         print "-------------------- TestPlc.gather_logs : PLC's /var/lib/psql/data/pg_log/"
1355         self.gather_pgsql_logs ()
1356         # (2) 
1357         print "-------------------- TestPlc.gather_logs : nodes's QEMU logs"
1358         for site_spec in self.plc_spec['sites']:
1359             test_site = TestSite (self,site_spec)
1360             for node_spec in site_spec['nodes']:
1361                 test_node=TestNode(self,test_site,node_spec)
1362                 test_node.gather_qemu_logs()
1363         # (3)
1364         print "-------------------- TestPlc.gather_logs : nodes's /var/log"
1365         self.gather_nodes_var_logs()
1366         # (4)
1367         print "-------------------- TestPlc.gather_logs : sample sliver's /var/log"
1368         self.gather_slivers_var_logs()
1369         return True
1370
1371     def gather_slivers_var_logs(self):
1372         for test_sliver in self.all_sliver_objs():
1373             remote = test_sliver.tar_var_logs()
1374             utils.system("mkdir -p logs/sliver.var-log.%s"%test_sliver.name())
1375             command = remote + " | tar -C logs/sliver.var-log.%s -xf -"%test_sliver.name()
1376             utils.system(command)
1377         return True
1378
1379     def gather_var_logs (self):
1380         utils.system("mkdir -p logs/myplc.var-log.%s"%self.name())
1381         to_plc = self.actual_command_in_guest("tar -C /var/log/ -cf - .")        
1382         command = to_plc + "| tar -C logs/myplc.var-log.%s -xf -"%self.name()
1383         utils.system(command)
1384         command = "chmod a+r,a+x logs/myplc.var-log.%s/httpd"%self.name()
1385         utils.system(command)
1386
1387     def gather_pgsql_logs (self):
1388         utils.system("mkdir -p logs/myplc.pgsql-log.%s"%self.name())
1389         to_plc = self.actual_command_in_guest("tar -C /var/lib/pgsql/data/pg_log/ -cf - .")        
1390         command = to_plc + "| tar -C logs/myplc.pgsql-log.%s -xf -"%self.name()
1391         utils.system(command)
1392
1393     def gather_nodes_var_logs (self):
1394         for site_spec in self.plc_spec['sites']:
1395             test_site = TestSite (self,site_spec)
1396             for node_spec in site_spec['nodes']:
1397                 test_node=TestNode(self,test_site,node_spec)
1398                 test_ssh = TestSsh (test_node.name(),key="keys/key1.rsa")
1399                 command = test_ssh.actual_command("tar -C /var/log -cf - .")
1400                 command = command + "| tar -C logs/node.var-log.%s -xf -"%test_node.name()
1401                 utils.system("mkdir -p logs/node.var-log.%s"%test_node.name())
1402                 utils.system(command)
1403
1404
1405     # returns the filename to use for sql dump/restore, using options.dbname if set
1406     def dbfile (self, database):
1407         # uses options.dbname if it is found
1408         try:
1409             name=self.options.dbname
1410             if not isinstance(name,StringTypes):
1411                 raise Exception
1412         except:
1413             t=datetime.datetime.now()
1414             d=t.date()
1415             name=str(d)
1416         return "/root/%s-%s.sql"%(database,name)
1417
1418     def db_dump(self):
1419         'dump the planetlab5 DB in /root in the PLC - filename has time'
1420         dump=self.dbfile("planetab5")
1421         self.run_in_guest('pg_dump -U pgsqluser planetlab5 -f '+ dump)
1422         utils.header('Dumped planetlab5 database in %s'%dump)
1423         return True
1424
1425     def db_restore(self):
1426         'restore the planetlab5 DB - looks broken, but run -n might help'
1427         dump=self.dbfile("planetab5")
1428         ##stop httpd service
1429         self.run_in_guest('service httpd stop')
1430         # xxx - need another wrapper
1431         self.run_in_guest_piped('echo drop database planetlab5','psql --user=pgsqluser template1')
1432         self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab5')
1433         self.run_in_guest('psql -U pgsqluser planetlab5 -f '+dump)
1434         ##starting httpd service
1435         self.run_in_guest('service httpd start')
1436
1437         utils.header('Database restored from ' + dump)
1438
1439     @standby_generic 
1440     def standby_1(): pass
1441     @standby_generic 
1442     def standby_2(): pass
1443     @standby_generic 
1444     def standby_3(): pass
1445     @standby_generic 
1446     def standby_4(): pass
1447     @standby_generic 
1448     def standby_5(): pass
1449     @standby_generic 
1450     def standby_6(): pass
1451     @standby_generic 
1452     def standby_7(): pass
1453     @standby_generic 
1454     def standby_8(): pass
1455     @standby_generic 
1456     def standby_9(): pass
1457     @standby_generic 
1458     def standby_10(): pass
1459     @standby_generic 
1460     def standby_11(): pass
1461     @standby_generic 
1462     def standby_12(): pass
1463     @standby_generic 
1464     def standby_13(): pass
1465     @standby_generic 
1466     def standby_14(): pass
1467     @standby_generic 
1468     def standby_15(): pass
1469     @standby_generic 
1470     def standby_16(): pass
1471     @standby_generic 
1472     def standby_17(): pass
1473     @standby_generic 
1474     def standby_18(): pass
1475     @standby_generic 
1476     def standby_19(): pass
1477     @standby_generic 
1478     def standby_20(): pass