85fb971fdb4e93f4e04d8711c11c2753c41df368
[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     def create_vs (self):
454         "vserver creation (no install done)"
455         if self.is_local():
456             # a full path for the local calls
457             build_dir=os.path.dirname(sys.argv[0])
458             # sometimes this is empty - set to "." in such a case
459             if not build_dir: build_dir="."
460             build_dir += "/build"
461         else:
462             # use a standard name - will be relative to remote buildname
463             build_dir="build"
464         # run checkout in any case - would do an update if already exists
465         build_checkout = "svn checkout %s %s"%(self.options.build_url,build_dir)
466         if self.run_in_host(build_checkout) != 0:
467             return False
468         # the repo url is taken from arch-rpms-url 
469         # with the last step (i386) removed
470         repo_url = self.options.arch_rpms_url
471         for level in [ 'arch' ]:
472             repo_url = os.path.dirname(repo_url)
473         # pass the vbuild-nightly options to vtest-init-vserver
474         test_env_options=""
475         test_env_options += " -p %s"%self.options.personality
476         test_env_options += " -d %s"%self.options.pldistro
477         test_env_options += " -f %s"%self.options.fcdistro
478         script="vtest-init-vserver.sh"
479         vserver_name = self.vservername
480         vserver_options="--netdev eth0 --interface %s"%self.vserverip
481         try:
482             vserver_hostname=socket.gethostbyaddr(self.vserverip)[0]
483             vserver_options += " --hostname %s"%vserver_hostname
484         except:
485             print "Cannot reverse lookup %s"%self.vserverip
486             print "This is considered fatal, as this might pollute the test results"
487             return False
488         create_vserver="%(build_dir)s/%(script)s %(test_env_options)s %(vserver_name)s %(repo_url)s -- %(vserver_options)s"%locals()
489         return self.run_in_host(create_vserver) == 0
490
491     ### install_rpm 
492     def install(self):
493         "yum install myplc, noderepo, and the plain bootstrapfs"
494
495         # workaround for getting pgsql8.2 on centos5
496         if self.options.fcdistro == "centos5":
497             self.run_in_guest("rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm")
498
499         if self.options.personality == "linux32":
500             arch = "i386"
501         elif self.options.personality == "linux64":
502             arch = "x86_64"
503         else:
504             raise Exception, "Unsupported personality %r"%self.options.personality
505         
506         nodefamily="%s-%s-%s"%(self.options.pldistro,self.options.fcdistro,arch)
507
508         # try to install slicerepo - not fatal yet
509         self.run_in_guest("yum -y install slicerepo-%s"%nodefamily)
510         
511         return \
512             self.run_in_guest("yum -y install myplc")==0 and \
513             self.run_in_guest("yum -y install noderepo-%s"%nodefamily)==0 and \
514             self.run_in_guest("yum -y install bootstrapfs-%s-plain"%nodefamily)==0 
515
516     ### 
517     def configure(self):
518         "run plc-config-tty"
519         tmpname='%s.plc-config-tty'%(self.name())
520         fileconf=open(tmpname,'w')
521         for var in [ 'PLC_NAME',
522                      'PLC_ROOT_PASSWORD',
523                      'PLC_ROOT_USER',
524                      'PLC_MAIL_ENABLED',
525                      'PLC_MAIL_SUPPORT_ADDRESS',
526                      'PLC_DB_HOST',
527                      'PLC_DB_PASSWORD',
528                      # Above line was added for integrating SFA Testing
529                      'PLC_API_HOST',
530                      'PLC_WWW_HOST',
531                      'PLC_BOOT_HOST',
532                      'PLC_NET_DNS1',
533                      'PLC_NET_DNS2']:
534             fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
535         fileconf.write('w\n')
536         fileconf.write('q\n')
537         fileconf.close()
538         utils.system('cat %s'%tmpname)
539         self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
540         utils.system('rm %s'%tmpname)
541         return True
542
543     def start(self):
544         "service plc start"
545         self.run_in_guest('service plc start')
546         return True
547
548     def stop(self):
549         "service plc stop"
550         self.run_in_guest('service plc stop')
551         return True
552         
553     def vs_start (self):
554         "start the PLC vserver"
555         self.start_guest()
556         return True
557
558     # stores the keys from the config for further use
559     def store_keys(self):
560         "stores test users ssh keys in keys/"
561         for key_spec in self.plc_spec['keys']:
562                 TestKey(self,key_spec).store_key()
563         return True
564
565     def clean_keys(self):
566         "removes keys cached in keys/"
567         utils.system("rm -rf %s/keys/"%os.path(sys.argv[0]))
568
569     # fetches the ssh keys in the plc's /etc/planetlab and stores them in keys/
570     # for later direct access to the nodes
571     def fetch_keys(self):
572         "gets ssh keys in /etc/planetlab/ and stores them locally in keys/"
573         dir="./keys"
574         if not os.path.isdir(dir):
575             os.mkdir(dir)
576         vservername=self.vservername
577         overall=True
578         prefix = 'debug_ssh_key'
579         for ext in [ 'pub', 'rsa' ] :
580             src="/vservers/%(vservername)s/etc/planetlab/%(prefix)s.%(ext)s"%locals()
581             dst="keys/%(vservername)s-debug.%(ext)s"%locals()
582             if self.test_ssh.fetch(src,dst) != 0: overall=False
583         return overall
584
585     def sites (self):
586         "create sites with PLCAPI"
587         return self.do_sites()
588     
589     def clean_sites (self):
590         "delete sites with PLCAPI"
591         return self.do_sites(action="delete")
592     
593     def do_sites (self,action="add"):
594         for site_spec in self.plc_spec['sites']:
595             test_site = TestSite (self,site_spec)
596             if (action != "add"):
597                 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
598                 test_site.delete_site()
599                 # deleted with the site
600                 #test_site.delete_users()
601                 continue
602             else:
603                 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
604                 test_site.create_site()
605                 test_site.create_users()
606         return True
607
608     def clean_all_sites (self):
609         "Delete all sites in PLC, and related objects"
610         print 'auth_root',self.auth_root()
611         site_ids = [s['site_id'] for s in self.apiserver.GetSites(self.auth_root(), {}, ['site_id'])]
612         for site_id in site_ids:
613             print 'Deleting site_id',site_id
614             self.apiserver.DeleteSite(self.auth_root(),site_id)
615
616     def nodes (self):
617         "create nodes with PLCAPI"
618         return self.do_nodes()
619     def clean_nodes (self):
620         "delete nodes with PLCAPI"
621         return self.do_nodes(action="delete")
622
623     def do_nodes (self,action="add"):
624         for site_spec in self.plc_spec['sites']:
625             test_site = TestSite (self,site_spec)
626             if action != "add":
627                 utils.header("Deleting nodes in site %s"%test_site.name())
628                 for node_spec in site_spec['nodes']:
629                     test_node=TestNode(self,test_site,node_spec)
630                     utils.header("Deleting %s"%test_node.name())
631                     test_node.delete_node()
632             else:
633                 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
634                 for node_spec in site_spec['nodes']:
635                     utils.pprint('Creating node %s'%node_spec,node_spec)
636                     test_node = TestNode (self,test_site,node_spec)
637                     test_node.create_node ()
638         return True
639
640     def nodegroups (self):
641         "create nodegroups with PLCAPI"
642         return self.do_nodegroups("add")
643     def clean_nodegroups (self):
644         "delete nodegroups with PLCAPI"
645         return self.do_nodegroups("delete")
646
647     # create nodegroups if needed, and populate
648     def do_nodegroups (self, action="add"):
649         # 1st pass to scan contents
650         groups_dict = {}
651         for site_spec in self.plc_spec['sites']:
652             test_site = TestSite (self,site_spec)
653             for node_spec in site_spec['nodes']:
654                 test_node=TestNode (self,test_site,node_spec)
655                 if node_spec.has_key('nodegroups'):
656                     nodegroupnames=node_spec['nodegroups']
657                     if isinstance(nodegroupnames,StringTypes):
658                         nodegroupnames = [ nodegroupnames ]
659                     for nodegroupname in nodegroupnames:
660                         if not groups_dict.has_key(nodegroupname):
661                             groups_dict[nodegroupname]=[]
662                         groups_dict[nodegroupname].append(test_node.name())
663         auth=self.auth_root()
664         overall = True
665         for (nodegroupname,group_nodes) in groups_dict.iteritems():
666             if action == "add":
667                 print 'nodegroups:','dealing with nodegroup',nodegroupname,'on nodes',group_nodes
668                 # first, check if the nodetagtype is here
669                 tag_types = self.apiserver.GetTagTypes(auth,{'tagname':nodegroupname})
670                 if tag_types:
671                     tag_type_id = tag_types[0]['tag_type_id']
672                 else:
673                     tag_type_id = self.apiserver.AddTagType(auth,
674                                                             {'tagname':nodegroupname,
675                                                              'description': 'for nodegroup %s'%nodegroupname,
676                                                              'category':'test',
677                                                              'min_role_id':10})
678                 print 'located tag (type)',nodegroupname,'as',tag_type_id
679                 # create nodegroup
680                 nodegroups = self.apiserver.GetNodeGroups (auth, {'groupname':nodegroupname})
681                 if not nodegroups:
682                     self.apiserver.AddNodeGroup(auth, nodegroupname, tag_type_id, 'yes')
683                     print 'created nodegroup',nodegroupname,'from tagname',nodegroupname,'and value','yes'
684                 # set node tag on all nodes, value='yes'
685                 for nodename in group_nodes:
686                     try:
687                         self.apiserver.AddNodeTag(auth, nodename, nodegroupname, "yes")
688                     except:
689                         traceback.print_exc()
690                         print 'node',nodename,'seems to already have tag',nodegroupname
691                     # check anyway
692                     try:
693                         expect_yes = self.apiserver.GetNodeTags(auth,
694                                                                 {'hostname':nodename,
695                                                                  'tagname':nodegroupname},
696                                                                 ['value'])[0]['value']
697                         if expect_yes != "yes":
698                             print 'Mismatch node tag on node',nodename,'got',expect_yes
699                             overall=False
700                     except:
701                         if not self.options.dry_run:
702                             print 'Cannot find tag',nodegroupname,'on node',nodename
703                             overall = False
704             else:
705                 try:
706                     print 'cleaning nodegroup',nodegroupname
707                     self.apiserver.DeleteNodeGroup(auth,nodegroupname)
708                 except:
709                     traceback.print_exc()
710                     overall=False
711         return overall
712
713     # return a list of tuples (nodename,qemuname)
714     def all_node_infos (self) :
715         node_infos = []
716         for site_spec in self.plc_spec['sites']:
717             node_infos += [ (node_spec['node_fields']['hostname'],node_spec['host_box']) \
718                            for node_spec in site_spec['nodes'] ]
719         return node_infos
720     
721     def all_nodenames (self): return [ x[0] for x in self.all_node_infos() ]
722
723     # silent_minutes : during the first <silent_minutes> minutes nothing gets printed
724     def nodes_check_boot_state (self, target_boot_state, timeout_minutes, silent_minutes,period=15):
725         if self.options.dry_run:
726             print 'dry_run'
727             return True
728         # compute timeout
729         timeout = datetime.datetime.now()+datetime.timedelta(minutes=timeout_minutes)
730         graceout = datetime.datetime.now()+datetime.timedelta(minutes=silent_minutes)
731         # the nodes that haven't checked yet - start with a full list and shrink over time
732         tocheck = self.all_hostnames()
733         utils.header("checking nodes %r"%tocheck)
734         # create a dict hostname -> status
735         status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
736         while tocheck:
737             # get their status
738             tocheck_status=self.apiserver.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
739             # update status
740             for array in tocheck_status:
741                 hostname=array['hostname']
742                 boot_state=array['boot_state']
743                 if boot_state == target_boot_state:
744                     utils.header ("%s has reached the %s state"%(hostname,target_boot_state))
745                 else:
746                     # if it's a real node, never mind
747                     (site_spec,node_spec)=self.locate_hostname(hostname)
748                     if TestNode.is_real_model(node_spec['node_fields']['model']):
749                         utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
750                         # let's cheat
751                         boot_state = target_boot_state
752                     elif datetime.datetime.now() > graceout:
753                         utils.header ("%s still in '%s' state"%(hostname,boot_state))
754                         graceout=datetime.datetime.now()+datetime.timedelta(1)
755                 status[hostname] = boot_state
756             # refresh tocheck
757             tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != target_boot_state ]
758             if not tocheck:
759                 return True
760             if datetime.datetime.now() > timeout:
761                 for hostname in tocheck:
762                     utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
763                 return False
764             # otherwise, sleep for a while
765             time.sleep(period)
766         # only useful in empty plcs
767         return True
768
769     def nodes_booted(self):
770         return self.nodes_check_boot_state('boot',timeout_minutes=30,silent_minutes=20)
771
772     def check_nodes_ssh(self,debug,timeout_minutes,silent_minutes,period=15):
773         # compute timeout
774         timeout = datetime.datetime.now()+datetime.timedelta(minutes=timeout_minutes)
775         graceout = datetime.datetime.now()+datetime.timedelta(minutes=silent_minutes)
776         vservername=self.vservername
777         if debug: 
778             message="debug"
779             local_key = "keys/%(vservername)s-debug.rsa"%locals()
780         else: 
781             message="boot"
782             local_key = "keys/key1.rsa"
783         node_infos = self.all_node_infos()
784         utils.header("checking ssh access (expected in %s mode) to nodes:"%message)
785         for (nodename,qemuname) in node_infos:
786             utils.header("hostname=%s -- qemubox=%s"%(nodename,qemuname))
787         utils.header("max timeout is %d minutes, silent for %d minutes (period is %s)"%\
788                          (timeout_minutes,silent_minutes,period))
789         while node_infos:
790             for node_info in node_infos:
791                 (hostname,qemuname) = node_info
792                 # try to run 'hostname' in the node
793                 command = TestSsh (hostname,key=local_key).actual_command("hostname;uname -a")
794                 # don't spam logs - show the command only after the grace period 
795                 success = utils.system ( command, silent=datetime.datetime.now() < graceout)
796                 if success==0:
797                     utils.header('Successfully entered root@%s (%s)'%(hostname,message))
798                     # refresh node_infos
799                     node_infos.remove(node_info)
800                 else:
801                     # we will have tried real nodes once, in case they're up - but if not, just skip
802                     (site_spec,node_spec)=self.locate_hostname(hostname)
803                     if TestNode.is_real_model(node_spec['node_fields']['model']):
804                         utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
805                         node_infos.remove(node_info)
806             if  not node_infos:
807                 return True
808             if datetime.datetime.now() > timeout:
809                 for (hostname,qemuname) in node_infos:
810                     utils.header("FAILURE to ssh into %s (on %s)"%(hostname,qemuname))
811                 return False
812             # otherwise, sleep for a while
813             time.sleep(period)
814         # only useful in empty plcs
815         return True
816         
817     def nodes_ssh_debug(self):
818         "Tries to ssh into nodes in debug mode with the debug ssh key"
819         return self.check_nodes_ssh(debug=True,timeout_minutes=30,silent_minutes=5)
820     
821     def nodes_ssh_boot(self):
822         "Tries to ssh into nodes in production mode with the root ssh key"
823         return self.check_nodes_ssh(debug=False,timeout_minutes=30,silent_minutes=15)
824     
825     @node_mapper
826     def init_node (self): 
827         "all nodes : init a clean local directory for holding node-dep stuff like iso image..."
828         pass
829     @node_mapper
830     def bootcd (self): 
831         "all nodes: invoke GetBootMedium and store result locally"
832         pass
833     @node_mapper
834     def configure_qemu (self): 
835         "all nodes: compute qemu config qemu.conf and store it locally"
836         pass
837     @node_mapper
838     def reinstall_node (self): 
839         "all nodes: mark PLCAPI boot_state as reinstall"
840         pass
841     @node_mapper
842     def export_qemu (self): 
843         "all nodes: push local node-dep directory on the qemu box"
844         pass
845         
846     ### check hooks : invoke scripts from hooks/{node,slice}
847     def check_hooks_node (self): 
848         return self.locate_first_node().check_hooks()
849     def check_hooks_sliver (self) : 
850         return self.locate_first_sliver().check_hooks()
851     
852     def check_hooks (self):
853         "runs unit tests in the node and slice contexts - see hooks/{node,slice}"
854         return self.check_hooks_node() and self.check_hooks_sliver()
855
856     ### initscripts
857     def do_check_initscripts(self):
858         overall = True
859         for slice_spec in self.plc_spec['slices']:
860             if not slice_spec.has_key('initscriptname'):
861                 continue
862             initscript=slice_spec['initscriptname']
863             for nodename in slice_spec['nodenames']:
864                 (site,node) = self.locate_node (nodename)
865                 # xxx - passing the wrong site - probably harmless
866                 test_site = TestSite (self,site)
867                 test_slice = TestSlice (self,test_site,slice_spec)
868                 test_node = TestNode (self,test_site,node)
869                 test_sliver = TestSliver (self, test_node, test_slice)
870                 if not test_sliver.check_initscript(initscript):
871                     overall = False
872         return overall
873             
874     def check_initscripts(self):
875         "check that the initscripts have triggered"
876         return self.do_check_initscripts()
877     
878     def initscripts (self):
879         "create initscripts with PLCAPI"
880         for initscript in self.plc_spec['initscripts']:
881             utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
882             self.apiserver.AddInitScript(self.auth_root(),initscript['initscript_fields'])
883         return True
884
885     def clean_initscripts (self):
886         "delete initscripts with PLCAPI"
887         for initscript in self.plc_spec['initscripts']:
888             initscript_name = initscript['initscript_fields']['name']
889             print('Attempting to delete %s in plc %s'%(initscript_name,self.plc_spec['name']))
890             try:
891                 self.apiserver.DeleteInitScript(self.auth_root(),initscript_name)
892                 print initscript_name,'deleted'
893             except:
894                 print 'deletion went wrong - probably did not exist'
895         return True
896
897     ### manage slices
898     def slices (self):
899         "create slices with PLCAPI"
900         return self.do_slices()
901
902     def clean_slices (self):
903         "delete slices with PLCAPI"
904         return self.do_slices("delete")
905
906     def do_slices (self,  action="add"):
907         for slice in self.plc_spec['slices']:
908             site_spec = self.locate_site (slice['sitename'])
909             test_site = TestSite(self,site_spec)
910             test_slice=TestSlice(self,test_site,slice)
911             if action != "add":
912                 utils.header("Deleting slices in site %s"%test_site.name())
913                 test_slice.delete_slice()
914             else:    
915                 utils.pprint("Creating slice",slice)
916                 test_slice.create_slice()
917                 utils.header('Created Slice %s'%slice['slice_fields']['name'])
918         return True
919         
920     @slice_mapper_options
921     def check_slice(self): 
922         "tries to ssh-enter the slice with the user key, to ensure slice creation"
923         pass
924
925     @node_mapper
926     def clear_known_hosts (self): 
927         "remove test nodes entries from the local known_hosts file"
928         pass
929     
930     @node_mapper
931     def start_node (self) : 
932         "all nodes: start the qemu instance (also runs qemu-bridge-init start)"
933         pass
934
935     def check_tcp (self):
936         "check TCP connectivity between 2 slices (or in loopback if only one is defined)"
937         specs = self.plc_spec['tcp_test']
938         overall=True
939         for spec in specs:
940             port = spec['port']
941             # server side
942             s_test_sliver = self.locate_sliver_obj (spec['server_node'],spec['server_slice'])
943             if not s_test_sliver.run_tcp_server(port,timeout=10):
944                 overall=False
945                 break
946
947             # idem for the client side
948             c_test_sliver = self.locate_sliver_obj(spec['server_node'],spec['server_slice'])
949             if not c_test_sliver.run_tcp_client(s_test_sliver.test_node.name(),port):
950                 overall=False
951         return overall
952
953     def plcsh_stress_test (self):
954         "runs PLCAPI stress test, that checks Add/Update/Delete on all types - preserves contents"
955         # install the stress-test in the plc image
956         location = "/usr/share/plc_api/plcsh_stress_test.py"
957         remote="/vservers/%s/%s"%(self.vservername,location)
958         self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
959         command = location
960         command += " -- --check"
961         if self.options.size == 1:
962             command +=  " --tiny"
963         return ( self.run_in_guest(command) == 0)
964
965     # populate runs the same utility without slightly different options
966     # in particular runs with --preserve (dont cleanup) and without --check
967     # also it gets run twice, once with the --foreign option for creating fake foreign entries
968
969     ### install_sfa_rpm
970     def install_sfa(self):
971         "yum install sfa, sfa-plc and sfa-client"
972         if self.options.personality == "linux32":
973             arch = "i386"
974         elif self.options.personality == "linux64":
975             arch = "x86_64"
976         else:
977             raise Exception, "Unsupported personality %r"%self.options.personality
978         return self.run_in_guest("yum -y install sfa sfa-client sfa-plc sfa-sfatables")==0
979
980     ###
981     def configure_sfa(self):
982         "run sfa-config-tty"
983         tmpname='%s.sfa-config-tty'%(self.name())
984         fileconf=open(tmpname,'w')
985         for var in [ 'SFA_REGISTRY_ROOT_AUTH',
986                      'SFA_REGISTRY_LEVEL1_AUTH',
987                      'SFA_REGISTRY_HOST',
988                      'SFA_AGGREGATE_HOST',
989                      'SFA_SM_HOST',
990                      'SFA_PLC_USER',
991                      'SFA_PLC_PASSWORD',
992                      'SFA_PLC_DB_HOST',
993                      'SFA_PLC_DB_USER',
994                      'SFA_PLC_DB_PASSWORD',
995                      'SFA_PLC_URL']:
996             fileconf.write ('e %s\n%s\n'%(var,self.plc_spec['sfa'][var]))
997         fileconf.write('w\n')
998         fileconf.write('R\n')
999         fileconf.write('q\n')
1000         fileconf.close()
1001         utils.system('cat %s'%tmpname)
1002         self.run_in_guest_piped('cat %s'%tmpname,'sfa-config-tty')
1003         utils.system('rm %s'%tmpname)
1004         return True
1005
1006     def import_sfa(self):
1007         "sfa-import-plc"
1008         auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1009         return self.run_in_guest('sfa-import-plc.py')==0
1010 # not needed anymore
1011 #        self.run_in_guest('cp /etc/sfa/authorities/%s/%s.pkey /etc/sfa/authorities/server.key'%(auth,auth))
1012
1013     def start_sfa(self):
1014         "service sfa start"
1015         return self.run_in_guest('service sfa start')==0
1016
1017     def setup_sfa(self):
1018         "sfi client configuration"
1019         dir_name=".sfi"
1020         if os.path.exists(dir_name):
1021            utils.system('rm -rf %s'%dir_name)
1022         utils.system('mkdir %s'%dir_name)
1023         file_name=dir_name + os.sep + 'fake-pi1.pkey'
1024         fileconf=open(file_name,'w')
1025         fileconf.write (self.plc_spec['keys'][0]['private'])
1026         fileconf.close()
1027
1028         file_name=dir_name + os.sep + 'sfi_config'
1029         fileconf=open(file_name,'w')
1030         SFI_AUTH=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']+".main"
1031         fileconf.write ("SFI_AUTH='%s'"%SFI_AUTH)
1032         fileconf.write('\n')
1033         SFI_USER=SFI_AUTH+'.fake-pi1'
1034         fileconf.write ("SFI_USER='%s'"%SFI_USER)
1035         fileconf.write('\n')
1036         SFI_REGISTRY='http://' + self.plc_spec['sfa']['SFA_PLC_DB_HOST'] + ':12345/'
1037         fileconf.write ("SFI_REGISTRY='%s'"%SFI_REGISTRY)
1038         fileconf.write('\n')
1039         SFI_SM='http://' + self.plc_spec['sfa']['SFA_PLC_DB_HOST'] + ':12347/'
1040         fileconf.write ("SFI_SM='%s'"%SFI_SM)
1041         fileconf.write('\n')
1042         fileconf.close()
1043
1044         file_name=dir_name + os.sep + 'person.xml'
1045         fileconf=open(file_name,'w')
1046         for record in self.plc_spec['sfa']['sfa_person_xml']:
1047            person_record=record
1048         fileconf.write(person_record)
1049         fileconf.write('\n')
1050         fileconf.close()
1051
1052         file_name=dir_name + os.sep + 'slice.xml'
1053         fileconf=open(file_name,'w')
1054         for record in self.plc_spec['sfa']['sfa_slice_xml']:
1055             slice_record=record
1056         #slice_record=self.plc_spec['sfa']['sfa_slice_xml']
1057         fileconf.write(slice_record)
1058         fileconf.write('\n')
1059         fileconf.close()
1060
1061         file_name=dir_name + os.sep + 'slice.rspec'
1062         fileconf=open(file_name,'w')
1063         slice_rspec=''
1064         for (key, value) in self.plc_spec['sfa']['sfa_slice_rspec'].items():
1065             slice_rspec +=value 
1066         fileconf.write(slice_rspec)
1067         fileconf.write('\n')
1068         fileconf.close()
1069         location = "root/"
1070         remote="/vservers/%s/%s"%(self.vservername,location)
1071         self.test_ssh.copy_abs(dir_name, remote, recursive=True)
1072
1073         #utils.system('cat %s'%tmpname)
1074         utils.system('rm -rf %s'%dir_name)
1075         return True
1076
1077     def add_sfa(self):
1078         "run sfi.py add (on Registry) and sfi.py create (on SM) to form new objects"
1079         test_plc=self
1080         test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1081         success=test_user_sfa.add_user()
1082
1083         for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1084             site_spec = self.locate_site (slice_spec['sitename'])
1085             test_site = TestSite(self,site_spec)
1086             test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1087             success1=test_slice_sfa.add_slice()
1088             success2=test_slice_sfa.create_slice()
1089         return success and success1 and success2
1090
1091     def update_sfa(self):
1092         "run sfi.py update (on Registry) and sfi.py create (on SM) on existing objects"
1093         test_plc=self
1094         test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1095         success1=test_user_sfa.update_user()
1096         
1097         for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1098             site_spec = self.locate_site (slice_spec['sitename'])
1099             test_site = TestSite(self,site_spec)
1100             test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1101             success2=test_slice_sfa.update_slice()
1102         return success1 and success2
1103
1104     def view_sfa(self):
1105         "run sfi.py list and sfi.py show (both on Registry) and sfi.py slices and sfi.py resources (both on SM)"
1106         auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1107         return \
1108         self.run_in_guest("sfi.py -d /root/.sfi/ list %s.main"%auth)==0 and \
1109         self.run_in_guest("sfi.py -d /root/.sfi/ show %s.main"%auth)==0 and \
1110         self.run_in_guest("sfi.py -d /root/.sfi/ slices")==0 and \
1111         self.run_in_guest("sfi.py -d /root/.sfi/ resources -o resources")==0
1112
1113     @slice_mapper_options_sfa
1114     def check_slice_sfa(self): 
1115         "tries to ssh-enter the SFA slice"
1116         pass
1117
1118     def delete_sfa(self):
1119         "run sfi.py delete (on SM), sfi.py remove (on Registry)"
1120         test_plc=self
1121         test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1122         success1=test_user_sfa.delete_user()
1123         for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1124             site_spec = self.locate_site (slice_spec['sitename'])
1125             test_site = TestSite(self,site_spec)
1126             test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1127             success2=test_slice_sfa.delete_slice()
1128
1129         return success1 and success2
1130
1131     def stop_sfa(self):
1132         "service sfa stop"
1133         return self.run_in_guest('service sfa stop')==0
1134
1135     def populate (self):
1136         "creates random entries in the PLCAPI"
1137         # install the stress-test in the plc image
1138         location = "/usr/share/plc_api/plcsh_stress_test.py"
1139         remote="/vservers/%s/%s"%(self.vservername,location)
1140         self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1141         command = location
1142         command += " -- --preserve --short-names"
1143         local = (self.run_in_guest(command) == 0);
1144         # second run with --foreign
1145         command += ' --foreign'
1146         remote = (self.run_in_guest(command) == 0);
1147         return ( local and remote)
1148
1149     def gather_logs (self):
1150         "gets all possible logs from plc's/qemu node's/slice's for future reference"
1151         # (1.a) get the plc's /var/log/ and store it locally in logs/myplc.var-log.<plcname>/*
1152         # (1.b) get the plc's  /var/lib/pgsql/data/pg_log/ -> logs/myplc.pgsql-log.<plcname>/*
1153         # (2) get all the nodes qemu log and store it as logs/node.qemu.<node>.log
1154         # (3) get the nodes /var/log and store is as logs/node.var-log.<node>/*
1155         # (4) as far as possible get the slice's /var/log as logs/sliver.var-log.<sliver>/*
1156         # (1.a)
1157         print "-------------------- TestPlc.gather_logs : PLC's /var/log"
1158         self.gather_var_logs ()
1159         # (1.b)
1160         print "-------------------- TestPlc.gather_logs : PLC's /var/lib/psql/data/pg_log/"
1161         self.gather_pgsql_logs ()
1162         # (2) 
1163         print "-------------------- TestPlc.gather_logs : nodes's QEMU logs"
1164         for site_spec in self.plc_spec['sites']:
1165             test_site = TestSite (self,site_spec)
1166             for node_spec in site_spec['nodes']:
1167                 test_node=TestNode(self,test_site,node_spec)
1168                 test_node.gather_qemu_logs()
1169         # (3)
1170         print "-------------------- TestPlc.gather_logs : nodes's /var/log"
1171         self.gather_nodes_var_logs()
1172         # (4)
1173         print "-------------------- TestPlc.gather_logs : sample sliver's /var/log"
1174         self.gather_slivers_var_logs()
1175         return True
1176
1177     def gather_slivers_var_logs(self):
1178         for test_sliver in self.all_sliver_objs():
1179             remote = test_sliver.tar_var_logs()
1180             utils.system("mkdir -p logs/sliver.var-log.%s"%test_sliver.name())
1181             command = remote + " | tar -C logs/sliver.var-log.%s -xf -"%test_sliver.name()
1182             utils.system(command)
1183         return True
1184
1185     def gather_var_logs (self):
1186         utils.system("mkdir -p logs/myplc.var-log.%s"%self.name())
1187         to_plc = self.actual_command_in_guest("tar -C /var/log/ -cf - .")        
1188         command = to_plc + "| tar -C logs/myplc.var-log.%s -xf -"%self.name()
1189         utils.system(command)
1190         command = "chmod a+r,a+x logs/myplc.var-log.%s/httpd"%self.name()
1191         utils.system(command)
1192
1193     def gather_pgsql_logs (self):
1194         utils.system("mkdir -p logs/myplc.pgsql-log.%s"%self.name())
1195         to_plc = self.actual_command_in_guest("tar -C /var/lib/pgsql/data/pg_log/ -cf - .")        
1196         command = to_plc + "| tar -C logs/myplc.pgsql-log.%s -xf -"%self.name()
1197         utils.system(command)
1198
1199     def gather_nodes_var_logs (self):
1200         for site_spec in self.plc_spec['sites']:
1201             test_site = TestSite (self,site_spec)
1202             for node_spec in site_spec['nodes']:
1203                 test_node=TestNode(self,test_site,node_spec)
1204                 test_ssh = TestSsh (test_node.name(),key="keys/key1.rsa")
1205                 command = test_ssh.actual_command("tar -C /var/log -cf - .")
1206                 command = command + "| tar -C logs/node.var-log.%s -xf -"%test_node.name()
1207                 utils.system("mkdir -p logs/node.var-log.%s"%test_node.name())
1208                 utils.system(command)
1209
1210
1211     # returns the filename to use for sql dump/restore, using options.dbname if set
1212     def dbfile (self, database):
1213         # uses options.dbname if it is found
1214         try:
1215             name=self.options.dbname
1216             if not isinstance(name,StringTypes):
1217                 raise Exception
1218         except:
1219             t=datetime.datetime.now()
1220             d=t.date()
1221             name=str(d)
1222         return "/root/%s-%s.sql"%(database,name)
1223
1224     def db_dump(self):
1225         'dump the planetlab5 DB in /root in the PLC - filename has time'
1226         dump=self.dbfile("planetab5")
1227         self.run_in_guest('pg_dump -U pgsqluser planetlab5 -f '+ dump)
1228         utils.header('Dumped planetlab5 database in %s'%dump)
1229         return True
1230
1231     def db_restore(self):
1232         'restore the planetlab5 DB - looks broken, but run -n might help'
1233         dump=self.dbfile("planetab5")
1234         ##stop httpd service
1235         self.run_in_guest('service httpd stop')
1236         # xxx - need another wrapper
1237         self.run_in_guest_piped('echo drop database planetlab5','psql --user=pgsqluser template1')
1238         self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab5')
1239         self.run_in_guest('psql -U pgsqluser planetlab5 -f '+dump)
1240         ##starting httpd service
1241         self.run_in_guest('service httpd start')
1242
1243         utils.header('Database restored from ' + dump)
1244
1245     @standby_generic 
1246     def standby_1(): pass
1247     @standby_generic 
1248     def standby_2(): pass
1249     @standby_generic 
1250     def standby_3(): pass
1251     @standby_generic 
1252     def standby_4(): pass
1253     @standby_generic 
1254     def standby_5(): pass
1255     @standby_generic 
1256     def standby_6(): pass
1257     @standby_generic 
1258     def standby_7(): pass
1259     @standby_generic 
1260     def standby_8(): pass
1261     @standby_generic 
1262     def standby_9(): pass
1263     @standby_generic 
1264     def standby_10(): pass
1265     @standby_generic 
1266     def standby_11(): pass
1267     @standby_generic 
1268     def standby_12(): pass
1269     @standby_generic 
1270     def standby_13(): pass
1271     @standby_generic 
1272     def standby_14(): pass
1273     @standby_generic 
1274     def standby_15(): pass
1275     @standby_generic 
1276     def standby_16(): pass
1277     @standby_generic 
1278     def standby_17(): pass
1279     @standby_generic 
1280     def standby_18(): pass
1281     @standby_generic 
1282     def standby_19(): pass
1283     @standby_generic 
1284     def standby_20(): pass