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