diff --git a/release/scripts/io/netrender/balancing.py b/release/scripts/io/netrender/balancing.py index ebd54f6a8b9..0a654394103 100644 --- a/release/scripts/io/netrender/balancing.py +++ b/release/scripts/io/netrender/balancing.py @@ -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 diff --git a/release/scripts/io/netrender/client.py b/release/scripts/io/netrender/client.py index fa9aa766b26..877f4f85dec 100644 --- a/release/scripts/io/netrender/client.py +++ b/release/scripts/io/netrender/client.py @@ -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): diff --git a/release/scripts/io/netrender/master.py b/release/scripts/io/netrender/master.py index 00ea688a886..f27d738d54a 100644 --- a/release/scripts/io/netrender/master.py +++ b/release/scripts/io/netrender/master.py @@ -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) diff --git a/release/scripts/io/netrender/master_html.py b/release/scripts/io/netrender/master_html.py index 3310ccabf37..c76e57991d9 100644 --- a/release/scripts/io/netrender/master_html.py +++ b/release/scripts/io/netrender/master_html.py @@ -104,7 +104,7 @@ def get(handler): output("

Master

") - output("""""") + output("""""") startTable(caption = "Rules", class_style = "rules") @@ -175,7 +175,8 @@ def get(handler): for job in handler.server.jobs: results = job.framesStatus() rowTable( - """""" % job.id + + """""" % job.id + + """""" % job.id + """""" % job.id, job.id, link(job.name, "/html/job" + job.id), diff --git a/release/scripts/io/netrender/netrender.js b/release/scripts/io/netrender/netrender.js index e219d80651d..cf4ab39a6fa 100644 --- a/release/scripts/io/netrender/netrender.js +++ b/release/scripts/io/netrender/netrender.js @@ -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); diff --git a/release/scripts/io/netrender/slave.py b/release/scripts/io/netrender/slave.py index e7cac28ace3..10c26954784 100644 --- a/release/scripts/io/netrender/slave.py +++ b/release/scripts/io/netrender/slave.py @@ -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 diff --git a/release/scripts/io/netrender/ui.py b/release/scripts/io/netrender/ui.py index 0189341679a..182f1be7428 100644 --- a/release/scripts/io/netrender/ui.py +++ b/release/scripts/io/netrender/ui.py @@ -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: