netrender: load balancer fixes, cancel all jobs button and small html interface changes
authorMartin Poirier <theeth@yahoo.com>
Wed, 23 Sep 2009 21:46:29 +0000 (21:46 +0000)
committerMartin Poirier <theeth@yahoo.com>
Wed, 23 Sep 2009 21:46:29 +0000 (21:46 +0000)
release/io/netrender/balancing.py
release/io/netrender/master.py
release/io/netrender/master_html.py
release/io/netrender/operators.py
release/io/netrender/ui.py
release/io/netrender/utils.py

index 62b6dcee519633bb059727d2b10dcd86d5ca3eb9..b1a461bf0ca82a47bf8f6a3869373ff0b52b469a 100644 (file)
@@ -71,14 +71,14 @@ class NewJobPriority(PriorityRule):
                self.limit = limit
                
        def test(self, job):
-               return job.countFrames(status = DISPATCHED) < self.limit
+               return job.countFrames(status = DONE) < self.limit
 
 class MinimumTimeBetweenDispatchPriority(PriorityRule):
        def __init__(self, limit = 10):
                self.limit = limit
                
        def test(self, job):
-               return (time.time() - job.last_dispatched) / 60 > self.limit
+               return job.countFrames(status = DISPATCHED) == 0 and (time.time() - job.last_dispatched) / 60 > self.limit
 
 class ExcludeQueuedEmptyJob(ExclusionRule):
        def test(self, job):
@@ -91,4 +91,4 @@ class ExcludeSlavesLimit(ExclusionRule):
                self.limit = limit
                
        def test(self, job):
-               return not ( self.count_jobs() == 1 or float(job.countSlaves() + 1) / self.count_slaves() <= self.limit )
\ No newline at end of file
+               return not ( self.count_jobs() == 1 or self.count_slaves() == 1 or float(job.countSlaves() + 1) / self.count_slaves() <= self.limit )
index 0e3c7063cab8d6484abad75da520ac0d62bebd46..84a6ad8cca1414eec1f804ae272ecad4a33d97bd 100644 (file)
@@ -50,6 +50,7 @@ class MRenderJob(netrender.model.RenderJob):
                self.last_dispatched = time.time()
        
                # special server properties
+               self.last_update = 0
                self.save_path = ""
                self.files_map = {path: MRenderFile(path, start, end) for path, start, end in files}
                self.status = JOB_WAITING
@@ -68,13 +69,23 @@ class MRenderJob(netrender.model.RenderJob):
                self.start()
                return True
        
+       def testFinished(self):
+               for f in self.frames:
+                       if f.status == QUEUED or f.status == DISPATCHED:
+                               break
+               else:
+                       self.status = JOB_FINISHED
+
        def start(self):
                self.status = JOB_QUEUED
-       
+
        def update(self):
-               self.credits -= 5 # cost of one frame
-               self.credits += (time.time() - self.last_dispatched) / 60
-               self.last_dispatched = time.time()
+               if self.last_update == 0:
+                       self.credits += (time.time() - self.last_dispatched) / 60
+               else:
+                       self.credits += (time.time() - self.last_update) / 60
+
+               self.last_update = time.time()
        
        def addLog(self, frames):
                log_name = "_".join(("%04d" % f for f in frames)) + ".log"
@@ -98,7 +109,8 @@ class MRenderJob(netrender.model.RenderJob):
                frames = []
                for f in self.frames:
                        if f.status == QUEUED:
-                               self.update()
+                               self.credits -= 1 # cost of one frame
+                               self.last_dispatched = time.time()
                                frames.append(f)
                                if len(frames) >= self.chunks:
                                        break
@@ -512,7 +524,7 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
                                        job_time = float(self.headers['job-time'])
                                        
                                        frame = job[job_frame]
-                                       
+
                                        if job_result == DONE:
                                                length = int(self.headers['content-length'])
                                                buf = self.rfile.read(length)
@@ -527,6 +539,8 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
                                                
                                        frame.status = job_result
                                        frame.time = job_time
+
+                                       job.testFinished()
                        
                                        self.server.updateSlave(self.headers['slave-id'])
                                        
@@ -581,7 +595,7 @@ class RenderMasterServer(http.server.HTTPServer):
                self.balancer.addException(netrender.balancing.ExcludeQueuedEmptyJob())
                self.balancer.addException(netrender.balancing.ExcludeSlavesLimit(self.countJobs, self.countSlaves))
                self.balancer.addPriority(netrender.balancing.NewJobPriority())
-               self.balancer.addPriority(netrender.balancing.MinimumTimeBetweenDispatchPriority())
+               self.balancer.addPriority(netrender.balancing.MinimumTimeBetweenDispatchPriority(limit = 2))
                
                if not os.path.exists(self.path):
                        os.mkdir(self.path)
@@ -612,6 +626,8 @@ class RenderMasterServer(http.server.HTTPServer):
                self.jobs = []
        
        def update(self):
+               for job in self.jobs:
+                       job.update()
                self.balancer.balance(self.jobs)
        
        def countJobs(self, status = JOB_QUEUED):
index 6a337c8d71d8de5d80343843eb9e00c8f5781767..8e11c86a88c30cd471fda24442fe7fe6d17e2ecc 100644 (file)
@@ -52,11 +52,13 @@ def get(handler):
                output("<h2>Jobs</h2>")
                
                startTable()
-               headerTable("id", "name", "length", "done", "dispatched", "error")
+               headerTable("id", "name", "credits", "time since last", "length", "done", "dispatched", "error", "priority", "exception")
+
+               handler.server.update()
                
                for job in handler.server.jobs:
                        results = job.framesStatus()
-                       rowTable(link(job.id, "/html/job" + job.id), job.name, len(job), results[DONE], results[DISPATCHED], results[ERROR])
+                       rowTable(link(job.id, "/html/job" + job.id), job.name, round(job.credits, 1), int(time.time() - job.last_dispatched), len(job), results[DONE], results[DISPATCHED], results[ERROR], handler.server.balancer.applyPriorities(job), handler.server.balancer.applyExceptions(job))
                
                endTable()
                
index bfc67c25285bfc002fc872e0a3a691f656a8f208..498edd745293330d47238f5faab71caceacf521e 100644 (file)
@@ -262,12 +262,46 @@ class RENDER_OT_netclientcancel(bpy.types.Operator):
                        
                        response = conn.getresponse()
                        print( response.status, response.reason )
+
+                       netsettings.jobs.remove(netsettings.active_job_index)
                
                return ('FINISHED',)
        
        def invoke(self, context, event):
                return self.execute(context)
        
+@rnaOperator
+class RENDER_OT_netclientcancelall(bpy.types.Operator):
+       '''Operator documentation text, will be used for the operator tooltip and python docs.'''
+       __idname__ = "render.netclientcancelall"
+       __label__ = "Net Render Client Cancel All"
+       
+       # List of operator properties, the attributes will be assigned
+       # to the class instance from the operator settings before calling.
+       
+       __props__ = []
+       
+       def poll(self, context):
+               return True
+               
+       def execute(self, context):
+               netsettings = context.scene.network_render
+               conn = clientConnection(context.scene)
+               
+               if conn:
+                       conn.request("POST", "/cancel")
+                       
+                       response = conn.getresponse()
+                       print( response.status, response.reason )
+               
+                       while(len(netsettings.jobs) > 0):
+                               netsettings.jobs.remove(0)
+
+               return ('FINISHED',)
+       
+       def invoke(self, context, event):
+               return self.execute(context)
+
 @rnaOperator
 class netclientdownload(bpy.types.Operator):
        '''Operator documentation text, will be used for the operator tooltip and python docs.'''
index 2ec0b62de4a2965325cbf0ee115ad957d75b5703..3aad8362c433f85e7c6dcf7df3173ada0119cd85 100644 (file)
@@ -165,6 +165,7 @@ class SCENE_PT_network_jobs(RenderButtonsPanel):
                subcol = col.column(align=True)
                subcol.itemO("render.netclientstatus", icon="ICON_FILE_REFRESH", text="")
                subcol.itemO("render.netclientcancel", icon="ICON_ZOOMOUT", text="")
+               subcol.itemO("render.netclientcancelall", icon="ICON_ZOOMOUT", text="")
                subcol.itemO("render.netclientdownload", icon='ICON_RENDER_ANIMATION', text="")
 
                if len(bpy.data.netrender_jobs) == 0 and len(netsettings.jobs) > 0:
index 62288aecf9460501a1ad563fa945eadc5c313720..06393a738a0f41b5eb0522f978df7a0089d283e7 100644 (file)
@@ -11,7 +11,8 @@ VERSION = b"0.5"
 # Jobs status
 JOB_WAITING = 0 # before all data has been entered
 JOB_PAUSED = 1 # paused by user
-JOB_QUEUED = 2 # ready to be dispatched
+JOB_FINISHED = 2 # finished rendering
+JOB_QUEUED = 3 # ready to be dispatched
 
 # Frames status
 QUEUED = 0
@@ -82,4 +83,4 @@ def prefixPath(prefix_directory, file_path, prefix_path):
        else:
                full_path = prefix_directory + file_path
        
-       return full_path
\ No newline at end of file
+       return full_path