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