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