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