8fabf8c0de60b4961a9bf8f2558dc66adb5be554
[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_options (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_mapper_options_sfa (method):
70     def actual(self):
71         test_plc=self
72         overall=True
73         slice_method = TestSliceSfa.__dict__[method.__name__]
74         for slice_spec in self.plc_spec['sfa']['sfa_slice_specs']:
75             site_spec = self.locate_site (slice_spec['sitename'])
76             test_site = TestSite(self,site_spec)
77             test_slice=TestSliceSfa(test_plc,test_site,slice_spec)
78             if not slice_method(test_slice,self.options): overall=False
79         return overall
80     # restore the doc text
81     actual.__doc__=method.__doc__
82     return actual
83
84 SEP='<sep>'
85 SEPSFA='<sep_sfa>'
86
87 class TestPlc:
88
89     default_steps = [
90         'display', 'resources_pre', SEP,
91         'delete_vs','create_vs','install', 'configure', 'start', SEP,
92         'fetch_keys', 'store_keys', 'clear_known_hosts', SEP,
93         'initscripts', 'sites', 'nodes', 'slices', 'nodegroups', 'leases', SEP,
94         'reinstall_node', 'init_node','bootcd', 'configure_qemu', 'export_qemu',
95         'kill_all_qemus', 'start_node', SEP,
96         # better use of time: do this now that the nodes are taking off
97         'plcsh_stress_test', SEP,
98         'install_sfa', 'configure_sfa', 'cross_configure_sfa', 'import_sfa', 'start_sfa', SEPSFA,
99         'configure_sfi', 'add_sfa', 'update_sfa', 'view_sfa', SEPSFA,
100         'nodes_ssh_debug', 'nodes_ssh_boot', 'check_slice', 'check_initscripts', SEPSFA,
101         'check_slice_sfa', 'delete_sfa', 'stop_sfa', SEPSFA,
102         'check_tcp',  'check_hooks',  SEP,
103         'force_gather_logs', 'force_resources_post', SEP,
104         ]
105     other_steps = [ 
106         'show_boxes', 'resources_list','resources_release','resources_release_plc','resources_release_qemu',SEP,
107         'stop', 'vs_start', SEP,
108         'clean_initscripts', 'clean_nodegroups','clean_all_sites', SEP,
109         'clean_sites', 'clean_nodes', 'clean_slices', 'clean_keys', SEP,
110         'clean_leases', 'list_leases', SEP,
111         'populate' , SEP,
112         'list_all_qemus', 'list_qemus', 'kill_qemus', SEP,
113         'plcclean_sfa', 'dbclean_sfa', 'uninstall_sfa', 'clean_sfi', SEP,
114         'db_dump' , 'db_restore', SEP,
115         'standby_1 through 20',SEP,
116         ]
117
118     @staticmethod
119     def printable_steps (list):
120         single_line=" ".join(list)+" "
121         return single_line.replace(" "+SEP+" "," \\\n").replace(" "+SEPSFA+" "," \\\n")
122     @staticmethod
123     def valid_step (step):
124         return step != SEP and step != SEPSFA
125
126     # turn off the sfa-related steps when build has skipped SFA
127     # this is originally for centos5 as recent SFAs won't build on this platformb
128     @staticmethod
129     def check_whether_build_has_sfa (rpms_url):
130         # warning, we're now building 'sface' so let's be a bit more picky
131         retcod=os.system ("curl --silent %s/ | grep -q sfa-"%rpms_url)
132         # full builds are expected to return with 0 here
133         if retcod!=0:
134             # move all steps containing 'sfa' from default_steps to other_steps
135             sfa_steps= [ step for step in TestPlc.default_steps if step.find('sfa')>=0 ]
136             TestPlc.other_steps += sfa_steps
137             for step in sfa_steps: TestPlc.default_steps.remove(step)
138
139     def __init__ (self,plc_spec,options):
140         self.plc_spec=plc_spec
141         self.options=options
142         self.test_ssh=TestSsh(self.plc_spec['hostname'],self.options.buildname)
143         try:
144             self.vserverip=plc_spec['vserverip']
145             self.vservername=plc_spec['vservername']
146             self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
147             self.vserver=True
148         except:
149             raise Exception,'chroot-based myplc testing is deprecated'
150         self.apiserver=TestApiserver(self.url,options.dry_run)
151         
152     def name(self):
153         name=self.plc_spec['name']
154         return "%s.%s"%(name,self.vservername)
155
156     def hostname(self):
157         return self.plc_spec['hostname']
158
159     def is_local (self):
160         return self.test_ssh.is_local()
161
162     # define the API methods on this object through xmlrpc
163     # would help, but not strictly necessary
164     def connect (self):
165         pass
166
167     def actual_command_in_guest (self,command):
168         return self.test_ssh.actual_command(self.host_to_guest(command))
169     
170     def start_guest (self):
171       return utils.system(self.test_ssh.actual_command(self.start_guest_in_host()))
172     
173     def run_in_guest (self,command):
174         return utils.system(self.actual_command_in_guest(command))
175     
176     def run_in_host (self,command):
177         return self.test_ssh.run_in_buildname(command)
178
179     #command gets run in the vserver
180     def host_to_guest(self,command):
181         return "vserver %s exec %s"%(self.vservername,command)
182     
183     #command gets run in the vserver
184     def start_guest_in_host(self):
185         return "vserver %s start"%(self.vservername)
186     
187     # xxx quick n dirty
188     def run_in_guest_piped (self,local,remote):
189         return utils.system(local+" | "+self.test_ssh.actual_command(self.host_to_guest(remote),keep_stdin=True))
190
191     def auth_root (self):
192         return {'Username':self.plc_spec['PLC_ROOT_USER'],
193                 'AuthMethod':'password',
194                 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
195                 'Role' : self.plc_spec['role']
196                 }
197     def locate_site (self,sitename):
198         for site in self.plc_spec['sites']:
199             if site['site_fields']['name'] == sitename:
200                 return site
201             if site['site_fields']['login_base'] == sitename:
202                 return site
203         raise Exception,"Cannot locate site %s"%sitename
204         
205     def locate_node (self,nodename):
206         for site in self.plc_spec['sites']:
207             for node in site['nodes']:
208                 if node['name'] == nodename:
209                     return (site,node)
210         raise Exception,"Cannot locate node %s"%nodename
211         
212     def locate_hostname (self,hostname):
213         for site in self.plc_spec['sites']:
214             for node in site['nodes']:
215                 if node['node_fields']['hostname'] == hostname:
216                     return (site,node)
217         raise Exception,"Cannot locate hostname %s"%hostname
218         
219     def locate_key (self,keyname):
220         for key in self.plc_spec['keys']:
221             if key['name'] == keyname:
222                 return key
223         raise Exception,"Cannot locate key %s"%keyname
224
225     def locate_slice (self, slicename):
226         for slice in self.plc_spec['slices']:
227             if slice['slice_fields']['name'] == slicename:
228                 return slice
229         raise Exception,"Cannot locate slice %s"%slicename
230
231     def all_sliver_objs (self):
232         result=[]
233         for slice_spec in self.plc_spec['slices']:
234             slicename = slice_spec['slice_fields']['name']
235             for nodename in slice_spec['nodenames']:
236                 result.append(self.locate_sliver_obj (nodename,slicename))
237         return result
238
239     def locate_sliver_obj (self,nodename,slicename):
240         (site,node) = self.locate_node(nodename)
241         slice = self.locate_slice (slicename)
242         # build objects
243         test_site = TestSite (self, site)
244         test_node = TestNode (self, test_site,node)
245         # xxx the slice site is assumed to be the node site - mhh - probably harmless
246         test_slice = TestSlice (self, test_site, slice)
247         return TestSliver (self, test_node, test_slice)
248
249     def locate_first_node(self):
250         nodename=self.plc_spec['slices'][0]['nodenames'][0]
251         (site,node) = self.locate_node(nodename)
252         test_site = TestSite (self, site)
253         test_node = TestNode (self, test_site,node)
254         return test_node
255
256     def locate_first_sliver (self):
257         slice_spec=self.plc_spec['slices'][0]
258         slicename=slice_spec['slice_fields']['name']
259         nodename=slice_spec['nodenames'][0]
260         return self.locate_sliver_obj(nodename,slicename)
261
262     # all different hostboxes used in this plc
263     def gather_hostBoxes(self):
264         # maps on sites and nodes, return [ (host_box,test_node) ]
265         tuples=[]
266         for site_spec in self.plc_spec['sites']:
267             test_site = TestSite (self,site_spec)
268             for node_spec in site_spec['nodes']:
269                 test_node = TestNode (self, test_site, node_spec)
270                 if not test_node.is_real():
271                     tuples.append( (test_node.host_box(),test_node) )
272         # transform into a dict { 'host_box' -> [ test_node .. ] }
273         result = {}
274         for (box,node) in tuples:
275             if not result.has_key(box):
276                 result[box]=[node]
277             else:
278                 result[box].append(node)
279         return result
280                     
281     # a step for checking this stuff
282     def show_boxes (self):
283         'print summary of nodes location'
284         for (box,nodes) in self.gather_hostBoxes().iteritems():
285             print box,":"," + ".join( [ node.name() for node in nodes ] )
286         return True
287
288     # make this a valid step
289     def kill_all_qemus(self):
290         'kill all qemu instances on the qemu boxes involved by this setup'
291         # this is the brute force version, kill all qemus on that host box
292         for (box,nodes) in self.gather_hostBoxes().iteritems():
293             # pass the first nodename, as we don't push template-qemu on testboxes
294             nodedir=nodes[0].nodedir()
295             TestBox(box,self.options.buildname).kill_all_qemus(nodedir)
296         return True
297
298     # make this a valid step
299     def list_all_qemus(self):
300         'list all qemu instances on the qemu boxes involved by this setup'
301         for (box,nodes) in self.gather_hostBoxes().iteritems():
302             # this is the brute force version, kill all qemus on that host box
303             TestBox(box,self.options.buildname).list_all_qemus()
304         return True
305
306     # kill only the right qemus
307     def list_qemus(self):
308         'list qemu instances for our nodes'
309         for (box,nodes) in self.gather_hostBoxes().iteritems():
310             # the fine-grain version
311             for node in nodes:
312                 node.list_qemu()
313         return True
314
315     # kill only the right qemus
316     def kill_qemus(self):
317         'kill the qemu instances for our nodes'
318         for (box,nodes) in self.gather_hostBoxes().iteritems():
319             # the fine-grain version
320             for node in nodes:
321                 node.kill_qemu()
322         return True
323
324     #################### display config
325     def display (self):
326         "show test configuration after localization"
327         self.display_pass (1)
328         self.display_pass (2)
329         return True
330
331     # entry point
332     always_display_keys=['PLC_WWW_HOST','nodes','sites',]
333     def display_pass (self,passno):
334         for (key,val) in self.plc_spec.iteritems():
335             if not self.options.verbose and key not in TestPlc.always_display_keys: continue
336             if passno == 2:
337                 if key == 'sites':
338                     for site in val:
339                         self.display_site_spec(site)
340                         for node in site['nodes']:
341                             self.display_node_spec(node)
342                 elif key=='initscripts':
343                     for initscript in val:
344                         self.display_initscript_spec (initscript)
345                 elif key=='slices':
346                     for slice in val:
347                         self.display_slice_spec (slice)
348                 elif key=='keys':
349                     for key in val:
350                         self.display_key_spec (key)
351             elif passno == 1:
352                 if key not in ['sites','initscripts','slices','keys', 'sfa']:
353                     print '+   ',key,':',val
354
355     def display_site_spec (self,site):
356         print '+ ======== site',site['site_fields']['name']
357         for (k,v) in site.iteritems():
358             if not self.options.verbose and k not in TestPlc.always_display_keys: continue
359             if k=='nodes':
360                 if v: 
361                     print '+       ','nodes : ',
362                     for node in v:  
363                         print node['node_fields']['hostname'],'',
364                     print ''
365             elif k=='users':
366                 if v: 
367                     print '+       users : ',
368                     for user in v:  
369                         print user['name'],'',
370                     print ''
371             elif k == 'site_fields':
372                 print '+       login_base',':',v['login_base']
373             elif k == 'address_fields':
374                 pass
375             else:
376                 print '+       ',
377                 utils.pprint(k,v)
378         
379     def display_initscript_spec (self,initscript):
380         print '+ ======== initscript',initscript['initscript_fields']['name']
381
382     def display_key_spec (self,key):
383         print '+ ======== key',key['name']
384
385     def display_slice_spec (self,slice):
386         print '+ ======== slice',slice['slice_fields']['name']
387         for (k,v) in slice.iteritems():
388             if k=='nodenames':
389                 if v: 
390                     print '+       nodes : ',
391                     for nodename in v:  
392                         print nodename,'',
393                     print ''
394             elif k=='usernames':
395                 if v: 
396                     print '+       users : ',
397                     for username in v:  
398                         print username,'',
399                     print ''
400             elif k=='slice_fields':
401                 print '+       fields',':',
402                 print 'max_nodes=',v['max_nodes'],
403                 print ''
404             else:
405                 print '+       ',k,v
406
407     def display_node_spec (self,node):
408         print "+           node=%s host_box=%s"%(node['name'],node['host_box']),
409         print "hostname=",node['node_fields']['hostname'],
410         print "ip=",node['interface_fields']['ip']
411         if self.options.verbose:
412             utils.pprint("node details",node,depth=3)
413
414     # another entry point for just showing the boxes involved
415     def display_mapping (self):
416         TestPlc.display_mapping_plc(self.plc_spec)
417         return True
418
419     @staticmethod
420     def display_mapping_plc (plc_spec):
421         print '+ MyPLC',plc_spec['name']
422         print '+\tvserver address = root@%s:/vservers/%s'%(plc_spec['hostname'],plc_spec['vservername'])
423         print '+\tIP = %s/%s'%(plc_spec['PLC_API_HOST'],plc_spec['vserverip'])
424         for site_spec in plc_spec['sites']:
425             for node_spec in site_spec['nodes']:
426                 TestPlc.display_mapping_node(node_spec)
427
428     @staticmethod
429     def display_mapping_node (node_spec):
430         print '+   NODE %s'%(node_spec['name'])
431         print '+\tqemu box %s'%node_spec['host_box']
432         print '+\thostname=%s'%node_spec['node_fields']['hostname']
433
434     def resources_pre (self):
435         "run site-dependant pre-test script as defined in LocalTestResources"
436         from LocalTestResources import local_resources
437         return local_resources.step_pre(self)
438  
439     def resources_post (self):
440         "run site-dependant post-test script as defined in LocalTestResources"
441         from LocalTestResources import local_resources
442         return local_resources.step_post(self)
443  
444     def resources_list (self):
445         "run site-dependant list script as defined in LocalTestResources"
446         from LocalTestResources import local_resources
447         return local_resources.step_list(self)
448  
449     def resources_release (self):
450         "run site-dependant release script as defined in LocalTestResources"
451         from LocalTestResources import local_resources
452         return local_resources.step_release(self)
453  
454     def resources_release_plc (self):
455         "run site-dependant release script as defined in LocalTestResources"
456         from LocalTestResources import local_resources
457         return local_resources.step_release_plc(self)
458  
459     def resources_release_qemu (self):
460         "run site-dependant release script as defined in LocalTestResources"
461         from LocalTestResources import local_resources
462         return local_resources.step_release_qemu(self)
463  
464     def delete_vs(self):
465         "vserver delete the test myplc"
466         self.run_in_host("vserver --silent %s delete"%self.vservername)
467         return True
468
469     ### install
470     # historically the build was being fetched by the tests
471     # now the build pushes itself as a subdir of the tests workdir
472     # so that the tests do not have to worry about extracting the build (svn, git, or whatever)
473     def create_vs (self):
474         "vserver creation (no install done)"
475         # push the local build/ dir to the testplc box 
476         if self.is_local():
477             # a full path for the local calls
478             build_dir=os.path.dirname(sys.argv[0])
479             # sometimes this is empty - set to "." in such a case
480             if not build_dir: build_dir="."
481             build_dir += "/build"
482         else:
483             # use a standard name - will be relative to remote buildname
484             build_dir="build"
485             # remove for safety; do *not* mkdir first, otherwise we end up with build/build/
486             self.test_ssh.rmdir(build_dir)
487             self.test_ssh.copy(build_dir,recursive=True)
488         # the repo url is taken from arch-rpms-url 
489         # with the last step (i386) removed
490         repo_url = self.options.arch_rpms_url
491         for level in [ 'arch' ]:
492             repo_url = os.path.dirname(repo_url)
493         # pass the vbuild-nightly options to vtest-init-vserver
494         test_env_options=""
495         test_env_options += " -p %s"%self.options.personality
496         test_env_options += " -d %s"%self.options.pldistro
497         test_env_options += " -f %s"%self.options.fcdistro
498         script="vtest-init-vserver.sh"
499         vserver_name = self.vservername
500         vserver_options="--netdev eth0 --interface %s"%self.vserverip
501         try:
502             vserver_hostname=socket.gethostbyaddr(self.vserverip)[0]
503             vserver_options += " --hostname %s"%vserver_hostname
504         except:
505             print "Cannot reverse lookup %s"%self.vserverip
506             print "This is considered fatal, as this might pollute the test results"
507             return False
508         create_vserver="%(build_dir)s/%(script)s %(test_env_options)s %(vserver_name)s %(repo_url)s -- %(vserver_options)s"%locals()
509         return self.run_in_host(create_vserver) == 0
510
511     ### install_rpm 
512     def install(self):
513         "yum install myplc, noderepo, and the plain bootstrapfs"
514
515         # workaround for getting pgsql8.2 on centos5
516         if self.options.fcdistro == "centos5":
517             self.run_in_guest("rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm")
518
519         # compute nodefamily
520         if self.options.personality == "linux32":
521             arch = "i386"
522         elif self.options.personality == "linux64":
523             arch = "x86_64"
524         else:
525             raise Exception, "Unsupported personality %r"%self.options.personality
526         nodefamily="%s-%s-%s"%(self.options.pldistro,self.options.fcdistro,arch)
527
528         pkgs_list=[]
529         pkgs_list.append ("slicerepo-%s"%nodefamily)
530         pkgs_list.append ("myplc")
531         pkgs_list.append ("noderepo-%s"%nodefamily)
532         pkgs_list.append ("bootstrapfs-%s-plain"%nodefamily)
533         pkgs_string=" ".join(pkgs_list)
534         self.run_in_guest("yum -y install %s"%pkgs_string)
535         return self.run_in_guest("rpm -q %s"%pkgs_string)==0
536
537     ### 
538     def configure(self):
539         "run plc-config-tty"
540         tmpname='%s.plc-config-tty'%(self.name())
541         fileconf=open(tmpname,'w')
542         for var in [ 'PLC_NAME',
543                      'PLC_ROOT_USER',
544                      'PLC_ROOT_PASSWORD',
545                      'PLC_SLICE_PREFIX',
546                      'PLC_MAIL_ENABLED',
547                      'PLC_MAIL_SUPPORT_ADDRESS',
548                      'PLC_DB_HOST',
549                      'PLC_DB_PASSWORD',
550                      # Above line was added for integrating SFA Testing
551                      'PLC_API_HOST',
552                      'PLC_WWW_HOST',
553                      'PLC_BOOT_HOST',
554                      'PLC_NET_DNS1',
555                      'PLC_NET_DNS2',
556                      'PLC_RESERVATION_GRANULARITY',
557                      'PLC_OMF_ENABLED',
558                      ]:
559             fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
560         fileconf.write('w\n')
561         fileconf.write('q\n')
562         fileconf.close()
563         utils.system('cat %s'%tmpname)
564         self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
565         utils.system('rm %s'%tmpname)
566         return True
567
568     def start(self):
569         "service plc start"
570         self.run_in_guest('service plc start')
571         return True
572
573     def stop(self):
574         "service plc stop"
575         self.run_in_guest('service plc stop')
576         return True
577         
578     def vs_start (self):
579         "start the PLC vserver"
580         self.start_guest()
581         return True
582
583     # stores the keys from the config for further use
584     def store_keys(self):
585         "stores test users ssh keys in keys/"
586         for key_spec in self.plc_spec['keys']:
587                 TestKey(self,key_spec).store_key()
588         return True
589
590     def clean_keys(self):
591         "removes keys cached in keys/"
592         utils.system("rm -rf %s/keys/"%os.path(sys.argv[0]))
593
594     # fetches the ssh keys in the plc's /etc/planetlab and stores them in keys/
595     # for later direct access to the nodes
596     def fetch_keys(self):
597         "gets ssh keys in /etc/planetlab/ and stores them locally in keys/"
598         dir="./keys"
599         if not os.path.isdir(dir):
600             os.mkdir(dir)
601         vservername=self.vservername
602         overall=True
603         prefix = 'debug_ssh_key'
604         for ext in [ 'pub', 'rsa' ] :
605             src="/vservers/%(vservername)s/etc/planetlab/%(prefix)s.%(ext)s"%locals()
606             dst="keys/%(vservername)s-debug.%(ext)s"%locals()
607             if self.test_ssh.fetch(src,dst) != 0: overall=False
608         return overall
609
610     def sites (self):
611         "create sites with PLCAPI"
612         return self.do_sites()
613     
614     def clean_sites (self):
615         "delete sites with PLCAPI"
616         return self.do_sites(action="delete")
617     
618     def do_sites (self,action="add"):
619         for site_spec in self.plc_spec['sites']:
620             test_site = TestSite (self,site_spec)
621             if (action != "add"):
622                 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
623                 test_site.delete_site()
624                 # deleted with the site
625                 #test_site.delete_users()
626                 continue
627             else:
628                 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
629                 test_site.create_site()
630                 test_site.create_users()
631         return True
632
633     def clean_all_sites (self):
634         "Delete all sites in PLC, and related objects"
635         print 'auth_root',self.auth_root()
636         site_ids = [s['site_id'] for s in self.apiserver.GetSites(self.auth_root(), {}, ['site_id'])]
637         for site_id in site_ids:
638             print 'Deleting site_id',site_id
639             self.apiserver.DeleteSite(self.auth_root(),site_id)
640
641     def nodes (self):
642         "create nodes with PLCAPI"
643         return self.do_nodes()
644     def clean_nodes (self):
645         "delete nodes with PLCAPI"
646         return self.do_nodes(action="delete")
647
648     def do_nodes (self,action="add"):
649         for site_spec in self.plc_spec['sites']:
650             test_site = TestSite (self,site_spec)
651             if action != "add":
652                 utils.header("Deleting nodes in site %s"%test_site.name())
653                 for node_spec in site_spec['nodes']:
654                     test_node=TestNode(self,test_site,node_spec)
655                     utils.header("Deleting %s"%test_node.name())
656                     test_node.delete_node()
657             else:
658                 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
659                 for node_spec in site_spec['nodes']:
660                     utils.pprint('Creating node %s'%node_spec,node_spec)
661                     test_node = TestNode (self,test_site,node_spec)
662                     test_node.create_node ()
663         return True
664
665     def nodegroups (self):
666         "create nodegroups with PLCAPI"
667         return self.do_nodegroups("add")
668     def clean_nodegroups (self):
669         "delete nodegroups with PLCAPI"
670         return self.do_nodegroups("delete")
671
672     YEAR = 365*24*3600
673     @staticmethod
674     def translate_timestamp (start,grain,timestamp):
675         if timestamp < TestPlc.YEAR:    return start+timestamp*grain
676         else:                           return timestamp
677
678     @staticmethod
679     def timestamp_printable (timestamp):
680         return time.strftime('%m-%d %H:%M:%S UTC',time.gmtime(timestamp))
681
682     def leases(self):
683         "create leases (on reservable nodes only, use e.g. run -c default -c resa)"
684         now=int(time.time())
685         grain=self.apiserver.GetLeaseGranularity(self.auth_root())
686         print 'API answered grain=',grain
687         start=(now/grain)*grain
688         start += grain
689         # find out all nodes that are reservable
690         nodes=self.all_reservable_nodenames()
691         if not nodes: 
692             utils.header ("No reservable node found - proceeding without leases")
693             return True
694         ok=True
695         # attach them to the leases as specified in plc_specs
696         # this is where the 'leases' field gets interpreted as relative of absolute
697         for lease_spec in self.plc_spec['leases']:
698             # skip the ones that come with a null slice id
699             if not lease_spec['slice']: continue
700             lease_spec['t_from']=TestPlc.translate_timestamp(start,grain,lease_spec['t_from'])
701             lease_spec['t_until']=TestPlc.translate_timestamp(start,grain,lease_spec['t_until'])
702             lease_addition=self.apiserver.AddLeases(self.auth_root(),nodes,
703                                                     lease_spec['slice'],lease_spec['t_from'],lease_spec['t_until'])
704             if lease_addition['errors']:
705                 utils.header("Cannot create leases, %s"%lease_addition['errors'])
706                 ok=False
707             else:
708                 utils.header('Leases on nodes %r for %s from %d (%s) until %d (%s)'%\
709                               (nodes,lease_spec['slice'],
710                                lease_spec['t_from'],TestPlc.timestamp_printable(lease_spec['t_from']),
711                                lease_spec['t_until'],TestPlc.timestamp_printable(lease_spec['t_until'])))
712                 
713         return ok
714
715     def clean_leases (self):
716         "remove all leases in the myplc side"
717         lease_ids= [ l['lease_id'] for l in self.apiserver.GetLeases(self.auth_root())]
718         utils.header("Cleaning leases %r"%lease_ids)
719         self.apiserver.DeleteLeases(self.auth_root(),lease_ids)
720         return True
721
722     def list_leases (self):
723         "list all leases known to the myplc"
724         leases = self.apiserver.GetLeases(self.auth_root())
725         now=int(time.time())
726         for l in leases:
727             current=l['t_until']>=now
728             if self.options.verbose or current:
729                 utils.header("%s %s from %s until %s"%(l['hostname'],l['name'],
730                                                        TestPlc.timestamp_printable(l['t_from']), 
731                                                        TestPlc.timestamp_printable(l['t_until'])))
732         return True
733
734     # create nodegroups if needed, and populate
735     def do_nodegroups (self, action="add"):
736         # 1st pass to scan contents
737         groups_dict = {}
738         for site_spec in self.plc_spec['sites']:
739             test_site = TestSite (self,site_spec)
740             for node_spec in site_spec['nodes']:
741                 test_node=TestNode (self,test_site,node_spec)
742                 if node_spec.has_key('nodegroups'):
743                     nodegroupnames=node_spec['nodegroups']
744                     if isinstance(nodegroupnames,StringTypes):
745                         nodegroupnames = [ nodegroupnames ]
746                     for nodegroupname in nodegroupnames:
747                         if not groups_dict.has_key(nodegroupname):
748                             groups_dict[nodegroupname]=[]
749                         groups_dict[nodegroupname].append(test_node.name())
750         auth=self.auth_root()
751         overall = True
752         for (nodegroupname,group_nodes) in groups_dict.iteritems():
753             if action == "add":
754                 print 'nodegroups:','dealing with nodegroup',nodegroupname,'on nodes',group_nodes
755                 # first, check if the nodetagtype is here
756                 tag_types = self.apiserver.GetTagTypes(auth,{'tagname':nodegroupname})
757                 if tag_types:
758                     tag_type_id = tag_types[0]['tag_type_id']
759                 else:
760                     tag_type_id = self.apiserver.AddTagType(auth,
761                                                             {'tagname':nodegroupname,
762                                                              'description': 'for nodegroup %s'%nodegroupname,
763                                                              'category':'test',
764                                                              'min_role_id':10})
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_options
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         return self.run_in_guest("sfa-nuke-plc.py")==0
1075
1076     def plcclean_sfa(self):
1077         "cleans the PLC entries that were created as a side effect of running the script"
1078         # ignore result 
1079         sfa_spec=self.plc_spec['sfa']
1080
1081         slicename='%s_%s'%(sfa_spec['login_base'],sfa_spec['slicename'])
1082         try: self.apiserver.DeleteSlice(self.auth_root(),slicename)
1083         except: print "Slice %s already absent from PLC db"%slicename
1084
1085         username="%s@%s"%(sfa_spec['regularuser'],sfa_spec['domain'])
1086         try: self.apiserver.DeletePerson(self.auth_root(),username)
1087         except: print "User %s already absent from PLC db"%username
1088
1089         print "REMEMBER TO RUN import_sfa AGAIN"
1090         return True
1091
1092     def uninstall_sfa(self):
1093         "uses rpm to uninstall sfa - ignore result"
1094         self.run_in_guest("rpm -e sfa sfa-sfatables sfa-client sfa-plc")
1095         self.run_in_guest("rm -rf /var/lib/sfa")
1096         return True
1097
1098     ###
1099     def configure_sfa(self):
1100         "run sfa-config-tty"
1101         tmpname='%s.sfa-config-tty'%(self.name())
1102         fileconf=open(tmpname,'w')
1103         for var in [ 'SFA_REGISTRY_ROOT_AUTH',
1104                      'SFA_INTERFACE_HRN',
1105 #                     'SFA_REGISTRY_LEVEL1_AUTH',
1106                      'SFA_REGISTRY_HOST',
1107                      'SFA_AGGREGATE_HOST',
1108                      'SFA_SM_HOST',
1109                      'SFA_PLC_USER',
1110                      'SFA_PLC_PASSWORD',
1111                      'SFA_PLC_DB_HOST',
1112                      'SFA_PLC_DB_USER',
1113                      'SFA_PLC_DB_PASSWORD',
1114                      'SFA_PLC_URL',
1115                      ]:
1116             fileconf.write ('e %s\n%s\n'%(var,self.plc_spec['sfa'][var]))
1117         fileconf.write('w\n')
1118         fileconf.write('R\n')
1119         fileconf.write('q\n')
1120         fileconf.close()
1121         utils.system('cat %s'%tmpname)
1122         self.run_in_guest_piped('cat %s'%tmpname,'sfa-config-tty')
1123         utils.system('rm %s'%tmpname)
1124         return True
1125
1126     def aggregate_xml_line(self):
1127         return '<aggregate addr="%s" hrn="%s" port="12346"/>' % \
1128             (self.vserverip,self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH'])
1129
1130     def registry_xml_line(self):
1131         return '<registry addr="%s" hrn="%s" port="12345"/>' % \
1132             (self.vserverip,self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH'])
1133
1134
1135     # a cross step that takes all other plcs in argument
1136     def cross_configure_sfa(self, other_plcs):
1137         # of course with a single plc, other_plcs is an empty list
1138         if other_plcs:
1139             filename="%s-agg.xml"%self.name()
1140             agg_file=file(filename,"w")
1141             agg_file.write("<aggregates>%s</aggregates>\n" % \
1142                                " ".join([ plc.aggregate_xml_line() for plc in other_plcs ]))
1143             agg_file.close()
1144             if self.test_ssh.copy_abs(filename,'/vservers/%s/etc/sfa/aggregates.xml'%self.vservername) !=0 : return False
1145
1146             filename="%s-reg.xml"%self.name()
1147             agg_file=file(filename,"w")
1148             agg_file.write("<registries>%s</registries>\n" % \
1149                                " ".join([ plc.registry_xml_line() for plc in other_plcs ]))
1150             agg_file.close()
1151             if self.test_ssh.copy_abs(filename,'/vservers/%s/etc/sfa/aggregates.xml'%self.vservername) !=0 : return False
1152         return True
1153
1154     def import_sfa(self):
1155         "sfa-import-plc"
1156         auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1157         return self.run_in_guest('sfa-import-plc.py')==0
1158 # not needed anymore
1159 #        self.run_in_guest('cp /etc/sfa/authorities/%s/%s.pkey /etc/sfa/authorities/server.key'%(auth,auth))
1160
1161     def start_sfa(self):
1162         "service sfa start"
1163         return self.run_in_guest('service sfa start')==0
1164
1165     def configure_sfi(self):
1166         sfa_spec=self.plc_spec['sfa']
1167         "sfi client configuration"
1168         dir_name=".sfi"
1169         if os.path.exists(dir_name):
1170            utils.system('rm -rf %s'%dir_name)
1171         utils.system('mkdir %s'%dir_name)
1172         file_name=dir_name + os.sep + sfa_spec['piuser'] + '.pkey'
1173         fileconf=open(file_name,'w')
1174         fileconf.write (self.plc_spec['keys'][0]['private'])
1175         fileconf.close()
1176
1177         file_name=dir_name + os.sep + 'sfi_config'
1178         fileconf=open(file_name,'w')
1179         SFI_AUTH="%s.%s"%(sfa_spec['SFA_REGISTRY_ROOT_AUTH'],sfa_spec['login_base'])
1180         fileconf.write ("SFI_AUTH='%s'"%SFI_AUTH)
1181         fileconf.write('\n')
1182         SFI_USER=SFI_AUTH + '.' + sfa_spec['piuser']
1183         fileconf.write ("SFI_USER='%s'"%SFI_USER)
1184         fileconf.write('\n')
1185         SFI_REGISTRY='http://' + sfa_spec['SFA_PLC_DB_HOST'] + ':12345/'
1186         fileconf.write ("SFI_REGISTRY='%s'"%SFI_REGISTRY)
1187         fileconf.write('\n')
1188         SFI_SM='http://' + sfa_spec['SFA_PLC_DB_HOST'] + ':12347/'
1189         fileconf.write ("SFI_SM='%s'"%SFI_SM)
1190         fileconf.write('\n')
1191         fileconf.close()
1192
1193         file_name=dir_name + os.sep + 'person.xml'
1194         fileconf=open(file_name,'w')
1195         for record in sfa_spec['sfa_person_xml']:
1196            person_record=record
1197         fileconf.write(person_record)
1198         fileconf.write('\n')
1199         fileconf.close()
1200
1201         file_name=dir_name + os.sep + 'slice.xml'
1202         fileconf=open(file_name,'w')
1203         for record in sfa_spec['sfa_slice_xml']:
1204             slice_record=record
1205         #slice_record=sfa_spec['sfa_slice_xml']
1206         fileconf.write(slice_record)
1207         fileconf.write('\n')
1208         fileconf.close()
1209
1210         file_name=dir_name + os.sep + 'slice.rspec'
1211         fileconf=open(file_name,'w')
1212         slice_rspec=''
1213         for (key, value) in sfa_spec['sfa_slice_rspec'].items():
1214             slice_rspec +=value 
1215         fileconf.write(slice_rspec)
1216         fileconf.write('\n')
1217         fileconf.close()
1218         location = "root/"
1219         remote="/vservers/%s/%s"%(self.vservername,location)
1220         self.test_ssh.copy_abs(dir_name, remote, recursive=True)
1221
1222         #utils.system('cat %s'%tmpname)
1223         utils.system('rm -rf %s'%dir_name)
1224         return True
1225
1226     def clean_sfi (self):
1227         self.run_in_guest("rm -rf /root/.sfi")
1228         return True
1229
1230     def add_sfa(self):
1231         "run sfi.py add (on Registry) and sfi.py create (on SM) to form new objects"
1232         test_user_sfa=TestUserSfa(self)
1233         if not test_user_sfa.add_user(): return False
1234
1235         for slice_spec in self.plc_spec['sfa']['sfa_slice_specs']:
1236             site_spec = self.locate_site (slice_spec['sitename'])
1237             test_site = TestSite(self,site_spec)
1238             test_slice_sfa=TestSliceSfa(self,test_site,slice_spec)
1239             if not test_slice_sfa.add_slice(): return False
1240             if not test_slice_sfa.create_slice(): return False
1241         return True
1242
1243     def update_sfa(self):
1244         "run sfi.py update (on Registry) and sfi.py create (on SM) on existing objects"
1245         test_user_sfa=TestUserSfa(self)
1246         if not test_user_sfa.update_user(): return False
1247         
1248         for slice_spec in self.plc_spec['sfa']['sfa_slice_specs']:
1249             site_spec = self.locate_site (slice_spec['sitename'])
1250             test_site = TestSite(self,site_spec)
1251             test_slice_sfa=TestSliceSfa(self,test_site,slice_spec)
1252             if not test_slice_sfa.update_slice(): return False
1253         return True
1254
1255     def view_sfa(self):
1256         "run sfi.py list and sfi.py show (both on Registry) and sfi.py slices and sfi.py resources (both on SM)"
1257         sfa_spec=self.plc_spec['sfa']
1258         auth=sfa_spec['SFA_REGISTRY_ROOT_AUTH']
1259         return \
1260         self.run_in_guest("sfi.py -d /root/.sfi/ list %s.%s"%(auth,sfa_spec['login_base']))==0 and \
1261         self.run_in_guest("sfi.py -d /root/.sfi/ show %s.%s"%(auth,sfa_spec['login_base']))==0 and \
1262         self.run_in_guest("sfi.py -d /root/.sfi/ slices")==0 and \
1263         self.run_in_guest("sfi.py -d /root/.sfi/ resources -o resources")==0
1264
1265     @slice_mapper_options_sfa
1266     def check_slice_sfa(self): 
1267         "tries to ssh-enter the SFA slice"
1268         pass
1269
1270     def delete_sfa(self):
1271         "run sfi.py delete (on SM), sfi.py remove (on Registry)"
1272         test_user_sfa=TestUserSfa(self)
1273         success1=test_user_sfa.delete_user()
1274         for slice_spec in self.plc_spec['sfa']['sfa_slice_specs']:
1275             site_spec = self.locate_site (slice_spec['sitename'])
1276             test_site = TestSite(self,site_spec)
1277             test_slice_sfa=TestSliceSfa(self,test_site,slice_spec)
1278             success2=test_slice_sfa.delete_slice()
1279
1280         return success1 and success2
1281
1282     def stop_sfa(self):
1283         "service sfa stop"
1284         return self.run_in_guest('service sfa stop')==0
1285
1286     def populate (self):
1287         "creates random entries in the PLCAPI"
1288         # install the stress-test in the plc image
1289         location = "/usr/share/plc_api/plcsh_stress_test.py"
1290         remote="/vservers/%s/%s"%(self.vservername,location)
1291         self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1292         command = location
1293         command += " -- --preserve --short-names"
1294         local = (self.run_in_guest(command) == 0);
1295         # second run with --foreign
1296         command += ' --foreign'
1297         remote = (self.run_in_guest(command) == 0);
1298         return ( local and remote)
1299
1300     def gather_logs (self):
1301         "gets all possible logs from plc's/qemu node's/slice's for future reference"
1302         # (1.a) get the plc's /var/log/ and store it locally in logs/myplc.var-log.<plcname>/*
1303         # (1.b) get the plc's  /var/lib/pgsql/data/pg_log/ -> logs/myplc.pgsql-log.<plcname>/*
1304         # (2) get all the nodes qemu log and store it as logs/node.qemu.<node>.log
1305         # (3) get the nodes /var/log and store is as logs/node.var-log.<node>/*
1306         # (4) as far as possible get the slice's /var/log as logs/sliver.var-log.<sliver>/*
1307         # (1.a)
1308         print "-------------------- TestPlc.gather_logs : PLC's /var/log"
1309         self.gather_var_logs ()
1310         # (1.b)
1311         print "-------------------- TestPlc.gather_logs : PLC's /var/lib/psql/data/pg_log/"
1312         self.gather_pgsql_logs ()
1313         # (2) 
1314         print "-------------------- TestPlc.gather_logs : nodes's QEMU logs"
1315         for site_spec in self.plc_spec['sites']:
1316             test_site = TestSite (self,site_spec)
1317             for node_spec in site_spec['nodes']:
1318                 test_node=TestNode(self,test_site,node_spec)
1319                 test_node.gather_qemu_logs()
1320         # (3)
1321         print "-------------------- TestPlc.gather_logs : nodes's /var/log"
1322         self.gather_nodes_var_logs()
1323         # (4)
1324         print "-------------------- TestPlc.gather_logs : sample sliver's /var/log"
1325         self.gather_slivers_var_logs()
1326         return True
1327
1328     def gather_slivers_var_logs(self):
1329         for test_sliver in self.all_sliver_objs():
1330             remote = test_sliver.tar_var_logs()
1331             utils.system("mkdir -p logs/sliver.var-log.%s"%test_sliver.name())
1332             command = remote + " | tar -C logs/sliver.var-log.%s -xf -"%test_sliver.name()
1333             utils.system(command)
1334         return True
1335
1336     def gather_var_logs (self):
1337         utils.system("mkdir -p logs/myplc.var-log.%s"%self.name())
1338         to_plc = self.actual_command_in_guest("tar -C /var/log/ -cf - .")        
1339         command = to_plc + "| tar -C logs/myplc.var-log.%s -xf -"%self.name()
1340         utils.system(command)
1341         command = "chmod a+r,a+x logs/myplc.var-log.%s/httpd"%self.name()
1342         utils.system(command)
1343
1344     def gather_pgsql_logs (self):
1345         utils.system("mkdir -p logs/myplc.pgsql-log.%s"%self.name())
1346         to_plc = self.actual_command_in_guest("tar -C /var/lib/pgsql/data/pg_log/ -cf - .")        
1347         command = to_plc + "| tar -C logs/myplc.pgsql-log.%s -xf -"%self.name()
1348         utils.system(command)
1349
1350     def gather_nodes_var_logs (self):
1351         for site_spec in self.plc_spec['sites']:
1352             test_site = TestSite (self,site_spec)
1353             for node_spec in site_spec['nodes']:
1354                 test_node=TestNode(self,test_site,node_spec)
1355                 test_ssh = TestSsh (test_node.name(),key="keys/key1.rsa")
1356                 command = test_ssh.actual_command("tar -C /var/log -cf - .")
1357                 command = command + "| tar -C logs/node.var-log.%s -xf -"%test_node.name()
1358                 utils.system("mkdir -p logs/node.var-log.%s"%test_node.name())
1359                 utils.system(command)
1360
1361
1362     # returns the filename to use for sql dump/restore, using options.dbname if set
1363     def dbfile (self, database):
1364         # uses options.dbname if it is found
1365         try:
1366             name=self.options.dbname
1367             if not isinstance(name,StringTypes):
1368                 raise Exception
1369         except:
1370             t=datetime.datetime.now()
1371             d=t.date()
1372             name=str(d)
1373         return "/root/%s-%s.sql"%(database,name)
1374
1375     def db_dump(self):
1376         'dump the planetlab5 DB in /root in the PLC - filename has time'
1377         dump=self.dbfile("planetab5")
1378         self.run_in_guest('pg_dump -U pgsqluser planetlab5 -f '+ dump)
1379         utils.header('Dumped planetlab5 database in %s'%dump)
1380         return True
1381
1382     def db_restore(self):
1383         'restore the planetlab5 DB - looks broken, but run -n might help'
1384         dump=self.dbfile("planetab5")
1385         ##stop httpd service
1386         self.run_in_guest('service httpd stop')
1387         # xxx - need another wrapper
1388         self.run_in_guest_piped('echo drop database planetlab5','psql --user=pgsqluser template1')
1389         self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab5')
1390         self.run_in_guest('psql -U pgsqluser planetlab5 -f '+dump)
1391         ##starting httpd service
1392         self.run_in_guest('service httpd start')
1393
1394         utils.header('Database restored from ' + dump)
1395
1396     @standby_generic 
1397     def standby_1(): pass
1398     @standby_generic 
1399     def standby_2(): pass
1400     @standby_generic 
1401     def standby_3(): pass
1402     @standby_generic 
1403     def standby_4(): pass
1404     @standby_generic 
1405     def standby_5(): pass
1406     @standby_generic 
1407     def standby_6(): pass
1408     @standby_generic 
1409     def standby_7(): pass
1410     @standby_generic 
1411     def standby_8(): pass
1412     @standby_generic 
1413     def standby_9(): pass
1414     @standby_generic 
1415     def standby_10(): pass
1416     @standby_generic 
1417     def standby_11(): pass
1418     @standby_generic 
1419     def standby_12(): pass
1420     @standby_generic 
1421     def standby_13(): pass
1422     @standby_generic 
1423     def standby_14(): pass
1424     @standby_generic 
1425     def standby_15(): pass
1426     @standby_generic 
1427     def standby_16(): pass
1428     @standby_generic 
1429     def standby_17(): pass
1430     @standby_generic 
1431     def standby_18(): pass
1432     @standby_generic 
1433     def standby_19(): pass
1434     @standby_generic 
1435     def standby_20(): pass