Minor changes before merging master.
[sfa.git] / sfa / senslab / slabslices.py
index 3f42ef2..c63acf4 100644 (file)
@@ -12,86 +12,7 @@ class SlabSlices:
     def __init__(self, driver):
         self.driver = driver
         
-    ##Used in SFACE?    
-    #def get_slivers(self, xrn, node=None):
-        #hrn, hrn_type = urn_to_hrn(xrn)
-         
-        #slice_name = hrn_to_pl_slicename(hrn)
-        ## XX Should we just call PLCAPI.GetSliceTicket(slice_name) instead
-        ## of doing all of this?
-        ##return self.api.driver.GetSliceTicket(self.auth, slice_name) 
-        
-
-       
-        #sfa_slice = self.driver.GetSlices(slice_filter = slice_name, \
-        #                                   slice_filter_type = 'slice_hrn')
-
-        ## Get user information
-        ##TODO
-        #alchemy_person = dbsession.query(RegRecord).filter_by(record_id = \
-                                        #sfa_slice['record_id_user']).first()
-
-        #slivers = []
-        #sliver_attributes = []
-            
-        #if sfa_slice['oar_job_id'] is not -1:
-            #nodes_all = self.driver.GetNodes({'hostname': \
-                                                    #sfa_slice['node_ids']},
-                            #['node_id', 'hostname','site','boot_state'])
-            #nodeall_byhostname = dict([(n['hostname'], n) for n in nodes_all])
-            #nodes = sfa_slice['node_ids']
-            
-            #for node in nodes:
-                ##for sliver_attribute in filter(lambda a: a['node_id'] == \
-                                                #node['node_id'], slice_tags):
-                #sliver_attribute['tagname'] = 'slab-tag'
-                #sliver_attribute['value'] = 'slab-value'
-                #sliver_attributes.append(sliver_attribute['tagname'])
-                #attributes.append({'tagname': sliver_attribute['tagname'],
-                                    #'value': sliver_attribute['value']})
-
-            ## set nodegroup slice attributes
-            #for slice_tag in filter(lambda a: a['nodegroup_id'] \
-                                        #in node['nodegroup_ids'], slice_tags):
-                ## Do not set any nodegroup slice attributes for
-                ## which there is at least one sliver attribute
-                ## already set.
-                #if slice_tag not in slice_tags:
-                    #attributes.append({'tagname': slice_tag['tagname'],
-                        #'value': slice_tag['value']})
-
-            #for slice_tag in filter(lambda a: a['node_id'] is None, \
-                                                            #slice_tags):
-                ## Do not set any global slice attributes for
-                ## which there is at least one sliver attribute
-                ## already set.
-                #if slice_tag['tagname'] not in sliver_attributes:
-                    #attributes.append({'tagname': slice_tag['tagname'],
-                                   #'value': slice_tag['value']})
-
-            ## XXX Sanity check; though technically this should 
-            ## be a system invariant
-            ## checked with an assertion
-            #if sfa_slice['expires'] > MAXINT:  sfa_slice['expires']= MAXINT
-            
-            #slivers.append({
-                #'hrn': hrn,
-                #'name': sfa_slice['name'],
-                #'slice_id': sfa_slice['slice_id'],
-                #'instantiation': sfa_slice['instantiation'],
-                #'expires': sfa_slice['expires'],
-                #'keys': keys,
-                #'attributes': attributes
-            #})
-
-        #return slivers
-        
-        
-        
-
-
-        #return slivers
+    
     def get_peer(self, xrn):
         hrn, hrn_type = urn_to_hrn(xrn)
         #Does this slice belong to a local site or a peer senslab site?
@@ -130,53 +51,133 @@ class SlabSlices:
             sfa_peer = site_authority
 
         return sfa_peer
+
         
