various fixes
[tests.git] / system / TestPlc.py
index 9657451..7671459 100644 (file)
@@ -1,5 +1,7 @@
 # $Id$
 import os, os.path
+import datetime
+import time
 import sys
 import xmlrpclib
 import datetime
@@ -11,22 +13,38 @@ from TestSite import TestSite
 from TestNode import TestNode
 from TestUser import TestUser
 from TestKey import TestKey
+from TestSlice import TestSlice
+from TestSliver import TestSliver
+from TestBox import TestBox
 
 # step methods must take (self, options) and return a boolean
 
+def standby(minutes):
+        utils.header('Entering StandBy for %d mn'%minutes)
+        time.sleep(60*minutes)
+        return True
+
+def standby_generic (func):
+    def actual(self,options):
+        minutes=int(func.__name__.split("_")[1])
+        return standby(minutes)
+    return actual
+
 class TestPlc:
 
     def __init__ (self,plc_spec):
        self.plc_spec=plc_spec
-       self.url="https://%s:443/PLCAPI/"%plc_spec['hostname']
-       self.server=xmlrpclib.Server(self.url,allow_none=True)
        self.path=os.path.dirname(sys.argv[0])
         try:
             self.vserverip=plc_spec['vserverip']
             self.vservername=plc_spec['vservername']
+            self.url="https://%s:443/PLCAPI/"%plc_spec['vserverip']
             self.vserver=True
         except:
             self.vserver=False
+            self.url="https://%s:443/PLCAPI/"%plc_spec['hostname']
+        utils.header('Using API url %s'%self.url)
+       self.server=xmlrpclib.Server(self.url,allow_none=True)
         
     def name(self):
         name=self.plc_spec['name']
@@ -35,35 +53,56 @@ class TestPlc:
         else:
             return name+"[chroot]"
 
+    def is_local (self):
+        return self.plc_spec['hostname'] == 'localhost'
+
     # define the API methods on this object through xmlrpc
     # would help, but not strictly necessary
     def connect (self):
        pass
     
-    # build the full command so command gets run in the chroot/vserver
-    def run_command(self,command):
+    # command gets run in the chroot/vserver
+    def host_to_guest(self,command):
         if self.vserver:
             return "vserver %s exec %s"%(self.vservername,command)
         else:
-            return "chroot /plc/root %s"%command
+            return "chroot /plc/root %s"%utils.backslash_shell_specials(command)
 
-    def ssh_command(self,command):
-        if self.plc_spec['hostname'] == "localhost":
+    # command gets run on the right box
+    def to_host(self,command):
+        if self.is_local():
             return command
         else:
-            return "ssh " + self.plc_spec['hostname'] + " " + command
+            return "ssh %s %s"%(self.plc_spec['hostname'],utils.backslash_shell_specials(command))
 
     def full_command(self,command):
-        return self.ssh_command(self.run_command(command))
+        return self.to_host(self.host_to_guest(command))
 
     def run_in_guest (self,command):
         return utils.system(self.full_command(command))
     def run_in_host (self,command):
-        return utils.system(self.ssh_command(command))
+        return utils.system(self.to_host(command))
 
     # xxx quick n dirty
     def run_in_guest_piped (self,local,remote):
-        return utils.system(local+" | "+self.full_command(command))
+        return utils.system(local+" | "+self.full_command(remote))
+
+    # copy a file to the myplc root image - pass in_data=True if the file must go in /plc/data
+    def copy_in_guest (self, localfile, remotefile, in_data=False):
+        if in_data:
+            chroot_dest="/plc/data"
+        else:
+            chroot_dest="/plc/root"
+        if self.is_local():
+            if not self.vserver:
+                utils.system("cp %s %s/%s"%(localfile,chroot_dest,remotefile))
+            else:
+                utils.system("cp %s /vservers/%s/%s"%(localfile,self.vservername,remotefile))
+        else:
+            if not self.vserver:
+                utils.system("scp %s %s:%s/%s"%(localfile,self.plc_spec['hostname'],chroot_dest,remotefile))
+            else:
+                utils.system("scp %s %s@/vservers/%s/%s"%(localfile,self.plc_spec['hostname'],self.vservername,remotefile))
 
     def auth_root (self):
        return {'Username':self.plc_spec['PLC_ROOT_USER'],
@@ -79,19 +118,81 @@ class TestPlc:
                 return site
         raise Exception,"Cannot locate site %s"%sitename
         
+    def locate_node (self,nodename):
+        for site in self.plc_spec['sites']:
+            for node in site['nodes']:
+                if node['node_fields']['hostname'] == nodename:
+                    return (site,node)
+        raise Exception,"Cannot locate node %s"%nodename
+        
     def locate_key (self,keyname):
         for key in self.plc_spec['keys']:
             if key['name'] == keyname:
                 return key
         raise Exception,"Cannot locate key %s"%keyname
-        
-    def kill_all_vmwares(self):
-        utils.header('Killing any running vmware or vmplayer instance')
-        utils.system('pgrep vmware | xargs -r kill')
-        utils.system('pgrep vmplayer | xargs -r kill ')
-        utils.system('pgrep vmware | xargs -r kill -9')
-        utils.system('pgrep vmplayer | xargs -r kill -9')
-        
+
+    # all different hostboxes used in this plc
+    def gather_hostBoxes(self):
+        # maps on sites and nodes, return [ (host_box,test_node) ]
+        tuples=[]
+        for site_spec in self.plc_spec['sites']:
+            test_site = TestSite (self,site_spec)
+            for node_spec in site_spec['nodes']:
+                test_node = TestNode (self, test_site, node_spec)
+                if not test_node.is_real():
+                    tuples.append( (test_node.host_box(),test_node) )
+        # transform into a dict { 'host_box' -> [ hostnames .. ] }
+        result = {}
+        for (box,node) in tuples:
+            if not result.has_key(box):
+                result[box]=[node]
+            else:
+                result[box].append(node)
+        return result
+                    
+    # a step for checking this stuff
+    def showboxes (self,options):
+        print 'showboxes'
+        for (box,nodes) in self.gather_hostBoxes().iteritems():
+            print box,":"," + ".join( [ node.name() for node in nodes ] )
+        return True
+
+    # make this a valid step
+    def kill_all_qemus(self,options):
+        for (box,nodes) in self.gather_hostBoxes().iteritems():
+            # this is the brute force version, kill all qemus on that host box
+            TestBox(box).kill_all_qemus()
+        return True
+
+    # make this a valid step
+    def list_all_qemus(self,options):
+        for (box,nodes) in self.gather_hostBoxes().iteritems():
+           # push the script
+           TestBox(box).copy("qemu_kill.sh")   
+            # this is the brute force version, kill all qemus on that host box
+            TestBox(box).run("./qemu_kill.sh -l")
+        return True
+
+    # kill only the right qemus
+    def kill_qemus(self,options):
+        for (box,nodes) in self.gather_hostBoxes().iteritems():
+           # push the script
+           TestBox(box).copy("qemu_kill.sh")   
+            # the fine-grain version
+            for node in nodes:
+                node.kill_qemu()
+        return True
+
+    def clear_ssh_config (self,options):
+        # install local ssh_config file as root's .ssh/config - ssh should be quiet
+        # dir might need creation first
+        self.run_in_guest("mkdir /root/.ssh")
+        self.run_in_guest("chmod 700 /root/.ssh")
+        # this does not work - > redirection somehow makes it until an argument to cat
+        #self.run_in_guest_piped("cat ssh_config","cat > /root/.ssh/config")
+        self.copy_in_guest("ssh_config","/root/.ssh/config",True)
+        return True
+            
     #################### step methods
 
     ### uninstall
@@ -101,6 +202,8 @@ class TestPlc:
         self.run_in_host('rpm -e myplc')
         ##### Clean up the /plc directory
         self.run_in_host('rm -rf  /plc/data')
+        ##### stop any running vservers
+        self.run_in_host('for vserver in $(ls /vservers/* | sed -e s,/vservers/,,) ; do vserver $vserver stop ; done')
         return True
 
     def uninstall_vserver(self,options):
@@ -108,10 +211,15 @@ class TestPlc:
         return True
 
     def uninstall(self,options):
+        # if there's a chroot-based myplc running, and then a native-based myplc is being deployed
+        # it sounds safer to have the former uninstalled too
+        # now the vserver method cannot be invoked for chroot instances as vservername is required
         if self.vserver:
-            return self.uninstall_vserver(options)
+            self.uninstall_vserver(options)
+            self.uninstall_chroot(options)
         else:
-            return self.uninstall_chroot(options)
+            self.uninstall_chroot(options)
+        return True
 
     ### install
     def install_chroot(self,options):
@@ -121,18 +229,23 @@ class TestPlc:
     # xxx this would not work with hostname != localhost as mylc-init-vserver was extracted locally
     def install_vserver(self,options):
         # we need build dir for vtest-init-vserver
-        build_dir=self.path+"/build"
-        if not os.path.isdir(build_dir):
-            if utils.system("svn checkout %s %s"%(options.build_url,build_dir)) != 0:
-                raise Exception,"Cannot checkout build dir"
+        if self.is_local():
+            # a full path for the local calls
+            build_dir=self.path+"/build"
+        else:
+            # use a standard name - will be relative to HOME 
+            build_dir="tests-system-build"
+        build_checkout = "svn checkout %s %s"%(options.build_url,build_dir)
+        if self.run_in_host(build_checkout) != 0:
+            raise Exception,"Cannot checkout build dir"
         # the repo url is taken from myplc-url 
         # with the last two steps (i386/myplc...) removed
         repo_url = options.myplc_url
         repo_url = os.path.dirname(repo_url)
         repo_url = os.path.dirname(repo_url)
-        command="%s/vtest-init-vserver.sh %s %s -- --interface eth0:%s"%\
+        create_vserver="%s/vtest-init-vserver.sh %s %s -- --interface eth0:%s"%\
             (build_dir,self.vservername,repo_url,self.vserverip)
-        if utils.system(command) != 0:
+        if self.run_in_host(create_vserver) != 0:
             raise Exception,"Could not create vserver for %s"%self.vservername
         return True
 
@@ -146,8 +259,8 @@ class TestPlc:
     def install_rpm_chroot(self,options):
         utils.header('Installing from %s'%options.myplc_url)
         url=options.myplc_url
-        utils.system('rpm -Uvh '+url)
-        utils.system('service plc mount')
+        self.run_in_host('rpm -Uvh '+url)
+        self.run_in_host('service plc mount')
         return True
 
     def install_rpm_vserver(self,options):
@@ -156,7 +269,7 @@ class TestPlc:
 
     def install_rpm(self,options):
         if self.vserver:
-            return self.install_rpm_vserver_create(options)
+            return self.install_rpm_vserver(options)
         else:
             return self.install_rpm_chroot(options)
 
@@ -180,7 +293,7 @@ class TestPlc:
         fileconf.write('q\n')
         fileconf.close()
         utils.system('cat %s'%tmpname)
-        self.run_in_guest('plc-config-tty < %s'%tmpname)
+        self.run_in_guest_piped('cat %s'%tmpname,'plc-config-tty')
         utils.system('rm %s'%tmpname)
         return True
 
@@ -246,7 +359,7 @@ class TestPlc:
             else:
                 utils.header("Creating nodes for site %s in %s"%(test_site.name(),self.name()))
                 for node_spec in site_spec['nodes']:
-                    utils.show_spec('Creating node %s'%node_spec,node_spec)
+                    utils.pprint('Creating node %s'%node_spec,node_spec)
                     test_node = TestNode (self,test_site,node_spec)
                     test_node.create_node ()
         return True
@@ -278,6 +391,108 @@ class TestPlc:
                 self.server.AddNodeToNodeGroup(auth,node,nodegroupname)
         return True
 
+    def all_hostnames (self) :
+        hostnames = []
+        for site_spec in self.plc_spec['sites']:
+            hostnames += [ node_spec['node_fields']['hostname'] \
+                           for node_spec in site_spec['nodes'] ]
+        return hostnames
+
+    # gracetime : during the first <gracetime> minutes nothing gets printed
+    def do_nodes_booted (self, minutes, gracetime=2):
+        # compute timeout
+        timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
+        graceout = datetime.datetime.now()+datetime.timedelta(minutes=gracetime)
+        # the nodes that haven't checked yet - start with a full list and shrink over time
+        tocheck = self.all_hostnames()
+        utils.header("checking nodes %r"%tocheck)
+        # create a dict hostname -> status
+        status = dict ( [ (hostname,'undef') for hostname in tocheck ] )
+        while tocheck:
+            # get their status
+            tocheck_status=self.server.GetNodes(self.auth_root(), tocheck, ['hostname','boot_state' ] )
+            # update status
+            for array in tocheck_status:
+                hostname=array['hostname']
+                boot_state=array['boot_state']
+                if boot_state == 'boot':
+                    utils.header ("%s has reached the 'boot' state"%hostname)
+                else:
+                    # if it's a real node, never mind
+                    (site_spec,node_spec)=self.locate_node(hostname)
+                    if TestNode.is_real_model(node_spec['node_fields']['model']):
+                        utils.header("WARNING - Real node %s in %s - ignored"%(hostname,boot_state))
+                        # let's cheat
+                        boot_state = 'boot'
+                    if datetime.datetime.now() > graceout:
+                        utils.header ("%s still in '%s' state"%(hostname,boot_state))
+                        graceout=datetime.datetime.now()+datetime.timedelta(1)
+                status[hostname] = boot_state
+            # refresh tocheck
+            tocheck = [ hostname for (hostname,boot_state) in status.iteritems() if boot_state != 'boot' ]
+            if not tocheck:
+                return True
+            if datetime.datetime.now() > timeout:
+                for hostname in tocheck:
+                    utils.header("FAILURE due to %s in '%s' state"%(hostname,status[hostname]))
+                return False
+            # otherwise, sleep for a while
+            time.sleep(15)
+        # only useful in empty plcs
+        return True
+
+    def nodes_booted(self,options):
+        return self.do_nodes_booted(minutes=5)
+    
+    #to scan and store the nodes's public keys and avoid to ask for confirmation when  ssh 
+    def scan_publicKeys(self,hostnames):
+        try:
+            temp_knownhosts="/root/known_hosts"
+            remote_knownhosts="/root/.ssh/known_hosts"
+            self.run_in_host("touch %s"%temp_knownhosts )
+            for hostname in hostnames:
+                utils.header("Scan Public %s key and store it in the known_host file(under the root image) "%hostname)
+                scan=self.run_in_host('ssh-keyscan -t rsa %s >> %s '%(hostname,temp_knownhosts))
+            #Store the public keys in the right root image
+            self.copy_in_guest(temp_knownhosts,remote_knownhosts,True)
+            #clean the temp keys file used
+            self.run_in_host('rm -f  %s '%temp_knownhosts )
+        except Exception, err:
+            print err
+            
+    def do_check_nodesSsh(self,minutes):
+        # compute timeout
+        timeout = datetime.datetime.now()+datetime.timedelta(minutes=minutes)
+        tocheck = self.all_hostnames()
+        self.scan_publicKeys(tocheck)
+        utils.header("checking Connectivity on nodes %r"%tocheck)
+        while tocheck:
+            for hostname in tocheck:
+                # try to ssh in nodes
+                access=self.run_in_guest('ssh -i /etc/planetlab/root_ssh_key.rsa root@%s date'%hostname )
+                if (not access):
+                    utils.header('The node %s is sshable -->'%hostname)
+                    # refresh tocheck
+                    tocheck.remove(hostname)
+                else:
+                    (site_spec,node_spec)=self.locate_node(hostname)
+                    if TestNode.is_real_model(node_spec['node_fields']['model']):
+                        utils.header ("WARNING : check ssh access into real node %s - skipped"%hostname)
+                       tocheck.remove(hostname)
+            if  not tocheck:
+                return True
+            if datetime.datetime.now() > timeout:
+                for hostname in tocheck:
+                    utils.header("FAILURE to ssh into %s"%hostname)
+                return False
+            # otherwise, sleep for a while
+            time.sleep(15)
+        # only useful in empty plcs
+        return True
+        
+    def nodes_ssh(self, options):
+        return  self.do_check_nodesSsh(minutes=2)
+    
     def bootcd (self, options):
         for site_spec in self.plc_spec['sites']:
             test_site = TestSite (self,site_spec)
@@ -285,10 +500,25 @@ class TestPlc:
                 test_node=TestNode (self,test_site,node_spec)
                 test_node.create_boot_cd(options.path)
         return True
-            
+
+    def do_check_intiscripts(self):
+       for site_spec in self.plc_spec['sites']:
+               test_site = TestSite (self,site_spec)
+               test_node = TestNode (self,test_site,site_spec['nodes'])
+               for slice_spec in self.plc_spec['slices']:
+                       test_slice=TestSlice (self,test_site,slice_spec)
+                       test_sliver=TestSliver(self,test_node,test_slice)
+                       init_status=test_sliver.get_initscript(slice_spec)
+                       if (not init_status):
+                               return False
+               return init_status
+           
+    def check_initscripts(self, options):
+           return self.do_check_intiscripts()
+                   
     def initscripts (self, options):
         for initscript in self.plc_spec['initscripts']:
-            utils.show_spec('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
+            utils.pprint('Adding Initscript in plc %s'%self.plc_spec['name'],initscript)
             self.server.AddInitScript(self.auth_root(),initscript['initscript_fields'])
         return True
 
@@ -298,52 +528,49 @@ class TestPlc:
     def clean_slices (self, options):
         return self.do_slices("delete")
 
-    ### would need a TestSlice class
-    def do_slices (self, add_or_delete="add"):
+    def do_slices (self,  action="add"):
         for slice in self.plc_spec['slices']:
             site_spec = self.locate_site (slice['sitename'])
             test_site = TestSite(self,site_spec)
-            owner_spec = test_site.locate_user(slice['owner'])
-            auth = TestUser(self,test_site,owner_spec).auth()
-            slice_fields = slice['slice_fields']
-            slice_name = slice_fields['name']
-            if (add_or_delete == "delete"):
-                self.server.DeleteSlice(auth,slice_fields['name'])
-                utils.header("Deleted slice %s"%slice_fields['name'])
-                continue
-            utils.show_spec("Creating slice",slice_fields)
-            self.server.AddSlice(auth,slice_fields)
-            utils.header('Created Slice %s'%slice_fields['name'])
-            for username in slice['usernames']:
-                user_spec=test_site.locate_user(username)
-                test_user=TestUser(self,test_site,user_spec)
-                self.server.AddPersonToSlice(auth, test_user.name(), slice_name)
-
-            hostnames=[]
-            for nodename in slice['nodenames']:
-                node_spec=test_site.locate_node(nodename)
-                test_node=TestNode(self,test_site,node_spec)
-                hostnames += [test_node.name()]
-            utils.header("Adding %r in %s"%(hostnames,slice_name))
-            self.server.AddSliceToNodes(auth, slice_name, hostnames)
-            if slice.has_key('initscriptname'):
-                isname=slice['initscriptname']
-                utils.header("Adding initscript %s in %s"%(isname,slice_name))
-                self.server.AddSliceAttribute(self.auth_root(), slice_name,
-                                              'initscript',isname)
+            test_slice=TestSlice(self,test_site,slice)
+            if action != "add":
+                utils.header("Deleting slices in site %s"%test_site.name())
+                test_slice.delete_slice()
+            else:    
+                utils.pprint("Creating slice",slice)
+                test_slice.create_slice()
+                utils.header('Created Slice %s'%slice['slice_fields']['name'])
         return True
         
+    def check_slices(self, options):
+        for slice_spec in self.plc_spec['slices']:
+            site_spec = self.locate_site (slice_spec['sitename'])
+            test_site = TestSite(self,site_spec)
+            test_slice=TestSlice(self,test_site,slice_spec)
+            status=test_slice.do_check_slice(options)
+            if (not status):
+                return False
+        return status
+    
     def start_nodes (self, options):
-        self.kill_all_vmwares()
-        utils.header("Starting vmware nodes")
+        utils.header("Starting  nodes")
         for site_spec in self.plc_spec['sites']:
             TestSite(self,site_spec).start_nodes (options)
         return True
 
     def stop_nodes (self, options):
-        self.kill_all_vmwares ()
+        self.kill_all_qemus()
         return True
 
+    def check_tcp (self, options):
+           #we just need to create a sliver object nothing else
+           test_sliver=TestSliver(self,
+                                  TestNode(self, TestSite(self,self.plc_spec['sites'][0]),
+                                           self.plc_spec['sites'][0]['nodes'][0]),
+                                  TestSlice(self,TestSite(self,self.plc_spec['sites'][0]),
+                                            self.plc_spec['slices']))
+           return test_sliver.do_check_tcp(self.plc_spec['tcp_param'],options)
+
     # returns the filename to use for sql dump/restore, using options.dbname if set
     def dbfile (self, database, options):
         # uses options.dbname if it is found
@@ -376,3 +603,45 @@ class TestPlc:
         self.run_in_guest('service httpd start')
 
         utils.header('Database restored from ' + dump)
+
+    @standby_generic 
+    def standby_1(): pass
+    @standby_generic 
+    def standby_2(): pass
+    @standby_generic 
+    def standby_3(): pass
+    @standby_generic 
+    def standby_4(): pass
+    @standby_generic 
+    def standby_5(): pass
+    @standby_generic 
+    def standby_6(): pass
+    @standby_generic 
+    def standby_7(): pass
+    @standby_generic 
+    def standby_8(): pass
+    @standby_generic 
+    def standby_9(): pass
+    @standby_generic 
+    def standby_10(): pass
+    @standby_generic 
+    def standby_11(): pass
+    @standby_generic 
+    def standby_12(): pass
+    @standby_generic 
+    def standby_13(): pass
+    @standby_generic 
+    def standby_14(): pass
+    @standby_generic 
+    def standby_15(): pass
+    @standby_generic 
+    def standby_16(): pass
+    @standby_generic 
+    def standby_17(): pass
+    @standby_generic 
+    def standby_18(): pass
+    @standby_generic 
+    def standby_19(): pass
+    @standby_generic 
+    def standby_20(): pass
+