more flexible tools to release the resources attached to a given test
[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 from TestSliceSfa import TestSliceSfa
21 from TestUserSfa import TestUserSfa
22
23 # step methods must take (self) and return a boolean (options is a member of the class)
24
25 def standby(minutes,dry_run):
26     utils.header('Entering StandBy for %d mn'%minutes)
27     if dry_run:
28         print 'dry_run'
29     else:
30         time.sleep(60*minutes)
31     return True
32
33 def standby_generic (func):
34     def actual(self):
35         minutes=int(func.__name__.split("_")[1])
36         return standby(minutes,self.options.dry_run)
37     return actual
38
39 def node_mapper (method):
40     def actual(self):
41         overall=True
42         node_method = TestNode.__dict__[method.__name__]
43         for site_spec in self.plc_spec['sites']:
44             test_site = TestSite (self,site_spec)
45             for node_spec in site_spec['nodes']:
46                 test_node = TestNode (self,test_site,node_spec)
47                 if not node_method(test_node): overall=False
48         return overall
49     # restore the doc text
50     actual.__doc__=method.__doc__
51     return actual
52
53 def slice_mapper_options (method):
54     def actual(self):
55         overall=True
56         slice_method = TestSlice.__dict__[method.__name__]
57         for slice_spec in self.plc_spec['slices']:
58             site_spec = self.locate_site (slice_spec['sitename'])
59             test_site = TestSite(self,site_spec)
60             test_slice=TestSlice(self,test_site,slice_spec)
61             if not slice_method(test_slice,self.options): overall=False
62         return overall
63     # restore the doc text
64     actual.__doc__=method.__doc__
65     return actual
66
67 def slice_mapper_options_sfa (method):
68     def actual(self):
69         test_plc=self
70         overall=True
71         slice_method = TestSliceSfa.__dict__[method.__name__]
72         for slice_spec in self.plc_spec['sfa']['slices_sfa']:
73             site_spec = self.locate_site (slice_spec['sitename'])
74             test_site = TestSite(self,site_spec)
75             test_slice=TestSliceSfa(test_plc,test_site,slice_spec)
76             if not slice_method(test_slice,self.options): overall=False
77         return overall
78     # restore the doc text
79     actual.__doc__=method.__doc__
80     return actual
81
82 SEP='<sep>'
83
84 class TestPlc:
85
86     default_steps = [
87         'display', 'resources_pre', SEP,
88         'delete_vs','create_vs','install', 'configure', 'start', SEP,
89         'fetch_keys', 'store_keys', 'clear_known_hosts', SEP,
90         'initscripts', 'sites', 'nodes', 'slices', 'nodegroups', SEP,
91         'reinstall_node', 'init_node','bootcd', 'configure_qemu', 'export_qemu',
92         'kill_all_qemus', 'start_node', SEP,
93         # better use of time: do this now that the nodes are taking off
94         'plcsh_stress_test', SEP,
95         'install_sfa', 'configure_sfa', 'import_sfa', 'start_sfa', SEP,
96         'setup_sfa', 'add_sfa', 'update_sfa', 'view_sfa', SEP,
97         'nodes_ssh_debug', 'nodes_ssh_boot', 'check_slice', 'check_initscripts', SEP,
98         # optionally run sfa later; takes longer, but checks more about nm 
99         # 'install_sfa', 'configure_sfa', 'import_sfa', 'start_sfa', SEP,
100         # 'setup_sfa', 'add_sfa', 'update_sfa', 'view_sfa', SEP,
101         'check_slice_sfa', 'delete_sfa', 'stop_sfa', SEP,
102         'check_tcp',  'check_hooks',  SEP,
103         'force_gather_logs', 'force_resources_post',
104         ]
105     other_steps = [ 
106         'show_boxes', 'resources_list','resources_release','resources_release_plc','resources_release_qemu',SEP,
107         'stop', 'vs_start', SEP,
108         'clean_initscripts', 'clean_nodegroups','clean_all_sites', SEP,
109         'clean_sites', 'clean_nodes', 'clean_slices', 'clean_keys', SEP,
110         'populate' , SEP,
111         'list_all_qemus', 'list_qemus', 'kill_qemus', SEP,
112         'db_dump' , 'db_restore', SEP,
113         'standby_1 through 20',
114         ]
115
116     @staticmethod
117     def printable_steps (list):
118         return " ".join(list).replace(" "+SEP+" "," \\\n")
119     @staticmethod
120     def valid_step (step):
121         return step != SEP
122
123     def __init__ (self,plc_spec,options):
124         self.plc_spec=plc_spec
125         self.options=options
126         self.test_ssh=TestSsh(self.plc_spec['hostname'],self.options.buildname)
127         try:
128             self.vserverip=plc_spec['vserverip']
129             self.vservername=plc_spec['vservername']
130             self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
131             self.vserver=True
132         except:
133             raise Exception,'chroot-based myplc testing is deprecated'
134         self.apiserver=TestApiserver(self.url,options.dry_run)
135         
136     def name(self):
137         name=self.plc_spec['name']
138         return "%s.%s"%(name,self.vservername)
139
140     def hostname(self):
141         return self.plc_spec['hostname']
142
143     def is_local (self):
144         return self.test_ssh.is_local()
145
146     # define the API methods on this object through xmlrpc
147     # would help, but not strictly necessary
148     def connect (self):
149         pass
150
151     def actual_command_in_guest (self,command):
152         return self.test_ssh.actual_command(self.host_to_guest(command))
153     
154     def start_guest (self):
155       return utils.system(self.test_ssh.actual_command(self.start_guest_in_host()))
156     
157     def run_in_guest (self,command):
158         return utils.system(self.actual_command_in_guest(command))
159     
160     def run_in_host (self,command):
161         return self.test_ssh.run_in_buildname(command)
162
163     #command gets run in the vserver
164     def host_to_guest(self,command):
165         return "vserver %s exec %s"%(self.vservername,command)
166     
167     #command gets run in the vserver
168     def start_guest_in_host(self):
169         return "vserver %s start"%(self.vservername)
170     
171     # xxx quick n dirty
172     def run_in_guest_piped (self,local,remote):
173         return utils.system(local+" | "+self.test_ssh.actual_command(self.host_to_guest(remote),keep_stdin=True))
174
175     def auth_root (self):
176         return {'Username':self.plc_spec['PLC_ROOT_USER'],
177                 'AuthMethod':'password',
178                 'AuthString':self.plc_spec['PLC_ROOT_PASSWORD'],
179                 'Role' : self.plc_spec['role']
180                 }
181     def locate_site (self,sitename):
182         for site in self.plc_spec['sites']:
183             if site['site_fields']['name'] == sitename:
184                 return site
185             if site['site_fields']['login_base'] == sitename:
186                 return site
187         raise Exception,"Cannot locate site %s"%sitename
188         
189     def locate_node (self,nodename):
190         for site in self.plc_spec['sites']:
191             for node in site['nodes']:
192                 if node['name'] == nodename:
193                     return (site,node)
194         raise Exception,"Cannot locate node %s"%nodename
195         
196     def locate_hostname (self,hostname):
197         for site in self.plc_spec['sites']:
198             for node in site['nodes']:
199                 if node['node_fields']['hostname'] == hostname:
200                     return (site,node)
201         raise Exception,"Cannot locate hostname %s"%hostname
202         
203     def locate_key (self,keyname):
204         for key in self.plc_spec['keys']:
205             if key['name'] == keyname:
206                 return key
207         raise Exception,"Cannot locate key %s"%keyname
208
209     def locate_slice (self, slicename):
210         for slice in self.plc_spec['slices']:
211             if slice['slice_fields']['name'] == slicename:
212                 return slice
213         raise Exception,"Cannot locate slice %s"%slicename
214
215     def all_sliver_objs (self):
216         result=[]
217         for slice_spec in self.plc_spec['slices']:
218             slicename = slice_spec['slice_fields']['name']
219             for nodename in slice_spec['nodenames']:
220                 result.append(self.locate_sliver_obj (nodename,slicename))
221         return result
222
223     def locate_sliver_obj (self,nodename,slicename):
224         (site,node) = self.locate_node(nodename)
225         slice = self.locate_slice (slicename)
226         # build objects
227         test_site = TestSite (self, site)
228         test_node = TestNode (self, test_site,node)
229         # xxx the slice site is assumed to be the node site - mhh - probably harmless
230         test_slice = TestSlice (self, test_site, slice)
231         return TestSliver (self, test_node, test_slice)
232
233     def locate_first_node(self):
234         nodename=self.plc_spec['slices'][0]['nodenames'][0]
235         (site,node) = self.locate_node(nodename)
236         test_site = TestSite (self, site)
237         test_node = TestNode (self, test_site,node)
238         return test_node
239
240     def locate_first_sliver (self):
241         slice_spec=self.plc_spec['slices'][0]
242         slicename=slice_spec['slice_fields']['name']
243         nodename=slice_spec['nodenames'][0]
244         return self.locate_sliver_obj(nodename,slicename)
245
246     # all different hostboxes used in this plc
247     def gather_hostBoxes(self):
248         # maps on sites and nodes, return [ (host_box,test_node) ]
249         tuples=[]
250         for site_spec in self.plc_spec['sites']:
251             test_site = TestSite (self,site_spec)
252             for node_spec in site_spec['nodes']:
253                 test_node = TestNode (self, test_site, node_spec)
254                 if not test_node.is_real():
255                     tuples.append( (test_node.host_box(),test_node) )
256         # transform into a dict { 'host_box' -> [ test_node .. ] }
257         result = {}
258         for (box,node) in tuples:
259             if not result.has_key(box):
260                 result[box]=[node]
261             else:
262                 result[box].append(node)
263         return result
264                     
265     # a step for checking this stuff
266     def show_boxes (self):
267         'print summary of nodes location'
268         for (box,nodes) in self.gather_hostBoxes().iteritems():
269             print box,":"," + ".join( [ node.name() for node in nodes ] )
270         return True
271
272     # make this a valid step
273     def kill_all_qemus(self):
274         'kill all qemu instances on the qemu boxes involved by this setup'
275         # this is the brute force version, kill all qemus on that host box
276         for (box,nodes) in self.gather_hostBoxes().iteritems():
277             # pass the first nodename, as we don't push template-qemu on testboxes
278             nodedir=nodes[0].nodedir()
279             TestBox(box,self.options.buildname).kill_all_qemus(nodedir)
280         return True
281
282     # make this a valid step
283     def list_all_qemus(self):
284         'list all qemu instances on the qemu boxes involved by this setup'
285         for (box,nodes) in self.gather_hostBoxes().iteritems():
286             # this is the brute force version, kill all qemus on that host box
287             TestBox(box,self.options.buildname).list_all_qemus()
288         return True
289
290     # kill only the right qemus
291     def list_qemus(self):
292         'list qemu instances for our nodes'
293         for (box,nodes) in self.gather_hostBoxes().iteritems():
294             # the fine-grain version
295             for node in nodes:
296                 node.list_qemu()
297         return True
298
299     # kill only the right qemus
300     def kill_qemus(self):
301         'kill the qemu instances for our nodes'
302         for (box,nodes) in self.gather_hostBoxes().iteritems():
303             # the fine-grain version
304             for node in nodes:
305                 node.kill_qemu()
306         return True
307
308     #################### display config
309     def display (self):
310         "show test configuration after localization"
311         self.display_pass (1)
312         self.display_pass (2)
313         return True
314
315     # entry point
316     always_display_keys=['PLC_WWW_HOST','nodes','sites',]
317     def display_pass (self,passno):
318         for (key,val) in self.plc_spec.iteritems():
319             if not self.options.verbose and key not in TestPlc.always_display_keys: continue
320             if passno == 2:
321                 if key == 'sites':
322                     for site in val:
323                         self.display_site_spec(site)
324                         for node in site['nodes']:
325                             self.display_node_spec(node)
326                 elif key=='initscripts':
327                     for initscript in val:
328                         self.display_initscript_spec (initscript)
329                 elif key=='slices':
330                     for slice in val:
331                         self.display_slice_spec (slice)
332                 elif key=='keys':
333                     for key in val:
334                         self.display_key_spec (key)
335             elif passno == 1:
336                 if key not in ['sites','initscripts','slices','keys', 'sfa']:
337                     print '+   ',key,':',val
338
339     def display_site_spec (self,site):
340         print '+ ======== site',site['site_fields']['name']
341         for (k,v) in site.iteritems():
342             if not self.options.verbose and k not in TestPlc.always_display_keys: continue
343             if k=='nodes':
344                 if v: 
345                     print '+       ','nodes : ',
346                     for node in v:  
347                         print node['node_fields']['hostname'],'',
348                     print ''
349             elif k=='users':
350                 if v: 
351                     print '+       users : ',
352                     for user in v:  
353                         print user['name'],'',
354                     print ''
355             elif k == 'site_fields':
356                 print '+       login_base',':',v['login_base']
357             elif k == 'address_fields':
358                 pass
359             else:
360                 print '+       ',k,
361                 PrettyPrinter(indent=8,depth=2).pprint(v)
362         
363     def display_initscript_spec (self,initscript):
364         print '+ ======== initscript',initscript['initscript_fields']['name']
365
366     def display_key_spec (self,key):
367         print '+ ======== key',key['name']
368
369     def display_slice_spec (self,slice):
370         print '+ ======== slice',slice['slice_fields']['name']
371         for (k,v) in slice.iteritems():
372             if k=='nodenames':
373                 if v: 
374                     print '+       nodes : ',
375                     for nodename in v:  
376                         print nodename,'',
377                     print ''
378             elif k=='usernames':
379                 if v: 
380                     print '+       users : ',
381                     for username in v:  
382                         print username,'',
383                     print ''
384             elif k=='slice_fields':
385                 print '+       fields',':',
386                 print 'max_nodes=',v['max_nodes'],
387                 print ''
388             else:
389                 print '+       ',k,v
390
391     def display_node_spec (self,node):
392         print "+           node",node['name'],"host_box=",node['host_box'],
393         print "hostname=",node['node_fields']['hostname'],
394         print "ip=",node['interface_fields']['ip']
395     
396
397     # another entry point for just showing the boxes involved
398     def display_mapping (self):
399         TestPlc.display_mapping_plc(self.plc_spec)
400         return True
401
402     @staticmethod
403     def display_mapping_plc (plc_spec):
404         print '+ MyPLC',plc_spec['name']
405         print '+\tvserver address = root@%s:/vservers/%s'%(plc_spec['hostname'],plc_spec['vservername'])
406         print '+\tIP = %s/%s'%(plc_spec['PLC_API_HOST'],plc_spec['vserverip'])
407         for site_spec in plc_spec['sites']:
408             for node_spec in site_spec['nodes']:
409                 TestPlc.display_mapping_node(node_spec)
410
411     @staticmethod
412     def display_mapping_node (node_spec):
413         print '+   NODE %s'%(node_spec['name'])
414         print '+\tqemu box %s'%node_spec['host_box']
415         print '+\thostname=%s'%node_spec['node_fields']['hostname']
416
417     def resources_pre (self):
418         "run site-dependant pre-test script as defined in LocalTestResources"
419         from LocalTestResources import local_resources
420         return local_resources.step_pre(self)
421  
422     def resources_post (self):
423         "run site-dependant post-test script as defined in LocalTestResources"
424         from LocalTestResources import local_resources
425         return local_resources.step_post(self)
426  
427     def resources_list (self):
428         "run site-dependant list script as defined in LocalTestResources"
429         from LocalTestResources import local_resources
430         return local_resources.step_list(self)
431  
432     def resources_release (self):
433         "run site-dependant release script as defined in LocalTestResources"
434         from LocalTestResources import local_resources
435         return local_resources.step_release(self)
436  
437     def resources_release_plc (self):
438         "run site-dependant release script as defined in LocalTestResources"
439         from LocalTestResources import local_resources
440         return local_resources.step_release_plc(self)
441  
442     def resources_release_qemu (self):
443         "run site-dependant release script as defined in LocalTestResources"
444         from LocalTestResources import local_resources
445         return local_resources.step_release_qemu(self)
446  
447     def delete_vs(self):
448         "vserver delete the test myplc"
449         self.run_in_host("vserver --silent %s delete"%self.vservername)
450         return True
451
452     ### install
453     def create_vs (self):
454         "vserver creation (no install done)"
455         if self.is_local():
456             # a full path for the local calls
457             build_dir=os.path.dirname(sys.argv[0])
458             # sometimes this is empty - set to "." in such a case
459             if not build_dir: build_dir="."
460             build_dir += "/build"
461         else:
462             # use a standard name - will be relative to remote buildname
463             build_dir="build"
464         # run checkout in any case - would do an update if already exists
465         build_checkout = "svn checkout %s %s"%(self.options.build_url,build_dir)
466         if self.run_in_host(build_checkout) != 0:
467             return False
468         # the repo url is taken from arch-rpms-url 
469         # with the last step (i386) removed
470         repo_url = self.options.arch_rpms_url
471         for level in [ 'arch' ]:
472             repo_url = os.path.dirname(repo_url)
473         # pass the vbuild-nightly options to vtest-init-vserver
474         test_env_options=""
475         test_env_options += " -p %s"%self.options.personality
476         test_env_options += " -d %s"%self.options.pldistro
477         test_env_options += " -f %s"%self.options.fcdistro
478         script="vtest-init-vserver.sh"
479         vserver_name = self.vservername
480         vserver_options="--netdev eth0 --interface %s"%self.vserverip
481         try:
482             vserver_hostname=socket.gethostbyaddr(self.vserverip)[0]
483             vserver_options += " --hostname %s"%vserver_hostname
484         except:
485             print "Cannot reverse lookup %s"%self.vserverip
486             print "This is considered fatal, as this might pollute the test results"
487             return False
488         create_vserver="%(build_dir)s/%(script)s %(test_env_options)s %(vserver_name)s %(repo_url)s -- %(vserver_options)s"%locals()
489         return self.run_in_host(create_vserver) == 0
490
491     ### install_rpm 
492     def install(self):
493         "yum install myplc, noderepo, and the plain bootstrapfs"
494
495         # workaround for getting pgsql8.2 on centos5
496         if self.options.fcdistro == "centos5":
497             self.run_in_guest("rpm -Uvh http://yum.pgsqlrpms.org/8.2/pgdg-centos-8.2-5.noarch.rpm")
498             self.run_in_guest("rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm")
499
500         if self.options.personality == "linux32":
501             arch = "i386"
502         elif self.options.personality == "linux64":
503             arch = "x86_64"
504         else:
505             raise Exception, "Unsupported personality %r"%self.options.personality
506         
507         nodefamily="%s-%s-%s"%(self.options.pldistro,self.options.fcdistro,arch)
508
509         # try to install slicerepo - not fatal yet
510         self.run_in_guest("yum -y install slicerepo-%s"%nodefamily)
511         
512         return \
513             self.run_in_guest("yum -y install myplc")==0 and \
514             self.run_in_guest("yum -y install noderepo-%s"%nodefamily)==0 and \
515             self.run_in_guest("yum -y install bootstrapfs-%s-plain"%nodefamily)==0 
516
517     ### 
518     def configure(self):
519         "run plc-config-tty"
520         tmpname='%s.plc-config-tty'%(self.name())
521         fileconf=open(tmpname,'w')
522         for var in [ 'PLC_NAME',
523                      'PLC_ROOT_PASSWORD',
524                      'PLC_ROOT_USER',
525                      'PLC_MAIL_ENABLED',
526                      'PLC_MAIL_SUPPORT_ADDRESS',
527                      'PLC_DB_HOST',
528                      'PLC_DB_PASSWORD',
529                      # Above line was added for integrating SFA Testing
530                      'PLC_API_HOST',
531                      'PLC_WWW_HOST',
532                      'PLC_BOOT_HOST',
533                      'PLC_NET_DNS1',
534                      'PLC_NET_DNS2']:
535             fileconf.write ('e %s\n%s\n'%(var,self.plc_spec[var]))
536         fileconf.write('w\n')
537         fileconf.write('q\n')
538         fileconf.close()
539         utils.system('cat %s'%tmpname)
540         self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
541         utils.system('rm %s'%tmpname)
542         return True
543
544     def start(self):
545         "service plc start"
546         self.run_in_guest('service plc start')
547         return True
548
549     def stop(self):
550         "service plc stop"
551         self.run_in_guest('service plc stop')
552         return True
553         
554     def vs_start (self):
555         "start the PLC vserver"
556         self.start_guest()
557         return True
558
559     # stores the keys from the config for further use
560     def store_keys(self):
561         "stores test users ssh keys in keys/"
562         for key_spec in self.plc_spec['keys']:
563                 TestKey(self,key_spec).store_key()
564         return True
565
566     def clean_keys(self):
567         "removes keys cached in keys/"
568         utils.system("rm -rf %s/keys/"%os.path(sys.argv[0]))
569
570     # fetches the ssh keys in the plc's /etc/planetlab and stores them in keys/
571     # for later direct access to the nodes
572     def fetch_keys(self):
573         "gets ssh keys in /etc/planetlab/ and stores them locally in keys/"
574         dir="./keys"
575         if not os.path.isdir(dir):
576             os.mkdir(dir)
577         vservername=self.vservername
578         overall=True
579         prefix = 'debug_ssh_key'
580         for ext in [ 'pub', 'rsa' ] :
581             src="/vservers/%(vservername)s/etc/planetlab/%(prefix)s.%(ext)s"%locals()
582             dst="keys/%(vservername)s-debug.%(ext)s"%locals()
583             if self.test_ssh.fetch(src,dst) != 0: overall=False
584         return overall
585
586     def sites (self):
587         "create sites with PLCAPI"
588         return self.do_sites()
589     
590     def clean_sites (self):
591         "delete sites with PLCAPI"
592         return self.do_sites(action="delete")
593     
594     def do_sites (self,action="add"):
595         for site_spec in self.plc_spec['sites']:
596             test_site = TestSite (self,site_spec)
597             if (action != "add"):
598                 utils.header("Deleting site %s in %s"%(test_site.name(),self.name()))
599                 test_site.delete_site()
600                 # deleted with the site
601                 #test_site.delete_users()
602                 continue
603             else:
604                 utils.header("Creating site %s & users in %s"%(test_site.name(),self.name()))
605                 test_site.create_site()
606                 test_site.create_users()
607         return True
608
609     def clean_all_sites (self):
610         "Delete all sites in PLC, and related objects"
611         print 'auth_root',self.auth_root()
612         site_ids = [s['site_id'] for s in self.apiserver.GetSites(self.auth_root(), {}, ['site_id'])]
613         for site_id in site_ids:
614             print 'Deleting site_id',site_id
615             self.apiserver.DeleteSite(self.auth_root(),site_id)
616
617     def nodes (self):
618         "create nodes with PLCAPI"
619         return self.do_nodes()
620     def clean_nodes (self):
621         "delete nodes with PLCAPI"
622         return self.do_nodes(action="delete")
623
624     def do_nodes (self,action="add"):
625         for site_spec in self.plc_spec['sites']:
626             test_site = TestSite (self,site_spec)
627             if action != "add":
628                 utils.header("Deleting nodes in site %s"%test_site.name())
629                 for node_spec in site_spec['nodes']:
630                     test_node=TestNode(self,test_site,node_spec)
631                     utils.header("Deleting %s"%test_node.name())
632                     test_node.delete_node()
633             else:
634                 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
635                 for node_spec in site_spec['nodes']:
636                     utils.pprint('Creating node %s'%node_spec,node_spec)
637                     test_node = TestNode (self,test_site,node_spec)
638                     test_node.create_node ()
639         return True
640
641     def nodegroups (self):
642         "create nodegroups with PLCAPI"
643         return self.do_nodegroups("add")
644     def clean_nodegroups (self):
645         "delete nodegroups with PLCAPI"
646         return self.do_nodegroups("delete")
647
648     # create nodegroups if needed, and populate
649     def do_nodegroups (self, action="add"):
650         # 1st pass to scan contents
651         groups_dict = {}
652         for site_spec in self.plc_spec['sites']:
653             test_site = TestSite (self,site_spec)
654             for node_spec in site_spec['nodes']:
655                 test_node=TestNode (self,test_site,node_spec)
656                 if node_spec.has_key('nodegroups'):
657                     nodegroupnames=node_spec['nodegroups']
658                     if isinstance(nodegroupnames,StringTypes):
659                         nodegroupnames = [ nodegroupnames ]
660                     for nodegroupname in nodegroupnames:
661                         if not groups_dict.has_key(nodegroupname):
662                             groups_dict[nodegroupname]=[]
663                         groups_dict[nodegroupname].append(test_node.name())
664         auth=self.auth_root()
665         overall = True
666         for (nodegroupname,group_nodes) in groups_dict.iteritems():
667             if action == "add":
668                 print 'nodegroups:','dealing with nodegroup',nodegroupname,'on nodes',group_nodes
669                 # first, check if the nodetagtype is here
670                 tag_types = self.apiserver.GetTagTypes(auth,{'tagname':nodegroupname})
671                 if tag_types:
672                     tag_type_id = tag_types[0]['tag_type_id']
673                 else:
674                     tag_type_id = self.apiserver.AddTagType(auth,
675                                                             {'tagname':nodegroupname,
676                                                              'description': 'for nodegroup %s'%nodegroupname,
677                                                              'category':'test',
678                                                              'min_role_id':10})
679                 print 'located tag (type)',nodegroupname,'as',tag_type_id
680                 # create nodegroup
681                 nodegroups = self.apiserver.GetNodeGroups (auth, {'groupname':nodegroupname})
682                 if not nodegroups:
683                     self.apiserver.AddNodeGroup(auth, nodegroupname, tag_type_id, 'yes')
684                     print 'created nodegroup',nodegroupname,'from tagname',nodegroupname,'and value','yes'
685                 # set node tag on all nodes, value='yes'
686                 for nodename in group_nodes:
687                     try:
688                         self.apiserver.AddNodeTag(auth, nodename, nodegroupname, "yes")
689                     except:
690                         traceback.print_exc()
691                         print 'node',nodename,'seems to already have tag',nodegroupname
692                     # check anyway
693                     try:
694                         expect_yes = self.apiserver.GetNodeTags(auth,
695                                                                 {'hostname':nodename,
696                                                                  'tagname':nodegroupname},
697                                                                 ['value'])[0]['value']
698                         if expect_yes != "yes":
699                             print 'Mismatch node tag on node',nodename,'got',expect_yes
700                             overall=False
701                     except:
702                         if not self.options.dry_run:
703                             print 'Cannot find tag',nodegroupname,'on node',nodename
704                             overall = False
705             else:
706                 try:
707                     print 'cleaning nodegroup',nodegroupname
708                     self.apiserver.DeleteNodeGroup(auth,nodegroupname)
709                 except:
710                     traceback.print_exc()
711                     overall=False
712         return overall
713
714     # return a list of tuples (nodename,qemuname)
715     def all_node_infos (self) :
716         node_infos = []
717         for site_spec in self.plc_spec['sites']:
718             node_infos += [ (node_spec['node_fields']['hostname'],node_spec['host_box']) \
719                            for node_spec in site_spec['nodes'] ]
720         return node_infos
721     
722     def all_nodenames (self): return [ x[0] for x in self.all_node_infos() ]
723
724     # silent_minutes : during the first <silent_minutes> minutes nothing gets printed
725     def nodes_check_boot_state (self, target_boot_state, timeout_minutes, silent_minutes,period=15):
726         if self.options.dry_run:
727             print 'dry_run'
728             return True
729         # compute timeout
730         timeout = datetime.datetime.now()+datetime.timedelta(minutes=timeout_minutes)
731         graceout = datetime.datetime.now()+datetime.timedelta(minutes=silent_minutes)
732         # the nodes that haven't checked yet - start with a full list and shrink over time
733         tocheck = self.all_hostnames()
734         utils.header("checking nodes %r"%tocheck)
735         # create a dict hostname -> status
736         status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
737         while tocheck:
738             # get their status
739             tocheck_status=self.apiserver.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
740             # update status
741             for array in tocheck_status:
742                 hostname=array['hostname']
743                 boot_state=array['boot_state']
744                 if boot_state == target_boot_state:
745                     utils.header ("%s has reached the %s state"%(hostname,target_boot_state))
746                 else:
747                     # if it's a real node, never mind
748                     (site_spec,node_spec)=self.locate_hostname(hostname)
749                     if TestNode.is_real_model(node_spec['node_fields']['model']):
750                         utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
751                         # let's cheat
752                         boot_state = target_boot_state
753                     elif datetime.datetime.now() > graceout:
754                         utils.header ("%s still in '%s' state"%(hostname,boot_state))
755                         graceout=datetime.datetime.now()+datetime.timedelta(1)
756                 status[hostname] = boot_state
757             # refresh tocheck
758             tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != target_boot_state ]
759             if not tocheck:
760                 return True
761             if datetime.datetime.now() > timeout:
762                 for hostname in tocheck:
763                     utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
764                 return False
765             # otherwise, sleep for a while
766             time.sleep(period)
767         # only useful in empty plcs
768         return True
769
770     def nodes_booted(self):
771         return self.nodes_check_boot_state('boot',timeout_minutes=30,silent_minutes=20)
772
773     def check_nodes_ssh(self,debug,timeout_minutes,silent_minutes,period=15):
774         # compute timeout
775         timeout = datetime.datetime.now()+datetime.timedelta(minutes=timeout_minutes)
776         graceout = datetime.datetime.now()+datetime.timedelta(minutes=silent_minutes)
777         vservername=self.vservername
778         if debug: 
779             message="debug"
780             local_key = "keys/%(vservername)s-debug.rsa"%locals()
781         else: 
782             message="boot"
783             local_key = "keys/key1.rsa"
784         node_infos = self.all_node_infos()
785         utils.header("checking ssh access (expected in %s mode) to nodes:"%message)
786         for (nodename,qemuname) in node_infos:
787             utils.header("hostname=%s -- qemubox=%s"%(nodename,qemuname))
788         utils.header("max timeout is %d minutes, silent for %d minutes (period is %s)"%\
789                          (timeout_minutes,silent_minutes,period))
790         while node_infos:
791             for node_info in node_infos:
792                 (hostname,qemuname) = node_info
793                 # try to run 'hostname' in the node
794                 command = TestSsh (hostname,key=local_key).actual_command("hostname;uname -a")
795                 # don't spam logs - show the command only after the grace period 
796                 success = utils.system ( command, silent=datetime.datetime.now() < graceout)
797                 if success==0:
798                     utils.header('Successfully entered root@%s (%s)'%(hostname,message))
799                     # refresh node_infos
800                     node_infos.remove(node_info)
801                 else:
802                     # we will have tried real nodes once, in case they're up - but if not, just skip
803                     (site_spec,node_spec)=self.locate_hostname(hostname)
804                     if TestNode.is_real_model(node_spec['node_fields']['model']):
805                         utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
806                         node_infos.remove(node_info)
807             if  not node_infos:
808                 return True
809             if datetime.datetime.now() > timeout:
810                 for (hostname,qemuname) in node_infos:
811                     utils.header("FAILURE to ssh into %s (on %s)"%(hostname,qemuname))
812                 return False
813             # otherwise, sleep for a while
814             time.sleep(period)
815         # only useful in empty plcs
816         return True
817         
818     def nodes_ssh_debug(self):
819         "Tries to ssh into nodes in debug mode with the debug ssh key"
820         return self.check_nodes_ssh(debug=True,timeout_minutes=30,silent_minutes=5)
821     
822     def nodes_ssh_boot(self):
823         "Tries to ssh into nodes in production mode with the root ssh key"
824         return self.check_nodes_ssh(debug=False,timeout_minutes=30,silent_minutes=15)
825     
826     @node_mapper
827     def init_node (self): 
828         "all nodes : init a clean local directory for holding node-dep stuff like iso image..."
829         pass
830     @node_mapper
831     def bootcd (self): 
832         "all nodes: invoke GetBootMedium and store result locally"
833         pass
834     @node_mapper
835     def configure_qemu (self): 
836         "all nodes: compute qemu config qemu.conf and store it locally"
837         pass
838     @node_mapper
839     def reinstall_node (self): 
840         "all nodes: mark PLCAPI boot_state as reinstall"
841         pass
842     @node_mapper
843     def export_qemu (self): 
844         "all nodes: push local node-dep directory on the qemu box"
845         pass
846         
847     ### check hooks : invoke scripts from hooks/{node,slice}
848     def check_hooks_node (self): 
849         return self.locate_first_node().check_hooks()
850     def check_hooks_sliver (self) : 
851         return self.locate_first_sliver().check_hooks()
852     
853     def check_hooks (self):
854         "runs unit tests in the node and slice contexts - see hooks/{node,slice}"
855         return self.check_hooks_node() and self.check_hooks_sliver()
856
857     ### initscripts
858     def do_check_initscripts(self):
859         overall = True
860         for slice_spec in self.plc_spec['slices']:
861             if not slice_spec.has_key('initscriptname'):
862                 continue
863             initscript=slice_spec['initscriptname']
864             for nodename in slice_spec['nodenames']:
865                 (site,node) = self.locate_node (nodename)
866                 # xxx - passing the wrong site - probably harmless
867                 test_site = TestSite (self,site)
868                 test_slice = TestSlice (self,test_site,slice_spec)
869                 test_node = TestNode (self,test_site,node)
870                 test_sliver = TestSliver (self, test_node, test_slice)
871                 if not test_sliver.check_initscript(initscript):
872                     overall = False
873         return overall
874             
875     def check_initscripts(self):
876         "check that the initscripts have triggered"
877         return self.do_check_initscripts()
878     
879     def initscripts (self):
880         "create initscripts with PLCAPI"
881         for initscript in self.plc_spec['initscripts']:
882             utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
883             self.apiserver.AddInitScript(self.auth_root(),initscript['initscript_fields'])
884         return True
885
886     def clean_initscripts (self):
887         "delete initscripts with PLCAPI"
888         for initscript in self.plc_spec['initscripts']:
889             initscript_name = initscript['initscript_fields']['name']
890             print('Attempting to delete %s in plc %s'%(initscript_name,self.plc_spec['name']))
891             try:
892                 self.apiserver.DeleteInitScript(self.auth_root(),initscript_name)
893                 print initscript_name,'deleted'
894             except:
895                 print 'deletion went wrong - probably did not exist'
896         return True
897
898     ### manage slices
899     def slices (self):
900         "create slices with PLCAPI"
901         return self.do_slices()
902
903     def clean_slices (self):
904         "delete slices with PLCAPI"
905         return self.do_slices("delete")
906
907     def do_slices (self,  action="add"):
908         for slice in self.plc_spec['slices']:
909             site_spec = self.locate_site (slice['sitename'])
910             test_site = TestSite(self,site_spec)
911             test_slice=TestSlice(self,test_site,slice)
912             if action != "add":
913                 utils.header("Deleting slices in site %s"%test_site.name())
914                 test_slice.delete_slice()
915             else:    
916                 utils.pprint("Creating slice",slice)
917                 test_slice.create_slice()
918                 utils.header('Created Slice %s'%slice['slice_fields']['name'])
919         return True
920         
921     @slice_mapper_options
922     def check_slice(self): 
923         "tries to ssh-enter the slice with the user key, to ensure slice creation"
924         pass
925
926     @node_mapper
927     def clear_known_hosts (self): 
928         "remove test nodes entries from the local known_hosts file"
929         pass
930     
931     @node_mapper
932     def start_node (self) : 
933         "all nodes: start the qemu instance (also runs qemu-bridge-init start)"
934         pass
935
936     def check_tcp (self):
937         "check TCP connectivity between 2 slices (or in loopback if only one is defined)"
938         specs = self.plc_spec['tcp_test']
939         overall=True
940         for spec in specs:
941             port = spec['port']
942             # server side
943             s_test_sliver = self.locate_sliver_obj (spec['server_node'],spec['server_slice'])
944             if not s_test_sliver.run_tcp_server(port,timeout=10):
945                 overall=False
946                 break
947
948             # idem for the client side
949             c_test_sliver = self.locate_sliver_obj(spec['server_node'],spec['server_slice'])
950             if not c_test_sliver.run_tcp_client(s_test_sliver.test_node.name(),port):
951                 overall=False
952         return overall
953
954     def plcsh_stress_test (self):
955         "runs PLCAPI stress test, that checks Add/Update/Delete on all types - preserves contents"
956         # install the stress-test in the plc image
957         location = "/usr/share/plc_api/plcsh_stress_test.py"
958         remote="/vservers/%s/%s"%(self.vservername,location)
959         self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
960         command = location
961         command += " -- --check"
962         if self.options.size == 1:
963             command +=  " --tiny"
964         return ( self.run_in_guest(command) == 0)
965
966     # populate runs the same utility without slightly different options
967     # in particular runs with --preserve (dont cleanup) and without --check
968     # also it gets run twice, once with the --foreign option for creating fake foreign entries
969
970     ### install_sfa_rpm
971     def install_sfa(self):
972         "yum install sfa, sfa-plc and sfa-client"
973         if self.options.personality == "linux32":
974             arch = "i386"
975         elif self.options.personality == "linux64":
976             arch = "x86_64"
977         else:
978             raise Exception, "Unsupported personality %r"%self.options.personality
979         return self.run_in_guest("yum -y install sfa sfa-client sfa-plc sfa-sfatables")==0
980
981     ###
982     def configure_sfa(self):
983         "run sfa-config-tty"
984         tmpname='%s.sfa-config-tty'%(self.name())
985         fileconf=open(tmpname,'w')
986         for var in [ 'SFA_REGISTRY_ROOT_AUTH',
987                      'SFA_REGISTRY_LEVEL1_AUTH',
988                      'SFA_REGISTRY_HOST',
989                      'SFA_AGGREGATE_HOST',
990                      'SFA_SM_HOST',
991                      'SFA_PLC_USER',
992                      'SFA_PLC_PASSWORD',
993                      'SFA_PLC_DB_HOST',
994                      'SFA_PLC_DB_USER',
995                      'SFA_PLC_DB_PASSWORD',
996                      'SFA_PLC_URL']:
997             fileconf.write ('e %s\n%s\n'%(var,self.plc_spec['sfa'][var]))
998         fileconf.write('w\n')
999         fileconf.write('R\n')
1000         fileconf.write('q\n')
1001         fileconf.close()
1002         utils.system('cat %s'%tmpname)
1003         self.run_in_guest_piped('cat %s'%tmpname,'sfa-config-tty')
1004         utils.system('rm %s'%tmpname)
1005         return True
1006
1007     def import_sfa(self):
1008         "sfa-import-plc"
1009         auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1010         return self.run_in_guest('sfa-import-plc.py')==0
1011 # not needed anymore
1012 #        self.run_in_guest('cp /etc/sfa/authorities/%s/%s.pkey /etc/sfa/authorities/server.key'%(auth,auth))
1013
1014     def start_sfa(self):
1015         "service sfa start"
1016         return self.run_in_guest('service sfa start')==0
1017
1018     def setup_sfa(self):
1019         "sfi client configuration"
1020         dir_name=".sfi"
1021         if os.path.exists(dir_name):
1022            utils.system('rm -rf %s'%dir_name)
1023         utils.system('mkdir %s'%dir_name)
1024         file_name=dir_name + os.sep + 'fake-pi1.pkey'
1025         fileconf=open(file_name,'w')
1026         fileconf.write (self.plc_spec['keys'][0]['private'])
1027         fileconf.close()
1028
1029         file_name=dir_name + os.sep + 'sfi_config'
1030         fileconf=open(file_name,'w')
1031         SFI_AUTH=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']+".main"
1032         fileconf.write ("SFI_AUTH='%s'"%SFI_AUTH)
1033         fileconf.write('\n')
1034         SFI_USER=SFI_AUTH+'.fake-pi1'
1035         fileconf.write ("SFI_USER='%s'"%SFI_USER)
1036         fileconf.write('\n')
1037         SFI_REGISTRY='http://' + self.plc_spec['sfa']['SFA_PLC_DB_HOST'] + ':12345/'
1038         fileconf.write ("SFI_REGISTRY='%s'"%SFI_REGISTRY)
1039         fileconf.write('\n')
1040         SFI_SM='http://' + self.plc_spec['sfa']['SFA_PLC_DB_HOST'] + ':12347/'
1041         fileconf.write ("SFI_SM='%s'"%SFI_SM)
1042         fileconf.write('\n')
1043         fileconf.close()
1044
1045         file_name=dir_name + os.sep + 'person.xml'
1046         fileconf=open(file_name,'w')
1047         for record in self.plc_spec['sfa']['sfa_person_xml']:
1048            person_record=record
1049         fileconf.write(person_record)
1050         fileconf.write('\n')
1051         fileconf.close()
1052
1053         file_name=dir_name + os.sep + 'slice.xml'
1054         fileconf=open(file_name,'w')
1055         for record in self.plc_spec['sfa']['sfa_slice_xml']:
1056             slice_record=record
1057         #slice_record=self.plc_spec['sfa']['sfa_slice_xml']
1058         fileconf.write(slice_record)
1059         fileconf.write('\n')
1060         fileconf.close()
1061
1062         file_name=dir_name + os.sep + 'slice.rspec'
1063         fileconf=open(file_name,'w')
1064         slice_rspec=''
1065         for (key, value) in self.plc_spec['sfa']['sfa_slice_rspec'].items():
1066             slice_rspec +=value 
1067         fileconf.write(slice_rspec)
1068         fileconf.write('\n')
1069         fileconf.close()
1070         location = "root/"
1071         remote="/vservers/%s/%s"%(self.vservername,location)
1072         self.test_ssh.copy_abs(dir_name, remote, recursive=True)
1073
1074         #utils.system('cat %s'%tmpname)
1075         utils.system('rm -rf %s'%dir_name)
1076         return True
1077
1078     def add_sfa(self):
1079         "run sfi.py add (on Registry) and sfi.py create (on SM) to form new objects"
1080         test_plc=self
1081         test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1082         success=test_user_sfa.add_user()
1083
1084         for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1085             site_spec = self.locate_site (slice_spec['sitename'])
1086             test_site = TestSite(self,site_spec)
1087             test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1088             success1=test_slice_sfa.add_slice()
1089             success2=test_slice_sfa.create_slice()
1090         return success and success1 and success2
1091
1092     def update_sfa(self):
1093         "run sfi.py update (on Registry) and sfi.py create (on SM) on existing objects"
1094         test_plc=self
1095         test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1096         success1=test_user_sfa.update_user()
1097         
1098         for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1099             site_spec = self.locate_site (slice_spec['sitename'])
1100             test_site = TestSite(self,site_spec)
1101             test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1102             success2=test_slice_sfa.update_slice()
1103         return success1 and success2
1104
1105     def view_sfa(self):
1106         "run sfi.py list and sfi.py show (both on Registry) and sfi.py slices and sfi.py resources (both on SM)"
1107         auth=self.plc_spec['sfa']['SFA_REGISTRY_ROOT_AUTH']
1108         return \
1109         self.run_in_guest("sfi.py -d /root/.sfi/ list %s.main"%auth)==0 and \
1110         self.run_in_guest("sfi.py -d /root/.sfi/ show %s.main"%auth)==0 and \
1111         self.run_in_guest("sfi.py -d /root/.sfi/ slices")==0 and \
1112         self.run_in_guest("sfi.py -d /root/.sfi/ resources -o resources")==0
1113
1114     @slice_mapper_options_sfa
1115     def check_slice_sfa(self): 
1116         "tries to ssh-enter the SFA slice"
1117         pass
1118
1119     def delete_sfa(self):
1120         "run sfi.py delete (on SM), sfi.py remove (on Registry)"
1121         test_plc=self
1122         test_user_sfa=TestUserSfa(test_plc,self.plc_spec['sfa'])
1123         success1=test_user_sfa.delete_user()
1124         for slice_spec in self.plc_spec['sfa']['slices_sfa']:
1125             site_spec = self.locate_site (slice_spec['sitename'])
1126             test_site = TestSite(self,site_spec)
1127             test_slice_sfa=TestSliceSfa(test_plc,test_site,slice_spec)
1128             success2=test_slice_sfa.delete_slice()
1129
1130         return success1 and success2
1131
1132     def stop_sfa(self):
1133         "service sfa stop"
1134         return self.run_in_guest('service sfa stop')==0
1135
1136     def populate (self):
1137         "creates random entries in the PLCAPI"
1138         # install the stress-test in the plc image
1139         location = "/usr/share/plc_api/plcsh_stress_test.py"
1140         remote="/vservers/%s/%s"%(self.vservername,location)
1141         self.test_ssh.copy_abs("plcsh_stress_test.py",remote)
1142         command = location
1143         command += " -- --preserve --short-names"
1144         local = (self.run_in_guest(command) == 0);
1145         # second run with --foreign
1146         command += ' --foreign'
1147         remote = (self.run_in_guest(command) == 0);
1148         return ( local and remote)
1149
1150     def gather_logs (self):
1151         "gets all possible logs from plc's/qemu node's/slice's for future reference"
1152         # (1.a) get the plc's /var/log/ and store it locally in logs/myplc.var-log.<plcname>/*
1153         # (1.b) get the plc's  /var/lib/pgsql/data/pg_log/ -> logs/myplc.pgsql-log.<plcname>/*
1154         # (2) get all the nodes qemu log and store it as logs/node.qemu.<node>.log
1155         # (3) get the nodes /var/log and store is as logs/node.var-log.<node>/*
1156         # (4) as far as possible get the slice's /var/log as logs/sliver.var-log.<sliver>/*
1157         # (1.a)
1158         print "-------------------- TestPlc.gather_logs : PLC's /var/log"
1159         self.gather_var_logs ()
1160         # (1.b)
1161         print "-------------------- TestPlc.gather_logs : PLC's /var/lib/psql/data/pg_log/"
1162         self.gather_pgsql_logs ()
1163         # (2) 
1164         print "-------------------- TestPlc.gather_logs : nodes's QEMU logs"
1165         for site_spec in self.plc_spec['sites']:
1166             test_site = TestSite (self,site_spec)
1167             for node_spec in site_spec['nodes']:
1168                 test_node=TestNode(self,test_site,node_spec)
1169                 test_node.gather_qemu_logs()
1170         # (3)
1171         print "-------------------- TestPlc.gather_logs : nodes's /var/log"
1172         self.gather_nodes_var_logs()
1173         # (4)
1174         print "-------------------- TestPlc.gather_logs : sample sliver's /var/log"
1175         self.gather_slivers_var_logs()
1176         return True
1177
1178     def gather_slivers_var_logs(self):
1179         for test_sliver in self.all_sliver_objs():
1180             remote = test_sliver.tar_var_logs()
1181             utils.system("mkdir -p logs/sliver.var-log.%s"%test_sliver.name())
1182             command = remote + " | tar -C logs/sliver.var-log.%s -xf -"%test_sliver.name()
1183             utils.system(command)
1184         return True
1185
1186     def gather_var_logs (self):
1187         utils.system("mkdir -p logs/myplc.var-log.%s"%self.name())
1188         to_plc = self.actual_command_in_guest("tar -C /var/log/ -cf - .")        
1189         command = to_plc + "| tar -C logs/myplc.var-log.%s -xf -"%self.name()
1190         utils.system(command)
1191         command = "chmod a+r,a+x logs/myplc.var-log.%s/httpd"%self.name()
1192         utils.system(command)
1193
1194     def gather_pgsql_logs (self):
1195         utils.system("mkdir -p logs/myplc.pgsql-log.%s"%self.name())
1196         to_plc = self.actual_command_in_guest("tar -C /var/lib/pgsql/data/pg_log/ -cf - .")        
1197         command = to_plc + "| tar -C logs/myplc.pgsql-log.%s -xf -"%self.name()
1198         utils.system(command)
1199
1200     def gather_nodes_var_logs (self):
1201         for site_spec in self.plc_spec['sites']:
1202             test_site = TestSite (self,site_spec)
1203             for node_spec in site_spec['nodes']:
1204                 test_node=TestNode(self,test_site,node_spec)
1205                 test_ssh = TestSsh (test_node.name(),key="keys/key1.rsa")
1206                 command = test_ssh.actual_command("tar -C /var/log -cf - .")
1207                 command = command + "| tar -C logs/node.var-log.%s -xf -"%test_node.name()
1208                 utils.system("mkdir -p logs/node.var-log.%s"%test_node.name())
1209                 utils.system(command)
1210
1211
1212     # returns the filename to use for sql dump/restore, using options.dbname if set
1213     def dbfile (self, database):
1214         # uses options.dbname if it is found
1215         try:
1216             name=self.options.dbname
1217             if not isinstance(name,StringTypes):
1218                 raise Exception
1219         except:
1220             t=datetime.datetime.now()
1221             d=t.date()
1222             name=str(d)
1223         return "/root/%s-%s.sql"%(database,name)
1224
1225     def db_dump(self):
1226         'dump the planetlab5 DB in /root in the PLC - filename has time'
1227         dump=self.dbfile("planetab5")
1228         self.run_in_guest('pg_dump -U pgsqluser planetlab5 -f '+ dump)
1229         utils.header('Dumped planetlab5 database in %s'%dump)
1230         return True
1231
1232     def db_restore(self):
1233         'restore the planetlab5 DB - looks broken, but run -n might help'
1234         dump=self.dbfile("planetab5")
1235         ##stop httpd service
1236         self.run_in_guest('service httpd stop')
1237         # xxx - need another wrapper
1238         self.run_in_guest_piped('echo drop database planetlab5','psql --user=pgsqluser template1')
1239         self.run_in_guest('createdb -U postgres --encoding=UNICODE --owner=pgsqluser planetlab5')
1240         self.run_in_guest('psql -U pgsqluser planetlab5 -f '+dump)
1241         ##starting httpd service
1242         self.run_in_guest('service httpd start')
1243
1244         utils.header('Database restored from ' + dump)
1245
1246     @standby_generic 
1247     def standby_1(): pass
1248     @standby_generic 
1249     def standby_2(): pass
1250     @standby_generic 
1251     def standby_3(): pass
1252     @standby_generic 
1253     def standby_4(): pass
1254     @standby_generic 
1255     def standby_5(): pass
1256     @standby_generic 
1257     def standby_6(): pass
1258     @standby_generic 
1259     def standby_7(): pass
1260     @standby_generic 
1261     def standby_8(): pass
1262     @standby_generic 
1263     def standby_9(): pass
1264     @standby_generic 
1265     def standby_10(): pass
1266     @standby_generic 
1267     def standby_11(): pass
1268     @standby_generic 
1269     def standby_12(): pass
1270     @standby_generic 
1271     def standby_13(): pass
1272     @standby_generic 
1273     def standby_14(): pass
1274     @standby_generic 
1275     def standby_15(): pass
1276     @standby_generic 
1277     def standby_16(): pass
1278     @standby_generic 
1279     def standby_17(): pass
1280     @standby_generic 
1281     def standby_18(): pass
1282     @standby_generic 
1283     def standby_19(): pass
1284     @standby_generic 
1285     def standby_20(): pass