forked from bartvdbraak/blender
netrender:
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
This commit is contained in:
parent
a994a52497
commit
a8c3831a1d
@ -161,7 +161,7 @@ class MinimumTimeBetweenDispatchPriority(PriorityRule):
|
|||||||
|
|
||||||
class ExcludeQueuedEmptyJob(ExclusionRule):
|
class ExcludeQueuedEmptyJob(ExclusionRule):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Exclude queued and empty jobs"
|
return "Exclude non queued and empty jobs"
|
||||||
|
|
||||||
def test(self, job):
|
def test(self, job):
|
||||||
return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0
|
return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0
|
||||||
|
@ -197,7 +197,7 @@ class NetworkRenderEngine(bpy.types.RenderEngine):
|
|||||||
|
|
||||||
address = "" if netsettings.server_address == "[default]" else netsettings.server_address
|
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):
|
def render_slave(self, scene):
|
||||||
|
@ -105,6 +105,17 @@ class MRenderJob(netrender.model.RenderJob):
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self.status = JOB_FINISHED
|
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):
|
def start(self):
|
||||||
self.status = JOB_QUEUED
|
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")
|
log_pattern = re.compile("/log_([a-zA-Z0-9]+)_([0-9]+).log")
|
||||||
reset_pattern = re.compile("/reset(all|)_([a-zA-Z0-9]+)_([0-9]+)")
|
reset_pattern = re.compile("/reset(all|)_([a-zA-Z0-9]+)_([0-9]+)")
|
||||||
cancel_pattern = re.compile("/cancel_([a-zA-Z0-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]+)")
|
edit_pattern = re.compile("/edit_([a-zA-Z0-9]+)")
|
||||||
|
|
||||||
class RenderHandler(http.server.BaseHTTPRequestHandler):
|
class RenderHandler(http.server.BaseHTTPRequestHandler):
|
||||||
@ -483,13 +495,21 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
|
|||||||
match = cancel_pattern.match(self.path)
|
match = cancel_pattern.match(self.path)
|
||||||
|
|
||||||
if match:
|
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_id = match.groups()[0]
|
||||||
|
|
||||||
job = self.server.getJobID(job_id)
|
job = self.server.getJobID(job_id)
|
||||||
|
|
||||||
if job:
|
if job:
|
||||||
self.server.stats("", "Cancelling job")
|
self.server.stats("", "Cancelling job")
|
||||||
self.server.removeJob(job)
|
self.server.removeJob(job, clear)
|
||||||
self.send_head()
|
self.send_head()
|
||||||
else:
|
else:
|
||||||
# no such job id
|
# no such job id
|
||||||
@ -497,12 +517,46 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
|
|||||||
else:
|
else:
|
||||||
# invalid url
|
# invalid url
|
||||||
self.send_head(http.client.NO_CONTENT)
|
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":
|
elif self.path == "/clear":
|
||||||
# cancel all jobs
|
# 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.stats("", "Clearing jobs")
|
||||||
self.server.clear()
|
self.server.clear(clear)
|
||||||
|
|
||||||
self.send_head()
|
self.send_head()
|
||||||
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
@ -798,11 +852,11 @@ class RenderMasterServer(http.server.HTTPServer):
|
|||||||
slave.job.usage += slave_usage
|
slave.job.usage += slave_usage
|
||||||
|
|
||||||
|
|
||||||
def clear(self):
|
def clear(self, clear_files = False):
|
||||||
removed = self.jobs[:]
|
removed = self.jobs[:]
|
||||||
|
|
||||||
for job in removed:
|
for job in removed:
|
||||||
self.removeJob(job)
|
self.removeJob(job, clear_files)
|
||||||
|
|
||||||
def balance(self):
|
def balance(self):
|
||||||
self.balancer.balance(self.jobs)
|
self.balancer.balance(self.jobs)
|
||||||
@ -821,10 +875,13 @@ class RenderMasterServer(http.server.HTTPServer):
|
|||||||
def countSlaves(self):
|
def countSlaves(self):
|
||||||
return len(self.slaves)
|
return len(self.slaves)
|
||||||
|
|
||||||
def removeJob(self, job):
|
def removeJob(self, job, clear_files = False):
|
||||||
self.jobs.remove(job)
|
self.jobs.remove(job)
|
||||||
self.jobs_map.pop(job.id)
|
self.jobs_map.pop(job.id)
|
||||||
|
|
||||||
|
if clear_files:
|
||||||
|
shutil.rmtree(job.save_path)
|
||||||
|
|
||||||
for slave in self.slaves:
|
for slave in self.slaves:
|
||||||
if slave.job == job:
|
if slave.job == job:
|
||||||
slave.job = None
|
slave.job = None
|
||||||
@ -856,7 +913,10 @@ class RenderMasterServer(http.server.HTTPServer):
|
|||||||
|
|
||||||
return None, None
|
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 = RenderMasterServer(address, RenderHandler, path)
|
||||||
httpd.timeout = 1
|
httpd.timeout = 1
|
||||||
httpd.stats = update_stats
|
httpd.stats = update_stats
|
||||||
@ -881,3 +941,5 @@ def runMaster(address, broadcast, path, update_stats, test_break):
|
|||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
httpd.server_close()
|
httpd.server_close()
|
||||||
|
if clear:
|
||||||
|
clearMaster(path)
|
||||||
|
@ -104,7 +104,7 @@ def get(handler):
|
|||||||
|
|
||||||
output("<h2>Master</h2>")
|
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")
|
startTable(caption = "Rules", class_style = "rules")
|
||||||
|
|
||||||
@ -175,7 +175,8 @@ def get(handler):
|
|||||||
for job in handler.server.jobs:
|
for job in handler.server.jobs:
|
||||||
results = job.framesStatus()
|
results = job.framesStatus()
|
||||||
rowTable(
|
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,
|
"""<button title="reset all frames" onclick="request('/resetall_%s_0', null);">R</button>""" % job.id,
|
||||||
job.id,
|
job.id,
|
||||||
link(job.name, "/html/job" + job.id),
|
link(job.name, "/html/job" + job.id),
|
||||||
|
@ -11,6 +11,28 @@ function edit(id, info)
|
|||||||
request("/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)
|
function balance_edit(id, old_value)
|
||||||
{
|
{
|
||||||
var new_value = prompt("New limit", old_value);
|
var new_value = prompt("New limit", old_value);
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#
|
#
|
||||||
# ##### END GPL LICENSE BLOCK #####
|
# ##### END GPL LICENSE BLOCK #####
|
||||||
|
|
||||||
import sys, os, platform
|
import sys, os, platform, shutil
|
||||||
import http, http.client, http.server, urllib
|
import http, http.client, http.server, urllib
|
||||||
import subprocess, time
|
import subprocess, time
|
||||||
|
|
||||||
@ -45,6 +45,9 @@ else:
|
|||||||
def RestoreErrorMode(val):
|
def RestoreErrorMode(val):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def clearSlave(path):
|
||||||
|
shutil.rmtree(path)
|
||||||
|
|
||||||
def slave_Info():
|
def slave_Info():
|
||||||
sysname, nodename, release, version, machine, processor = platform.uname()
|
sysname, nodename, release, version, machine, processor = platform.uname()
|
||||||
slave = netrender.model.RenderSlave()
|
slave = netrender.model.RenderSlave()
|
||||||
@ -103,7 +106,6 @@ def render_slave(engine, netsettings):
|
|||||||
os.mkdir(NODE_PREFIX)
|
os.mkdir(NODE_PREFIX)
|
||||||
|
|
||||||
while not engine.test_break():
|
while not engine.test_break():
|
||||||
|
|
||||||
conn.request("GET", "/job", headers={"slave-id":slave_id})
|
conn.request("GET", "/job", headers={"slave-id":slave_id})
|
||||||
response = conn.getresponse()
|
response = conn.getresponse()
|
||||||
|
|
||||||
@ -162,7 +164,7 @@ def render_slave(engine, netsettings):
|
|||||||
cancelled = False
|
cancelled = False
|
||||||
stdout = bytes()
|
stdout = bytes()
|
||||||
run_t = time.time()
|
run_t = time.time()
|
||||||
while process.poll() == None and not cancelled:
|
while not cancelled and process.poll() == None:
|
||||||
stdout += process.stdout.read(32)
|
stdout += process.stdout.read(32)
|
||||||
current_t = time.time()
|
current_t = time.time()
|
||||||
cancelled = engine.test_break()
|
cancelled = engine.test_break()
|
||||||
@ -238,10 +240,12 @@ def render_slave(engine, netsettings):
|
|||||||
for i in range(timeout):
|
for i in range(timeout):
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
if engine.test_break():
|
if engine.test_break():
|
||||||
conn.close()
|
break
|
||||||
return
|
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
if netsettings.slave_clear:
|
||||||
|
clearSlave(NODE_PREFIX)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
pass
|
pass
|
||||||
|
@ -117,13 +117,48 @@ class RENDER_PT_network_settings(RenderButtonsPanel):
|
|||||||
col.label(text="Port:")
|
col.label(text="Port:")
|
||||||
col.prop(netsettings, "server_port", text="")
|
col.prop(netsettings, "server_port", text="")
|
||||||
|
|
||||||
if netsettings.mode == "RENDER_MASTER":
|
if netsettings.mode != "RENDER_MASTER":
|
||||||
layout.prop(netsettings, "server_broadcast", text="Broadcast")
|
|
||||||
else:
|
|
||||||
layout.operator("render.netclientscan", icon='FILE_REFRESH', text="")
|
layout.operator("render.netclientscan", icon='FILE_REFRESH', text="")
|
||||||
|
|
||||||
layout.operator("render.netclientweb", icon='QUESTION')
|
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
|
@rnaType
|
||||||
class RENDER_PT_network_job(RenderButtonsPanel):
|
class RENDER_PT_network_job(RenderButtonsPanel):
|
||||||
bl_label = "Job Settings"
|
bl_label = "Job Settings"
|
||||||
@ -306,11 +341,21 @@ NetRenderSettings.IntProperty( attr="server_port",
|
|||||||
min=1,
|
min=1,
|
||||||
max=65535)
|
max=65535)
|
||||||
|
|
||||||
NetRenderSettings.BoolProperty( attr="server_broadcast",
|
NetRenderSettings.BoolProperty( attr="master_broadcast",
|
||||||
name="Broadcast server address",
|
name="Broadcast",
|
||||||
description="broadcast server address on local network",
|
description="broadcast master server address on local network",
|
||||||
default = True)
|
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")
|
default_path = os.environ.get("TEMP")
|
||||||
|
|
||||||
if not default_path:
|
if not default_path:
|
||||||
|
Loading…
Reference in New Issue
Block a user