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