open up traffic to qemu nodes + miscell
[tests.git] / system / TestPlc.py
1 # $Id$
2 import os, os.path
3 import datetime
4 import time
5 import sys
6 import datetime
7 import traceback
8 from types import StringTypes
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
21 # step methods must take (self) and return a boolean (options is a member of the class)
22
23 def standby(minutes,dry_run):
24     utils.header('Entering StandBy for %d mn'%minutes)
25     if dry_run:
26         print 'dry_run'
27     else:
28         time.sleep(60*minutes)
29     return True
30
31 def standby_generic (func):
32     def actual(self):
33         minutes=int(func.__name__.split("_")[1])
34         return standby(minutes,self.options.dry_run)
35     return actual
36
37 def node_mapper (method):
38     def actual(self):
39         overall=True
40         node_method = TestNode.__dict__[method.__name__]
41         for site_spec in self.plc_spec['sites']:
42             test_site = TestSite (self,site_spec)
43             for node_spec in site_spec['nodes']:
44                 test_node = TestNode (self,test_site,node_spec)
45                 if not node_method(test_node): overall=False
46         return overall
47     return actual
48
49 def slice_mapper_options (method):
50     def actual(self):
51         overall=True
52         slice_method = TestSlice.__dict__[method.__name__]
53         for slice_spec in self.plc_spec['slices']:
54             site_spec = self.locate_site (slice_spec['sitename'])
55             test_site = TestSite(self,site_spec)
56             test_slice=TestSlice(self,test_site,slice_spec)
57             if not slice_method(test_slice,self.options): overall=False
58         return overall
59     return actual
60
61 class TestPlc:
62
63     def __init__ (self,plc_spec,options):
64         self.plc_spec=plc_spec
65         self.options=options
66         self.test_ssh=TestSsh(self.plc_spec['hostname'],self.options.buildname)
67         try:
68             self.vserverip=plc_spec['vserverip']
69             self.vservername=plc_spec['vservername']
70             self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
71             self.vserver=True
72         except:
73             self.vserver=False
74             self.url="https://%s:443/PLCAPI/"%plc_spec['hostname']
75 #        utils.header('Using API url %s'%self.url)
76         self.apiserver=TestApiserver(self.url,options.dry_run)
77         
78     def name(self):
79         name=self.plc_spec['name']
80         if self.vserver:
81             return name+".vserver.%s"%self.vservername
82         else:
83             return name+".chroot"
84
85     def hostname(self):
86         return self.plc_spec['hostname']
87
88     def is_local (self):
89         return self.test_ssh.is_local()
90
91     # define the API methods on this object through xmlrpc
92     # would help, but not strictly necessary
93     def connect (self):
94         pass
95
96     def actual_command_in_guest (self,command):
97         return self.test_ssh.actual_command(self.host_to_guest(command))
98     
99     def run_in_guest (self,command):
100         return utils.system(self.actual_command_in_guest(command))
101     
102     def run_in_host (self,command):
103         return self.test_ssh.run_in_buildname(command)
104
105     #command gets run in the chroot/vserver
106     def host_to_guest(self,command):
107         if self.vserver:
108             return "vserver %s exec %s"%(self.vservername,command)
109         else:
110             return "chroot /plc/root %s"%TestSsh.backslash_shell_specials(command)
111     
112     # copy a file to the myplc root image - pass in_data=True if the file must go in /plc/data
113     def copy_in_guest (self, localfile, remotefile, in_data=False):
114         if in_data:
115             chroot_dest="/plc/data"
116         else:
117             chroot_dest="/plc/root"
118         if self.is_local():
119             if not self.vserver:
120                 utils.system("cp %s %s/%s"%(localfile,chroot_dest,remotefile))
121             else:
122                 utils.system("cp %s /vservers/%s/%s"%(localfile,self.vservername,remotefile))
123         else:
124             if not self.vserver:
125                 utils.system("scp %s %s:%s/%s"%(localfile,self.hostname(),chroot_dest,remotefile))
126             else:
127                 utils.system("scp %s %s@/vservers/%s/%s"%(localfile,self.hostname(),self.vservername,remotefile))
128
129
130     # xxx quick n dirty
131     def run_in_guest_piped (self,local,remote):
132         return utils.system(local+" | "+self.test_ssh.actual_command(self.host_to_guest(remote)))
133
134     def auth_root (self):
135         return {'Username':self.plc_spec['PLC_ROOT_USER'],
136                 'AuthMethod':'password',
137                 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
138                 'Role' : self.plc_spec['role']
139                 }
140     def locate_site (self,sitename):
141         for site in self.plc_spec['sites']:
142             if site['site_fields']['name'] == sitename:
143                 return site
144             if site['site_fields']['login_base'] == sitename:
145                 return site
146         raise Exception,"Cannot locate site %s"%sitename
147         
148     def locate_node (self,nodename):
149         for site in self.plc_spec['sites']:
150             for node in site['nodes']:
151                 if node['name'] == nodename:
152                     return (site,node)
153         raise Exception,"Cannot locate node %s"%nodename
154         
155     def locate_hostname (self,hostname):
156         for site in self.plc_spec['sites']:
157             for node in site['nodes']:
158                 if node['node_fields']['hostname'] == hostname:
159                     return (site,node)
160         raise Exception,"Cannot locate hostname %s"%hostname
161         
162     def locate_key (self,keyname):
163         for key in self.plc_spec['keys']:
164             if key['name'] == keyname:
165                 return key
166         raise Exception,"Cannot locate key %s"%keyname
167
168     def locate_slice (self, slicename):
169         for slice in self.plc_spec['slices']:
170             if slice['slice_fields']['name'] == slicename:
171                 return slice
172         raise Exception,"Cannot locate slice %s"%slicename
173
174     # all different hostboxes used in this plc
175     def gather_hostBoxes(self):
176         # maps on sites and nodes, return [ (host_box,test_node) ]
177         tuples=[]
178         for site_spec in self.plc_spec['sites']:
179             test_site = TestSite (self,site_spec)
180             for node_spec in site_spec['nodes']:
181                 test_node = TestNode (self, test_site, node_spec)
182                 if not test_node.is_real():
183                     tuples.append( (test_node.host_box(),test_node) )
184         # transform into a dict { 'host_box' -> [ test_node .. ] }
185         result = {}
186         for (box,node) in tuples:
187             if not result.has_key(box):
188                 result[box]=[node]
189             else:
190                 result[box].append(node)
191         return result
192                     
193     # a step for checking this stuff
194     def show_boxes (self):
195         for (box,nodes) in self.gather_hostBoxes().iteritems():
196             print box,":"," + ".join( [ node.name() for node in nodes ] )
197         return True
198
199     # make this a valid step
200     def kill_all_qemus(self):
201         # this is the brute force version, kill all qemus on that host box
202         for (box,nodes) in self.gather_hostBoxes().iteritems():
203             # pass the first nodename, as we don't push template-qemu on testboxes
204             nodename=nodes[0].name()
205             TestBox(box,self.options.buildname).kill_all_qemus(nodename)
206         return True
207
208     # make this a valid step
209     def list_all_qemus(self):
210         for (box,nodes) in self.gather_hostBoxes().iteritems():
211             # this is the brute force version, kill all qemus on that host box
212             TestBox(box,self.options.buildname).list_all_qemus()
213         return True
214
215     # kill only the right qemus
216     def list_qemus(self):
217         for (box,nodes) in self.gather_hostBoxes().iteritems():
218             # the fine-grain version
219             for node in nodes:
220                 node.list_qemu()
221         return True
222
223     # kill only the right qemus
224     def kill_qemus(self):
225         for (box,nodes) in self.gather_hostBoxes().iteritems():
226             # the fine-grain version
227             for node in nodes:
228                 node.kill_qemu()
229         return True
230
231     #################### step methods
232
233     ### uninstall
234     def uninstall_chroot(self):
235         self.run_in_host('service plc safestop')
236         #####detecting the last myplc version installed and remove it
237         self.run_in_host('rpm -e myplc')
238         ##### Clean up the /plc directory
239         self.run_in_host('rm -rf /plc/data')
240         ##### stop any running vservers
241         self.run_in_host('for vserver in $(ls -d /vservers/* | sed -e s,/vservers/,,) ; do case $vserver in vtest*) echo Shutting down vserver $vserver ; vserver $vserver stop ;; esac ; done')
242         return True
243
244     def uninstall_vserver(self):
245         self.run_in_host("vserver --silent %s delete"%self.vservername)
246         return True
247
248     def uninstall(self):
249         # if there's a chroot-based myplc running, and then a native-based myplc is being deployed
250         # it sounds safer to have the former uninstalled too
251         # now the vserver method cannot be invoked for chroot instances as vservername is required
252         if self.vserver:
253             self.uninstall_vserver()
254             self.uninstall_chroot()
255         else:
256             self.uninstall_chroot()
257         return True
258
259     ### install
260     def install_chroot(self):
261         # nothing to do
262         return True
263
264     def install_vserver(self):
265         # we need build dir for vtest-init-vserver
266         if self.is_local():
267             # a full path for the local calls
268             build_dir=os.path(sys.argv[0])+"/build"
269         else:
270             # use a standard name - will be relative to HOME 
271             build_dir="options.buildname"
272         # run checkout in any case - would do an update if already exists
273         build_checkout = "svn checkout %s %s"%(self.options.build_url,build_dir)
274         if self.run_in_host(build_checkout) != 0:
275             return False
276         # the repo url is taken from myplc-url 
277         # with the last two steps (i386/myplc...) removed
278         repo_url = self.options.myplc_url
279         for level in [ 'rpmname','arch' ]:
280             repo_url = os.path.dirname(repo_url)
281         create_vserver="%s/vtest-init-vserver.sh %s %s -- --interface eth0:%s"%\
282             (build_dir,self.vservername,repo_url,self.vserverip)
283         return self.run_in_host(create_vserver) == 0
284
285     def install(self):
286         if self.vserver:
287             return self.install_vserver()
288         else:
289             return self.install_chroot()
290     
291     ### install_rpm - make this an optional step
292     def cache_rpm(self):
293         url = self.options.myplc_url
294         rpm = os.path.basename(url)
295         cache_fetch="pwd;if [ -f %(rpm)s ] ; then echo Using cached rpm %(rpm)s ; else echo Fetching %(url)s ; curl -O %(url)s; fi"%locals()
296         return self.run_in_host(cache_fetch)==0
297
298     def install_rpm_chroot(self):
299         url = self.options.myplc_url
300         rpm = os.path.basename(url)
301         if not self.cache_rpm():
302             return False
303         utils.header('Installing the :  %s'%rpm)
304         return self.run_in_host('rpm -Uvh '+rpm)==0 and self.run_in_host('service plc mount')==0
305
306     def install_rpm_vserver(self):
307         return self.run_in_guest("yum -y install myplc-native")==0
308
309     def install_rpm(self):
310         if self.vserver:
311             return self.install_rpm_vserver()
312         else:
313             return self.install_rpm_chroot()
314
315     ### 
316     def configure(self):
317         tmpname='%s.plc-config-tty'%(self.name())
318         fileconf=open(tmpname,'w')
319         for var in [ 'PLC_NAME',
320                      'PLC_ROOT_PASSWORD',
321                      'PLC_ROOT_USER',
322                      'PLC_MAIL_ENABLED',
323                      'PLC_MAIL_SUPPORT_ADDRESS',
324                      'PLC_DB_HOST',
325                      'PLC_API_HOST',
326                      'PLC_WWW_HOST',
327                      'PLC_BOOT_HOST',
328                      'PLC_NET_DNS1',
329                      'PLC_NET_DNS2']:
330             fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
331         fileconf.write('w\n')
332         fileconf.write('q\n')
333         fileconf.close()
334         utils.system('cat %s'%tmpname)
335         self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
336         utils.system('rm %s'%tmpname)
337         return True
338
339     # the chroot install is slightly different to this respect
340     def start(self):
341         if self.vserver:
342             self.run_in_guest('service plc start')
343         else:
344             self.run_in_host('service plc start')
345         return True
346         
347     def stop(self):
348         if self.vserver:
349             self.run_in_guest('service plc stop')
350         else:
351             self.run_in_host('service plc stop')
352         return True
353         
354     # could use a TestKey class
355     def store_keys(self):
356         for key_spec in self.plc_spec['keys']:
357                 TestKey(self,key_spec).store_key()
358         return True
359
360     def clean_keys(self):
361         utils.system("rm -rf %s/keys/"%os.path(sys.argv[0]))
362
363     def sites (self):
364         return self.do_sites()
365     
366     def clean_sites (self):
367         return self.do_sites(action="delete")
368     
369     def do_sites (self,action="add"):
370         for site_spec in self.plc_spec['sites']:
371             test_site = TestSite (self,site_spec)
372             if (action != "add"):
373                 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
374                 test_site.delete_site()
375                 # deleted with the site
376                 #test_site.delete_users()
377                 continue
378             else:
379                 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
380                 test_site.create_site()
381                 test_site.create_users()
382         return True
383
384     def nodes (self):
385         return self.do_nodes()
386     def clean_nodes (self):
387         return self.do_nodes(action="delete")
388
389     def do_nodes (self,action="add"):
390         for site_spec in self.plc_spec['sites']:
391             test_site = TestSite (self,site_spec)
392             if action != "add":
393                 utils.header("Deleting nodes in site %s"%test_site.name())
394                 for node_spec in site_spec['nodes']:
395                     test_node=TestNode(self,test_site,node_spec)
396                     utils.header("Deleting %s"%test_node.name())
397                     test_node.delete_node()
398             else:
399                 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
400                 for node_spec in site_spec['nodes']:
401                     utils.pprint('Creating node %s'%node_spec,node_spec)
402                     test_node = TestNode (self,test_site,node_spec)
403                     test_node.create_node ()
404         return True
405
406     # create nodegroups if needed, and populate
407     # no need for a clean_nodegroups if we are careful enough
408     def nodegroups (self):
409         # 1st pass to scan contents
410         groups_dict = {}
411         for site_spec in self.plc_spec['sites']:
412             test_site = TestSite (self,site_spec)
413             for node_spec in site_spec['nodes']:
414                 test_node=TestNode (self,test_site,node_spec)
415                 if node_spec.has_key('nodegroups'):
416                     nodegroupnames=node_spec['nodegroups']
417                     if isinstance(nodegroupnames,StringTypes):
418                         nodegroupnames = [ nodegroupnames ]
419                     for nodegroupname in nodegroupnames:
420                         if not groups_dict.has_key(nodegroupname):
421                             groups_dict[nodegroupname]=[]
422                         groups_dict[nodegroupname].append(test_node.name())
423         auth=self.auth_root()
424         for (nodegroupname,group_nodes) in groups_dict.iteritems():
425             try:
426                 self.apiserver.GetNodeGroups(auth,{'name':nodegroupname})[0]
427             except:
428                 self.apiserver.AddNodeGroup(auth,{'name':nodegroupname})
429             for node in group_nodes:
430                 self.apiserver.AddNodeToNodeGroup(auth,node,nodegroupname)
431         return True
432
433     def all_hostnames (self) :
434         hostnames = []
435         for site_spec in self.plc_spec['sites']:
436             hostnames += [ node_spec['node_fields']['hostname'] \
437                            for node_spec in site_spec['nodes'] ]
438         return hostnames
439
440     # gracetime : during the first <gracetime> minutes nothing gets printed
441     def do_nodes_booted (self, minutes, gracetime=2):
442         if self.options.dry_run:
443             print 'dry_run'
444             return True
445         # compute timeout
446         timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
447         graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
448         # the nodes that haven't checked yet - start with a full list and shrink over time
449         tocheck = self.all_hostnames()
450         utils.header("checking nodes %r"%tocheck)
451         # create a dict hostname -> status
452         status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
453         while tocheck:
454             # get their status
455             tocheck_status=self.apiserver.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
456             # update status
457             for array in tocheck_status:
458                 hostname=array['hostname']
459                 boot_state=array['boot_state']
460                 if boot_state == 'boot':
461                     utils.header ("%s has reached the 'boot' state"%hostname)
462                 else:
463                     # if it's a real node, never mind
464                     (site_spec,node_spec)=self.locate_hostname(hostname)
465                     if TestNode.is_real_model(node_spec['node_fields']['model']):
466                         utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
467                         # let's cheat
468                         boot_state = 'boot'
469                     if datetime.datetime.now() > graceout:
470                         utils.header ("%s still in '%s' state"%(hostname,boot_state))
471                         graceout=datetime.datetime.now()+datetime.timedelta(1)
472                 status[hostname] = boot_state
473             # refresh tocheck
474             tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != 'boot' ]
475             if not tocheck:
476                 return True
477             if datetime.datetime.now() > timeout:
478                 for hostname in tocheck:
479                     utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
480                 return False
481             # otherwise, sleep for a while
482             time.sleep(15)
483         # only useful in empty plcs
484         return True
485
486     def nodes_booted(self):
487         return self.do_nodes_booted(minutes=0)
488     
489
490     def do_nodes_ssh(self,minutes):
491         # compute timeout
492         timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
493         tocheck = self.all_hostnames()
494 #        self.scan_publicKeys(tocheck)
495         utils.header("checking Connectivity on nodes %r"%tocheck)
496         while tocheck:
497             for hostname in tocheck:
498                 # try to ssh in nodes
499                 node_test_ssh = TestSsh (hostname,key="/etc/planetlab/root_ssh_key.rsa")
500                 access=self.run_in_guest(node_test_ssh.actual_command("date"))
501                 if not access:
502                     utils.header('The node %s is sshable -->'%hostname)
503                     # refresh tocheck
504                     tocheck.remove(hostname)
505                 else:
506                     # we will have tried real nodes once, in case they're up - but if not, just skip
507                     (site_spec,node_spec)=self.locate_hostname(hostname)
508                     if TestNode.is_real_model(node_spec['node_fields']['model']):
509                         utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
510                         tocheck.remove(hostname)
511             if  not tocheck:
512                 return True
513             if datetime.datetime.now() > timeout:
514                 for hostname in tocheck:
515                     utils.header("FAILURE to ssh into %s"%hostname)
516                 return False
517             # otherwise, sleep for a while
518             time.sleep(15)
519         # only useful in empty plcs
520         return True
521         
522     def nodes_ssh(self):
523         return self.do_nodes_ssh(minutes=2)
524     
525     @node_mapper
526     def init_node (self): pass
527     @node_mapper
528     def bootcd (self): pass
529     @node_mapper
530     def configure_qemu (self): pass
531     @node_mapper
532     def reinstall_node (self): pass
533         
534     def do_check_initscripts(self):
535         overall = True
536         for slice_spec in self.plc_spec['slices']:
537             if not slice_spec.has_key('initscriptname'):
538                 continue
539             initscript=slice_spec['initscriptname']
540             for nodename in slice_spec['nodenames']:
541                 (site,node) = self.locate_node (nodename)
542                 # xxx - passing the wrong site - probably harmless
543                 test_site = TestSite (self,site)
544                 test_slice = TestSlice (self,test_site,slice_spec)
545                 test_node = TestNode (self,test_site,node)
546                 test_sliver = TestSliver (self, test_node, test_slice)
547                 if not test_sliver.check_initscript(initscript):
548                     overall = False
549         return overall
550             
551     def check_initscripts(self):
552             return self.do_check_initscripts()
553                     
554     def initscripts (self):
555         for initscript in self.plc_spec['initscripts']:
556             utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
557             self.apiserver.AddInitScript(self.auth_root(),initscript['initscript_fields'])
558         return True
559
560     def slices (self):
561         return self.do_slices()
562
563     def clean_slices (self):
564         return self.do_slices("delete")
565
566     def do_slices (self,  action="add"):
567         for slice in self.plc_spec['slices']:
568             site_spec = self.locate_site (slice['sitename'])
569             test_site = TestSite(self,site_spec)
570             test_slice=TestSlice(self,test_site,slice)
571             if action != "add":
572                 utils.header("Deleting slices in site %s"%test_site.name())
573                 test_slice.delete_slice()
574             else:    
575                 utils.pprint("Creating slice",slice)
576                 test_slice.create_slice()
577                 utils.header('Created Slice %s'%slice['slice_fields']['name'])
578         return True
579         
580     @slice_mapper_options
581     def check_slice(self): pass
582
583     @node_mapper
584     def clear_known_hosts (self): pass
585     
586     @node_mapper
587     def start_node (self) : pass
588
589     def locate_first_sliver (self):
590         slice_spec = self.plc_spec['slices'][0]
591         slicename = slice_spec['slice_fields']['name']
592         nodename = slice_spec['nodenames'][0]
593         return self.locate_sliver_obj(nodename,slicename)
594
595     def locate_sliver_obj (self,nodename,slicename):
596         (site,node) = self.locate_node(nodename)
597         slice = self.locate_slice (slicename)
598         # build objects
599         test_site = TestSite (self, site)
600         test_node = TestNode (self, test_site,node)
601         # xxx the slice site is assumed to be the node site - mhh - probably harmless
602         test_slice = TestSlice (self, test_site, slice)
603         return TestSliver (self, test_node, test_slice)
604
605     def check_tcp (self):
606         specs = self.plc_spec['tcp_test']
607         overall=True
608         for spec in specs:
609             port = spec['port']
610             # server side
611             s_test_sliver = self.locate_sliver_obj (spec['server_node'],spec['server_slice'])
612             if not s_test_sliver.run_tcp_server(port,timeout=10):
613                 overall=False
614                 break
615
616             # idem for the client side
617             c_test_sliver = self.locate_sliver_obj(spec['server_node'],spec['server_slice'])
618             if not c_test_sliver.run_tcp_client(s_test_sliver.test_node.name(),port):
619                 overall=False
620         return overall
621     
622
623     def gather_logs (self):
624         # (1) get the plc's /var/log and store it locally in logs/<plcname>-var-log/*
625         # (2) get all the nodes qemu log and store it as logs/<node>-qemu.log
626         # (3) get the nodes /var/log and store is as logs/<node>-var-log/*
627         # (4) as far as possible get the slice's /var/log as logs/<slice>-<node>-var-log/*
628         # (1)
629         print "-------------------- TestPlc.gather_logs : PLC's /var/log"
630         self.gather_var_logs ()
631         # (2) 
632         print "-------------------- TestPlc.gather_logs : nodes's QEMU logs"
633         for site_spec in self.plc_spec['sites']:
634             test_site = TestSite (self,site_spec)
635             for node_spec in site_spec['nodes']:
636                 test_node=TestNode(self,test_site,node_spec)
637                 test_node.gather_qemu_logs()
638         # (3)
639         print "-------------------- TestPlc.gather_logs : nodes's /var/log"
640         self.gather_nodes_var_logs()
641         # (4)
642         print "-------------------- TestPlc.gather_logs : sample sliver's /var/log"
643         self.gather_first_sliver_logs()
644         return True
645
646     def gather_first_sliver_logs(self):
647         try:
648             test_sliver = self.locate_first_sliver()
649             remote = test_sliver.tar_var_logs()
650             utils.system("mkdir -p logs/%s-var-log"%test_sliver.name())
651             command = remote + " | tar -C logs/%s-var-log -xf -"%test_sliver.name()
652             utils.system(command)
653         except Exception,e:
654             print 'Cannot locate first sliver - giving up',e
655         return True
656
657     def gather_var_logs (self):
658         to_plc = self.actual_command_in_guest("tar -C /var/log/ -cf - .")        
659         command = to_plc + "| tar -C logs/%s-var-log -xf -"%self.name()
660         utils.system("mkdir -p logs/%s-var-log"%self.name())
661         utils.system(command)
662
663     def gather_nodes_var_logs (self):
664         for site_spec in self.plc_spec['sites']:
665             test_site = TestSite (self,site_spec)
666             for node_spec in site_spec['nodes']:
667                 test_node=TestNode(self,test_site,node_spec)
668                 test_ssh = TestSsh (test_node.name(),key="/etc/planetlab/root_ssh_key.rsa")
669                 to_plc = self.actual_command_in_guest ( test_ssh.actual_command("tar -C /var/log -cf - ."))
670                 command = to_plc + "| tar -C logs/%s-var-log -xf -"%test_node.name()
671                 utils.system("mkdir -p logs/%s-var-log"%test_node.name())
672                 utils.system(command)
673
674
675     # returns the filename to use for sql dump/restore, using options.dbname if set
676     def dbfile (self, database):
677         # uses options.dbname if it is found
678         try:
679             name=self.options.dbname
680             if not isinstance(name,StringTypes):
681                 raise Exception
682         except:
683             t=datetime.datetime.now()
684             d=t.date()
685             name=str(d)
686         return "/root/%s-%s.sql"%(database,name)
687
688     def db_dump(self):
689         dump=self.dbfile("planetab4")
690         self.run_in_guest('pg_dump -U pgsqluser planetlab4 -f '+ dump)
691         utils.header('Dumped planetlab4 database in %s'%dump)
692         return True
693
694     def db_restore(self):
695         dump=self.dbfile("planetab4")
696         ##stop httpd service
697         self.run_in_guest('service httpd stop')
698         # xxx - need another wrapper
699         self.run_in_guest_piped('echo drop database planetlab4','psql --user=pgsqluser template1')
700         self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab4')
701         self.run_in_guest('psql -U pgsqluser planetlab4 -f '+dump)
702         ##starting httpd service
703         self.run_in_guest('service httpd start')
704
705         utils.header('Database restored from ' + dump)
706
707     @standby_generic 
708     def standby_1(): pass
709     @standby_generic 
710     def standby_2(): pass
711     @standby_generic 
712     def standby_3(): pass
713     @standby_generic 
714     def standby_4(): pass
715     @standby_generic 
716     def standby_5(): pass
717     @standby_generic 
718     def standby_6(): pass
719     @standby_generic 
720     def standby_7(): pass
721     @standby_generic 
722     def standby_8(): pass
723     @standby_generic 
724     def standby_9(): pass
725     @standby_generic 
726     def standby_10(): pass
727     @standby_generic 
728     def standby_11(): pass
729     @standby_generic 
730     def standby_12(): pass
731     @standby_generic 
732     def standby_13(): pass
733     @standby_generic 
734     def standby_14(): pass
735     @standby_generic 
736     def standby_15(): pass
737     @standby_generic 
738     def standby_16(): pass
739     @standby_generic 
740     def standby_17(): pass
741     @standby_generic 
742     def standby_18(): pass
743     @standby_generic 
744     def standby_19(): pass
745     @standby_generic 
746     def standby_20(): pass
747