-        
-    def verify_slice_leases(self, sfa_slice, requested_jobs_dict, kept_leases, \
-        peer):
+    def verify_slice_leases(self, sfa_slice, requested_jobs_dict, peer):
 
        
         #First get the list of current leases from OAR          
-        leases = self.driver.GetLeases({'name':sfa_slice['name']})
-        #leases = self.driver.GetLeases({'name':sfa_slice['name']},\
-                                     #['lease_id'])
-        if leases : 
-            current_leases = [lease['lease_id'] for lease in leases]
-            #Deleted leases are the ones with lease id not declared in the Rspec
-            deleted_leases = list(set(current_leases).difference(kept_leases))
-    
-            try:
-                if peer:
-                    #peer = RegAuyhority object is unsubscriptable
-                    #TODO :UnBindObjectFromPeer Quick and dirty 
-                    #auth='senslab2 SA 27/07/12
-                    
-                    #Commented out UnBindObjectFromPeer SA 09/10/12
-                    #self.driver.UnBindObjectFromPeer('senslab2', 'slice', \
-                                    #sfa_slice['record_id_slice'], peer.hrn)
+        leases = self.driver.GetLeases({'name':sfa_slice['slice_hrn']})
+        logger.debug("SLABSLICES verify_slice_leases requested_jobs_dict %s \
+                        leases %s "%(requested_jobs_dict, leases ))
+        
+        current_nodes_reserved_by_start_time = {}
+        requested_nodes_by_start_time = {}
+        leases_by_start_time = {}
+        reschedule_jobs_dict = {}
+
+        
+        #Create reduced dictionary with key start_time and value 
+        # the list of nodes
+        #-for the leases already registered by OAR first
+        # then for the new leases requested by the user
+        
+        #Leases already scheduled/running in OAR
+        for lease in leases :
+            current_nodes_reserved_by_start_time[lease['t_from']] = \
+                    lease['reserved_nodes']
+            leases_by_start_time[lease['t_from']] = lease
+            
+        
+        #Requested jobs     
+        for start_time in requested_jobs_dict:
+            requested_nodes_by_start_time[int(start_time)]  = \
+                    requested_jobs_dict[start_time]['hostname']            
+        #Check if there is any difference between the leases already
+        #registered in OAR and the requested jobs.   
+        #Difference could be:
+        #-Lease deleted in the requested jobs
+        #-Added/removed nodes
+        #-Newly added lease 
+
+        logger.debug("SLABSLICES verify_slice_leases \
+                        requested_nodes_by_start_time %s \
+                        "%(requested_nodes_by_start_time ))
+        #Find all deleted leases
+        start_time_list = \
+            list(set(leases_by_start_time.keys()).\
+            difference(requested_nodes_by_start_time.keys()))
+        deleted_leases = [leases_by_start_time[start_time]['lease_id'] \
+                            for start_time in start_time_list]
+
+
+            
+        #Find added or removed nodes in exisiting leases
+        for start_time in requested_nodes_by_start_time: 
+            logger.debug("SLABSLICES verify_slice_leases  start_time %s \
+                         "%( start_time))
+            if start_time in current_nodes_reserved_by_start_time:
                 
-                self.driver.DeleteLeases(deleted_leases, \
-                                        sfa_slice['name'])
-               
-            #TODO verify_slice_leases: catch other exception?
-            except KeyError: 
-                logger.log_exc('Failed to remove slice leases')
+                if requested_nodes_by_start_time[start_time] == \
+                    current_nodes_reserved_by_start_time[start_time]:
+                    continue
                 
-        #Add new leases        
-        for start_time in requested_jobs_dict:
-            job = requested_jobs_dict[start_time]
-            self.driver.AddLeases(job['hostname'], \
-                        sfa_slice, int(job['start_time']), \
-                        int(job['duration']))
-                        
+                else:
+                    update_node_set = \
+                            set(requested_nodes_by_start_time[start_time])
+                    added_nodes = \
+                        update_node_set.difference(\
+                        current_nodes_reserved_by_start_time[start_time])
+                    shared_nodes = \
+                        update_node_set.intersection(\
+                        current_nodes_reserved_by_start_time[start_time])
+                    old_nodes_set = \
+                        set(\
+                        current_nodes_reserved_by_start_time[start_time])
+                    removed_nodes = \
+                        old_nodes_set.difference(\
+                        requested_nodes_by_start_time[start_time])
+                    logger.debug("SLABSLICES verify_slice_leases \
+                        shared_nodes %s  added_nodes %s removed_nodes %s"\
+                        %(shared_nodes, added_nodes,removed_nodes ))
+                    #If the lease is modified, delete it before 
+                    #creating it again.
+                    #Add the deleted lease job id in the list
+                    #WARNING :rescheduling does not work if there is already  
+                    # 2 running/scheduled jobs because deleting a job 
+                    #takes time SA 18/10/2012
+                    if added_nodes or removed_nodes:
+                        deleted_leases.append(\
+                            leases_by_start_time[start_time]['lease_id'])
+                        #Reschedule the job 
+                        if added_nodes or shared_nodes:
+                            reschedule_jobs_dict[str(start_time)] = \
+                                        requested_jobs_dict[str(start_time)]
+
+            else: 
+                    #New lease
+                    
+                    job = requested_jobs_dict[str(start_time)]
+                    logger.debug("SLABSLICES \
+                    NEWLEASE slice %s  job %s"\
+                    %(sfa_slice, job)) 
+                    self.driver.AddLeases(job['hostname'], \
+                            sfa_slice, int(job['start_time']), \
+                            int(job['duration']))
+
+        #Deleted leases are the ones with lease id not declared in the Rspec
+        if deleted_leases:
+            self.driver.DeleteLeases(deleted_leases, sfa_slice['slice_hrn'])
+            logger.debug("SLABSLICES \
+                    verify_slice_leases slice %s deleted_leases %s"\
+                    %(sfa_slice, deleted_leases))
+                    
+                    
+        if reschedule_jobs_dict : 
+            for start_time in  reschedule_jobs_dict:
+                job = reschedule_jobs_dict[start_time]
+                self.driver.AddLeases(job['hostname'], \
+                    sfa_slice, int(job['start_time']), \
+                    int(job['duration']))
         return leases
 
     def verify_slice_nodes(self, sfa_slice, requested_slivers, peer):
         current_slivers = []
         deleted_nodes = []
-        
-        if sfa_slice['node_ids']:
-            nodes = self.driver.GetNodes(sfa_slice['node_ids'], ['hostname'])
+
+        if 'node_ids' in sfa_slice:
+            nodes = self.driver.GetNodes(sfa_slice['list_node_ids'], \
+                ['hostname'])
             current_slivers = [node['hostname'] for node in nodes]
     
             # remove nodes not in rspec
@@ -186,14 +187,16 @@ class SlabSlices:
             #added_nodes = list(set(requested_slivers).\
                                         #difference(current_slivers))
 
-            #Update the table with the nodes that populate the slice
+
             logger.debug("SLABSLICES \tverify_slice_nodes slice %s\
                                          \r\n \r\n deleted_nodes %s"\
-                                        %(sfa_slice,deleted_nodes))
+                                        %(sfa_slice, deleted_nodes))
 
             if deleted_nodes:
-                self.driver.DeleteSliceFromNodes(sfa_slice['name'], \
-                                                                deleted_nodes)
+                #Delete the entire experience
+                self.driver.DeleteSliceFromNodes(sfa_slice)
+                #self.driver.DeleteSliceFromNodes(sfa_slice['slice_hrn'], \
+                                                                #deleted_nodes)
             return nodes
 
             
@@ -675,4 +678,4 @@ class SlabSlices:
                                 #value: %s, node_id: %s\nCause:%s'\
                                 #% (name, value,  node_id, str(error)))
 
\ No newline at end of file