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