Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/io/netrender/__init__.py4
-rw-r--r--release/scripts/io/netrender/balancing.py222
-rw-r--r--release/scripts/io/netrender/client.py478
-rw-r--r--release/scripts/io/netrender/master.py1618
-rw-r--r--release/scripts/io/netrender/master_html.py446
-rw-r--r--release/scripts/io/netrender/model.py484
-rw-r--r--release/scripts/io/netrender/operators.py762
-rw-r--r--release/scripts/io/netrender/slave.py410
-rw-r--r--release/scripts/io/netrender/ui.py504
-rw-r--r--release/scripts/io/netrender/utils.py98
-rw-r--r--release/scripts/presets/render/HDTV_1080p.py2
-rw-r--r--release/scripts/presets/render/HDTV_720p.py2
-rw-r--r--release/scripts/presets/render/TV_PAL.py2
-rw-r--r--release/scripts/presets/render/TV_PAL_16_colon_9.py2
-rw-r--r--release/scripts/ui/space_userpref.py12
-rw-r--r--release/scripts/ui/space_view3d_toolbar.py8
16 files changed, 2527 insertions, 2527 deletions
diff --git a/release/scripts/io/netrender/__init__.py b/release/scripts/io/netrender/__init__.py
index 9e48164a5eb..77ab95535aa 100644
--- a/release/scripts/io/netrender/__init__.py
+++ b/release/scripts/io/netrender/__init__.py
@@ -4,12 +4,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
diff --git a/release/scripts/io/netrender/balancing.py b/release/scripts/io/netrender/balancing.py
index d8951b1e2e8..e09b19f956c 100644
--- a/release/scripts/io/netrender/balancing.py
+++ b/release/scripts/io/netrender/balancing.py
@@ -4,12 +4,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
@@ -22,129 +22,129 @@ from netrender.utils import *
import netrender.model
class RatingRule:
- def rate(self, job):
- return 0
+ def rate(self, job):
+ return 0
class ExclusionRule:
- def test(self, job):
- return False
+ def test(self, job):
+ return False
class PriorityRule:
- def test(self, job):
- return False
+ def test(self, job):
+ return False
class Balancer:
- def __init__(self):
- self.rules = []
- self.priorities = []
- self.exceptions = []
-
- def addRule(self, rule):
- self.rules.append(rule)
-
- def addPriority(self, priority):
- self.priorities.append(priority)
-
- def addException(self, exception):
- self.exceptions.append(exception)
-
- def applyRules(self, job):
- return sum((rule.rate(job) for rule in self.rules))
-
- def applyPriorities(self, job):
- for priority in self.priorities:
- if priority.test(job):
- return True # priorities are first
-
- return False
-
- def applyExceptions(self, job):
- for exception in self.exceptions:
- if exception.test(job):
- return True # exceptions are last
-
- return False
-
- def sortKey(self, job):
- return (1 if self.applyExceptions(job) else 0, # exceptions after
- 0 if self.applyPriorities(job) else 1, # priorities first
- self.applyRules(job))
-
- def balance(self, jobs):
- if jobs:
- # use inline copy to make sure the list is still accessible while sorting
- jobs[:] = sorted(jobs, key=self.sortKey)
- return jobs[0]
- else:
- return None
-
+ def __init__(self):
+ self.rules = []
+ self.priorities = []
+ self.exceptions = []
+
+ def addRule(self, rule):
+ self.rules.append(rule)
+
+ def addPriority(self, priority):
+ self.priorities.append(priority)
+
+ def addException(self, exception):
+ self.exceptions.append(exception)
+
+ def applyRules(self, job):
+ return sum((rule.rate(job) for rule in self.rules))
+
+ def applyPriorities(self, job):
+ for priority in self.priorities:
+ if priority.test(job):
+ return True # priorities are first
+
+ return False
+
+ def applyExceptions(self, job):
+ for exception in self.exceptions:
+ if exception.test(job):
+ return True # exceptions are last
+
+ return False
+
+ def sortKey(self, job):
+ return (1 if self.applyExceptions(job) else 0, # exceptions after
+ 0 if self.applyPriorities(job) else 1, # priorities first
+ self.applyRules(job))
+
+ def balance(self, jobs):
+ if jobs:
+ # use inline copy to make sure the list is still accessible while sorting
+ jobs[:] = sorted(jobs, key=self.sortKey)
+ return jobs[0]
+ else:
+ return None
+
# ==========================
class RatingUsage(RatingRule):
- def __str__(self):
- return "Usage rating"
-
- def rate(self, job):
- # less usage is better
- return job.usage / job.priority
+ def __str__(self):
+ return "Usage rating"
+
+ def rate(self, job):
+ # less usage is better
+ return job.usage / job.priority
class RatingUsageByCategory(RatingRule):
- def __str__(self):
- return "Usage per category rating"
-
- def __init__(self, get_jobs):
- self.getJobs = get_jobs
- def rate(self, job):
- total_category_usage = sum([j.usage for j in self.getJobs() if j.category == job.category])
- maximum_priority = max([j.priority for j in self.getJobs() if j.category == job.category])
-
- # less usage is better
- return total_category_usage / maximum_priority
-
+ def __str__(self):
+ return "Usage per category rating"
+
+ def __init__(self, get_jobs):
+ self.getJobs = get_jobs
+ def rate(self, job):
+ total_category_usage = sum([j.usage for j in self.getJobs() if j.category == job.category])
+ maximum_priority = max([j.priority for j in self.getJobs() if j.category == job.category])
+
+ # less usage is better
+ return total_category_usage / maximum_priority
+
class NewJobPriority(PriorityRule):
- def str_limit(self):
- return "less than %i frame%s done" % (self.limit, "s" if self.limit > 1 else "")
-
- def __str__(self):
- return "Priority to new jobs"
-
- def __init__(self, limit = 1):
- self.limit = limit
-
- def test(self, job):
- return job.countFrames(status = DONE) < self.limit
+ def str_limit(self):
+ return "less than %i frame%s done" % (self.limit, "s" if self.limit > 1 else "")
+
+ def __str__(self):
+ return "Priority to new jobs"
+
+ def __init__(self, limit = 1):
+ self.limit = limit
+
+ def test(self, job):
+ return job.countFrames(status = DONE) < self.limit
class MinimumTimeBetweenDispatchPriority(PriorityRule):
- def str_limit(self):
- return "more than %i minute%s since last" % (self.limit, "s" if self.limit > 1 else "")
-
- def __str__(self):
- return "Priority to jobs that haven't been dispatched recently"
-
- def __init__(self, limit = 10):
- self.limit = limit
-
- def test(self, job):
- return job.countFrames(status = DISPATCHED) == 0 and (time.time() - job.last_dispatched) / 60 > self.limit
+ def str_limit(self):
+ return "more than %i minute%s since last" % (self.limit, "s" if self.limit > 1 else "")
+
+ def __str__(self):
+ return "Priority to jobs that haven't been dispatched recently"
+
+ def __init__(self, limit = 10):
+ self.limit = limit
+
+ def test(self, job):
+ return job.countFrames(status = DISPATCHED) == 0 and (time.time() - job.last_dispatched) / 60 > self.limit
class ExcludeQueuedEmptyJob(ExclusionRule):
- def __str__(self):
- return "Exclude queued and empty jobs"
-
- def test(self, job):
- return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0
-
+ def __str__(self):
+ return "Exclude queued and empty jobs"
+
+ def test(self, job):
+ return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0
+
class ExcludeSlavesLimit(ExclusionRule):
- def str_limit(self):
- return "more than %.0f%% of all slaves" % (self.limit * 100)
-
- def __str__(self):
- return "Exclude jobs that would use too many slaves"
-
- def __init__(self, count_jobs, count_slaves, limit = 0.75):
- self.count_jobs = count_jobs
- self.count_slaves = count_slaves
- self.limit = limit
-
- def test(self, job):
- return not ( self.count_jobs() == 1 or self.count_slaves() <= 1 or float(job.countSlaves() + 1) / self.count_slaves() <= self.limit )
+ def str_limit(self):
+ return "more than %.0f%% of all slaves" % (self.limit * 100)
+
+ def __str__(self):
+ return "Exclude jobs that would use too many slaves"
+
+ def __init__(self, count_jobs, count_slaves, limit = 0.75):
+ self.count_jobs = count_jobs
+ self.count_slaves = count_slaves
+ self.limit = limit
+
+ def test(self, job):
+ return not ( self.count_jobs() == 1 or self.count_slaves() <= 1 or float(job.countSlaves() + 1) / self.count_slaves() <= self.limit )
diff --git a/release/scripts/io/netrender/client.py b/release/scripts/io/netrender/client.py
index bbbb9f26051..d71d6a072ba 100644
--- a/release/scripts/io/netrender/client.py
+++ b/release/scripts/io/netrender/client.py
@@ -4,12 +4,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
@@ -28,253 +28,253 @@ import netrender.master as master
from netrender.utils import *
def addFluidFiles(job, path):
- if os.path.exists(path):
- pattern = re.compile("fluidsurface_(final|preview)_([0-9]+)\.(bobj|bvel)\.gz")
-
- for fluid_file in sorted(os.listdir(path)):
- match = pattern.match(fluid_file)
-
- if match:
- # fluid frames starts at 0, which explains the +1
- # This is stupid
- current_frame = int(match.groups()[1]) + 1
- job.addFile(path + fluid_file, current_frame, current_frame)
+ if os.path.exists(path):
+ pattern = re.compile("fluidsurface_(final|preview)_([0-9]+)\.(bobj|bvel)\.gz")
+
+ for fluid_file in sorted(os.listdir(path)):
+ match = pattern.match(fluid_file)
+
+ if match:
+ # fluid frames starts at 0, which explains the +1
+ # This is stupid
+ current_frame = int(match.groups()[1]) + 1
+ job.addFile(path + fluid_file, current_frame, current_frame)
def addPointCache(job, ob, point_cache, default_path):
- if not point_cache.disk_cache:
- return
-
-
- name = point_cache.name
- if name == "":
- name = "".join(["%02X" % ord(c) for c in ob.name])
-
- cache_path = bpy.utils.expandpath(point_cache.filepath) if point_cache.external else default_path
-
- index = "%02i" % point_cache.index
-
- if os.path.exists(cache_path):
- pattern = re.compile(name + "_([0-9]+)_" + index + "\.bphys")
-
- cache_files = []
-
- for cache_file in sorted(os.listdir(cache_path)):
- match = pattern.match(cache_file)
-
- if match:
- cache_frame = int(match.groups()[0])
- cache_files.append((cache_frame, cache_file))
-
- cache_files.sort()
-
- if len(cache_files) == 1:
- cache_frame, cache_file = cache_files[0]
- job.addFile(cache_path + cache_file, cache_frame, cache_frame)
- else:
- for i in range(len(cache_files)):
- current_item = cache_files[i]
- next_item = cache_files[i+1] if i + 1 < len(cache_files) else None
- previous_item = cache_files[i - 1] if i > 0 else None
-
- current_frame, current_file = current_item
-
- if not next_item and not previous_item:
- job.addFile(cache_path + current_file, current_frame, current_frame)
- elif next_item and not previous_item:
- next_frame = next_item[0]
- job.addFile(cache_path + current_file, current_frame, next_frame - 1)
- elif not next_item and previous_item:
- previous_frame = previous_item[0]
- job.addFile(cache_path + current_file, previous_frame + 1, current_frame)
- else:
- next_frame = next_item[0]
- previous_frame = previous_item[0]
- job.addFile(cache_path + current_file, previous_frame + 1, next_frame - 1)
-
+ if not point_cache.disk_cache:
+ return
+
+
+ name = point_cache.name
+ if name == "":
+ name = "".join(["%02X" % ord(c) for c in ob.name])
+
+ cache_path = bpy.utils.expandpath(point_cache.filepath) if point_cache.external else default_path
+
+ index = "%02i" % point_cache.index
+
+ if os.path.exists(cache_path):
+ pattern = re.compile(name + "_([0-9]+)_" + index + "\.bphys")
+
+ cache_files = []
+
+ for cache_file in sorted(os.listdir(cache_path)):
+ match = pattern.match(cache_file)
+
+ if match:
+ cache_frame = int(match.groups()[0])
+ cache_files.append((cache_frame, cache_file))
+
+ cache_files.sort()
+
+ if len(cache_files) == 1:
+ cache_frame, cache_file = cache_files[0]
+ job.addFile(cache_path + cache_file, cache_frame, cache_frame)
+ else:
+ for i in range(len(cache_files)):
+ current_item = cache_files[i]
+ next_item = cache_files[i+1] if i + 1 < len(cache_files) else None
+ previous_item = cache_files[i - 1] if i > 0 else None
+
+ current_frame, current_file = current_item
+
+ if not next_item and not previous_item:
+ job.addFile(cache_path + current_file, current_frame, current_frame)
+ elif next_item and not previous_item:
+ next_frame = next_item[0]
+ job.addFile(cache_path + current_file, current_frame, next_frame - 1)
+ elif not next_item and previous_item:
+ previous_frame = previous_item[0]
+ job.addFile(cache_path + current_file, previous_frame + 1, current_frame)
+ else:
+ next_frame = next_item[0]
+ previous_frame = previous_item[0]
+ job.addFile(cache_path + current_file, previous_frame + 1, next_frame - 1)
+
def clientSendJob(conn, scene, anim = False):
- netsettings = scene.network_render
- job = netrender.model.RenderJob()
-
- if anim:
- for f in range(scene.start_frame, scene.end_frame + 1):
- job.addFrame(f)
- else:
- job.addFrame(scene.current_frame)
-
- filename = bpy.data.filename
- job.addFile(filename)
-
- job_name = netsettings.job_name
- path, name = os.path.split(filename)
- if job_name == "[default]":
- job_name = name
-
- ###########################
- # LIBRARIES
- ###########################
- for lib in bpy.data.libraries:
- job.addFile(bpy.utils.expandpath(lib.filename))
-
- ###########################
- # IMAGES
- ###########################
- for image in bpy.data.images:
- if image.source == "FILE" and not image.packed_file:
- job.addFile(bpy.utils.expandpath(image.filename))
-
- ###########################
- # FLUID + POINT CACHE
- ###########################
- root, ext = os.path.splitext(name)
- default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
-
- for object in bpy.data.objects:
- for modifier in object.modifiers:
- if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
- addFluidFiles(job, bpy.utils.expandpath(modifier.settings.path))
- elif modifier.type == "CLOTH":
- addPointCache(job, object, modifier.point_cache, default_path)
- elif modifier.type == "SOFT_BODY":
- addPointCache(job, object, modifier.point_cache, default_path)
- elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
- addPointCache(job, object, modifier.domain_settings.point_cache_low, default_path)
- if modifier.domain_settings.highres:
- addPointCache(job, object, modifier.domain_settings.point_cache_high, default_path)
-
- # particles modifier are stupid and don't contain data
- # we have to go through the object property
- for psys in object.particle_systems:
- addPointCache(job, object, psys.point_cache, default_path)
-
- #print(job.files)
-
- job.name = job_name
- job.category = netsettings.job_category
-
- for slave in netrender.blacklist:
- job.blacklist.append(slave.id)
-
- job.chunks = netsettings.chunks
- job.priority = netsettings.priority
-
- # try to send path first
- conn.request("POST", "/job", repr(job.serialize()))
- response = conn.getresponse()
-
- job_id = response.getheader("job-id")
-
- # if not ACCEPTED (but not processed), send files
- if response.status == http.client.ACCEPTED:
- for rfile in job.files:
- f = open(rfile.filepath, "rb")
- conn.request("PUT", fileURL(job_id, rfile.index), f)
- f.close()
- response = conn.getresponse()
-
- # server will reply with ACCEPTED until all files are found
-
- return job_id
+ netsettings = scene.network_render
+ job = netrender.model.RenderJob()
+
+ if anim:
+ for f in range(scene.start_frame, scene.end_frame + 1):
+ job.addFrame(f)
+ else:
+ job.addFrame(scene.current_frame)
+
+ filename = bpy.data.filename
+ job.addFile(filename)
+
+ job_name = netsettings.job_name
+ path, name = os.path.split(filename)
+ if job_name == "[default]":
+ job_name = name
+
+ ###########################
+ # LIBRARIES
+ ###########################
+ for lib in bpy.data.libraries:
+ job.addFile(bpy.utils.expandpath(lib.filename))
+
+ ###########################
+ # IMAGES
+ ###########################
+ for image in bpy.data.images:
+ if image.source == "FILE" and not image.packed_file:
+ job.addFile(bpy.utils.expandpath(image.filename))
+
+ ###########################
+ # FLUID + POINT CACHE
+ ###########################
+ root, ext = os.path.splitext(name)
+ default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
+
+ for object in bpy.data.objects:
+ for modifier in object.modifiers:
+ if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
+ addFluidFiles(job, bpy.utils.expandpath(modifier.settings.path))
+ elif modifier.type == "CLOTH":
+ addPointCache(job, object, modifier.point_cache, default_path)
+ elif modifier.type == "SOFT_BODY":
+ addPointCache(job, object, modifier.point_cache, default_path)
+ elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
+ addPointCache(job, object, modifier.domain_settings.point_cache_low, default_path)
+ if modifier.domain_settings.highres:
+ addPointCache(job, object, modifier.domain_settings.point_cache_high, default_path)
+
+ # particles modifier are stupid and don't contain data
+ # we have to go through the object property
+ for psys in object.particle_systems:
+ addPointCache(job, object, psys.point_cache, default_path)
+
+ #print(job.files)
+
+ job.name = job_name
+ job.category = netsettings.job_category
+
+ for slave in netrender.blacklist:
+ job.blacklist.append(slave.id)
+
+ job.chunks = netsettings.chunks
+ job.priority = netsettings.priority
+
+ # try to send path first
+ conn.request("POST", "/job", repr(job.serialize()))
+ response = conn.getresponse()
+
+ job_id = response.getheader("job-id")
+
+ # if not ACCEPTED (but not processed), send files
+ if response.status == http.client.ACCEPTED:
+ for rfile in job.files:
+ f = open(rfile.filepath, "rb")
+ conn.request("PUT", fileURL(job_id, rfile.index), f)
+ f.close()
+ response = conn.getresponse()
+
+ # server will reply with ACCEPTED until all files are found
+
+ return job_id
def requestResult(conn, job_id, frame):
- conn.request("GET", renderURL(job_id, frame))
+ conn.request("GET", renderURL(job_id, frame))
@rnaType
class NetworkRenderEngine(bpy.types.RenderEngine):
- bl_idname = 'NET_RENDER'
- bl_label = "Network Render"
- def render(self, scene):
- if scene.network_render.mode == "RENDER_CLIENT":
- self.render_client(scene)
- elif scene.network_render.mode == "RENDER_SLAVE":
- self.render_slave(scene)
- elif scene.network_render.mode == "RENDER_MASTER":
- self.render_master(scene)
- else:
- print("UNKNOWN OPERATION MODE")
-
- def render_master(self, scene):
- netsettings = scene.network_render
-
- 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)
-
-
- def render_slave(self, scene):
- slave.render_slave(self, scene.network_render)
-
- def render_client(self, scene):
- netsettings = scene.network_render
- self.update_stats("", "Network render client initiation")
-
-
- conn = clientConnection(netsettings.server_address, netsettings.server_port)
-
- if conn:
- # Sending file
-
- self.update_stats("", "Network render exporting")
-
- new_job = False
-
- job_id = netsettings.job_id
-
- # reading back result
-
- self.update_stats("", "Network render waiting for results")
-
- requestResult(conn, job_id, scene.current_frame)
- response = conn.getresponse()
-
- if response.status == http.client.NO_CONTENT:
- new_job = True
- netsettings.job_id = clientSendJob(conn, scene)
- job_id = netsettings.job_id
-
- requestResult(conn, job_id, scene.current_frame)
- response = conn.getresponse()
-
- while response.status == http.client.ACCEPTED and not self.test_break():
- time.sleep(1)
- requestResult(conn, job_id, scene.current_frame)
- response = conn.getresponse()
-
- # cancel new jobs (animate on network) on break
- if self.test_break() and new_job:
- conn.request("POST", cancelURL(job_id))
- response = conn.getresponse()
- print( response.status, response.reason )
- netsettings.job_id = 0
-
- if response.status != http.client.OK:
- conn.close()
- return
-
- r = scene.render_data
- x= int(r.resolution_x*r.resolution_percentage*0.01)
- y= int(r.resolution_y*r.resolution_percentage*0.01)
-
- f = open(netsettings.path + "output.exr", "wb")
- buf = response.read(1024)
-
- while buf:
- f.write(buf)
- buf = response.read(1024)
-
- f.close()
-
- result = self.begin_result(0, 0, x, y)
- result.load_from_file(netsettings.path + "output.exr", 0, 0)
- self.end_result(result)
-
- conn.close()
+ bl_idname = 'NET_RENDER'
+ bl_label = "Network Render"
+ def render(self, scene):
+ if scene.network_render.mode == "RENDER_CLIENT":
+ self.render_client(scene)
+ elif scene.network_render.mode == "RENDER_SLAVE":
+ self.render_slave(scene)
+ elif scene.network_render.mode == "RENDER_MASTER":
+ self.render_master(scene)
+ else:
+ print("UNKNOWN OPERATION MODE")
+
+ def render_master(self, scene):
+ netsettings = scene.network_render
+
+ 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)
+
+
+ def render_slave(self, scene):
+ slave.render_slave(self, scene.network_render)
+
+ def render_client(self, scene):
+ netsettings = scene.network_render
+ self.update_stats("", "Network render client initiation")
+
+
+ conn = clientConnection(netsettings.server_address, netsettings.server_port)
+
+ if conn:
+ # Sending file
+
+ self.update_stats("", "Network render exporting")
+
+ new_job = False
+
+ job_id = netsettings.job_id
+
+ # reading back result
+
+ self.update_stats("", "Network render waiting for results")
+
+ requestResult(conn, job_id, scene.current_frame)
+ response = conn.getresponse()
+
+ if response.status == http.client.NO_CONTENT:
+ new_job = True
+ netsettings.job_id = clientSendJob(conn, scene)
+ job_id = netsettings.job_id
+
+ requestResult(conn, job_id, scene.current_frame)
+ response = conn.getresponse()
+
+ while response.status == http.client.ACCEPTED and not self.test_break():
+ time.sleep(1)
+ requestResult(conn, job_id, scene.current_frame)
+ response = conn.getresponse()
+
+ # cancel new jobs (animate on network) on break
+ if self.test_break() and new_job:
+ conn.request("POST", cancelURL(job_id))
+ response = conn.getresponse()
+ print( response.status, response.reason )
+ netsettings.job_id = 0
+
+ if response.status != http.client.OK:
+ conn.close()
+ return
+
+ r = scene.render_data
+ x= int(r.resolution_x*r.resolution_percentage*0.01)
+ y= int(r.resolution_y*r.resolution_percentage*0.01)
+
+ f = open(netsettings.path + "output.exr", "wb")
+ buf = response.read(1024)
+
+ while buf:
+ f.write(buf)
+ buf = response.read(1024)
+
+ f.close()
+
+ result = self.begin_result(0, 0, x, y)
+ result.load_from_file(netsettings.path + "output.exr", 0, 0)
+ self.end_result(result)
+
+ conn.close()
def compatible(module):
- module = __import__(module)
- for subclass in module.__dict__.values():
- try: subclass.COMPAT_ENGINES.add('NET_RENDER')
- except: pass
- del module
+ module = __import__(module)
+ for subclass in module.__dict__.values():
+ try: subclass.COMPAT_ENGINES.add('NET_RENDER')
+ except: pass
+ del module
compatible("properties_render")
compatible("properties_world")
diff --git a/release/scripts/io/netrender/master.py b/release/scripts/io/netrender/master.py
index 83980c95da7..a33606d19a0 100644
--- a/release/scripts/io/netrender/master.py
+++ b/release/scripts/io/netrender/master.py
@@ -4,12 +4,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
@@ -26,135 +26,135 @@ import netrender.balancing
import netrender.master_html
class MRenderFile(netrender.model.RenderFile):
- def __init__(self, filepath, index, start, end):
- super().__init__(filepath, index, start, end)
- self.found = False
-
- def test(self):
- self.found = os.path.exists(self.filepath)
- return self.found
+ def __init__(self, filepath, index, start, end):
+ super().__init__(filepath, index, start, end)
+ self.found = False
+
+ def test(self):
+ self.found = os.path.exists(self.filepath)
+ return self.found
class MRenderSlave(netrender.model.RenderSlave):
- def __init__(self, name, address, stats):
- super().__init__()
- self.id = hashlib.md5(bytes(repr(name) + repr(address), encoding='utf8')).hexdigest()
- self.name = name
- self.address = address
- self.stats = stats
- self.last_seen = time.time()
-
- self.job = None
- self.job_frames = []
-
- netrender.model.RenderSlave._slave_map[self.id] = self
-
- def seen(self):
- self.last_seen = time.time()
-
- def finishedFrame(self, frame_number):
- self.job_frames.remove(frame_number)
- if not self.job_frames:
- self.job = None
+ def __init__(self, name, address, stats):
+ super().__init__()
+ self.id = hashlib.md5(bytes(repr(name) + repr(address), encoding='utf8')).hexdigest()
+ self.name = name
+ self.address = address
+ self.stats = stats
+ self.last_seen = time.time()
+
+ self.job = None
+ self.job_frames = []
+
+ netrender.model.RenderSlave._slave_map[self.id] = self
+
+ def seen(self):
+ self.last_seen = time.time()
+
+ def finishedFrame(self, frame_number):
+ self.job_frames.remove(frame_number)
+ if not self.job_frames:
+ self.job = None
class MRenderJob(netrender.model.RenderJob):
- def __init__(self, job_id, job_info):
- super().__init__(job_info)
- self.id = job_id
- self.last_dispatched = time.time()
-
- # force one chunk for process jobs
- if self.type == netrender.model.JOB_PROCESS:
- self.chunks = 1
-
- # Force WAITING status on creation
- self.status = JOB_WAITING
-
- # special server properties
- self.last_update = 0
- self.save_path = ""
- self.files = [MRenderFile(rfile.filepath, rfile.index, rfile.start, rfile.end) for rfile in job_info.files]
-
- def save(self):
- if self.save_path:
- f = open(self.save_path + "job.txt", "w")
- f.write(repr(self.serialize()))
- f.close()
-
- def edit(self, info_map):
- if "status" in info_map:
- self.status = info_map["status"]
-
- if "priority" in info_map:
- self.priority = info_map["priority"]
-
- if "chunks" in info_map:
- self.chunks = info_map["chunks"]
-
- def testStart(self):
- for f in self.files:
- if not f.test():
- return False
-
- 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 addLog(self, frames):
- log_name = "_".join(("%04d" % f for f in frames)) + ".log"
- log_path = self.save_path + log_name
-
- for number in frames:
- frame = self[number]
- if frame:
- frame.log_path = log_path
-
- def addFrame(self, frame_number, command):
- frame = MRenderFrame(frame_number, command)
- self.frames.append(frame)
- return frame
-
- def reset(self, all):
- for f in self.frames:
- f.reset(all)
-
- def getFrames(self):
- frames = []
- for f in self.frames:
- if f.status == QUEUED:
- self.last_dispatched = time.time()
- frames.append(f)
- if len(frames) >= self.chunks:
- break
-
- return frames
+ def __init__(self, job_id, job_info):
+ super().__init__(job_info)
+ self.id = job_id
+ self.last_dispatched = time.time()
+
+ # force one chunk for process jobs
+ if self.type == netrender.model.JOB_PROCESS:
+ self.chunks = 1
+
+ # Force WAITING status on creation
+ self.status = JOB_WAITING
+
+ # special server properties
+ self.last_update = 0
+ self.save_path = ""
+ self.files = [MRenderFile(rfile.filepath, rfile.index, rfile.start, rfile.end) for rfile in job_info.files]
+
+ def save(self):
+ if self.save_path:
+ f = open(self.save_path + "job.txt", "w")
+ f.write(repr(self.serialize()))
+ f.close()
+
+ def edit(self, info_map):
+ if "status" in info_map:
+ self.status = info_map["status"]
+
+ if "priority" in info_map:
+ self.priority = info_map["priority"]
+
+ if "chunks" in info_map:
+ self.chunks = info_map["chunks"]
+
+ def testStart(self):
+ for f in self.files:
+ if not f.test():
+ return False
+
+ 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 addLog(self, frames):
+ log_name = "_".join(("%04d" % f for f in frames)) + ".log"
+ log_path = self.save_path + log_name
+
+ for number in frames:
+ frame = self[number]
+ if frame:
+ frame.log_path = log_path
+
+ def addFrame(self, frame_number, command):
+ frame = MRenderFrame(frame_number, command)
+ self.frames.append(frame)
+ return frame
+
+ def reset(self, all):
+ for f in self.frames:
+ f.reset(all)
+
+ def getFrames(self):
+ frames = []
+ for f in self.frames:
+ if f.status == QUEUED:
+ self.last_dispatched = time.time()
+ frames.append(f)
+ if len(frames) >= self.chunks:
+ break
+
+ return frames
class MRenderFrame(netrender.model.RenderFrame):
- def __init__(self, frame, command):
- super().__init__()
- self.number = frame
- self.slave = None
- self.time = 0
- self.status = QUEUED
- self.command = command
-
- self.log_path = None
-
- def reset(self, all):
- if all or self.status == ERROR:
- self.log_path = None
- self.slave = None
- self.time = 0
- self.status = QUEUED
+ def __init__(self, frame, command):
+ super().__init__()
+ self.number = frame
+ self.slave = None
+ self.time = 0
+ self.status = QUEUED
+ self.command = command
+
+ self.log_path = None
+
+ def reset(self, all):
+ if all or self.status == ERROR:
+ self.log_path = None
+ self.slave = None
+ self.time = 0
+ self.status = QUEUED
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
@@ -169,692 +169,692 @@ cancel_pattern = re.compile("/cancel_([a-zA-Z0-9]+)")
edit_pattern = re.compile("/edit_([a-zA-Z0-9]+)")
class RenderHandler(http.server.BaseHTTPRequestHandler):
- def send_head(self, code = http.client.OK, headers = {}, content = "application/octet-stream"):
- self.send_response(code)
- self.send_header("Content-type", content)
-
- for key, value in headers.items():
- self.send_header(key, value)
-
- self.end_headers()
-
- def do_HEAD(self):
-
- if self.path == "/status":
- job_id = self.headers.get('job-id', "")
- job_frame = int(self.headers.get('job-frame', -1))
-
- job = self.server.getJobID(job_id)
- if job:
- frame = job[job_frame]
-
-
- if frame:
- self.send_head(http.client.OK)
- else:
- # no such frame
- self.send_head(http.client.NO_CONTENT)
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
-
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-
- def do_GET(self):
-
- if self.path == "/version":
- self.send_head()
- self.server.stats("", "Version check")
- self.wfile.write(VERSION)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/render"):
- match = render_pattern.match(self.path)
-
- if match:
- job_id = match.groups()[0]
- frame_number = int(match.groups()[1])
-
- job = self.server.getJobID(job_id)
-
- if job:
- frame = job[frame_number]
-
- if frame:
- if frame.status in (QUEUED, DISPATCHED):
- self.send_head(http.client.ACCEPTED)
- elif frame.status == DONE:
- self.server.stats("", "Sending result to client")
- f = open(job.save_path + "%04d" % frame_number + ".exr", 'rb')
-
- self.send_head()
-
- shutil.copyfileobj(f, self.wfile)
-
- f.close()
- elif frame.status == ERROR:
- self.send_head(http.client.PARTIAL_CONTENT)
- else:
- # no such frame
- self.send_head(http.client.NO_CONTENT)
- 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.startswith("/log"):
- match = log_pattern.match(self.path)
-
- if match:
- job_id = match.groups()[0]
- frame_number = int(match.groups()[1])
-
- job = self.server.getJobID(job_id)
-
- if job:
- frame = job[frame_number]
-
- if frame:
- if not frame.log_path or frame.status in (QUEUED, DISPATCHED):
- self.send_head(http.client.PROCESSING)
- else:
- self.server.stats("", "Sending log to client")
- f = open(frame.log_path, 'rb')
-
- self.send_head(content = "text/plain")
-
- shutil.copyfileobj(f, self.wfile)
-
- f.close()
- else:
- # no such frame
- self.send_head(http.client.NO_CONTENT)
- 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 == "/status":
- job_id = self.headers.get('job-id', "")
- job_frame = int(self.headers.get('job-frame', -1))
-
- if job_id:
-
- job = self.server.getJobID(job_id)
- if job:
- if job_frame != -1:
- frame = job[frame]
-
- if frame:
- message = frame.serialize()
- else:
- # no such frame
- self.send_heat(http.client.NO_CONTENT)
- return
- else:
- message = job.serialize()
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
- return
- else: # status of all jobs
- message = []
-
- for job in self.server:
- message.append(job.serialize())
-
-
- self.server.stats("", "Sending status")
- self.send_head()
- self.wfile.write(bytes(repr(message), encoding='utf8'))
-
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/job":
- self.server.balance()
-
- slave_id = self.headers['slave-id']
-
- slave = self.server.getSeenSlave(slave_id)
-
- if slave: # only if slave id is valid
- job, frames = self.server.newDispatch(slave_id)
-
- if job and frames:
- for f in frames:
- print("dispatch", f.number)
- f.status = DISPATCHED
- f.slave = slave
-
- slave.job = job
- slave.job_frames = [f.number for f in frames]
-
- self.send_head(headers={"job-id": job.id})
-
- message = job.serialize(frames)
-
- self.wfile.write(bytes(repr(message), encoding='utf8'))
-
- self.server.stats("", "Sending job to slave")
- else:
- # no job available, return error code
- slave.job = None
- slave.job_frames = []
-
- self.send_head(http.client.ACCEPTED)
- else: # invalid slave id
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/file"):
- match = file_pattern.match(self.path)
-
- if match:
- slave_id = self.headers['slave-id']
- slave = self.server.getSeenSlave(slave_id)
-
- if not slave:
- # invalid slave id
- print("invalid slave id")
-
- job_id = match.groups()[0]
- file_index = int(match.groups()[1])
-
- job = self.server.getJobID(job_id)
-
- if job:
- render_file = job.files[file_index]
-
- if render_file:
- self.server.stats("", "Sending file to slave")
- f = open(render_file.filepath, 'rb')
-
- self.send_head()
- shutil.copyfileobj(f, self.wfile)
-
- f.close()
- else:
- # no such file
- self.send_head(http.client.NO_CONTENT)
- 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 == "/slaves":
- message = []
-
- self.server.stats("", "Sending slaves status")
-
- for slave in self.server.slaves:
- message.append(slave.serialize())
-
- self.send_head()
-
- self.wfile.write(bytes(repr(message), encoding='utf8'))
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- else:
- # hand over the rest to the html section
- netrender.master_html.get(self)
-
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- def do_POST(self):
-
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- if self.path == "/job":
-
- length = int(self.headers['content-length'])
-
- job_info = netrender.model.RenderJob.materialize(eval(str(self.rfile.read(length), encoding='utf8')))
-
- job_id = self.server.nextJobID()
-
- job = MRenderJob(job_id, job_info)
-
- for frame in job_info.frames:
- frame = job.addFrame(frame.number, frame.command)
-
- self.server.addJob(job)
-
- headers={"job-id": job_id}
-
- if job.testStart():
- self.server.stats("", "New job, started")
- self.send_head(headers=headers)
- else:
- self.server.stats("", "New job, missing files (%i total)" % len(job.files))
- self.send_head(http.client.ACCEPTED, headers=headers)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/edit"):
- match = edit_pattern.match(self.path)
-
- if match:
- job_id = match.groups()[0]
-
- job = self.server.getJobID(job_id)
-
- if job:
- length = int(self.headers['content-length'])
- info_map = eval(str(self.rfile.read(length), encoding='utf8'))
-
- job.edit(info_map)
- 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.startswith("/cancel"):
- match = cancel_pattern.match(self.path)
-
- if match:
- job_id = match.groups()[0]
-
- job = self.server.getJobID(job_id)
-
- if job:
- self.server.stats("", "Cancelling job")
- self.server.removeJob(job)
- 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
- self.server.stats("", "Clearing jobs")
- self.server.clear()
-
- self.send_head()
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/reset"):
- match = reset_pattern.match(self.path)
-
- if match:
- all = match.groups()[0] == 'all'
- job_id = match.groups()[1]
- job_frame = int(match.groups()[2])
-
- job = self.server.getJobID(job_id)
-
- if job:
- if job_frame != 0:
-
- frame = job[job_frame]
- if frame:
- self.server.stats("", "Reset job frame")
- frame.reset(all)
- self.send_head()
- else:
- # no such frame
- self.send_head(http.client.NO_CONTENT)
-
- else:
- self.server.stats("", "Reset job")
- job.reset(all)
- self.send_head()
-
- else: # job not found
- self.send_head(http.client.NO_CONTENT)
- else: # invalid url
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/slave":
- length = int(self.headers['content-length'])
- job_frame_string = self.headers['job-frame']
-
- self.server.stats("", "New slave connected")
-
- slave_info = netrender.model.RenderSlave.materialize(eval(str(self.rfile.read(length), encoding='utf8')), cache = False)
-
- slave_id = self.server.addSlave(slave_info.name, self.client_address, slave_info.stats)
-
- self.send_head(headers = {"slave-id": slave_id})
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/log":
- length = int(self.headers['content-length'])
-
- log_info = netrender.model.LogFile.materialize(eval(str(self.rfile.read(length), encoding='utf8')))
-
- slave_id = log_info.slave_id
-
- slave = self.server.getSeenSlave(slave_id)
-
- if slave: # only if slave id is valid
- job = self.server.getJobID(log_info.job_id)
-
- if job:
- self.server.stats("", "Log announcement")
- job.addLog(log_info.frames)
- self.send_head(http.client.OK)
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
- else: # invalid slave id
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- def do_PUT(self):
-
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- if self.path.startswith("/file"):
- match = file_pattern.match(self.path)
-
- if match:
- self.server.stats("", "Receiving job")
-
- length = int(self.headers['content-length'])
- job_id = match.groups()[0]
- file_index = int(match.groups()[1])
-
- job = self.server.getJobID(job_id)
-
- if job:
-
- render_file = job.files[file_index]
-
- if render_file:
- main_file = job.files[0].filepath # filename of the first file
-
- main_path, main_name = os.path.split(main_file)
-
- if file_index > 0:
- file_path = prefixPath(job.save_path, render_file.filepath, main_path)
- else:
- file_path = job.save_path + main_name
-
- buf = self.rfile.read(length)
-
- # add same temp file + renames as slave
-
- f = open(file_path, "wb")
- f.write(buf)
- f.close()
- del buf
-
- render_file.filepath = file_path # set the new path
-
- if job.testStart():
- self.server.stats("", "File upload, starting job")
- self.send_head(http.client.OK)
- else:
- self.server.stats("", "File upload, file missings")
- self.send_head(http.client.ACCEPTED)
- else: # invalid file
- print("file not found", job_id, file_index)
- self.send_head(http.client.NO_CONTENT)
- else: # job not found
- print("job not found", job_id, file_index)
- self.send_head(http.client.NO_CONTENT)
- else: # invalid url
- print("no match")
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/render":
- self.server.stats("", "Receiving render result")
-
- # need some message content here or the slave doesn't like it
- self.wfile.write(bytes("foo", encoding='utf8'))
-
- slave_id = self.headers['slave-id']
-
- slave = self.server.getSeenSlave(slave_id)
-
- if slave: # only if slave id is valid
- job_id = self.headers['job-id']
-
- job = self.server.getJobID(job_id)
-
- if job:
- job_frame = int(self.headers['job-frame'])
- job_result = int(self.headers['job-result'])
- job_time = float(self.headers['job-time'])
-
- frame = job[job_frame]
-
- if frame:
- if job.type == netrender.model.JOB_BLENDER:
- if job_result == DONE:
- length = int(self.headers['content-length'])
- buf = self.rfile.read(length)
- f = open(job.save_path + "%04d" % job_frame + ".exr", 'wb')
- f.write(buf)
- f.close()
-
- del buf
- elif job_result == ERROR:
- # blacklist slave on this job on error
- # slaves might already be in blacklist if errors on the whole chunk
- if not slave.id in job.blacklist:
- job.blacklist.append(slave.id)
-
- self.server.stats("", "Receiving result")
-
- slave.finishedFrame(job_frame)
-
- frame.status = job_result
- frame.time = job_time
-
- job.testFinished()
-
- self.send_head()
- else: # frame not found
- self.send_head(http.client.NO_CONTENT)
- else: # job not found
- self.send_head(http.client.NO_CONTENT)
- else: # invalid slave id
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/log"):
- self.server.stats("", "Receiving log file")
-
- match = log_pattern.match(self.path)
-
- if match:
- job_id = match.groups()[0]
-
- job = self.server.getJobID(job_id)
-
- if job:
- job_frame = int(match.groups()[1])
-
- frame = job[job_frame]
-
- if frame and frame.log_path:
- length = int(self.headers['content-length'])
- buf = self.rfile.read(length)
- f = open(frame.log_path, 'ab')
- f.write(buf)
- f.close()
-
- del buf
-
- self.server.getSeenSlave(self.headers['slave-id'])
-
- self.send_head()
- else: # frame not found
- self.send_head(http.client.NO_CONTENT)
- else: # job not found
- self.send_head(http.client.NO_CONTENT)
- else: # invalid url
- self.send_head(http.client.NO_CONTENT)
+ def send_head(self, code = http.client.OK, headers = {}, content = "application/octet-stream"):
+ self.send_response(code)
+ self.send_header("Content-type", content)
+
+ for key, value in headers.items():
+ self.send_header(key, value)
+
+ self.end_headers()
+
+ def do_HEAD(self):
+
+ if self.path == "/status":
+ job_id = self.headers.get('job-id', "")
+ job_frame = int(self.headers.get('job-frame', -1))
+
+ job = self.server.getJobID(job_id)
+ if job:
+ frame = job[job_frame]
+
+
+ if frame:
+ self.send_head(http.client.OK)
+ else:
+ # no such frame
+ self.send_head(http.client.NO_CONTENT)
+ else:
+ # no such job id
+ self.send_head(http.client.NO_CONTENT)
+
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+ def do_GET(self):
+
+ if self.path == "/version":
+ self.send_head()
+ self.server.stats("", "Version check")
+ self.wfile.write(VERSION)
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ elif self.path.startswith("/render"):
+ match = render_pattern.match(self.path)
+
+ if match:
+ job_id = match.groups()[0]
+ frame_number = int(match.groups()[1])
+
+ job = self.server.getJobID(job_id)
+
+ if job:
+ frame = job[frame_number]
+
+ if frame:
+ if frame.status in (QUEUED, DISPATCHED):
+ self.send_head(http.client.ACCEPTED)
+ elif frame.status == DONE:
+ self.server.stats("", "Sending result to client")
+ f = open(job.save_path + "%04d" % frame_number + ".exr", 'rb')
+
+ self.send_head()
+
+ shutil.copyfileobj(f, self.wfile)
+
+ f.close()
+ elif frame.status == ERROR:
+ self.send_head(http.client.PARTIAL_CONTENT)
+ else:
+ # no such frame
+ self.send_head(http.client.NO_CONTENT)
+ 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.startswith("/log"):
+ match = log_pattern.match(self.path)
+
+ if match:
+ job_id = match.groups()[0]
+ frame_number = int(match.groups()[1])
+
+ job = self.server.getJobID(job_id)
+
+ if job:
+ frame = job[frame_number]
+
+ if frame:
+ if not frame.log_path or frame.status in (QUEUED, DISPATCHED):
+ self.send_head(http.client.PROCESSING)
+ else:
+ self.server.stats("", "Sending log to client")
+ f = open(frame.log_path, 'rb')
+
+ self.send_head(content = "text/plain")
+
+ shutil.copyfileobj(f, self.wfile)
+
+ f.close()
+ else:
+ # no such frame
+ self.send_head(http.client.NO_CONTENT)
+ 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 == "/status":
+ job_id = self.headers.get('job-id', "")
+ job_frame = int(self.headers.get('job-frame', -1))
+
+ if job_id:
+
+ job = self.server.getJobID(job_id)
+ if job:
+ if job_frame != -1:
+ frame = job[frame]
+
+ if frame:
+ message = frame.serialize()
+ else:
+ # no such frame
+ self.send_heat(http.client.NO_CONTENT)
+ return
+ else:
+ message = job.serialize()
+ else:
+ # no such job id
+ self.send_head(http.client.NO_CONTENT)
+ return
+ else: # status of all jobs
+ message = []
+
+ for job in self.server:
+ message.append(job.serialize())
+
+
+ self.server.stats("", "Sending status")
+ self.send_head()
+ self.wfile.write(bytes(repr(message), encoding='utf8'))
+
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ elif self.path == "/job":
+ self.server.balance()
+
+ slave_id = self.headers['slave-id']
+
+ slave = self.server.getSeenSlave(slave_id)
+
+ if slave: # only if slave id is valid
+ job, frames = self.server.newDispatch(slave_id)
+
+ if job and frames:
+ for f in frames:
+ print("dispatch", f.number)
+ f.status = DISPATCHED
+ f.slave = slave
+
+ slave.job = job
+ slave.job_frames = [f.number for f in frames]
+
+ self.send_head(headers={"job-id": job.id})
+
+ message = job.serialize(frames)
+
+ self.wfile.write(bytes(repr(message), encoding='utf8'))
+
+ self.server.stats("", "Sending job to slave")
+ else:
+ # no job available, return error code
+ slave.job = None
+ slave.job_frames = []
+
+ self.send_head(http.client.ACCEPTED)
+ else: # invalid slave id
+ self.send_head(http.client.NO_CONTENT)
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ elif self.path.startswith("/file"):
+ match = file_pattern.match(self.path)
+
+ if match:
+ slave_id = self.headers['slave-id']
+ slave = self.server.getSeenSlave(slave_id)
+
+ if not slave:
+ # invalid slave id
+ print("invalid slave id")
+
+ job_id = match.groups()[0]
+ file_index = int(match.groups()[1])
+
+ job = self.server.getJobID(job_id)
+
+ if job:
+ render_file = job.files[file_index]
+
+ if render_file:
+ self.server.stats("", "Sending file to slave")
+ f = open(render_file.filepath, 'rb')
+
+ self.send_head()
+ shutil.copyfileobj(f, self.wfile)
+
+ f.close()
+ else:
+ # no such file
+ self.send_head(http.client.NO_CONTENT)
+ 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 == "/slaves":
+ message = []
+
+ self.server.stats("", "Sending slaves status")
+
+ for slave in self.server.slaves:
+ message.append(slave.serialize())
+
+ self.send_head()
+
+ self.wfile.write(bytes(repr(message), encoding='utf8'))
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ else:
+ # hand over the rest to the html section
+ netrender.master_html.get(self)
+
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ def do_POST(self):
+
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ if self.path == "/job":
+
+ length = int(self.headers['content-length'])
+
+ job_info = netrender.model.RenderJob.materialize(eval(str(self.rfile.read(length), encoding='utf8')))
+
+ job_id = self.server.nextJobID()
+
+ job = MRenderJob(job_id, job_info)
+
+ for frame in job_info.frames:
+ frame = job.addFrame(frame.number, frame.command)
+
+ self.server.addJob(job)
+
+ headers={"job-id": job_id}
+
+ if job.testStart():
+ self.server.stats("", "New job, started")
+ self.send_head(headers=headers)
+ else:
+ self.server.stats("", "New job, missing files (%i total)" % len(job.files))
+ self.send_head(http.client.ACCEPTED, headers=headers)
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ elif self.path.startswith("/edit"):
+ match = edit_pattern.match(self.path)
+
+ if match:
+ job_id = match.groups()[0]
+
+ job = self.server.getJobID(job_id)
+
+ if job:
+ length = int(self.headers['content-length'])
+ info_map = eval(str(self.rfile.read(length), encoding='utf8'))
+
+ job.edit(info_map)
+ 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.startswith("/cancel"):
+ match = cancel_pattern.match(self.path)
+
+ if match:
+ job_id = match.groups()[0]
+
+ job = self.server.getJobID(job_id)
+
+ if job:
+ self.server.stats("", "Cancelling job")
+ self.server.removeJob(job)
+ 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
+ self.server.stats("", "Clearing jobs")
+ self.server.clear()
+
+ self.send_head()
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ elif self.path.startswith("/reset"):
+ match = reset_pattern.match(self.path)
+
+ if match:
+ all = match.groups()[0] == 'all'
+ job_id = match.groups()[1]
+ job_frame = int(match.groups()[2])
+
+ job = self.server.getJobID(job_id)
+
+ if job:
+ if job_frame != 0:
+
+ frame = job[job_frame]
+ if frame:
+ self.server.stats("", "Reset job frame")
+ frame.reset(all)
+ self.send_head()
+ else:
+ # no such frame
+ self.send_head(http.client.NO_CONTENT)
+
+ else:
+ self.server.stats("", "Reset job")
+ job.reset(all)
+ self.send_head()
+
+ else: # job not found
+ self.send_head(http.client.NO_CONTENT)
+ else: # invalid url
+ self.send_head(http.client.NO_CONTENT)
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ elif self.path == "/slave":
+ length = int(self.headers['content-length'])
+ job_frame_string = self.headers['job-frame']
+
+ self.server.stats("", "New slave connected")
+
+ slave_info = netrender.model.RenderSlave.materialize(eval(str(self.rfile.read(length), encoding='utf8')), cache = False)
+
+ slave_id = self.server.addSlave(slave_info.name, self.client_address, slave_info.stats)
+
+ self.send_head(headers = {"slave-id": slave_id})
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ elif self.path == "/log":
+ length = int(self.headers['content-length'])
+
+ log_info = netrender.model.LogFile.materialize(eval(str(self.rfile.read(length), encoding='utf8')))
+
+ slave_id = log_info.slave_id
+
+ slave = self.server.getSeenSlave(slave_id)
+
+ if slave: # only if slave id is valid
+ job = self.server.getJobID(log_info.job_id)
+
+ if job:
+ self.server.stats("", "Log announcement")
+ job.addLog(log_info.frames)
+ self.send_head(http.client.OK)
+ else:
+ # no such job id
+ self.send_head(http.client.NO_CONTENT)
+ else: # invalid slave id
+ self.send_head(http.client.NO_CONTENT)
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ def do_PUT(self):
+
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ if self.path.startswith("/file"):
+ match = file_pattern.match(self.path)
+
+ if match:
+ self.server.stats("", "Receiving job")
+
+ length = int(self.headers['content-length'])
+ job_id = match.groups()[0]
+ file_index = int(match.groups()[1])
+
+ job = self.server.getJobID(job_id)
+
+ if job:
+
+ render_file = job.files[file_index]
+
+ if render_file:
+ main_file = job.files[0].filepath # filename of the first file
+
+ main_path, main_name = os.path.split(main_file)
+
+ if file_index > 0:
+ file_path = prefixPath(job.save_path, render_file.filepath, main_path)
+ else:
+ file_path = job.save_path + main_name
+
+ buf = self.rfile.read(length)
+
+ # add same temp file + renames as slave
+
+ f = open(file_path, "wb")
+ f.write(buf)
+ f.close()
+ del buf
+
+ render_file.filepath = file_path # set the new path
+
+ if job.testStart():
+ self.server.stats("", "File upload, starting job")
+ self.send_head(http.client.OK)
+ else:
+ self.server.stats("", "File upload, file missings")
+ self.send_head(http.client.ACCEPTED)
+ else: # invalid file
+ print("file not found", job_id, file_index)
+ self.send_head(http.client.NO_CONTENT)
+ else: # job not found
+ print("job not found", job_id, file_index)
+ self.send_head(http.client.NO_CONTENT)
+ else: # invalid url
+ print("no match")
+ self.send_head(http.client.NO_CONTENT)
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ elif self.path == "/render":
+ self.server.stats("", "Receiving render result")
+
+ # need some message content here or the slave doesn't like it
+ self.wfile.write(bytes("foo", encoding='utf8'))
+
+ slave_id = self.headers['slave-id']
+
+ slave = self.server.getSeenSlave(slave_id)
+
+ if slave: # only if slave id is valid
+ job_id = self.headers['job-id']
+
+ job = self.server.getJobID(job_id)
+
+ if job:
+ job_frame = int(self.headers['job-frame'])
+ job_result = int(self.headers['job-result'])
+ job_time = float(self.headers['job-time'])
+
+ frame = job[job_frame]
+
+ if frame:
+ if job.type == netrender.model.JOB_BLENDER:
+ if job_result == DONE:
+ length = int(self.headers['content-length'])
+ buf = self.rfile.read(length)
+ f = open(job.save_path + "%04d" % job_frame + ".exr", 'wb')
+ f.write(buf)
+ f.close()
+
+ del buf
+ elif job_result == ERROR:
+ # blacklist slave on this job on error
+ # slaves might already be in blacklist if errors on the whole chunk
+ if not slave.id in job.blacklist:
+ job.blacklist.append(slave.id)
+
+ self.server.stats("", "Receiving result")
+
+ slave.finishedFrame(job_frame)
+
+ frame.status = job_result
+ frame.time = job_time
+
+ job.testFinished()
+
+ self.send_head()
+ else: # frame not found
+ self.send_head(http.client.NO_CONTENT)
+ else: # job not found
+ self.send_head(http.client.NO_CONTENT)
+ else: # invalid slave id
+ self.send_head(http.client.NO_CONTENT)
+ # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ elif self.path.startswith("/log"):
+ self.server.stats("", "Receiving log file")
+
+ match = log_pattern.match(self.path)
+
+ if match:
+ job_id = match.groups()[0]
+
+ job = self.server.getJobID(job_id)
+
+ if job:
+ job_frame = int(match.groups()[1])
+
+ frame = job[job_frame]
+
+ if frame and frame.log_path:
+ length = int(self.headers['content-length'])
+ buf = self.rfile.read(length)
+ f = open(frame.log_path, 'ab')
+ f.write(buf)
+ f.close()
+
+ del buf
+
+ self.server.getSeenSlave(self.headers['slave-id'])
+
+ self.send_head()
+ else: # frame not found
+ self.send_head(http.client.NO_CONTENT)
+ else: # job not found
+ self.send_head(http.client.NO_CONTENT)
+ else: # invalid url
+ self.send_head(http.client.NO_CONTENT)
class RenderMasterServer(http.server.HTTPServer):
- def __init__(self, address, handler_class, path):
- super().__init__(address, handler_class)
- self.jobs = []
- self.jobs_map = {}
- self.slaves = []
- self.slaves_map = {}
- self.job_id = 0
- self.path = path + "master_" + str(os.getpid()) + os.sep
-
- self.slave_timeout = 30 # 30 mins: need a parameter for that
-
- self.balancer = netrender.balancing.Balancer()
- self.balancer.addRule(netrender.balancing.RatingUsageByCategory(self.getJobs))
- self.balancer.addRule(netrender.balancing.RatingUsage())
- self.balancer.addException(netrender.balancing.ExcludeQueuedEmptyJob())
- self.balancer.addException(netrender.balancing.ExcludeSlavesLimit(self.countJobs, self.countSlaves, limit = 0.9))
- self.balancer.addPriority(netrender.balancing.NewJobPriority())
- self.balancer.addPriority(netrender.balancing.MinimumTimeBetweenDispatchPriority(limit = 2))
-
- if not os.path.exists(self.path):
- os.mkdir(self.path)
-
- def nextJobID(self):
- self.job_id += 1
- return str(self.job_id)
-
- def addSlave(self, name, address, stats):
- slave = MRenderSlave(name, address, stats)
- self.slaves.append(slave)
- self.slaves_map[slave.id] = slave
-
- return slave.id
-
- def removeSlave(self, slave):
- self.slaves.remove(slave)
- self.slaves_map.pop(slave.id)
-
- def getSlave(self, slave_id):
- return self.slaves_map.get(slave_id, None)
-
- def getSeenSlave(self, slave_id):
- slave = self.getSlave(slave_id)
- if slave:
- slave.seen()
-
- return slave
-
- def timeoutSlaves(self):
- removed = []
-
- t = time.time()
-
- for slave in self.slaves:
- if (t - slave.last_seen) / 60 > self.slave_timeout:
- removed.append(slave)
-
- if slave.job:
- for f in slave.job_frames:
- slave.job[f].status = ERROR
-
- for slave in removed:
- self.removeSlave(slave)
-
- def updateUsage(self):
- blend = 0.5
- for job in self.jobs:
- job.usage *= (1 - blend)
-
- if self.slaves:
- slave_usage = blend / self.countSlaves()
-
- for slave in self.slaves:
- if slave.job:
- slave.job.usage += slave_usage
-
-
- def clear(self):
- removed = self.jobs[:]
-
- for job in removed:
- self.removeJob(job)
-
- def balance(self):
- self.balancer.balance(self.jobs)
-
- def getJobs(self):
- return self.jobs
-
- def countJobs(self, status = JOB_QUEUED):
- total = 0
- for j in self.jobs:
- if j.status == status:
- total += 1
-
- return total
-
- def countSlaves(self):
- return len(self.slaves)
-
- def removeJob(self, job):
- self.jobs.remove(job)
- self.jobs_map.pop(job.id)
-
- for slave in self.slaves:
- if slave.job == job:
- slave.job = None
- slave.job_frames = []
-
- def addJob(self, job):
- self.jobs.append(job)
- self.jobs_map[job.id] = job
-
- # create job directory
- job.save_path = self.path + "job_" + job.id + os.sep
- if not os.path.exists(job.save_path):
- os.mkdir(job.save_path)
-
- job.save()
-
- def getJobID(self, id):
- return self.jobs_map.get(id, None)
-
- def __iter__(self):
- for job in self.jobs:
- yield job
-
- def newDispatch(self, slave_id):
- if self.jobs:
- for job in self.jobs:
- if not self.balancer.applyExceptions(job) and slave_id not in job.blacklist:
- return job, job.getFrames()
-
- return None, None
+ def __init__(self, address, handler_class, path):
+ super().__init__(address, handler_class)
+ self.jobs = []
+ self.jobs_map = {}
+ self.slaves = []
+ self.slaves_map = {}
+ self.job_id = 0
+ self.path = path + "master_" + str(os.getpid()) + os.sep
+
+ self.slave_timeout = 30 # 30 mins: need a parameter for that
+
+ self.balancer = netrender.balancing.Balancer()
+ self.balancer.addRule(netrender.balancing.RatingUsageByCategory(self.getJobs))
+ self.balancer.addRule(netrender.balancing.RatingUsage())
+ self.balancer.addException(netrender.balancing.ExcludeQueuedEmptyJob())
+ self.balancer.addException(netrender.balancing.ExcludeSlavesLimit(self.countJobs, self.countSlaves, limit = 0.9))
+ self.balancer.addPriority(netrender.balancing.NewJobPriority())
+ self.balancer.addPriority(netrender.balancing.MinimumTimeBetweenDispatchPriority(limit = 2))
+
+ if not os.path.exists(self.path):
+ os.mkdir(self.path)
+
+ def nextJobID(self):
+ self.job_id += 1
+ return str(self.job_id)
+
+ def addSlave(self, name, address, stats):
+ slave = MRenderSlave(name, address, stats)
+ self.slaves.append(slave)
+ self.slaves_map[slave.id] = slave
+
+ return slave.id
+
+ def removeSlave(self, slave):
+ self.slaves.remove(slave)
+ self.slaves_map.pop(slave.id)
+
+ def getSlave(self, slave_id):
+ return self.slaves_map.get(slave_id, None)
+
+ def getSeenSlave(self, slave_id):
+ slave = self.getSlave(slave_id)
+ if slave:
+ slave.seen()
+
+ return slave
+
+ def timeoutSlaves(self):
+ removed = []
+
+ t = time.time()
+
+ for slave in self.slaves:
+ if (t - slave.last_seen) / 60 > self.slave_timeout:
+ removed.append(slave)
+
+ if slave.job:
+ for f in slave.job_frames:
+ slave.job[f].status = ERROR
+
+ for slave in removed:
+ self.removeSlave(slave)
+
+ def updateUsage(self):
+ blend = 0.5
+ for job in self.jobs:
+ job.usage *= (1 - blend)
+
+ if self.slaves:
+ slave_usage = blend / self.countSlaves()
+
+ for slave in self.slaves:
+ if slave.job:
+ slave.job.usage += slave_usage
+
+
+ def clear(self):
+ removed = self.jobs[:]
+
+ for job in removed:
+ self.removeJob(job)
+
+ def balance(self):
+ self.balancer.balance(self.jobs)
+
+ def getJobs(self):
+ return self.jobs
+
+ def countJobs(self, status = JOB_QUEUED):
+ total = 0
+ for j in self.jobs:
+ if j.status == status:
+ total += 1
+
+ return total
+
+ def countSlaves(self):
+ return len(self.slaves)
+
+ def removeJob(self, job):
+ self.jobs.remove(job)
+ self.jobs_map.pop(job.id)
+
+ for slave in self.slaves:
+ if slave.job == job:
+ slave.job = None
+ slave.job_frames = []
+
+ def addJob(self, job):
+ self.jobs.append(job)
+ self.jobs_map[job.id] = job
+
+ # create job directory
+ job.save_path = self.path + "job_" + job.id + os.sep
+ if not os.path.exists(job.save_path):
+ os.mkdir(job.save_path)
+
+ job.save()
+
+ def getJobID(self, id):
+ return self.jobs_map.get(id, None)
+
+ def __iter__(self):
+ for job in self.jobs:
+ yield job
+
+ def newDispatch(self, slave_id):
+ if self.jobs:
+ for job in self.jobs:
+ if not self.balancer.applyExceptions(job) and slave_id not in job.blacklist:
+ return job, job.getFrames()
+
+ return None, None
def runMaster(address, broadcast, path, update_stats, test_break):
- httpd = RenderMasterServer(address, RenderHandler, path)
- httpd.timeout = 1
- httpd.stats = update_stats
-
- if broadcast:
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
-
- start_time = time.time()
-
- while not test_break():
- httpd.handle_request()
-
- if time.time() - start_time >= 10: # need constant here
- httpd.timeoutSlaves()
-
- httpd.updateUsage()
-
- if broadcast:
- print("broadcasting address")
- s.sendto(bytes("%i" % address[1], encoding='utf8'), 0, ('<broadcast>', 8000))
- start_time = time.time()
-
- httpd.server_close()
+ httpd = RenderMasterServer(address, RenderHandler, path)
+ httpd.timeout = 1
+ httpd.stats = update_stats
+
+ if broadcast:
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
+
+ start_time = time.time()
+
+ while not test_break():
+ httpd.handle_request()
+
+ if time.time() - start_time >= 10: # need constant here
+ httpd.timeoutSlaves()
+
+ httpd.updateUsage()
+
+ if broadcast:
+ print("broadcasting address")
+ s.sendto(bytes("%i" % address[1], encoding='utf8'), 0, ('<broadcast>', 8000))
+ start_time = time.time()
+
+ httpd.server_close()
diff --git a/release/scripts/io/netrender/master_html.py b/release/scripts/io/netrender/master_html.py
index 1f530efa7c6..af4db43ae9d 100644
--- a/release/scripts/io/netrender/master_html.py
+++ b/release/scripts/io/netrender/master_html.py
@@ -4,12 +4,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
@@ -24,226 +24,226 @@ from netrender.utils import *
src_folder = os.path.split(__file__)[0]
def get(handler):
- def output(text):
- handler.wfile.write(bytes(text, encoding='utf8'))
-
- def head(title):
- output("<html><head>")
- output("<script src='/html/netrender.js' type='text/javascript'></script>")
+ def output(text):
+ handler.wfile.write(bytes(text, encoding='utf8'))
+
+ def head(title):
+ output("<html><head>")
+ output("<script src='/html/netrender.js' type='text/javascript'></script>")
# output("<script src='/html/json2.js' type='text/javascript'></script>")
- output("<title>")
- output(title)
- output("</title></head><body>")
- output("<link rel='stylesheet' href='/html/netrender.css' type='text/css'>")
-
-
- def link(text, url):
- return "<a href='%s'>%s</a>" % (url, text)
-
- def startTable(border=1, class_style = None, caption = None):
- output("<table border='%i'" % border)
-
- if class_style:
- output(" class='%s'" % class_style)
-
- output(">")
-
- if caption:
- output("<caption>%s</caption>" % caption)
-
- def headerTable(*headers):
- output("<thead><tr>")
-
- for c in headers:
- output("<td>" + c + "</td>")
-
- output("</tr></thead>")
-
- def rowTable(*data, id = None, class_style = None, extra = None):
- output("<tr")
-
- if id:
- output(" id='%s'" % id)
-
- if class_style:
- output(" class='%s'" % class_style)
-
- if extra:
- output(" %s" % extra)
-
- output(">")
-
- for c in data:
- output("<td>" + str(c) + "</td>")
-
- output("</tr>")
-
- def endTable():
- output("</table>")
-
- if handler.path == "/html/netrender.js":
- f = open(os.path.join(src_folder, "netrender.js"), 'rb')
-
- handler.send_head(content = "text/javascript")
- shutil.copyfileobj(f, handler.wfile)
-
- f.close()
- elif handler.path == "/html/netrender.css":
- f = open(os.path.join(src_folder, "netrender.css"), 'rb')
-
- handler.send_head(content = "text/css")
- shutil.copyfileobj(f, handler.wfile)
-
- f.close()
- elif handler.path == "/html" or handler.path == "/":
- handler.send_head(content = "text/html")
- head("NetRender")
-
- output("<h2>Master</h2>")
-
- output("""<button title="remove all jobs" onclick="request('/clear', null);">CLEAR JOB LIST</button>""")
-
- startTable(caption = "Rules", class_style = "rules")
-
- headerTable("type", "description", "limit")
-
- for rule in handler.server.balancer.rules:
- rowTable("rating", rule, rule.str_limit() if hasattr(rule, "limit") else "&nbsp;")
-
- for rule in handler.server.balancer.priorities:
- rowTable("priority", rule, rule.str_limit() if hasattr(rule, "limit") else "&nbsp;")
-
- for rule in handler.server.balancer.exceptions:
- rowTable("exception", rule, rule.str_limit() if hasattr(rule, "limit") else "&nbsp;")
-
- endTable()
-
- output("<h2>Slaves</h2>")
-
- startTable()
- headerTable("name", "address", "last seen", "stats", "job")
-
- for slave in handler.server.slaves:
- rowTable(slave.name, slave.address[0], time.ctime(slave.last_seen), slave.stats, link(slave.job.name, "/html/job" + slave.job.id) if slave.job else "None")
-
- endTable()
-
- output("<h2>Jobs</h2>")
-
- startTable()
- headerTable(
- "&nbsp;",
- "id",
- "name",
- "category",
- "chunks",
- "priority",
- "usage",
- "wait",
- "status",
- "length",
- "done",
- "dispatched",
- "error",
- "first",
- "exception"
- )
-
- handler.server.balance()
-
- 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="reset all frames" onclick="request('/resetall_%s_0', null);">R</button>""" % job.id,
- job.id,
- link(job.name, "/html/job" + job.id),
- job.category if job.category else "<i>None</i>",
- str(job.chunks) +
- """<button title="increase priority" onclick="request('/edit_%s', &quot;{'chunks': %i}&quot;);">+</button>""" % (job.id, job.chunks + 1) +
- """<button title="decrease priority" onclick="request('/edit_%s', &quot;{'chunks': %i}&quot;);" %s>-</button>""" % (job.id, job.chunks - 1, "disabled=True" if job.chunks == 1 else ""),
- str(job.priority) +
- """<button title="increase chunks size" onclick="request('/edit_%s', &quot;{'priority': %i}&quot;);">+</button>""" % (job.id, job.priority + 1) +
- """<button title="decrease chunks size" onclick="request('/edit_%s', &quot;{'priority': %i}&quot;);" %s>-</button>""" % (job.id, job.priority - 1, "disabled=True" if job.priority == 1 else ""),
- "%0.1f%%" % (job.usage * 100),
- "%is" % int(time.time() - job.last_dispatched),
- job.statusText(),
- len(job),
- results[DONE],
- results[DISPATCHED],
- str(results[ERROR]) +
- """<button title="reset error frames" onclick="request('/reset_%s_0', null);" %s>R</button>""" % (job.id, "disabled=True" if not results[ERROR] else ""),
- handler.server.balancer.applyPriorities(job), handler.server.balancer.applyExceptions(job)
- )
-
- endTable()
-
- output("</body></html>")
-
- elif handler.path.startswith("/html/job"):
- handler.send_head(content = "text/html")
- job_id = handler.path[9:]
-
- head("NetRender")
-
- job = handler.server.getJobID(job_id)
-
- if job:
- output("<h2>Files</h2>")
-
- startTable()
- headerTable("path")
-
- tot_cache = 0
- tot_fluid = 0
-
- for file in job.files:
- if file.filepath.endswith(".bphys"):
- tot_cache += 1
- elif file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
- tot_fluid += 1
- else:
- rowTable(file.filepath)
-
- if tot_cache > 0:
- rowTable("%i physic cache files" % tot_cache, class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.cache&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
- for file in job.files:
- if file.filepath.endswith(".bphys"):
- rowTable(os.path.split(file.filepath)[1], class_style = "cache")
-
- if tot_fluid > 0:
- rowTable("%i fluid bake files" % tot_fluid, class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.fluid&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
- for file in job.files:
- if file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
- rowTable(os.path.split(file.filepath)[1], class_style = "fluid")
-
- endTable()
-
- output("<h2>Blacklist</h2>")
-
- if job.blacklist:
- startTable()
- headerTable("name", "address")
-
- for slave_id in job.blacklist:
- slave = handler.server.slaves_map[slave_id]
- rowTable(slave.name, slave.address[0])
-
- endTable()
- else:
- output("<i>Empty</i>")
-
- output("<h2>Frames</h2>")
-
- startTable()
- headerTable("no", "status", "render time", "slave", "log", "result")
-
- for frame in job.frames:
- rowTable(frame.number, frame.statusText(), "%.1fs" % frame.time, frame.slave.name if frame.slave else "&nbsp;", link("view log", logURL(job_id, frame.number)) if frame.log_path else "&nbsp;", link("view result", renderURL(job_id, frame.number)) if frame.status == DONE else "&nbsp;")
-
- endTable()
- else:
- output("no such job")
-
- output("</body></html>")
+ output("<title>")
+ output(title)
+ output("</title></head><body>")
+ output("<link rel='stylesheet' href='/html/netrender.css' type='text/css'>")
+
+
+ def link(text, url):
+ return "<a href='%s'>%s</a>" % (url, text)
+
+ def startTable(border=1, class_style = None, caption = None):
+ output("<table border='%i'" % border)
+
+ if class_style:
+ output(" class='%s'" % class_style)
+
+ output(">")
+
+ if caption:
+ output("<caption>%s</caption>" % caption)
+
+ def headerTable(*headers):
+ output("<thead><tr>")
+
+ for c in headers:
+ output("<td>" + c + "</td>")
+
+ output("</tr></thead>")
+
+ def rowTable(*data, id = None, class_style = None, extra = None):
+ output("<tr")
+
+ if id:
+ output(" id='%s'" % id)
+
+ if class_style:
+ output(" class='%s'" % class_style)
+
+ if extra:
+ output(" %s" % extra)
+
+ output(">")
+
+ for c in data:
+ output("<td>" + str(c) + "</td>")
+
+ output("</tr>")
+
+ def endTable():
+ output("</table>")
+
+ if handler.path == "/html/netrender.js":
+ f = open(os.path.join(src_folder, "netrender.js"), 'rb')
+
+ handler.send_head(content = "text/javascript")
+ shutil.copyfileobj(f, handler.wfile)
+
+ f.close()
+ elif handler.path == "/html/netrender.css":
+ f = open(os.path.join(src_folder, "netrender.css"), 'rb')
+
+ handler.send_head(content = "text/css")
+ shutil.copyfileobj(f, handler.wfile)
+
+ f.close()
+ elif handler.path == "/html" or handler.path == "/":
+ handler.send_head(content = "text/html")
+ head("NetRender")
+
+ output("<h2>Master</h2>")
+
+ output("""<button title="remove all jobs" onclick="request('/clear', null);">CLEAR JOB LIST</button>""")
+
+ startTable(caption = "Rules", class_style = "rules")
+
+ headerTable("type", "description", "limit")
+
+ for rule in handler.server.balancer.rules:
+ rowTable("rating", rule, rule.str_limit() if hasattr(rule, "limit") else "&nbsp;")
+
+ for rule in handler.server.balancer.priorities:
+ rowTable("priority", rule, rule.str_limit() if hasattr(rule, "limit") else "&nbsp;")
+
+ for rule in handler.server.balancer.exceptions:
+ rowTable("exception", rule, rule.str_limit() if hasattr(rule, "limit") else "&nbsp;")
+
+ endTable()
+
+ output("<h2>Slaves</h2>")
+
+ startTable()
+ headerTable("name", "address", "last seen", "stats", "job")
+
+ for slave in handler.server.slaves:
+ rowTable(slave.name, slave.address[0], time.ctime(slave.last_seen), slave.stats, link(slave.job.name, "/html/job" + slave.job.id) if slave.job else "None")
+
+ endTable()
+
+ output("<h2>Jobs</h2>")
+
+ startTable()
+ headerTable(
+ "&nbsp;",
+ "id",
+ "name",
+ "category",
+ "chunks",
+ "priority",
+ "usage",
+ "wait",
+ "status",
+ "length",
+ "done",
+ "dispatched",
+ "error",
+ "first",
+ "exception"
+ )
+
+ handler.server.balance()
+
+ 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="reset all frames" onclick="request('/resetall_%s_0', null);">R</button>""" % job.id,
+ job.id,
+ link(job.name, "/html/job" + job.id),
+ job.category if job.category else "<i>None</i>",
+ str(job.chunks) +
+ """<button title="increase priority" onclick="request('/edit_%s', &quot;{'chunks': %i}&quot;);">+</button>""" % (job.id, job.chunks + 1) +
+ """<button title="decrease priority" onclick="request('/edit_%s', &quot;{'chunks': %i}&quot;);" %s>-</button>""" % (job.id, job.chunks - 1, "disabled=True" if job.chunks == 1 else ""),
+ str(job.priority) +
+ """<button title="increase chunks size" onclick="request('/edit_%s', &quot;{'priority': %i}&quot;);">+</button>""" % (job.id, job.priority + 1) +
+ """<button title="decrease chunks size" onclick="request('/edit_%s', &quot;{'priority': %i}&quot;);" %s>-</button>""" % (job.id, job.priority - 1, "disabled=True" if job.priority == 1 else ""),
+ "%0.1f%%" % (job.usage * 100),
+ "%is" % int(time.time() - job.last_dispatched),
+ job.statusText(),
+ len(job),
+ results[DONE],
+ results[DISPATCHED],
+ str(results[ERROR]) +
+ """<button title="reset error frames" onclick="request('/reset_%s_0', null);" %s>R</button>""" % (job.id, "disabled=True" if not results[ERROR] else ""),
+ handler.server.balancer.applyPriorities(job), handler.server.balancer.applyExceptions(job)
+ )
+
+ endTable()
+
+ output("</body></html>")
+
+ elif handler.path.startswith("/html/job"):
+ handler.send_head(content = "text/html")
+ job_id = handler.path[9:]
+
+ head("NetRender")
+
+ job = handler.server.getJobID(job_id)
+
+ if job:
+ output("<h2>Files</h2>")
+
+ startTable()
+ headerTable("path")
+
+ tot_cache = 0
+ tot_fluid = 0
+
+ for file in job.files:
+ if file.filepath.endswith(".bphys"):
+ tot_cache += 1
+ elif file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
+ tot_fluid += 1
+ else:
+ rowTable(file.filepath)
+
+ if tot_cache > 0:
+ rowTable("%i physic cache files" % tot_cache, class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.cache&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
+ for file in job.files:
+ if file.filepath.endswith(".bphys"):
+ rowTable(os.path.split(file.filepath)[1], class_style = "cache")
+
+ if tot_fluid > 0:
+ rowTable("%i fluid bake files" % tot_fluid, class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.fluid&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
+ for file in job.files:
+ if file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
+ rowTable(os.path.split(file.filepath)[1], class_style = "fluid")
+
+ endTable()
+
+ output("<h2>Blacklist</h2>")
+
+ if job.blacklist:
+ startTable()
+ headerTable("name", "address")
+
+ for slave_id in job.blacklist:
+ slave = handler.server.slaves_map[slave_id]
+ rowTable(slave.name, slave.address[0])
+
+ endTable()
+ else:
+ output("<i>Empty</i>")
+
+ output("<h2>Frames</h2>")
+
+ startTable()
+ headerTable("no", "status", "render time", "slave", "log", "result")
+
+ for frame in job.frames:
+ rowTable(frame.number, frame.statusText(), "%.1fs" % frame.time, frame.slave.name if frame.slave else "&nbsp;", link("view log", logURL(job_id, frame.number)) if frame.log_path else "&nbsp;", link("view result", renderURL(job_id, frame.number)) if frame.status == DONE else "&nbsp;")
+
+ endTable()
+ else:
+ output("no such job")
+
+ output("</body></html>")
diff --git a/release/scripts/io/netrender/model.py b/release/scripts/io/netrender/model.py
index f541b0c5e1a..c5b4af47aa1 100644
--- a/release/scripts/io/netrender/model.py
+++ b/release/scripts/io/netrender/model.py
@@ -4,12 +4,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
@@ -23,256 +23,256 @@ import subprocess, shutil, time, hashlib
from netrender.utils import *
class LogFile:
- def __init__(self, job_id = 0, slave_id = 0, frames = []):
- self.job_id = job_id
- self.slave_id = slave_id
- self.frames = frames
-
- def serialize(self):
- return {
- "job_id": self.job_id,
- "slave_id": self.slave_id,
- "frames": self.frames
- }
-
- @staticmethod
- def materialize(data):
- if not data:
- return None
-
- logfile = LogFile()
- logfile.job_id = data["job_id"]
- logfile.slave_id = data["slave_id"]
- logfile.frames = data["frames"]
-
- return logfile
+ def __init__(self, job_id = 0, slave_id = 0, frames = []):
+ self.job_id = job_id
+ self.slave_id = slave_id
+ self.frames = frames
+
+ def serialize(self):
+ return {
+ "job_id": self.job_id,
+ "slave_id": self.slave_id,
+ "frames": self.frames
+ }
+
+ @staticmethod
+ def materialize(data):
+ if not data:
+ return None
+
+ logfile = LogFile()
+ logfile.job_id = data["job_id"]
+ logfile.slave_id = data["slave_id"]
+ logfile.frames = data["frames"]
+
+ return logfile
class RenderSlave:
- _slave_map = {}
-
- def __init__(self):
- self.id = ""
- self.name = ""
- self.address = ("",0)
- self.stats = ""
- self.total_done = 0
- self.total_error = 0
- self.last_seen = 0.0
-
- def serialize(self):
- return {
- "id": self.id,
- "name": self.name,
- "address": self.address,
- "stats": self.stats,
- "total_done": self.total_done,
- "total_error": self.total_error,
- "last_seen": self.last_seen
- }
-
- @staticmethod
- def materialize(data, cache = True):
- if not data:
- return None
-
- slave_id = data["id"]
-
- if cache and slave_id in RenderSlave._slave_map:
- return RenderSlave._slave_map[slave_id]
-
- slave = RenderSlave()
- slave.id = slave_id
- slave.name = data["name"]
- slave.address = data["address"]
- slave.stats = data["stats"]
- slave.total_done = data["total_done"]
- slave.total_error = data["total_error"]
- slave.last_seen = data["last_seen"]
-
- if cache:
- RenderSlave._slave_map[slave_id] = slave
-
- return slave
+ _slave_map = {}
+
+ def __init__(self):
+ self.id = ""
+ self.name = ""
+ self.address = ("",0)
+ self.stats = ""
+ self.total_done = 0
+ self.total_error = 0
+ self.last_seen = 0.0
+
+ def serialize(self):
+ return {
+ "id": self.id,
+ "name": self.name,
+ "address": self.address,
+ "stats": self.stats,
+ "total_done": self.total_done,
+ "total_error": self.total_error,
+ "last_seen": self.last_seen
+ }
+
+ @staticmethod
+ def materialize(data, cache = True):
+ if not data:
+ return None
+
+ slave_id = data["id"]
+
+ if cache and slave_id in RenderSlave._slave_map:
+ return RenderSlave._slave_map[slave_id]
+
+ slave = RenderSlave()
+ slave.id = slave_id
+ slave.name = data["name"]
+ slave.address = data["address"]
+ slave.stats = data["stats"]
+ slave.total_done = data["total_done"]
+ slave.total_error = data["total_error"]
+ slave.last_seen = data["last_seen"]
+
+ if cache:
+ RenderSlave._slave_map[slave_id] = slave
+
+ return slave
JOB_BLENDER = 1
JOB_PROCESS = 2
JOB_TYPES = {
- JOB_BLENDER: "Blender",
- JOB_PROCESS: "Process"
- }
+ JOB_BLENDER: "Blender",
+ JOB_PROCESS: "Process"
+ }
class RenderFile:
- def __init__(self, filepath = "", index = 0, start = -1, end = -1):
- self.filepath = filepath
- self.index = index
- self.start = start
- self.end = end
-
- def serialize(self):
- return {
- "filepath": self.filepath,
- "index": self.index,
- "start": self.start,
- "end": self.end
- }
-
- @staticmethod
- def materialize(data):
- if not data:
- return None
-
- rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"])
-
- return rfile
+ def __init__(self, filepath = "", index = 0, start = -1, end = -1):
+ self.filepath = filepath
+ self.index = index
+ self.start = start
+ self.end = end
+
+ def serialize(self):
+ return {
+ "filepath": self.filepath,
+ "index": self.index,
+ "start": self.start,
+ "end": self.end
+ }
+
+ @staticmethod
+ def materialize(data):
+ if not data:
+ return None
+
+ rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"])
+
+ return rfile
class RenderJob:
- def __init__(self, job_info = None):
- self.id = ""
- self.type = JOB_BLENDER
- self.name = ""
- self.category = "None"
- self.status = JOB_WAITING
- self.files = []
- self.chunks = 0
- self.priority = 0
- self.blacklist = []
-
- self.usage = 0.0
- self.last_dispatched = 0.0
- self.frames = []
-
- if job_info:
- self.type = job_info.type
- self.name = job_info.name
- self.category = job_info.category
- self.status = job_info.status
- self.files = job_info.files
- self.chunks = job_info.chunks
- self.priority = job_info.priority
- self.blacklist = job_info.blacklist
-
- def addFile(self, file_path, start=-1, end=-1):
- self.files.append(RenderFile(file_path, len(self.files), start, end))
-
- def addFrame(self, frame_number, command = ""):
- frame = RenderFrame(frame_number, command)
- self.frames.append(frame)
- return frame
-
- def __len__(self):
- return len(self.frames)
-
- def countFrames(self, status=QUEUED):
- total = 0
- for f in self.frames:
- if f.status == status:
- total += 1
-
- return total
-
- def countSlaves(self):
- return len(set((frame.slave for frame in self.frames if frame.status == DISPATCHED)))
-
- def statusText(self):
- return JOB_STATUS_TEXT[self.status]
-
- def framesStatus(self):
- results = {
- QUEUED: 0,
- DISPATCHED: 0,
- DONE: 0,
- ERROR: 0
- }
-
- for frame in self.frames:
- results[frame.status] += 1
-
- return results
-
- def __contains__(self, frame_number):
- for f in self.frames:
- if f.number == frame_number:
- return True
- else:
- return False
-
- def __getitem__(self, frame_number):
- for f in self.frames:
- if f.number == frame_number:
- return f
- else:
- return None
-
- def serialize(self, frames = None):
- min_frame = min((f.number for f in frames)) if frames else -1
- max_frame = max((f.number for f in frames)) if frames else -1
- return {
- "id": self.id,
- "type": self.type,
- "name": self.name,
- "category": self.category,
- "status": self.status,
- "files": [f.serialize() for f in self.files if f.start == -1 or not frames or (f.start <= max_frame and f.end >= min_frame)],
- "frames": [f.serialize() for f in self.frames if not frames or f in frames],
- "chunks": self.chunks,
- "priority": self.priority,
- "usage": self.usage,
- "blacklist": self.blacklist,
- "last_dispatched": self.last_dispatched
- }
-
- @staticmethod
- def materialize(data):
- if not data:
- return None
-
- job = RenderJob()
- job.id = data["id"]
- job.type = data["type"]
- job.name = data["name"]
- job.category = data["category"]
- job.status = data["status"]
- job.files = [RenderFile.materialize(f) for f in data["files"]]
- job.frames = [RenderFrame.materialize(f) for f in data["frames"]]
- job.chunks = data["chunks"]
- job.priority = data["priority"]
- job.usage = data["usage"]
- job.blacklist = data["blacklist"]
- job.last_dispatched = data["last_dispatched"]
-
- return job
+ def __init__(self, job_info = None):
+ self.id = ""
+ self.type = JOB_BLENDER
+ self.name = ""
+ self.category = "None"
+ self.status = JOB_WAITING
+ self.files = []
+ self.chunks = 0
+ self.priority = 0
+ self.blacklist = []
+
+ self.usage = 0.0
+ self.last_dispatched = 0.0
+ self.frames = []
+
+ if job_info:
+ self.type = job_info.type
+ self.name = job_info.name
+ self.category = job_info.category
+ self.status = job_info.status
+ self.files = job_info.files
+ self.chunks = job_info.chunks
+ self.priority = job_info.priority
+ self.blacklist = job_info.blacklist
+
+ def addFile(self, file_path, start=-1, end=-1):
+ self.files.append(RenderFile(file_path, len(self.files), start, end))
+
+ def addFrame(self, frame_number, command = ""):
+ frame = RenderFrame(frame_number, command)
+ self.frames.append(frame)
+ return frame
+
+ def __len__(self):
+ return len(self.frames)
+
+ def countFrames(self, status=QUEUED):
+ total = 0
+ for f in self.frames:
+ if f.status == status:
+ total += 1
+
+ return total
+
+ def countSlaves(self):
+ return len(set((frame.slave for frame in self.frames if frame.status == DISPATCHED)))
+
+ def statusText(self):
+ return JOB_STATUS_TEXT[self.status]
+
+ def framesStatus(self):
+ results = {
+ QUEUED: 0,
+ DISPATCHED: 0,
+ DONE: 0,
+ ERROR: 0
+ }
+
+ for frame in self.frames:
+ results[frame.status] += 1
+
+ return results
+
+ def __contains__(self, frame_number):
+ for f in self.frames:
+ if f.number == frame_number:
+ return True
+ else:
+ return False
+
+ def __getitem__(self, frame_number):
+ for f in self.frames:
+ if f.number == frame_number:
+ return f
+ else:
+ return None
+
+ def serialize(self, frames = None):
+ min_frame = min((f.number for f in frames)) if frames else -1
+ max_frame = max((f.number for f in frames)) if frames else -1
+ return {
+ "id": self.id,
+ "type": self.type,
+ "name": self.name,
+ "category": self.category,
+ "status": self.status,
+ "files": [f.serialize() for f in self.files if f.start == -1 or not frames or (f.start <= max_frame and f.end >= min_frame)],
+ "frames": [f.serialize() for f in self.frames if not frames or f in frames],
+ "chunks": self.chunks,
+ "priority": self.priority,
+ "usage": self.usage,
+ "blacklist": self.blacklist,
+ "last_dispatched": self.last_dispatched
+ }
+
+ @staticmethod
+ def materialize(data):
+ if not data:
+ return None
+
+ job = RenderJob()
+ job.id = data["id"]
+ job.type = data["type"]
+ job.name = data["name"]
+ job.category = data["category"]
+ job.status = data["status"]
+ job.files = [RenderFile.materialize(f) for f in data["files"]]
+ job.frames = [RenderFrame.materialize(f) for f in data["frames"]]
+ job.chunks = data["chunks"]
+ job.priority = data["priority"]
+ job.usage = data["usage"]
+ job.blacklist = data["blacklist"]
+ job.last_dispatched = data["last_dispatched"]
+
+ return job
class RenderFrame:
- def __init__(self, number = 0, command = ""):
- self.number = number
- self.time = 0
- self.status = QUEUED
- self.slave = None
- self.command = command
-
- def statusText(self):
- return FRAME_STATUS_TEXT[self.status]
-
- def serialize(self):
- return {
- "number": self.number,
- "time": self.time,
- "status": self.status,
- "slave": None if not self.slave else self.slave.serialize(),
- "command": self.command
- }
-
- @staticmethod
- def materialize(data):
- if not data:
- return None
-
- frame = RenderFrame()
- frame.number = data["number"]
- frame.time = data["time"]
- frame.status = data["status"]
- frame.slave = RenderSlave.materialize(data["slave"])
- frame.command = data["command"]
-
- return frame
+ def __init__(self, number = 0, command = ""):
+ self.number = number
+ self.time = 0
+ self.status = QUEUED
+ self.slave = None
+ self.command = command
+
+ def statusText(self):
+ return FRAME_STATUS_TEXT[self.status]
+
+ def serialize(self):
+ return {
+ "number": self.number,
+ "time": self.time,
+ "status": self.status,
+ "slave": None if not self.slave else self.slave.serialize(),
+ "command": self.command
+ }
+
+ @staticmethod
+ def materialize(data):
+ if not data:
+ return None
+
+ frame = RenderFrame()
+ frame.number = data["number"]
+ frame.time = data["time"]
+ frame.status = data["status"]
+ frame.slave = RenderSlave.materialize(data["slave"])
+ frame.command = data["command"]
+
+ return frame
diff --git a/release/scripts/io/netrender/operators.py b/release/scripts/io/netrender/operators.py
index 6fc5d30dc4e..8052b43e5b6 100644
--- a/release/scripts/io/netrender/operators.py
+++ b/release/scripts/io/netrender/operators.py
@@ -4,12 +4,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
@@ -28,414 +28,414 @@ import netrender.model
@rnaType
class RENDER_OT_netslave_bake(bpy.types.Operator):
- '''NEED DESCRIPTION'''
- bl_idname = "render.netslavebake"
- bl_label = "Bake all in file"
-
- def poll(self, context):
- return True
-
- def execute(self, context):
- scene = context.scene
- netsettings = scene.network_render
-
- filename = bpy.data.filename
- path, name = os.path.split(filename)
- root, ext = os.path.splitext(name)
- default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
- relative_path = os.sep + os.sep + "blendcache_" + root + os.sep
-
- # Force all point cache next to the blend file
- for object in bpy.data.objects:
- for modifier in object.modifiers:
- if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
- modifier.settings.path = relative_path
- bpy.ops.fluid.bake({"active_object": object, "scene": scene})
- elif modifier.type == "CLOTH":
- modifier.point_cache.step = 1
- modifier.point_cache.disk_cache = True
- modifier.point_cache.external = False
- elif modifier.type == "SOFT_BODY":
- modifier.point_cache.step = 1
- modifier.point_cache.disk_cache = True
- modifier.point_cache.external = False
- elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
- modifier.domain_settings.point_cache_low.step = 1
- modifier.domain_settings.point_cache_low.disk_cache = True
- modifier.domain_settings.point_cache_low.external = False
- modifier.domain_settings.point_cache_high.step = 1
- modifier.domain_settings.point_cache_high.disk_cache = True
- modifier.domain_settings.point_cache_high.external = False
-
- # particles modifier are stupid and don't contain data
- # we have to go through the object property
- for psys in object.particle_systems:
- psys.point_cache.step = 1
- psys.point_cache.disk_cache = True
- psys.point_cache.external = False
- psys.point_cache.filepath = relative_path
-
- bpy.ops.ptcache.bake_all()
-
- #bpy.ops.wm.save_mainfile(path = path + os.sep + root + "_baked.blend")
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
+ '''NEED DESCRIPTION'''
+ bl_idname = "render.netslavebake"
+ bl_label = "Bake all in file"
+
+ def poll(self, context):
+ return True
+
+ def execute(self, context):
+ scene = context.scene
+ netsettings = scene.network_render
+
+ filename = bpy.data.filename
+ path, name = os.path.split(filename)
+ root, ext = os.path.splitext(name)
+ default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
+ relative_path = os.sep + os.sep + "blendcache_" + root + os.sep
+
+ # Force all point cache next to the blend file
+ for object in bpy.data.objects:
+ for modifier in object.modifiers:
+ if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
+ modifier.settings.path = relative_path
+ bpy.ops.fluid.bake({"active_object": object, "scene": scene})
+ elif modifier.type == "CLOTH":
+ modifier.point_cache.step = 1
+ modifier.point_cache.disk_cache = True
+ modifier.point_cache.external = False
+ elif modifier.type == "SOFT_BODY":
+ modifier.point_cache.step = 1
+ modifier.point_cache.disk_cache = True
+ modifier.point_cache.external = False
+ elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
+ modifier.domain_settings.point_cache_low.step = 1
+ modifier.domain_settings.point_cache_low.disk_cache = True
+ modifier.domain_settings.point_cache_low.external = False
+ modifier.domain_settings.point_cache_high.step = 1
+ modifier.domain_settings.point_cache_high.disk_cache = True
+ modifier.domain_settings.point_cache_high.external = False
+
+ # particles modifier are stupid and don't contain data
+ # we have to go through the object property
+ for psys in object.particle_systems:
+ psys.point_cache.step = 1
+ psys.point_cache.disk_cache = True
+ psys.point_cache.external = False
+ psys.point_cache.filepath = relative_path
+
+ bpy.ops.ptcache.bake_all()
+
+ #bpy.ops.wm.save_mainfile(path = path + os.sep + root + "_baked.blend")
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ return self.execute(context)
+
@rnaType
class RENDER_OT_netclientanim(bpy.types.Operator):
- '''Start rendering an animation on network'''
- bl_idname = "render.netclientanim"
- bl_label = "Animation on network"
-
- def poll(self, context):
- return True
-
- def execute(self, context):
- scene = context.scene
- netsettings = scene.network_render
-
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- # Sending file
- scene.network_render.job_id = client.clientSendJob(conn, scene, True)
- conn.close()
-
- bpy.ops.screen.render('INVOKE_AREA', animation=True)
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
+ '''Start rendering an animation on network'''
+ bl_idname = "render.netclientanim"
+ bl_label = "Animation on network"
+
+ def poll(self, context):
+ return True
+
+ def execute(self, context):
+ scene = context.scene
+ netsettings = scene.network_render
+
+ conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
+
+ if conn:
+ # Sending file
+ scene.network_render.job_id = client.clientSendJob(conn, scene, True)
+ conn.close()
+
+ bpy.ops.screen.render('INVOKE_AREA', animation=True)
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ return self.execute(context)
@rnaType
class RENDER_OT_netclientsend(bpy.types.Operator):
- '''Send Render Job to the Network'''
- bl_idname = "render.netclientsend"
- bl_label = "Send job"
-
- def poll(self, context):
- return True
-
- def execute(self, context):
- scene = context.scene
- netsettings = scene.network_render
-
- try:
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- # Sending file
- scene.network_render.job_id = client.clientSendJob(conn, scene, True)
- conn.close()
- self.report('INFO', "Job sent to master")
- except Exception as err:
- self.report('ERROR', str(err))
-
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
+ '''Send Render Job to the Network'''
+ bl_idname = "render.netclientsend"
+ bl_label = "Send job"
+
+ def poll(self, context):
+ return True
+
+ def execute(self, context):
+ scene = context.scene
+ netsettings = scene.network_render
+
+ try:
+ conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
+
+ if conn:
+ # Sending file
+ scene.network_render.job_id = client.clientSendJob(conn, scene, True)
+ conn.close()
+ self.report('INFO', "Job sent to master")
+ except Exception as err:
+ self.report('ERROR', str(err))
+
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ return self.execute(context)
@rnaType
class RENDER_OT_netclientstatus(bpy.types.Operator):
- '''Refresh the status of the current jobs'''
- bl_idname = "render.netclientstatus"
- bl_label = "Client Status"
-
- def poll(self, context):
- return True
-
- def execute(self, context):
- netsettings = context.scene.network_render
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- conn.request("GET", "/status")
-
- response = conn.getresponse()
- print( response.status, response.reason )
-
- jobs = (netrender.model.RenderJob.materialize(j) for j in eval(str(response.read(), encoding='utf8')))
-
- while(len(netsettings.jobs) > 0):
- netsettings.jobs.remove(0)
-
- netrender.jobs = []
-
- for j in jobs:
- netrender.jobs.append(j)
- netsettings.jobs.add()
- job = netsettings.jobs[-1]
-
- j.results = j.framesStatus() # cache frame status
-
- job.name = j.name
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
+ '''Refresh the status of the current jobs'''
+ bl_idname = "render.netclientstatus"
+ bl_label = "Client Status"
+
+ def poll(self, context):
+ return True
+
+ def execute(self, context):
+ netsettings = context.scene.network_render
+ conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
+
+ if conn:
+ conn.request("GET", "/status")
+
+ response = conn.getresponse()
+ print( response.status, response.reason )
+
+ jobs = (netrender.model.RenderJob.materialize(j) for j in eval(str(response.read(), encoding='utf8')))
+
+ while(len(netsettings.jobs) > 0):
+ netsettings.jobs.remove(0)
+
+ netrender.jobs = []
+
+ for j in jobs:
+ netrender.jobs.append(j)
+ netsettings.jobs.add()
+ job = netsettings.jobs[-1]
+
+ j.results = j.framesStatus() # cache frame status
+
+ job.name = j.name
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ return self.execute(context)
@rnaType
class RENDER_OT_netclientblacklistslave(bpy.types.Operator):
- '''Operator documentation text, will be used for the operator tooltip and python docs.'''
- bl_idname = "render.netclientblacklistslave"
- bl_label = "Client Blacklist Slave"
-
- def poll(self, context):
- return True
-
- def execute(self, context):
- netsettings = context.scene.network_render
-
- if netsettings.active_slave_index >= 0:
-
- # deal with data
- slave = netrender.slaves.pop(netsettings.active_slave_index)
- netrender.blacklist.append(slave)
-
- # deal with rna
- netsettings.slaves_blacklist.add()
- netsettings.slaves_blacklist[-1].name = slave.name
-
- netsettings.slaves.remove(netsettings.active_slave_index)
- netsettings.active_slave_index = -1
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
+ '''Operator documentation text, will be used for the operator tooltip and python docs.'''
+ bl_idname = "render.netclientblacklistslave"
+ bl_label = "Client Blacklist Slave"
+
+ def poll(self, context):
+ return True
+
+ def execute(self, context):
+ netsettings = context.scene.network_render
+
+ if netsettings.active_slave_index >= 0:
+
+ # deal with data
+ slave = netrender.slaves.pop(netsettings.active_slave_index)
+ netrender.blacklist.append(slave)
+
+ # deal with rna
+ netsettings.slaves_blacklist.add()
+ netsettings.slaves_blacklist[-1].name = slave.name
+
+ netsettings.slaves.remove(netsettings.active_slave_index)
+ netsettings.active_slave_index = -1
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ return self.execute(context)
@rnaType
class RENDER_OT_netclientwhitelistslave(bpy.types.Operator):
- '''Operator documentation text, will be used for the operator tooltip and python docs.'''
- bl_idname = "render.netclientwhitelistslave"
- bl_label = "Client Whitelist Slave"
-
- def poll(self, context):
- return True
-
- def execute(self, context):
- netsettings = context.scene.network_render
-
- if netsettings.active_blacklisted_slave_index >= 0:
-
- # deal with data
- slave = netrender.blacklist.pop(netsettings.active_blacklisted_slave_index)
- netrender.slaves.append(slave)
-
- # deal with rna
- netsettings.slaves.add()
- netsettings.slaves[-1].name = slave.name
-
- netsettings.slaves_blacklist.remove(netsettings.active_blacklisted_slave_index)
- netsettings.active_blacklisted_slave_index = -1
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
+ '''Operator documentation text, will be used for the operator tooltip and python docs.'''
+ bl_idname = "render.netclientwhitelistslave"
+ bl_label = "Client Whitelist Slave"
+
+ def poll(self, context):
+ return True
+
+ def execute(self, context):
+ netsettings = context.scene.network_render
+
+ if netsettings.active_blacklisted_slave_index >= 0:
+
+ # deal with data
+ slave = netrender.blacklist.pop(netsettings.active_blacklisted_slave_index)
+ netrender.slaves.append(slave)
+
+ # deal with rna
+ netsettings.slaves.add()
+ netsettings.slaves[-1].name = slave.name
+
+ netsettings.slaves_blacklist.remove(netsettings.active_blacklisted_slave_index)
+ netsettings.active_blacklisted_slave_index = -1
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ return self.execute(context)
@rnaType
class RENDER_OT_netclientslaves(bpy.types.Operator):
- '''Refresh status about available Render slaves'''
- bl_idname = "render.netclientslaves"
- bl_label = "Client Slaves"
-
- def poll(self, context):
- return True
-
- def execute(self, context):
- netsettings = context.scene.network_render
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- conn.request("GET", "/slaves")
-
- response = conn.getresponse()
- print( response.status, response.reason )
-
- slaves = (netrender.model.RenderSlave.materialize(s) for s in eval(str(response.read(), encoding='utf8')))
-
- while(len(netsettings.slaves) > 0):
- netsettings.slaves.remove(0)
-
- netrender.slaves = []
-
- for s in slaves:
- for i in range(len(netrender.blacklist)):
- slave = netrender.blacklist[i]
- if slave.id == s.id:
- netrender.blacklist[i] = s
- netsettings.slaves_blacklist[i].name = s.name
- break
- else:
- netrender.slaves.append(s)
-
- netsettings.slaves.add()
- slave = netsettings.slaves[-1]
- slave.name = s.name
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
+ '''Refresh status about available Render slaves'''
+ bl_idname = "render.netclientslaves"
+ bl_label = "Client Slaves"
+
+ def poll(self, context):
+ return True
+
+ def execute(self, context):
+ netsettings = context.scene.network_render
+ conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
+
+ if conn:
+ conn.request("GET", "/slaves")
+
+ response = conn.getresponse()
+ print( response.status, response.reason )
+
+ slaves = (netrender.model.RenderSlave.materialize(s) for s in eval(str(response.read(), encoding='utf8')))
+
+ while(len(netsettings.slaves) > 0):
+ netsettings.slaves.remove(0)
+
+ netrender.slaves = []
+
+ for s in slaves:
+ for i in range(len(netrender.blacklist)):
+ slave = netrender.blacklist[i]
+ if slave.id == s.id:
+ netrender.blacklist[i] = s
+ netsettings.slaves_blacklist[i].name = s.name
+ break
+ else:
+ netrender.slaves.append(s)
+
+ netsettings.slaves.add()
+ slave = netsettings.slaves[-1]
+ slave.name = s.name
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ return self.execute(context)
@rnaType
class RENDER_OT_netclientcancel(bpy.types.Operator):
- '''Cancel the selected network rendering job.'''
- bl_idname = "render.netclientcancel"
- bl_label = "Client Cancel"
-
- def poll(self, context):
- netsettings = context.scene.network_render
- return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
-
- def execute(self, context):
- netsettings = context.scene.network_render
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- job = netrender.jobs[netsettings.active_job_index]
-
- conn.request("POST", cancelURL(job.id))
-
- 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)
-
+ '''Cancel the selected network rendering job.'''
+ bl_idname = "render.netclientcancel"
+ bl_label = "Client Cancel"
+
+ def poll(self, context):
+ netsettings = context.scene.network_render
+ return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
+
+ def execute(self, context):
+ netsettings = context.scene.network_render
+ conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
+
+ if conn:
+ job = netrender.jobs[netsettings.active_job_index]
+
+ conn.request("POST", cancelURL(job.id))
+
+ 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)
+
@rnaType
class RENDER_OT_netclientcancelall(bpy.types.Operator):
- '''Cancel all running network rendering jobs.'''
- bl_idname = "render.netclientcancelall"
- bl_label = "Client Cancel All"
-
- def poll(self, context):
- return True
-
- def execute(self, context):
- netsettings = context.scene.network_render
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- conn.request("POST", "/clear")
-
- 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)
+ '''Cancel all running network rendering jobs.'''
+ bl_idname = "render.netclientcancelall"
+ bl_label = "Client Cancel All"
+
+ def poll(self, context):
+ return True
+
+ def execute(self, context):
+ netsettings = context.scene.network_render
+ conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
+
+ if conn:
+ conn.request("POST", "/clear")
+
+ 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)
@rnaType
class netclientdownload(bpy.types.Operator):
- '''Download render results from the network'''
- bl_idname = "render.netclientdownload"
- bl_label = "Client Download"
-
- def poll(self, context):
- netsettings = context.scene.network_render
- return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
-
- def execute(self, context):
- netsettings = context.scene.network_render
- rd = context.scene.render_data
-
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- job = netrender.jobs[netsettings.active_job_index]
-
- for frame in job.frames:
- client.requestResult(conn, job.id, frame.number)
- response = conn.getresponse()
-
- if response.status != http.client.OK:
- print("missing", frame.number)
- continue
-
- print("got back", frame.number)
-
- f = open(netsettings.path + "%06d" % frame.number + ".exr", "wb")
- buf = response.read(1024)
-
- while buf:
- f.write(buf)
- buf = response.read(1024)
-
- f.close()
-
- conn.close()
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
+ '''Download render results from the network'''
+ bl_idname = "render.netclientdownload"
+ bl_label = "Client Download"
+
+ def poll(self, context):
+ netsettings = context.scene.network_render
+ return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
+
+ def execute(self, context):
+ netsettings = context.scene.network_render
+ rd = context.scene.render_data
+
+ conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
+
+ if conn:
+ job = netrender.jobs[netsettings.active_job_index]
+
+ for frame in job.frames:
+ client.requestResult(conn, job.id, frame.number)
+ response = conn.getresponse()
+
+ if response.status != http.client.OK:
+ print("missing", frame.number)
+ continue
+
+ print("got back", frame.number)
+
+ f = open(netsettings.path + "%06d" % frame.number + ".exr", "wb")
+ buf = response.read(1024)
+
+ while buf:
+ f.write(buf)
+ buf = response.read(1024)
+
+ f.close()
+
+ conn.close()
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ return self.execute(context)
@rnaType
class netclientscan(bpy.types.Operator):
- __slots__ = []
- '''Operator documentation text, will be used for the operator tooltip and python docs.'''
- bl_idname = "render.netclientscan"
- bl_label = "Client Scan"
-
- def poll(self, context):
- return True
-
- def execute(self, context):
- address, port = clientScan(self.report)
-
- if address:
- scene = context.scene
- netsettings = scene.network_render
- netsettings.server_address = address
- netsettings.server_port = port
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- print(dir(self))
- return self.execute(context)
+ __slots__ = []
+ '''Operator documentation text, will be used for the operator tooltip and python docs.'''
+ bl_idname = "render.netclientscan"
+ bl_label = "Client Scan"
+
+ def poll(self, context):
+ return True
+
+ def execute(self, context):
+ address, port = clientScan(self.report)
+
+ if address:
+ scene = context.scene
+ netsettings = scene.network_render
+ netsettings.server_address = address
+ netsettings.server_port = port
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ print(dir(self))
+ return self.execute(context)
@rnaType
class netclientweb(bpy.types.Operator):
- '''Open new window with information about running rendering jobs'''
- bl_idname = "render.netclientweb"
- bl_label = "Open Master Monitor"
-
- def poll(self, context):
- netsettings = context.scene.network_render
- return netsettings.server_address != "[default]"
-
- def execute(self, context):
- netsettings = context.scene.network_render
-
-
- # open connection to make sure server exists
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- conn.close()
-
- webbrowser.open("http://%s:%i" % (netsettings.server_address, netsettings.server_port))
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
+ '''Open new window with information about running rendering jobs'''
+ bl_idname = "render.netclientweb"
+ bl_label = "Open Master Monitor"
+
+ def poll(self, context):
+ netsettings = context.scene.network_render
+ return netsettings.server_address != "[default]"
+
+ def execute(self, context):
+ netsettings = context.scene.network_render
+
+
+ # open connection to make sure server exists
+ conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
+
+ if conn:
+ conn.close()
+
+ webbrowser.open("http://%s:%i" % (netsettings.server_address, netsettings.server_port))
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ return self.execute(context)
diff --git a/release/scripts/io/netrender/slave.py b/release/scripts/io/netrender/slave.py
index 10f73dbe258..e7cac28ace3 100644
--- a/release/scripts/io/netrender/slave.py
+++ b/release/scripts/io/netrender/slave.py
@@ -4,12 +4,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
@@ -30,218 +30,218 @@ MAX_TIMEOUT = 10
INCREMENT_TIMEOUT = 1
if platform.system() == 'Windows' and platform.version() >= '5': # Error mode is only available on Win2k or higher, that's version 5
- import ctypes
- def SetErrorMode():
- val = ctypes.windll.kernel32.SetErrorMode(0x0002)
- ctypes.windll.kernel32.SetErrorMode(val | 0x0002)
- return val
-
- def RestoreErrorMode(val):
- ctypes.windll.kernel32.SetErrorMode(val)
+ import ctypes
+ def SetErrorMode():
+ val = ctypes.windll.kernel32.SetErrorMode(0x0002)
+ ctypes.windll.kernel32.SetErrorMode(val | 0x0002)
+ return val
+
+ def RestoreErrorMode(val):
+ ctypes.windll.kernel32.SetErrorMode(val)
else:
- def SetErrorMode():
- return 0
-
- def RestoreErrorMode(val):
- pass
+ def SetErrorMode():
+ return 0
+
+ def RestoreErrorMode(val):
+ pass
def slave_Info():
- sysname, nodename, release, version, machine, processor = platform.uname()
- slave = netrender.model.RenderSlave()
- slave.name = nodename
- slave.stats = sysname + " " + release + " " + machine + " " + processor
- return slave
+ sysname, nodename, release, version, machine, processor = platform.uname()
+ slave = netrender.model.RenderSlave()
+ slave.name = nodename
+ slave.stats = sysname + " " + release + " " + machine + " " + processor
+ return slave
def testCancel(conn, job_id, frame_number):
- conn.request("HEAD", "/status", headers={"job-id":job_id, "job-frame": str(frame_number)})
+ conn.request("HEAD", "/status", headers={"job-id":job_id, "job-frame": str(frame_number)})
- # cancelled if job isn't found anymore
- if conn.getresponse().status == http.client.NO_CONTENT:
- return True
- else:
- return False
+ # cancelled if job isn't found anymore
+ if conn.getresponse().status == http.client.NO_CONTENT:
+ return True
+ else:
+ return False
def testFile(conn, job_id, slave_id, file_index, JOB_PREFIX, file_path, main_path = None):
- job_full_path = prefixPath(JOB_PREFIX, file_path, main_path)
-
- if not os.path.exists(job_full_path):
- temp_path = JOB_PREFIX + "slave.temp.blend"
- conn.request("GET", fileURL(job_id, file_index), headers={"slave-id":slave_id})
- response = conn.getresponse()
-
- if response.status != http.client.OK:
- return None # file for job not returned by server, need to return an error code to server
-
- f = open(temp_path, "wb")
- buf = response.read(1024)
-
- while buf:
- f.write(buf)
- buf = response.read(1024)
-
- f.close()
-
- os.renames(temp_path, job_full_path)
-
- return job_full_path
+ job_full_path = prefixPath(JOB_PREFIX, file_path, main_path)
+
+ if not os.path.exists(job_full_path):
+ temp_path = JOB_PREFIX + "slave.temp.blend"
+ conn.request("GET", fileURL(job_id, file_index), headers={"slave-id":slave_id})
+ response = conn.getresponse()
+
+ if response.status != http.client.OK:
+ return None # file for job not returned by server, need to return an error code to server
+
+ f = open(temp_path, "wb")
+ buf = response.read(1024)
+
+ while buf:
+ f.write(buf)
+ buf = response.read(1024)
+
+ f.close()
+
+ os.renames(temp_path, job_full_path)
+
+ return job_full_path
def render_slave(engine, netsettings):
- timeout = 1
-
- engine.update_stats("", "Network render node initiation")
-
- conn = clientConnection(netsettings.server_address, netsettings.server_port)
-
- if conn:
- conn.request("POST", "/slave", repr(slave_Info().serialize()))
- response = conn.getresponse()
-
- slave_id = response.getheader("slave-id")
-
- NODE_PREFIX = netsettings.path + "slave_" + slave_id + os.sep
- if not os.path.exists(NODE_PREFIX):
- os.mkdir(NODE_PREFIX)
-
- while not engine.test_break():
-
- conn.request("GET", "/job", headers={"slave-id":slave_id})
- response = conn.getresponse()
-
- if response.status == http.client.OK:
- timeout = 1 # reset timeout on new job
-
- job = netrender.model.RenderJob.materialize(eval(str(response.read(), encoding='utf8')))
-
- JOB_PREFIX = NODE_PREFIX + "job_" + job.id + os.sep
- if not os.path.exists(JOB_PREFIX):
- os.mkdir(JOB_PREFIX)
-
-
- if job.type == netrender.model.JOB_BLENDER:
- job_path = job.files[0].filepath # path of main file
- main_path, main_file = os.path.split(job_path)
-
- job_full_path = testFile(conn, job.id, slave_id, 0, JOB_PREFIX, job_path)
- print("Fullpath", job_full_path)
- print("File:", main_file, "and %i other files" % (len(job.files) - 1,))
- engine.update_stats("", "Render File "+ main_file+ " for job "+ job.id)
-
- for rfile in job.files[1:]:
- print("\t", rfile.filepath)
- testFile(conn, job.id, slave_id, rfile.index, JOB_PREFIX, rfile.filepath, main_path)
-
- # announce log to master
- logfile = netrender.model.LogFile(job.id, slave_id, [frame.number for frame in job.frames])
- conn.request("POST", "/log", bytes(repr(logfile.serialize()), encoding='utf8'))
- response = conn.getresponse()
-
-
- first_frame = job.frames[0].number
-
- # start render
- start_t = time.time()
-
- if job.type == netrender.model.JOB_BLENDER:
- frame_args = []
-
- for frame in job.frames:
- print("frame", frame.number)
- frame_args += ["-f", str(frame.number)]
-
- val = SetErrorMode()
- process = subprocess.Popen([BLENDER_PATH, "-b", "-noaudio", job_full_path, "-o", JOB_PREFIX + "######", "-E", "BLENDER_RENDER", "-F", "MULTILAYER"] + frame_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- RestoreErrorMode(val)
- elif job.type == netrender.model.JOB_PROCESS:
- command = job.frames[0].command
- val = SetErrorMode()
- process = subprocess.Popen(command.split(" "), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- RestoreErrorMode(val)
-
- headers = {"slave-id":slave_id}
-
- cancelled = False
- stdout = bytes()
- run_t = time.time()
- while process.poll() == None and not cancelled:
- stdout += process.stdout.read(32)
- current_t = time.time()
- cancelled = engine.test_break()
- if current_t - run_t > CANCEL_POLL_SPEED:
-
- # update logs if needed
- if stdout:
- # (only need to update on one frame, they are linked
- conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
- response = conn.getresponse()
-
- stdout = bytes()
-
- run_t = current_t
- if testCancel(conn, job.id, first_frame):
- cancelled = True
-
- # read leftovers if needed
- stdout += process.stdout.read()
-
- if cancelled:
- # kill process if needed
- if process.poll() == None:
- process.terminate()
- continue # to next frame
-
- total_t = time.time() - start_t
-
- avg_t = total_t / len(job.frames)
-
- status = process.returncode
-
- print("status", status)
-
- # flush the rest of the logs
- if stdout:
- # (only need to update on one frame, they are linked
- conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
- if conn.getresponse().status == http.client.NO_CONTENT:
- continue
-
- headers = {"job-id":job.id, "slave-id":slave_id, "job-time":str(avg_t)}
-
-
- if status == 0: # non zero status is error
- headers["job-result"] = str(DONE)
- for frame in job.frames:
- headers["job-frame"] = str(frame.number)
-
- if job.type == netrender.model.JOB_BLENDER:
- # send image back to server
- f = open(JOB_PREFIX + "%06d" % frame.number + ".exr", 'rb')
- conn.request("PUT", "/render", f, headers=headers)
- f.close()
- if conn.getresponse().status == http.client.NO_CONTENT:
- continue
- elif job.type == netrender.model.JOB_PROCESS:
- conn.request("PUT", "/render", headers=headers)
- if conn.getresponse().status == http.client.NO_CONTENT:
- continue
- else:
- headers["job-result"] = str(ERROR)
- for frame in job.frames:
- headers["job-frame"] = str(frame.number)
- # send error result back to server
- conn.request("PUT", "/render", headers=headers)
- if conn.getresponse().status == http.client.NO_CONTENT:
- continue
- else:
- if timeout < MAX_TIMEOUT:
- timeout += INCREMENT_TIMEOUT
-
- for i in range(timeout):
- time.sleep(1)
- if engine.test_break():
- conn.close()
- return
-
- conn.close()
+ timeout = 1
+
+ engine.update_stats("", "Network render node initiation")
+
+ conn = clientConnection(netsettings.server_address, netsettings.server_port)
+
+ if conn:
+ conn.request("POST", "/slave", repr(slave_Info().serialize()))
+ response = conn.getresponse()
+
+ slave_id = response.getheader("slave-id")
+
+ NODE_PREFIX = netsettings.path + "slave_" + slave_id + os.sep
+ if not os.path.exists(NODE_PREFIX):
+ os.mkdir(NODE_PREFIX)
+
+ while not engine.test_break():
+
+ conn.request("GET", "/job", headers={"slave-id":slave_id})
+ response = conn.getresponse()
+
+ if response.status == http.client.OK:
+ timeout = 1 # reset timeout on new job
+
+ job = netrender.model.RenderJob.materialize(eval(str(response.read(), encoding='utf8')))
+
+ JOB_PREFIX = NODE_PREFIX + "job_" + job.id + os.sep
+ if not os.path.exists(JOB_PREFIX):
+ os.mkdir(JOB_PREFIX)
+
+
+ if job.type == netrender.model.JOB_BLENDER:
+ job_path = job.files[0].filepath # path of main file
+ main_path, main_file = os.path.split(job_path)
+
+ job_full_path = testFile(conn, job.id, slave_id, 0, JOB_PREFIX, job_path)
+ print("Fullpath", job_full_path)
+ print("File:", main_file, "and %i other files" % (len(job.files) - 1,))
+ engine.update_stats("", "Render File "+ main_file+ " for job "+ job.id)
+
+ for rfile in job.files[1:]:
+ print("\t", rfile.filepath)
+ testFile(conn, job.id, slave_id, rfile.index, JOB_PREFIX, rfile.filepath, main_path)
+
+ # announce log to master
+ logfile = netrender.model.LogFile(job.id, slave_id, [frame.number for frame in job.frames])
+ conn.request("POST", "/log", bytes(repr(logfile.serialize()), encoding='utf8'))
+ response = conn.getresponse()
+
+
+ first_frame = job.frames[0].number
+
+ # start render
+ start_t = time.time()
+
+ if job.type == netrender.model.JOB_BLENDER:
+ frame_args = []
+
+ for frame in job.frames:
+ print("frame", frame.number)
+ frame_args += ["-f", str(frame.number)]
+
+ val = SetErrorMode()
+ process = subprocess.Popen([BLENDER_PATH, "-b", "-noaudio", job_full_path, "-o", JOB_PREFIX + "######", "-E", "BLENDER_RENDER", "-F", "MULTILAYER"] + frame_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ RestoreErrorMode(val)
+ elif job.type == netrender.model.JOB_PROCESS:
+ command = job.frames[0].command
+ val = SetErrorMode()
+ process = subprocess.Popen(command.split(" "), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ RestoreErrorMode(val)
+
+ headers = {"slave-id":slave_id}
+
+ cancelled = False
+ stdout = bytes()
+ run_t = time.time()
+ while process.poll() == None and not cancelled:
+ stdout += process.stdout.read(32)
+ current_t = time.time()
+ cancelled = engine.test_break()
+ if current_t - run_t > CANCEL_POLL_SPEED:
+
+ # update logs if needed
+ if stdout:
+ # (only need to update on one frame, they are linked
+ conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
+ response = conn.getresponse()
+
+ stdout = bytes()
+
+ run_t = current_t
+ if testCancel(conn, job.id, first_frame):
+ cancelled = True
+
+ # read leftovers if needed
+ stdout += process.stdout.read()
+
+ if cancelled:
+ # kill process if needed
+ if process.poll() == None:
+ process.terminate()
+ continue # to next frame
+
+ total_t = time.time() - start_t
+
+ avg_t = total_t / len(job.frames)
+
+ status = process.returncode
+
+ print("status", status)
+
+ # flush the rest of the logs
+ if stdout:
+ # (only need to update on one frame, they are linked
+ conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
+ if conn.getresponse().status == http.client.NO_CONTENT:
+ continue
+
+ headers = {"job-id":job.id, "slave-id":slave_id, "job-time":str(avg_t)}
+
+
+ if status == 0: # non zero status is error
+ headers["job-result"] = str(DONE)
+ for frame in job.frames:
+ headers["job-frame"] = str(frame.number)
+
+ if job.type == netrender.model.JOB_BLENDER:
+ # send image back to server
+ f = open(JOB_PREFIX + "%06d" % frame.number + ".exr", 'rb')
+ conn.request("PUT", "/render", f, headers=headers)
+ f.close()
+ if conn.getresponse().status == http.client.NO_CONTENT:
+ continue
+ elif job.type == netrender.model.JOB_PROCESS:
+ conn.request("PUT", "/render", headers=headers)
+ if conn.getresponse().status == http.client.NO_CONTENT:
+ continue
+ else:
+ headers["job-result"] = str(ERROR)
+ for frame in job.frames:
+ headers["job-frame"] = str(frame.number)
+ # send error result back to server
+ conn.request("PUT", "/render", headers=headers)
+ if conn.getresponse().status == http.client.NO_CONTENT:
+ continue
+ else:
+ if timeout < MAX_TIMEOUT:
+ timeout += INCREMENT_TIMEOUT
+
+ for i in range(timeout):
+ time.sleep(1)
+ if engine.test_break():
+ conn.close()
+ return
+
+ conn.close()
if __name__ == "__main__":
- pass
+ pass
diff --git a/release/scripts/io/netrender/ui.py b/release/scripts/io/netrender/ui.py
index 40dd5d11b19..86eb4bf5c30 100644
--- a/release/scripts/io/netrender/ui.py
+++ b/release/scripts/io/netrender/ui.py
@@ -4,12 +4,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
@@ -37,319 +37,319 @@ DONE = 2
ERROR = 3
class RenderButtonsPanel(bpy.types.Panel):
- bl_space_type = "PROPERTIES"
- bl_region_type = "WINDOW"
- bl_context = "render"
- # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
-
- def poll(self, context):
- rd = context.scene.render_data
- return (rd.use_game_engine==False) and (rd.engine in self.COMPAT_ENGINES)
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
+ bl_context = "render"
+ # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
+
+ def poll(self, context):
+ rd = context.scene.render_data
+ return (rd.use_game_engine==False) and (rd.engine in self.COMPAT_ENGINES)
# Setting panel, use in the scene for now.
@rnaType
class RENDER_PT_network_settings(RenderButtonsPanel):
- bl_label = "Network Settings"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- rd = scene.render_data
-
- layout.active = True
-
- split = layout.split()
-
- col = split.column()
-
-
- if scene.network_render.mode in ("RENDER_MASTER", "RENDER_SLAVE"):
- col.operator("screen.render", text="Start", icon='PLAY').animation = True
-
- col.prop(scene.network_render, "mode")
- col.prop(scene.network_render, "path")
- col.prop(scene.network_render, "server_address")
- col.prop(scene.network_render, "server_port")
-
- if scene.network_render.mode == "RENDER_MASTER":
- col.prop(scene.network_render, "server_broadcast")
- else:
- col.operator("render.netclientscan", icon='FILE_REFRESH', text="")
-
- col.operator("render.netclientweb", icon='QUESTION')
+ bl_label = "Network Settings"
+ COMPAT_ENGINES = {'NET_RENDER'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ rd = scene.render_data
+
+ layout.active = True
+
+ split = layout.split()
+
+ col = split.column()
+
+
+ if scene.network_render.mode in ("RENDER_MASTER", "RENDER_SLAVE"):
+ col.operator("screen.render", text="Start", icon='PLAY').animation = True
+
+ col.prop(scene.network_render, "mode")
+ col.prop(scene.network_render, "path")
+ col.prop(scene.network_render, "server_address")
+ col.prop(scene.network_render, "server_port")
+
+ if scene.network_render.mode == "RENDER_MASTER":
+ col.prop(scene.network_render, "server_broadcast")
+ else:
+ col.operator("render.netclientscan", icon='FILE_REFRESH', text="")
+
+ col.operator("render.netclientweb", icon='QUESTION')
@rnaType
class RENDER_PT_network_job(RenderButtonsPanel):
- bl_label = "Job Settings"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- def poll(self, context):
- scene = context.scene
- return (super().poll(context)
- and scene.network_render.mode == "RENDER_CLIENT")
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- rd = scene.render_data
-
- layout.active = True
-
- split = layout.split()
-
- col = split.column()
- if scene.network_render.server_address != "[default]":
- col.operator("render.netclientanim", icon='RENDER_ANIMATION')
- col.operator("render.netclientsend", icon='FILE_BLEND')
- if scene.network_render.job_id:
- col.operator("screen.render", text="Get Results", icon='RENDER_ANIMATION').animation = True
- col.prop(scene.network_render, "job_name")
- col.prop(scene.network_render, "job_category")
- row = col.row()
- row.prop(scene.network_render, "priority")
- row.prop(scene.network_render, "chunks")
+ bl_label = "Job Settings"
+ COMPAT_ENGINES = {'NET_RENDER'}
+
+ def poll(self, context):
+ scene = context.scene
+ return (super().poll(context)
+ and scene.network_render.mode == "RENDER_CLIENT")
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ rd = scene.render_data
+
+ layout.active = True
+
+ split = layout.split()
+
+ col = split.column()
+ if scene.network_render.server_address != "[default]":
+ col.operator("render.netclientanim", icon='RENDER_ANIMATION')
+ col.operator("render.netclientsend", icon='FILE_BLEND')
+ if scene.network_render.job_id:
+ col.operator("screen.render", text="Get Results", icon='RENDER_ANIMATION').animation = True
+ col.prop(scene.network_render, "job_name")
+ col.prop(scene.network_render, "job_category")
+ row = col.row()
+ row.prop(scene.network_render, "priority")
+ row.prop(scene.network_render, "chunks")
@rnaType
class RENDER_PT_network_slaves(RenderButtonsPanel):
- bl_label = "Slaves Status"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- def poll(self, context):
- scene = context.scene
- return (super().poll(context)
- and scene.network_render.mode == "RENDER_CLIENT"
- and scene.network_render.server_address != "[default]")
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- netsettings = scene.network_render
-
- row = layout.row()
- row.template_list(netsettings, "slaves", netsettings, "active_slave_index", rows=2)
-
- sub = row.column(align=True)
- sub.operator("render.netclientslaves", icon='FILE_REFRESH', text="")
- sub.operator("render.netclientblacklistslave", icon='ZOOMOUT', text="")
-
- if len(netrender.slaves) == 0 and len(netsettings.slaves) > 0:
- while(len(netsettings.slaves) > 0):
- netsettings.slaves.remove(0)
-
- if netsettings.active_slave_index >= 0 and len(netsettings.slaves) > 0:
- layout.separator()
-
- slave = netrender.slaves[netsettings.active_slave_index]
-
- layout.label(text="Name: " + slave.name)
- layout.label(text="Address: " + slave.address[0])
- layout.label(text="Seen: " + time.ctime(slave.last_seen))
- layout.label(text="Stats: " + slave.stats)
+ bl_label = "Slaves Status"
+ COMPAT_ENGINES = {'NET_RENDER'}
+
+ def poll(self, context):
+ scene = context.scene
+ return (super().poll(context)
+ and scene.network_render.mode == "RENDER_CLIENT"
+ and scene.network_render.server_address != "[default]")
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ netsettings = scene.network_render
+
+ row = layout.row()
+ row.template_list(netsettings, "slaves", netsettings, "active_slave_index", rows=2)
+
+ sub = row.column(align=True)
+ sub.operator("render.netclientslaves", icon='FILE_REFRESH', text="")
+ sub.operator("render.netclientblacklistslave", icon='ZOOMOUT', text="")
+
+ if len(netrender.slaves) == 0 and len(netsettings.slaves) > 0:
+ while(len(netsettings.slaves) > 0):
+ netsettings.slaves.remove(0)
+
+ if netsettings.active_slave_index >= 0 and len(netsettings.slaves) > 0:
+ layout.separator()
+
+ slave = netrender.slaves[netsettings.active_slave_index]
+
+ layout.label(text="Name: " + slave.name)
+ layout.label(text="Address: " + slave.address[0])
+ layout.label(text="Seen: " + time.ctime(slave.last_seen))
+ layout.label(text="Stats: " + slave.stats)
@rnaType
class RENDER_PT_network_slaves_blacklist(RenderButtonsPanel):
- bl_label = "Slaves Blacklist"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- def poll(self, context):
- scene = context.scene
- return (super().poll(context)
- and scene.network_render.mode == "RENDER_CLIENT"
- and scene.network_render.server_address != "[default]")
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- netsettings = scene.network_render
-
- row = layout.row()
- row.template_list(netsettings, "slaves_blacklist", netsettings, "active_blacklisted_slave_index", rows=2)
-
- sub = row.column(align=True)
- sub.operator("render.netclientwhitelistslave", icon='ZOOMOUT', text="")
-
- if len(netrender.blacklist) == 0 and len(netsettings.slaves_blacklist) > 0:
- while(len(netsettings.slaves_blacklist) > 0):
- netsettings.slaves_blacklist.remove(0)
-
- if netsettings.active_blacklisted_slave_index >= 0 and len(netsettings.slaves_blacklist) > 0:
- layout.separator()
-
- slave = netrender.blacklist[netsettings.active_blacklisted_slave_index]
-
- layout.label(text="Name: " + slave.name)
- layout.label(text="Address: " + slave.address[0])
- layout.label(text="Seen: " + time.ctime(slave.last_seen))
- layout.label(text="Stats: " + slave.stats)
+ bl_label = "Slaves Blacklist"
+ COMPAT_ENGINES = {'NET_RENDER'}
+
+ def poll(self, context):
+ scene = context.scene
+ return (super().poll(context)
+ and scene.network_render.mode == "RENDER_CLIENT"
+ and scene.network_render.server_address != "[default]")
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ netsettings = scene.network_render
+
+ row = layout.row()
+ row.template_list(netsettings, "slaves_blacklist", netsettings, "active_blacklisted_slave_index", rows=2)
+
+ sub = row.column(align=True)
+ sub.operator("render.netclientwhitelistslave", icon='ZOOMOUT', text="")
+
+ if len(netrender.blacklist) == 0 and len(netsettings.slaves_blacklist) > 0:
+ while(len(netsettings.slaves_blacklist) > 0):
+ netsettings.slaves_blacklist.remove(0)
+
+ if netsettings.active_blacklisted_slave_index >= 0 and len(netsettings.slaves_blacklist) > 0:
+ layout.separator()
+
+ slave = netrender.blacklist[netsettings.active_blacklisted_slave_index]
+
+ layout.label(text="Name: " + slave.name)
+ layout.label(text="Address: " + slave.address[0])
+ layout.label(text="Seen: " + time.ctime(slave.last_seen))
+ layout.label(text="Stats: " + slave.stats)
@rnaType
class RENDER_PT_network_jobs(RenderButtonsPanel):
- bl_label = "Jobs"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- def poll(self, context):
- scene = context.scene
- return (super().poll(context)
- and scene.network_render.mode == "RENDER_CLIENT"
- and scene.network_render.server_address != "[default]")
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- netsettings = scene.network_render
-
- row = layout.row()
- row.template_list(netsettings, "jobs", netsettings, "active_job_index", rows=2)
-
- sub = row.column(align=True)
- sub.operator("render.netclientstatus", icon='FILE_REFRESH', text="")
- sub.operator("render.netclientcancel", icon='ZOOMOUT', text="")
- sub.operator("render.netclientcancelall", icon='PANEL_CLOSE', text="")
- sub.operator("render.netclientdownload", icon='RENDER_ANIMATION', text="")
-
- if len(netrender.jobs) == 0 and len(netsettings.jobs) > 0:
- while(len(netsettings.jobs) > 0):
- netsettings.jobs.remove(0)
-
- if netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0:
- layout.separator()
-
- job = netrender.jobs[netsettings.active_job_index]
-
- layout.label(text="Name: %s" % job.name)
- layout.label(text="Length: %04i" % len(job))
- layout.label(text="Done: %04i" % job.results[DONE])
- layout.label(text="Error: %04i" % job.results[ERROR])
+ bl_label = "Jobs"
+ COMPAT_ENGINES = {'NET_RENDER'}
+
+ def poll(self, context):
+ scene = context.scene
+ return (super().poll(context)
+ and scene.network_render.mode == "RENDER_CLIENT"
+ and scene.network_render.server_address != "[default]")
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ netsettings = scene.network_render
+
+ row = layout.row()
+ row.template_list(netsettings, "jobs", netsettings, "active_job_index", rows=2)
+
+ sub = row.column(align=True)
+ sub.operator("render.netclientstatus", icon='FILE_REFRESH', text="")
+ sub.operator("render.netclientcancel", icon='ZOOMOUT', text="")
+ sub.operator("render.netclientcancelall", icon='PANEL_CLOSE', text="")
+ sub.operator("render.netclientdownload", icon='RENDER_ANIMATION', text="")
+
+ if len(netrender.jobs) == 0 and len(netsettings.jobs) > 0:
+ while(len(netsettings.jobs) > 0):
+ netsettings.jobs.remove(0)
+
+ if netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0:
+ layout.separator()
+
+ job = netrender.jobs[netsettings.active_job_index]
+
+ layout.label(text="Name: %s" % job.name)
+ layout.label(text="Length: %04i" % len(job))
+ layout.label(text="Done: %04i" % job.results[DONE])
+ layout.label(text="Error: %04i" % job.results[ERROR])
@rnaType
class NetRenderSettings(bpy.types.IDPropertyGroup):
- pass
+ pass
@rnaType
class NetRenderSlave(bpy.types.IDPropertyGroup):
- pass
+ pass
@rnaType
class NetRenderJob(bpy.types.IDPropertyGroup):
- pass
+ pass
bpy.types.Scene.PointerProperty(attr="network_render", type=NetRenderSettings, name="Network Render", description="Network Render Settings")
NetRenderSettings.StringProperty( attr="server_address",
- name="Server address",
- description="IP or name of the master render server",
- maxlen = 128,
- default = "[default]")
+ name="Server address",
+ description="IP or name of the master render server",
+ maxlen = 128,
+ default = "[default]")
NetRenderSettings.IntProperty( attr="server_port",
- name="Server port",
- description="port of the master render server",
- default = 8000,
- min=1,
- max=65535)
+ name="Server port",
+ description="port of the master render server",
+ default = 8000,
+ min=1,
+ max=65535)
NetRenderSettings.BoolProperty( attr="server_broadcast",
- name="Broadcast server address",
- description="broadcast server address on local network",
- default = True)
+ name="Broadcast server address",
+ description="broadcast server address on local network",
+ default = True)
default_path = os.environ.get("TEMP", None)
if not default_path:
- if os.name == 'nt':
- default_path = "c:/tmp/"
- else:
- default_path = "/tmp/"
+ if os.name == 'nt':
+ default_path = "c:/tmp/"
+ else:
+ default_path = "/tmp/"
elif not default_path.endswith(os.sep):
- default_path += os.sep
+ default_path += os.sep
NetRenderSettings.StringProperty( attr="path",
- name="Path",
- description="Path for temporary files",
- maxlen = 128,
- default = default_path)
+ name="Path",
+ description="Path for temporary files",
+ maxlen = 128,
+ default = default_path)
NetRenderSettings.StringProperty( attr="job_name",
- name="Job name",
- description="Name of the job",
- maxlen = 128,
- default = "[default]")
+ name="Job name",
+ description="Name of the job",
+ maxlen = 128,
+ default = "[default]")
NetRenderSettings.StringProperty( attr="job_category",
- name="Job category",
- description="Category of the job",
- maxlen = 128,
- default = "")
+ name="Job category",
+ description="Category of the job",
+ maxlen = 128,
+ default = "")
NetRenderSettings.IntProperty( attr="chunks",
- name="Chunks",
- description="Number of frame to dispatch to each slave in one chunk",
- default = 5,
- min=1,
- max=65535)
+ name="Chunks",
+ description="Number of frame to dispatch to each slave in one chunk",
+ default = 5,
+ min=1,
+ max=65535)
NetRenderSettings.IntProperty( attr="priority",
- name="Priority",
- description="Priority of the job",
- default = 1,
- min=1,
- max=10)
+ name="Priority",
+ description="Priority of the job",
+ default = 1,
+ min=1,
+ max=10)
NetRenderSettings.StringProperty( attr="job_id",
- name="Network job id",
- description="id of the last sent render job",
- maxlen = 64,
- default = "")
+ name="Network job id",
+ description="id of the last sent render job",
+ maxlen = 64,
+ default = "")
NetRenderSettings.IntProperty( attr="active_slave_index",
- name="Index of the active slave",
- description="",
- default = -1,
- min= -1,
- max=65535)
+ name="Index of the active slave",
+ description="",
+ default = -1,
+ min= -1,
+ max=65535)
NetRenderSettings.IntProperty( attr="active_blacklisted_slave_index",
- name="Index of the active slave",
- description="",
- default = -1,
- min= -1,
- max=65535)
+ name="Index of the active slave",
+ description="",
+ default = -1,
+ min= -1,
+ max=65535)
NetRenderSettings.IntProperty( attr="active_job_index",
- name="Index of the active job",
- description="",
- default = -1,
- min= -1,
- max=65535)
+ name="Index of the active job",
+ description="",
+ default = -1,
+ min= -1,
+ max=65535)
NetRenderSettings.EnumProperty(attr="mode",
- items=(
- ("RENDER_CLIENT", "Client", "Act as render client"),
- ("RENDER_MASTER", "Master", "Act as render master"),
- ("RENDER_SLAVE", "Slave", "Act as render slave"),
- ),
- name="Network mode",
- description="Mode of operation of this instance",
- default="RENDER_CLIENT")
+ items=(
+ ("RENDER_CLIENT", "Client", "Act as render client"),
+ ("RENDER_MASTER", "Master", "Act as render master"),
+ ("RENDER_SLAVE", "Slave", "Act as render slave"),
+ ),
+ name="Network mode",
+ description="Mode of operation of this instance",
+ default="RENDER_CLIENT")
NetRenderSettings.CollectionProperty(attr="slaves", type=NetRenderSlave, name="Slaves", description="")
NetRenderSettings.CollectionProperty(attr="slaves_blacklist", type=NetRenderSlave, name="Slaves Blacklist", description="")
NetRenderSettings.CollectionProperty(attr="jobs", type=NetRenderJob, name="Job List", description="")
NetRenderSlave.StringProperty( attr="name",
- name="Name of the slave",
- description="",
- maxlen = 64,
- default = "")
+ name="Name of the slave",
+ description="",
+ maxlen = 64,
+ default = "")
NetRenderJob.StringProperty( attr="name",
- name="Name of the job",
- description="",
- maxlen = 128,
- default = "")
+ name="Name of the job",
+ description="",
+ maxlen = 128,
+ default = "")
diff --git a/release/scripts/io/netrender/utils.py b/release/scripts/io/netrender/utils.py
index eadc7504b57..84275710391 100644
--- a/release/scripts/io/netrender/utils.py
+++ b/release/scripts/io/netrender/utils.py
@@ -4,12 +4,12 @@
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
@@ -51,22 +51,22 @@ DONE = 2
ERROR = 3
FRAME_STATUS_TEXT = {
- QUEUED: "Queued",
- DISPATCHED: "Dispatched",
- DONE: "Done",
- ERROR: "Error"
- }
+ QUEUED: "Queued",
+ DISPATCHED: "Dispatched",
+ DONE: "Done",
+ ERROR: "Error"
+ }
def rnaType(rna_type):
- if bpy: bpy.types.register(rna_type)
- return rna_type
+ if bpy: bpy.types.register(rna_type)
+ return rna_type
def reporting(report, message, errorType = None):
if errorType:
t = 'ERROR'
else:
t = 'INFO'
-
+
if report:
report(t, message)
return None
@@ -82,14 +82,14 @@ def clientScan(report = None):
s.settimeout(30)
s.bind(('', 8000))
-
+
buf, address = s.recvfrom(64)
-
+
address = address[0]
port = int(str(buf, encoding='utf8'))
-
+
reporting(report, "Master server found")
-
+
return (address, port)
except socket.timeout:
reporting(report, "No master server on network", IOError)
@@ -105,10 +105,10 @@ def clientConnection(address, port, report = None):
address, port = clientScan()
if address == "":
return None
-
- try:
+
+ try:
conn = http.client.HTTPConnection(address, port)
-
+
if conn:
if clientVerifyVersion(conn):
return conn
@@ -123,21 +123,21 @@ def clientConnection(address, port, report = None):
raise
def clientVerifyVersion(conn):
- conn.request("GET", "/version")
- response = conn.getresponse()
-
- if response.status != http.client.OK:
- conn.close()
- return False
-
- server_version = response.read()
-
- if server_version != VERSION:
- print("Incorrect server version!")
- print("expected", str(VERSION, encoding='utf8'), "received", str(server_version, encoding='utf8'))
- return False
-
- return True
+ conn.request("GET", "/version")
+ response = conn.getresponse()
+
+ if response.status != http.client.OK:
+ conn.close()
+ return False
+
+ server_version = response.read()
+
+ if server_version != VERSION:
+ print("Incorrect server version!")
+ print("expected", str(VERSION, encoding='utf8'), "received", str(server_version, encoding='utf8'))
+ return False
+
+ return True
def fileURL(job_id, file_index):
return "/file_%s_%i" % (job_id, file_index)
@@ -152,20 +152,20 @@ def cancelURL(job_id):
return "/cancel_%s" % (job_id)
def prefixPath(prefix_directory, file_path, prefix_path):
- if os.path.isabs(file_path):
- # if an absolute path, make sure path exists, if it doesn't, use relative local path
- full_path = file_path
- if not os.path.exists(full_path):
- p, n = os.path.split(full_path)
-
- if prefix_path and p.startswith(prefix_path):
- directory = prefix_directory + p[len(prefix_path):]
- full_path = directory + os.sep + n
- if not os.path.exists(directory):
- os.mkdir(directory)
- else:
- full_path = prefix_directory + n
- else:
- full_path = prefix_directory + file_path
-
- return full_path
+ if os.path.isabs(file_path):
+ # if an absolute path, make sure path exists, if it doesn't, use relative local path
+ full_path = file_path
+ if not os.path.exists(full_path):
+ p, n = os.path.split(full_path)
+
+ if prefix_path and p.startswith(prefix_path):
+ directory = prefix_directory + p[len(prefix_path):]
+ full_path = directory + os.sep + n
+ if not os.path.exists(directory):
+ os.mkdir(directory)
+ else:
+ full_path = prefix_directory + n
+ else:
+ full_path = prefix_directory + file_path
+
+ return full_path
diff --git a/release/scripts/presets/render/HDTV_1080p.py b/release/scripts/presets/render/HDTV_1080p.py
index a0297a200f3..75a5faeb27d 100644
--- a/release/scripts/presets/render/HDTV_1080p.py
+++ b/release/scripts/presets/render/HDTV_1080p.py
@@ -4,4 +4,4 @@ bpy.context.scene.render_data.resolution_percentage = 100
bpy.context.scene.render_data.pixel_aspect_x = 1
bpy.context.scene.render_data.pixel_aspect_y = 1
bpy.context.scene.render_data.fps = 24
-bpy.context.scene.render_data.fps_base = 1 \ No newline at end of file
+bpy.context.scene.render_data.fps_base = 1
diff --git a/release/scripts/presets/render/HDTV_720p.py b/release/scripts/presets/render/HDTV_720p.py
index 4d34a928eb3..4f46f4045e0 100644
--- a/release/scripts/presets/render/HDTV_720p.py
+++ b/release/scripts/presets/render/HDTV_720p.py
@@ -4,4 +4,4 @@ bpy.context.scene.render_data.resolution_percentage = 100
bpy.context.scene.render_data.pixel_aspect_x = 1
bpy.context.scene.render_data.pixel_aspect_y = 1
bpy.context.scene.render_data.fps = 24
-bpy.context.scene.render_data.fps_base = 1 \ No newline at end of file
+bpy.context.scene.render_data.fps_base = 1
diff --git a/release/scripts/presets/render/TV_PAL.py b/release/scripts/presets/render/TV_PAL.py
index 7044070fb0c..0992b9b8de8 100644
--- a/release/scripts/presets/render/TV_PAL.py
+++ b/release/scripts/presets/render/TV_PAL.py
@@ -4,4 +4,4 @@ bpy.context.scene.render_data.resolution_percentage = 100
bpy.context.scene.render_data.pixel_aspect_x = 54
bpy.context.scene.render_data.pixel_aspect_y = 51
bpy.context.scene.render_data.fps = 25
-bpy.context.scene.render_data.fps_base = 1 \ No newline at end of file
+bpy.context.scene.render_data.fps_base = 1
diff --git a/release/scripts/presets/render/TV_PAL_16_colon_9.py b/release/scripts/presets/render/TV_PAL_16_colon_9.py
index 756d34c8af3..9b85805352d 100644
--- a/release/scripts/presets/render/TV_PAL_16_colon_9.py
+++ b/release/scripts/presets/render/TV_PAL_16_colon_9.py
@@ -4,4 +4,4 @@ bpy.context.scene.render_data.resolution_percentage = 100
bpy.context.scene.render_data.pixel_aspect_x = 64
bpy.context.scene.render_data.pixel_aspect_y = 45
bpy.context.scene.render_data.fps = 25
-bpy.context.scene.render_data.fps_base = 1 \ No newline at end of file
+bpy.context.scene.render_data.fps_base = 1
diff --git a/release/scripts/ui/space_userpref.py b/release/scripts/ui/space_userpref.py
index ed41b897539..4c807fd9bd2 100644
--- a/release/scripts/ui/space_userpref.py
+++ b/release/scripts/ui/space_userpref.py
@@ -1476,13 +1476,13 @@ class USERPREF_PT_input(bpy.types.Panel):
for km in kc.keymaps:
filtered_items = [kmi for kmi in km.items if filter in kmi.name.lower()]
-
+
if len(filtered_items) != 0:
km = km.active()
layout.set_context_pointer("keymap", km)
col = layout.column()
-
+
row = col.row()
row.label(text=km.name, icon="DOT")
@@ -1496,7 +1496,7 @@ class USERPREF_PT_input(bpy.types.Panel):
for kmi in filtered_items:
self.draw_kmi(kc, km, kmi, col, 1)
-
+
# "Add New" at end of keymap item list
col = self.indented_layout(layout, 1)
subcol = col.split(percentage=0.2).column()
@@ -1813,18 +1813,18 @@ class WM_OT_keyitem_add(bpy.types.Operator):
wm = context.manager
km = context.keymap
kc = wm.default_keyconfig
-
+
if km.modal:
km.add_modal_item("", 'A', 'PRESS') # kmi
else:
km.add_item("none", 'A', 'PRESS') # kmi
-
+
# clear filter and expand keymap so we can see the newly added item
if kc.filter != '':
kc.filter = ''
km.items_expanded = True
km.children_expanded = True
-
+
return {'FINISHED'}
diff --git a/release/scripts/ui/space_view3d_toolbar.py b/release/scripts/ui/space_view3d_toolbar.py
index fb8a4cf335f..b2c8c39cb3a 100644
--- a/release/scripts/ui/space_view3d_toolbar.py
+++ b/release/scripts/ui/space_view3d_toolbar.py
@@ -498,14 +498,14 @@ class VIEW3D_PT_tools_brush(PaintPanel):
if not context.particle_edit_object:
col = layout.split().column()
row = col.row()
-
+
if context.sculpt_object and brush:
defaulttools = 8
elif context.texture_paint_object and brush:
defaulttools = 4
else:
defaulttools = 2
-
+
row.template_list(settings, "brushes", settings, "active_brush_index", rows=2, maxrows=defaulttools)
col.template_ID(settings, "brush", new="brush.add")
@@ -539,7 +539,7 @@ class VIEW3D_PT_tools_brush(PaintPanel):
elif context.sculpt_object and brush:
col = layout.column()
col.separator()
-
+
row = col.row(align=True)
row.prop(brush, "size", slider=True)
row.prop(brush, "use_size_pressure", toggle=True, text="")
@@ -642,7 +642,7 @@ class VIEW3D_PT_tools_brush_tool(PaintPanel):
sculpt = context.sculpt_object
col = layout.column(align=True)
-
+
if context.sculpt_object:
col.prop(brush, "sculpt_tool", expand=True)
elif context.texture_paint_object: