netrender:
authorMartin Poirier <theeth@yahoo.com>
Wed, 6 Jan 2010 18:56:24 +0000 (18:56 +0000)
committerMartin Poirier <theeth@yahoo.com>
Wed, 6 Jan 2010 18:56:24 +0000 (18:56 +0000)
Slave and Master options to delete files when closed (default True for slave, False for Master)
Web interface option to remove files (on master) when deleting a job (or all jobs)
Web interface button to pause a job

release/scripts/io/netrender/balancing.py
release/scripts/io/netrender/client.py
release/scripts/io/netrender/master.py
release/scripts/io/netrender/master_html.py
release/scripts/io/netrender/netrender.js
release/scripts/io/netrender/slave.py
release/scripts/io/netrender/ui.py

index ebd54f6a8b9ed778085c97c61b1adef20ce6419b..0a654394103dbc57684ffee5db2365540e548432 100644 (file)
@@ -161,7 +161,7 @@ class MinimumTimeBetweenDispatchPriority(PriorityRule):
 
 class ExcludeQueuedEmptyJob(ExclusionRule):
     def __str__(self):
-        return "Exclude queued and empty jobs"
+        return "Exclude non queued and empty jobs"
 
     def test(self, job):
         return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0
index fa9aa766b2654d306e3296120d1f3457d11b38a6..877f4f85dec97d000fca7d7c2e3d48bab466e870 100644 (file)
@@ -197,7 +197,7 @@ class NetworkRenderEngine(bpy.types.RenderEngine):
 
         address = "" if netsettings.server_address == "[default]" else netsettings.server_address
 
-        master.runMaster((address, netsettings.server_port), netsettings.server_broadcast, netsettings.path, self.update_stats, self.test_break)
+        master.runMaster((address, netsettings.server_port), netsettings.master_broadcast, netsettings.master_clear, netsettings.path, self.update_stats, self.test_break)
 
 
     def render_slave(self, scene):
index 00ea688a88632744630feb9de885294be3d78d9c..f27d738d54a5fa9d055c439e3c2eb287ea524fcd 100644 (file)
@@ -105,6 +105,17 @@ class MRenderJob(netrender.model.RenderJob):
                 break
         else:
             self.status = JOB_FINISHED
+            
+    def pause(self, status = None):
+        if self.status not in {JOB_PAUSED, JOB_QUEUED}:
+            return 
+        
+        if status == None:
+            self.status = JOB_PAUSED if self.status == JOB_QUEUED else JOB_QUEUED
+        elif status:
+            self.status = JOB_QUEUED
+        else:
+            self.status = JOB_PAUSED
 
     def start(self):
         self.status = JOB_QUEUED
@@ -166,6 +177,7 @@ render_pattern = re.compile("/render_([a-zA-Z0-9]+)_([0-9]+).exr")
 log_pattern = re.compile("/log_([a-zA-Z0-9]+)_([0-9]+).log")
 reset_pattern = re.compile("/reset(all|)_([a-zA-Z0-9]+)_([0-9]+)")
 cancel_pattern = re.compile("/cancel_([a-zA-Z0-9]+)")
+pause_pattern = re.compile("/pause_([a-zA-Z0-9]+)")
 edit_pattern = re.compile("/edit_([a-zA-Z0-9]+)")
 
 class RenderHandler(http.server.BaseHTTPRequestHandler):
@@ -483,13 +495,21 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
             match = cancel_pattern.match(self.path)
 
             if match:
+                length = int(self.headers['content-length'])
+                
+                if length > 0:
+                    info_map = eval(str(self.rfile.read(length), encoding='utf8'))
+                    clear = info_map.get("clear", False)
+                else:
+                    clear = False
+                
                 job_id = match.groups()[0]
 
                 job = self.server.getJobID(job_id)
 
                 if job:
                     self.server.stats("", "Cancelling job")
-                    self.server.removeJob(job)
+                    self.server.removeJob(job, clear)
                     self.send_head()
                 else:
                     # no such job id
@@ -497,12 +517,46 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
             else:
                 # invalid url
                 self.send_head(http.client.NO_CONTENT)
+        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+        elif self.path.startswith("/pause"):
+            match = pause_pattern.match(self.path)
+
+            if match:
+                length = int(self.headers['content-length'])
+                
+                if length > 0:
+                    info_map = eval(str(self.rfile.read(length), encoding='utf8'))
+                    status = info_map.get("status", None)
+                else:
+                    status = None
+                
+                job_id = match.groups()[0]
+
+                job = self.server.getJobID(job_id)
 
+                if job:
+                    self.server.stats("", "Pausing job")
+                    job.pause(status)
+                    self.send_head()
+                else:
+                    # no such job id
+                    self.send_head(http.client.NO_CONTENT)
+            else:
+                # invalid url
+                self.send_head(http.client.NO_CONTENT)
         # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
         elif self.path == "/clear":
             # cancel all jobs
+            length = int(self.headers['content-length'])
+            
+            if length > 0:
+                info_map = eval(str(self.rfile.read(length), encoding='utf8'))
+                clear = info_map.get("clear", False)
+            else:
+                clear = False
+
             self.server.stats("", "Clearing jobs")
-            self.server.clear()
+            self.server.clear(clear)
 
             self.send_head()
         # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
@@ -798,11 +852,11 @@ class RenderMasterServer(http.server.HTTPServer):
                     slave.job.usage += slave_usage
 
 
-    def clear(self):
+    def clear(self, clear_files = False):
         removed = self.jobs[:]
 
         for job in removed:
-            self.removeJob(job)
+            self.removeJob(job, clear_files)
 
     def balance(self):
         self.balancer.balance(self.jobs)
@@ -821,10 +875,13 @@ class RenderMasterServer(http.server.HTTPServer):
     def countSlaves(self):
         return len(self.slaves)
 
-    def removeJob(self, job):
+    def removeJob(self, job, clear_files = False):
         self.jobs.remove(job)
         self.jobs_map.pop(job.id)
-
+        
+        if clear_files:
+            shutil.rmtree(job.save_path)
+        
         for slave in self.slaves:
             if slave.job == job:
                 slave.job = None
@@ -856,7 +913,10 @@ class RenderMasterServer(http.server.HTTPServer):
 
         return None, None
 
-def runMaster(address, broadcast, path, update_stats, test_break):
+def clearMaster(path):
+    shutil.rmtree(path)
+
+def runMaster(address, broadcast, clear, path, update_stats, test_break):
         httpd = RenderMasterServer(address, RenderHandler, path)
         httpd.timeout = 1
         httpd.stats = update_stats
@@ -881,3 +941,5 @@ def runMaster(address, broadcast, path, update_stats, test_break):
                         start_time = time.time()
 
         httpd.server_close()
+        if clear:
+            clearMaster(path)
index 3310ccabf37a6903e926bf9e18734ae2793bc016..c76e57991d9ec064c7727b64f51d2febe6bf6da3 100644 (file)
@@ -104,7 +104,7 @@ def get(handler):
 
         output("<h2>Master</h2>")
 
-        output("""<button title="remove all jobs" onclick="request('/clear', null);">CLEAR JOB LIST</button>""")
+        output("""<button title="remove all jobs" onclick="clear_jobs();">CLEAR JOB LIST</button>""")
 
         startTable(caption = "Rules", class_style = "rules")
 
@@ -175,7 +175,8 @@ def get(handler):
         for job in handler.server.jobs:
             results = job.framesStatus()
             rowTable(
-                        """<button title="cancel job" onclick="request('/cancel_%s', null);">X</button>""" % job.id +
+                        """<button title="cancel job" onclick="cancel_job('%s');">X</button>""" % job.id +
+                        """<button title="pause job" onclick="request('/pause_%s', null);">P</button>""" % job.id +
                         """<button title="reset all frames" onclick="request('/resetall_%s_0', null);">R</button>""" % job.id,
                         job.id,
                         link(job.name, "/html/job" + job.id),
index e219d80651df9175b578d5657a05e1b8f4621e66..cf4ab39a6fa787ea0f30363a40566fd1961ed616 100644 (file)
@@ -11,6 +11,28 @@ function edit(id, info)
        request("/edit_" + id, info)
 }
 
+function clear_jobs()
+{
+       var r=confirm("Also delete files on master?");
+       
+       if (r==true) {
+               request('/clear', "{'clear':True}");
+       } else {
+               request('/clear', "{'clear':False}");
+       }
+}
+
+function cancel_job(id)
+{
+       var r=confirm("Also delete files on master?");
+       
+       if (r==true) {
+               request('/cancel_' + id, "{'clear':True}");
+       } else {
+               request('/cancel_' + id, "{'clear':False}");
+       }
+}
+
 function balance_edit(id, old_value)
 {
        var new_value = prompt("New limit", old_value);
index e7cac28ace36814926caa4090e8e8cf403abe073..10c26954784954039479e949e890f79ce1bcffb1 100644 (file)
@@ -16,7 +16,7 @@
 #
 # ##### END GPL LICENSE BLOCK #####
 
-import sys, os, platform
+import sys, os, platform, shutil
 import http, http.client, http.server, urllib
 import subprocess, time
 
@@ -45,6 +45,9 @@ else:
     def RestoreErrorMode(val):
         pass
 
+def clearSlave(path):
+    shutil.rmtree(path)
+
 def slave_Info():
     sysname, nodename, release, version, machine, processor = platform.uname()
     slave = netrender.model.RenderSlave()
@@ -103,7 +106,6 @@ def render_slave(engine, netsettings):
             os.mkdir(NODE_PREFIX)
 
         while not engine.test_break():
-
             conn.request("GET", "/job", headers={"slave-id":slave_id})
             response = conn.getresponse()
 
@@ -162,7 +164,7 @@ def render_slave(engine, netsettings):
                 cancelled = False
                 stdout = bytes()
                 run_t = time.time()
-                while process.poll() == None and not cancelled:
+                while not cancelled and process.poll() == None:
                     stdout += process.stdout.read(32)
                     current_t = time.time()
                     cancelled = engine.test_break()
@@ -238,10 +240,12 @@ def render_slave(engine, netsettings):
                 for i in range(timeout):
                     time.sleep(1)
                     if engine.test_break():
-                        conn.close()
-                        return
+                        break
 
         conn.close()
+        
+        if netsettings.slave_clear:
+            clearSlave(NODE_PREFIX)
 
 if __name__ == "__main__":
     pass
index 0189341679a70ba906487fc4a4d53603ee7f718f..182f1be742800802640c95209a6d17be464ae7d3 100644 (file)
@@ -117,13 +117,48 @@ class RENDER_PT_network_settings(RenderButtonsPanel):
         col.label(text="Port:")
         col.prop(netsettings, "server_port", text="")
 
-        if netsettings.mode == "RENDER_MASTER":
-            layout.prop(netsettings, "server_broadcast", text="Broadcast")
-        else:
+        if netsettings.mode != "RENDER_MASTER":
             layout.operator("render.netclientscan", icon='FILE_REFRESH', text="")
 
         layout.operator("render.netclientweb", icon='QUESTION')
 
+@rnaType
+class RENDER_PT_network_slave_settings(RenderButtonsPanel):
+    bl_label = "Slave Settings"
+    COMPAT_ENGINES = {'NET_RENDER'}
+
+    def poll(self, context):
+        scene = context.scene
+        return (super().poll(context)
+                and scene.network_render.mode == "RENDER_SLAVE")
+
+    def draw(self, context):
+        layout = self.layout
+
+        scene = context.scene
+        netsettings = scene.network_render
+
+        layout.prop(netsettings, "slave_clear")
+        
+@rnaType
+class RENDER_PT_network_master_settings(RenderButtonsPanel):
+    bl_label = "Master Settings"
+    COMPAT_ENGINES = {'NET_RENDER'}
+
+    def poll(self, context):
+        scene = context.scene
+        return (super().poll(context)
+                and scene.network_render.mode == "RENDER_MASTER")
+
+    def draw(self, context):
+        layout = self.layout
+
+        scene = context.scene
+        netsettings = scene.network_render
+
+        layout.prop(netsettings, "master_broadcast")
+        layout.prop(netsettings, "master_clear")
+        
 @rnaType
 class RENDER_PT_network_job(RenderButtonsPanel):
     bl_label = "Job Settings"
@@ -306,11 +341,21 @@ NetRenderSettings.IntProperty( attr="server_port",
                 min=1,
                 max=65535)
 
-NetRenderSettings.BoolProperty( attr="server_broadcast",
-                name="Broadcast server address",
-                description="broadcast server address on local network",
+NetRenderSettings.BoolProperty( attr="master_broadcast",
+                name="Broadcast",
+                description="broadcast master server address on local network",
+                default = True)
+
+NetRenderSettings.BoolProperty( attr="slave_clear",
+                name="Clear on exit",
+                description="delete downloaded files on exit",
                 default = True)
 
+NetRenderSettings.BoolProperty( attr="master_clear",
+                name="Clear on exit",
+                description="delete saved files on exit",
+                default = False)
+
 default_path = os.environ.get("TEMP")
 
 if not default_path: