branching
[tests.git] / system / TestPlc.py
1 # $Id$
2 import os, os.path
3 import datetime
4 import time
5 import sys
6 import traceback
7 from types import StringTypes
8 import socket
9
10 import utils
11 from TestSite import TestSite
12 from TestNode import TestNode
13 from TestUser import TestUser
14 from TestKey import TestKey
15 from TestSlice import TestSlice
16 from TestSliver import TestSliver
17 from TestBox import TestBox
18 from TestSsh import TestSsh
19 from TestApiserver import TestApiserver
20
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', 'export_qemu',
70                      'kill_all_qemus', 'reinstall_node','start_node', SEP,
71                      'nodes_booted', 'nodes_ssh', 'check_slice',
72                      'check_initscripts', 'check_tcp',SEP,
73                      'force_gather_logs', 'force_kill_qemus', 'force_record_tracker','force_free_tracker' ]
74     other_steps = [ 'stop_all_vservers','fresh_install', 'cache_rpm', 'stop', SEP,
75                     'clean_initscripts', 'clean_sites', 'clean_nodes', 
76                     'clean_slices', 'clean_keys', SEP,
77                     'show_boxes', 'list_all_qemus', 'list_qemus', SEP,
78                     'db_dump' , 'db_restore', ' cleanup_tracker',
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             raise Exception,'chroot-based myplc testing is deprecated'
100         self.apiserver=TestApiserver(self.url,options.dry_run)
101         
102     def name(self):
103         name=self.plc_spec['name']
104         return "%s.%s"%(name,self.vservername)
105
106     def hostname(self):
107         return self.plc_spec['hostname']
108
109     def is_local (self):
110         return self.test_ssh.is_local()
111
112     # define the API methods on this object through xmlrpc
113     # would help, but not strictly necessary
114     def connect (self):
115         pass
116
117     def actual_command_in_guest (self,command):
118         return self.test_ssh.actual_command(self.host_to_guest(command))
119     
120     def run_in_guest (self,command):
121         return utils.system(self.actual_command_in_guest(command))
122     
123     def run_in_host (self,command):
124         return self.test_ssh.run_in_buildname(command)
125
126     #command gets run in the vserver
127     def host_to_guest(self,command):
128         return "vserver %s exec %s"%(self.vservername,command)
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),keep_stdin=True))
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             nodedir=nodes[0].nodedir()
205             TestBox(box,self.options.buildname).kill_all_qemus(nodedir)
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
232     ### utility methods for handling the pool of IP addresses allocated to plcs
233     # Logic
234     # (*) running plcs are recorded in the file named ~/running-test-plcs
235     # (*) this file contains a line for each running plc, older first
236     # (*) each line contains the vserver name + the hostname of the (vserver) testbox where it sits
237     # (*) the free_tracker method performs a vserver stop on the oldest entry
238     # (*) the record_tracker method adds an entry at the bottom of the file
239     # (*) the cleanup_tracker method stops all known vservers and removes the tracker file
240
241     TRACKER_FILE="~/running-test-plcs"
242
243     def record_tracker (self):
244         command="echo %s %s >> %s"%(self.vservername,self.test_ssh.hostname,TestPlc.TRACKER_FILE)
245         (code,output) = utils.output_of (self.test_ssh.actual_command(command))
246         if code != 0:
247             print "WARNING : COULD NOT record_tracker %s as a running plc on %s"%(self.vservername,self.test_ssh.hostname)
248             return False
249         print "Recorded %s in running plcs on host %s"%(self.vservername,self.test_ssh.hostname)
250         return True
251
252     def free_tracker (self):
253         command="head -1 %s"%TestPlc.TRACKER_FILE
254         (code,line) = utils.output_of(self.test_ssh.actual_command(command))
255         if code != 0:
256             print "No entry found in %s on %s"%(TestPlc.TRACKER_FILE,self.test_ssh.hostname)
257             return False
258         try:
259             [vserver_to_stop,hostname] = line.split()
260         except:
261             print "WARNING: free_tracker: Could not parse %s - skipped"%TestPlc.TRACKER_FILE
262             return False
263         stop_command = "vserver --silent %s stop"%vserver_to_stop
264         utils.system(self.test_ssh.actual_command(stop_command))
265         x=TestPlc.TRACKER_FILE
266         flush_command = "tail --lines=+2 %s > %s.tmp ; mv %s.tmp %s"%(x,x,x,x)
267         utils.system(self.test_ssh.actual_command(flush_command))
268         return True
269
270     # this should/could stop only the ones in TRACKER_FILE if that turns out to be reliable
271     def cleanup_tracker (self):
272         stop_all = "cd /vservers ; for i in * ; do vserver --silent $i stop ; done"
273         utils.system(self.test_ssh.actual_command(stop_all))
274         clean_tracker = "rm -f %s"%TestPlc.TRACKER_FILE
275         utils.system(self.test_ssh.actual_command(clean_tracker))
276
277     def uninstall(self):
278         self.run_in_host("vserver --silent %s delete"%self.vservername)
279         return True
280
281     ### install
282     def install(self):
283         # we need build dir for vtest-init-vserver
284         if self.is_local():
285             # a full path for the local calls
286             build_dir=os.path.dirname(sys.argv[0])+"/build"
287         else:
288             # use a standard name - will be relative to remote buildname
289             build_dir="build"
290         # run checkout in any case - would do an update if already exists
291         build_checkout = "svn checkout %s %s"%(self.options.build_url,build_dir)
292         if self.run_in_host(build_checkout) != 0:
293             return False
294         # the repo url is taken from arch-rpms-url 
295         # with the last step (i386.) removed
296         repo_url = self.options.arch_rpms_url
297         for level in [ 'arch' ]:
298             repo_url = os.path.dirname(repo_url)
299         if self.options.arch == "i386":
300             personality_option="-p linux32"
301         else:
302             personality_option="-p linux64"
303         script="vtest-init-vserver.sh"
304         vserver_name = self.vservername
305         vserver_options="--netdev eth0 --interface %s"%self.vserverip
306         try:
307             vserver_hostname=socket.gethostbyaddr(self.vserverip)[0]
308             vserver_options += " --hostname %s"%vserver_hostname
309         except:
310             pass
311         create_vserver="%(build_dir)s/%(script)s %(personality_option)s %(vserver_name)s %(repo_url)s -- %(vserver_options)s"%locals()
312         return self.run_in_host(create_vserver) == 0
313
314     ### install_rpm 
315     def install_rpm(self):
316         return self.run_in_guest("yum -y install myplc-native")==0
317
318     ### 
319     def configure(self):
320         tmpname='%s.plc-config-tty'%(self.name())
321         fileconf=open(tmpname,'w')
322         for var in [ 'PLC_NAME',
323                      'PLC_ROOT_PASSWORD',
324                      'PLC_ROOT_USER',
325                      'PLC_MAIL_ENABLED',
326                      'PLC_MAIL_SUPPORT_ADDRESS',
327                      'PLC_DB_HOST',
328                      'PLC_API_HOST',
329                      'PLC_WWW_HOST',
330                      'PLC_BOOT_HOST',
331                      'PLC_NET_DNS1',
332                      'PLC_NET_DNS2']:
333             fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
334         fileconf.write('w\n')
335         fileconf.write('q\n')
336         fileconf.close()
337         utils.system('cat %s'%tmpname)
338         self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
339         utils.system('rm %s'%tmpname)
340         return True
341
342     def start(self):
343         self.run_in_guest('service plc start')
344         return True
345         
346     def stop(self):
347         self.run_in_guest('service plc stop')
348         return True
349         
350     # could use a TestKey class
351     def store_keys(self):
352         for key_spec in self.plc_spec['keys']:
353                 TestKey(self,key_spec).store_key()
354         return True
355
356     def clean_keys(self):
357         utils.system("rm -rf %s/keys/"%os.path(sys.argv[0]))
358
359     def sites (self):
360         return self.do_sites()
361     
362     def clean_sites (self):
363         return self.do_sites(action="delete")
364     
365     def do_sites (self,action="add"):
366         for site_spec in self.plc_spec['sites']:
367             test_site = TestSite (self,site_spec)
368             if (action != "add"):
369                 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
370                 test_site.delete_site()
371                 # deleted with the site
372                 #test_site.delete_users()
373                 continue
374             else:
375                 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
376                 test_site.create_site()
377                 test_site.create_users()
378         return True
379
380     def nodes (self):
381         return self.do_nodes()
382     def clean_nodes (self):
383         return self.do_nodes(action="delete")
384
385     def do_nodes (self,action="add"):
386         for site_spec in self.plc_spec['sites']:
387             test_site = TestSite (self,site_spec)
388             if action != "add":
389                 utils.header("Deleting nodes in site %s"%test_site.name())
390                 for node_spec in site_spec['nodes']:
391                     test_node=TestNode(self,test_site,node_spec)
392                     utils.header("Deleting %s"%test_node.name())
393                     test_node.delete_node()
394             else:
395                 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
396                 for node_spec in site_spec['nodes']:
397                     utils.pprint('Creating node %s'%node_spec,node_spec)
398                     test_node = TestNode (self,test_site,node_spec)
399                     test_node.create_node ()
400         return True
401
402     # create nodegroups if needed, and populate
403     # no need for a clean_nodegroups if we are careful enough
404     def nodegroups (self):
405         # 1st pass to scan contents
406         groups_dict = {}
407         for site_spec in self.plc_spec['sites']:
408             test_site = TestSite (self,site_spec)
409             for node_spec in site_spec['nodes']:
410                 test_node=TestNode (self,test_site,node_spec)
411                 if node_spec.has_key('nodegroups'):
412                     nodegroupnames=node_spec['nodegroups']
413                     if isinstance(nodegroupnames,StringTypes):
414                         nodegroupnames = [ nodegroupnames ]
415                     for nodegroupname in nodegroupnames:
416                         if not groups_dict.has_key(nodegroupname):
417                             groups_dict[nodegroupname]=[]
418                         groups_dict[nodegroupname].append(test_node.name())
419         auth=self.auth_root()
420         for (nodegroupname,group_nodes) in groups_dict.iteritems():
421             print 'nodegroups:','dealing with nodegroup',nodegroupname,'on nodes',group_nodes
422             # first, check if the nodetagtype is here
423             tag_types = self.apiserver.GetNodeTagTypes(auth,{'name':nodegroupname})
424             if tag_types:
425                 tag_type_id = tag_types[0]['node_tag_type_id']
426                 print 'node-tag-type',nodegroupname,'already exists'
427             else:
428                 tag_type_id = self.apiserver.AddNodeTagType(auth,
429                                                             {'name':nodegroupname,
430                                                              'description':
431                                                                  'for nodegroup %s'%nodegroupname,
432                                                              'category':'test',
433                                                              'min_role_id':10})
434             # create nodegroup
435             nodegroups = self.apiserver.GetNodeGroups (auth, {'groupname':nodegroupname})
436             if nodegroups:
437                 print 'nodegroup',nodegroupname,'already exists'
438             else:
439                 self.apiserver.AddNodeGroup(auth,
440                                             {'groupname': nodegroupname,
441                                              'node_tag_type_id': tag_type_id,
442                                              'value': 'yes'})
443             # set node tag on all nodes, value='yes'
444             for nodename in group_nodes:
445                 # check if already set
446                 # xxx need node_id - need improvement in the API
447                 node_id = self.apiserver.GetNodes(auth,nodename)[0]['node_id']
448                 node_tag = self.apiserver.GetNodeTags(auth,
449                                                       {'node_id':node_id,
450                                                        'node_tag_type_id':tag_type_id})
451                 if node_tag:
452                     print 'node',nodename,'already has tag',nodegroupname
453                 else:
454                     self.apiserver.AddNodeTag(auth, node_id, nodegroupname,"yes")
455         return True
456
457     def all_hostnames (self) :
458         hostnames = []
459         for site_spec in self.plc_spec['sites']:
460             hostnames += [ node_spec['node_fields']['hostname'] \
461                            for node_spec in site_spec['nodes'] ]
462         return hostnames
463
464     # gracetime : during the first <gracetime> minutes nothing gets printed
465     def do_nodes_booted (self, minutes, gracetime,period=30):
466         if self.options.dry_run:
467             print 'dry_run'
468             return True
469         # compute timeout
470         timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
471         graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
472         # the nodes that haven't checked yet - start with a full list and shrink over time
473         tocheck = self.all_hostnames()
474         utils.header("checking nodes %r"%tocheck)
475         # create a dict hostname -> status
476         status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
477         while tocheck:
478             # get their status
479             tocheck_status=self.apiserver.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
480             # update status
481             for array in tocheck_status:
482                 hostname=array['hostname']
483                 boot_state=array['boot_state']
484                 if boot_state == 'boot':
485                     utils.header ("%s has reached the 'boot' state"%hostname)
486                 else:
487                     # if it's a real node, never mind
488                     (site_spec,node_spec)=self.locate_hostname(hostname)
489                     if TestNode.is_real_model(node_spec['node_fields']['model']):
490                         utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
491                         # let's cheat
492                         boot_state = 'boot'
493                     elif datetime.datetime.now() > graceout:
494                         utils.header ("%s still in '%s' state"%(hostname,boot_state))
495                         graceout=datetime.datetime.now()+datetime.timedelta(1)
496                 status[hostname] = boot_state
497             # refresh tocheck
498             tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != 'boot' ]
499             if not tocheck:
500                 return True
501             if datetime.datetime.now() > timeout:
502                 for hostname in tocheck:
503                     utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
504                 return False
505             # otherwise, sleep for a while
506             time.sleep(period)
507         # only useful in empty plcs
508         return True
509
510     def nodes_booted(self):
511         return self.do_nodes_booted(minutes=20,gracetime=15)
512
513     def do_nodes_ssh(self,minutes,gracetime,period=30):
514         # compute timeout
515         timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
516         graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
517         tocheck = self.all_hostnames()
518 #        self.scan_publicKeys(tocheck)
519         utils.header("checking Connectivity on nodes %r"%tocheck)
520         while tocheck:
521             for hostname in tocheck:
522                 # try to ssh in nodes
523                 node_test_ssh = TestSsh (hostname,key="/etc/planetlab/root_ssh_key.rsa")
524                 success=self.run_in_guest(node_test_ssh.actual_command("hostname"))==0
525                 if success:
526                     utils.header('The node %s is sshable -->'%hostname)
527                     # refresh tocheck
528                     tocheck.remove(hostname)
529                 else:
530                     # we will have tried real nodes once, in case they're up - but if not, just skip
531                     (site_spec,node_spec)=self.locate_hostname(hostname)
532                     if TestNode.is_real_model(node_spec['node_fields']['model']):
533                         utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
534                         tocheck.remove(hostname)
535                     elif datetime.datetime.now() > graceout:
536                         utils.header("Could not ssh-enter root context on %s"%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(period)
545         # only useful in empty plcs
546         return True
547         
548     def nodes_ssh(self):
549         return self.do_nodes_ssh(minutes=6,gracetime=4)
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     @node_mapper
560     def export_qemu (self): pass
561         
562     def do_check_initscripts(self):
563         overall = True
564         for slice_spec in self.plc_spec['slices']:
565             if not slice_spec.has_key('initscriptname'):
566                 continue
567             initscript=slice_spec['initscriptname']
568             for nodename in slice_spec['nodenames']:
569                 (site,node) = self.locate_node (nodename)
570                 # xxx - passing the wrong site - probably harmless
571                 test_site = TestSite (self,site)
572                 test_slice = TestSlice (self,test_site,slice_spec)
573                 test_node = TestNode (self,test_site,node)
574                 test_sliver = TestSliver (self, test_node, test_slice)
575                 if not test_sliver.check_initscript(initscript):
576                     overall = False
577         return overall
578             
579     def check_initscripts(self):
580             return self.do_check_initscripts()
581                     
582     def initscripts (self):
583         for initscript in self.plc_spec['initscripts']:
584             utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
585             self.apiserver.AddInitScript(self.auth_root(),initscript['initscript_fields'])
586         return True
587
588     def clean_initscripts (self):
589         for initscript in self.plc_spec['initscripts']:
590             initscript_name = initscript['initscript_fields']['name']
591             print('Attempting to delete %s in plc %s'%(initscript_name,self.plc_spec['name']))
592             try:
593                 self.apiserver.DeleteInitScript(self.auth_root(),initscript_name)
594                 print initscript_name,'deleted'
595             except:
596                 print 'deletion went wrong - probably did not exist'
597         return True
598
599     def slices (self):
600         return self.do_slices()
601
602     def clean_slices (self):
603         return self.do_slices("delete")
604
605     def do_slices (self,  action="add"):
606         for slice in self.plc_spec['slices']:
607             site_spec = self.locate_site (slice['sitename'])
608             test_site = TestSite(self,site_spec)
609             test_slice=TestSlice(self,test_site,slice)
610             if action != "add":
611                 utils.header("Deleting slices in site %s"%test_site.name())
612                 test_slice.delete_slice()
613             else:    
614                 utils.pprint("Creating slice",slice)
615                 test_slice.create_slice()
616                 utils.header('Created Slice %s'%slice['slice_fields']['name'])
617         return True
618         
619     @slice_mapper_options
620     def check_slice(self): pass
621
622     @node_mapper
623     def clear_known_hosts (self): pass
624     
625     @node_mapper
626     def start_node (self) : pass
627
628     def all_sliver_objs (self):
629         result=[]
630         for slice_spec in self.plc_spec['slices']:
631             slicename = slice_spec['slice_fields']['name']
632             for nodename in slice_spec['nodenames']:
633                 result.append(self.locate_sliver_obj (nodename,slicename))
634         return result
635
636     def locate_sliver_obj (self,nodename,slicename):
637         (site,node) = self.locate_node(nodename)
638         slice = self.locate_slice (slicename)
639         # build objects
640         test_site = TestSite (self, site)
641         test_node = TestNode (self, test_site,node)
642         # xxx the slice site is assumed to be the node site - mhh - probably harmless
643         test_slice = TestSlice (self, test_site, slice)
644         return TestSliver (self, test_node, test_slice)
645
646     def check_tcp (self):
647         specs = self.plc_spec['tcp_test']
648         overall=True
649         for spec in specs:
650             port = spec['port']
651             # server side
652             s_test_sliver = self.locate_sliver_obj (spec['server_node'],spec['server_slice'])
653             if not s_test_sliver.run_tcp_server(port,timeout=10):
654                 overall=False
655                 break
656
657             # idem for the client side
658             c_test_sliver = self.locate_sliver_obj(spec['server_node'],spec['server_slice'])
659             if not c_test_sliver.run_tcp_client(s_test_sliver.test_node.name(),port):
660                 overall=False
661         return overall
662     
663
664     def gather_logs (self):
665         # (1) get the plc's /var/log and store it locally in logs/myplc.var-log.<plcname>/*
666         # (2) get all the nodes qemu log and store it as logs/node.qemu.<node>.log
667         # (3) get the nodes /var/log and store is as logs/node.var-log.<node>/*
668         # (4) as far as possible get the slice's /var/log as logs/sliver.var-log.<sliver>/*
669         # (1)
670         print "-------------------- TestPlc.gather_logs : PLC's /var/log"
671         self.gather_var_logs ()
672         # (2) 
673         print "-------------------- TestPlc.gather_logs : nodes's QEMU logs"
674         for site_spec in self.plc_spec['sites']:
675             test_site = TestSite (self,site_spec)
676             for node_spec in site_spec['nodes']:
677                 test_node=TestNode(self,test_site,node_spec)
678                 test_node.gather_qemu_logs()
679         # (3)
680         print "-------------------- TestPlc.gather_logs : nodes's /var/log"
681         self.gather_nodes_var_logs()
682         # (4)
683         print "-------------------- TestPlc.gather_logs : sample sliver's /var/log"
684         self.gather_slivers_var_logs()
685         return True
686
687     def gather_slivers_var_logs(self):
688         for test_sliver in self.all_sliver_objs():
689             remote = test_sliver.tar_var_logs()
690             utils.system("mkdir -p logs/sliver.var-log.%s"%test_sliver.name())
691             command = remote + " | tar -C logs/sliver.var-log.%s -xf -"%test_sliver.name()
692             utils.system(command)
693         return True
694
695     def gather_var_logs (self):
696         to_plc = self.actual_command_in_guest("tar -C /var/log/ -cf - .")        
697         command = to_plc + "| tar -C logs/myplc.var-log.%s -xf -"%self.name()
698         utils.system("mkdir -p logs/myplc.var-log.%s"%self.name())
699         utils.system(command)
700
701     def gather_nodes_var_logs (self):
702         for site_spec in self.plc_spec['sites']:
703             test_site = TestSite (self,site_spec)
704             for node_spec in site_spec['nodes']:
705                 test_node=TestNode(self,test_site,node_spec)
706                 test_ssh = TestSsh (test_node.name(),key="/etc/planetlab/root_ssh_key.rsa")
707                 to_plc = self.actual_command_in_guest ( test_ssh.actual_command("tar -C /var/log -cf - ."))
708                 command = to_plc + "| tar -C logs/node.var-log.%s -xf -"%test_node.name()
709                 utils.system("mkdir -p logs/node.var-log.%s"%test_node.name())
710                 utils.system(command)
711
712
713     # returns the filename to use for sql dump/restore, using options.dbname if set
714     def dbfile (self, database):
715         # uses options.dbname if it is found
716         try:
717             name=self.options.dbname
718             if not isinstance(name,StringTypes):
719                 raise Exception
720         except:
721             t=datetime.datetime.now()
722             d=t.date()
723             name=str(d)
724         return "/root/%s-%s.sql"%(database,name)
725
726     def db_dump(self):
727         dump=self.dbfile("planetab4")
728         self.run_in_guest('pg_dump -U pgsqluser planetlab4 -f '+ dump)
729         utils.header('Dumped planetlab4 database in %s'%dump)
730         return True
731
732     def db_restore(self):
733         dump=self.dbfile("planetab4")
734         ##stop httpd service
735         self.run_in_guest('service httpd stop')
736         # xxx - need another wrapper
737         self.run_in_guest_piped('echo drop database planetlab4','psql --user=pgsqluser template1')
738         self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab4')
739         self.run_in_guest('psql -U pgsqluser planetlab4 -f '+dump)
740         ##starting httpd service
741         self.run_in_guest('service httpd start')
742
743         utils.header('Database restored from ' + dump)
744
745     @standby_generic 
746     def standby_1(): pass
747     @standby_generic 
748     def standby_2(): pass
749     @standby_generic 
750     def standby_3(): pass
751     @standby_generic 
752     def standby_4(): pass
753     @standby_generic 
754     def standby_5(): pass
755     @standby_generic 
756     def standby_6(): pass
757     @standby_generic 
758     def standby_7(): pass
759     @standby_generic 
760     def standby_8(): pass
761     @standby_generic 
762     def standby_9(): pass
763     @standby_generic 
764     def standby_10(): pass
765     @standby_generic 
766     def standby_11(): pass
767     @standby_generic 
768     def standby_12(): pass
769     @standby_generic 
770     def standby_13(): pass
771     @standby_generic 
772     def standby_14(): pass
773     @standby_generic 
774     def standby_15(): pass
775     @standby_generic 
776     def standby_16(): pass
777     @standby_generic 
778     def standby_17(): pass
779     @standby_generic 
780     def standby_18(): pass
781     @standby_generic 
782     def standby_19(): pass
783     @standby_generic 
784     def standby_20(): pass
785