More automatic stuff.

Server can now be set to broadcast on local network (every 10s, approximately 20 bytes of data) where client and slave can pick up its existence. This is on by default.

Default ip address is now "[default]", which means for the master that it will listen to all interface and for the clients and slave that they will automatically work out the master's address from its broadcast.
This commit is contained in:
Martin Poirier 2009-09-15 19:53:18 +00:00
parent fd664970f1
commit 22274d3807
6 changed files with 110 additions and 26 deletions

@ -138,12 +138,12 @@ class NetworkRenderEngine(bpy.types.RenderEngine):
print("UNKNOWN OPERATION MODE")
def render_master(self, scene):
server_address = (scene.network_render.server_address, scene.network_render.server_port)
httpd = master.RenderMasterServer(server_address, master.RenderHandler, scene.network_render.path)
httpd.timeout = 1
httpd.stats = self.update_stats
while not self.test_break():
httpd.handle_request()
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)
@ -152,6 +152,7 @@ class NetworkRenderEngine(bpy.types.RenderEngine):
netsettings = scene.network_render
self.update_stats("", "Network render client initiation")
conn = clientConnection(scene)
if conn:

@ -1,5 +1,5 @@
import sys, os
import http, http.client, http.server, urllib
import http, http.client, http.server, urllib, socket
import subprocess, shutil, time, hashlib
from netrender.utils import *
@ -529,7 +529,7 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
job_frame = int(self.headers['job-frame'])
buf = self.rfile.read(length)
f = open(job.save_path + "%04d" % job_frame + ".log", 'wb')
f = open(job.save_path + "%04d" % job_frame + ".log", 'ab')
f.write(buf)
f.close()
@ -613,3 +613,23 @@ class RenderMasterServer(http.server.HTTPServer):
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 broadcast:
if time.time() - start_time >= 10: # need constant here
print("broadcasting address")
s.sendto(bytes("%s:%i" % address, encoding='utf8'), 0, ('<broadcast>',address[1]))
start_time = time.time()

@ -1,6 +1,6 @@
import bpy
import sys, os
import http, http.client, http.server, urllib
import http, http.client, http.server, urllib, socket
from netrender.utils import *
import netrender.client as client
@ -316,4 +316,41 @@ class netclientdownload(bpy.types.Operator):
return ('FINISHED',)
def invoke(self, context, event):
return self.execute(context)
return self.execute(context)
@rnaOperator
class netclientscan(bpy.types.Operator):
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
__idname__ = "render.netclientscan"
__label__ = "Net Render Client Scan"
# List of operator properties, the attributes will be assigned
# to the class instance from the operator settings before calling.
__props__ = []
def poll(self, context):
return True
def execute(self, context):
netsettings = context.scene.network_render
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.settimeout(30)
s.bind(('', netsettings.server_port))
try:
buf, address = s.recvfrom(128)
print("received:", buf)
netsettings.server_address = address[0]
except socket.timeout:
print("no server info")
return ('FINISHED',)
def invoke(self, context, event):
return self.execute(context)

@ -105,6 +105,8 @@ def render_slave(engine, scene):
process = subprocess.Popen([sys.argv[0], "-b", job_full_path, "-o", JOB_PREFIX + "######", "-E", "BLENDER_RENDER", "-F", "MULTILAYER"] + frame_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
headers = {"job-id":job.id, "slave-id":slave_id}
cancelled = False
stdout = bytes()
run_t = time.time()
@ -113,10 +115,18 @@ def render_slave(engine, scene):
current_t = time.time()
cancelled = engine.test_break()
if current_t - run_t > CANCEL_POLL_SPEED:
# update logs. Eventually, it should support one log file for many frames
for frame in job.frames:
headers["job-frame"] = str(frame.number)
conn.request("PUT", "log", stdout, headers=headers)
response = conn.getresponse()
stdout = bytes()
run_t = current_t
if testCancel(conn, job.id):
cancelled = True
else:
run_t = current_t
if cancelled:
# kill process if needed
@ -132,6 +142,13 @@ def render_slave(engine, scene):
print("status", status)
# flush the rest of the logs
if stdout:
for frame in job.frames:
headers["job-frame"] = str(frame.number)
conn.request("PUT", "log", stdout, headers=headers)
response = conn.getresponse()
headers = {"job-id":job.id, "slave-id":slave_id, "job-time":str(avg_t)}
if status == 0: # non zero status is error
@ -150,12 +167,6 @@ def render_slave(engine, scene):
# send error result back to server
conn.request("PUT", "render", headers=headers)
response = conn.getresponse()
for frame in job.frames:
headers["job-frame"] = str(frame.number)
# send log in any case
conn.request("PUT", "log", stdout, headers=headers)
response = conn.getresponse()
else:
if timeout < MAX_TIMEOUT:
timeout += INCREMENT_TIMEOUT

@ -48,17 +48,24 @@ class SCENE_PT_network_settings(RenderButtonsPanel):
col = split.column()
col.itemO("render.netclientanim", icon='ICON_RENDER_ANIMATION', text="Animaton on network")
if scene.network_render.mode == "RENDER_CLIENT":
col.itemO("render.netclientanim", icon='ICON_RENDER_ANIMATION', text="Animaton on network")
col.itemR(scene.network_render, "mode")
col.itemR(scene.network_render, "path")
col.itemR(scene.network_render, "server_address")
col.itemR(scene.network_render, "server_port")
col.itemR(scene.network_render, "path")
if scene.network_render.mode == "RENDER_MASTER":
col.itemR(scene.network_render, "server_broadcast")
else:
col.itemO("render.netclientscan", icon="ICON_FILE_REFRESH", text="")
if scene.network_render.mode == "RENDER_CLIENT":
col.itemR(scene.network_render, "chunks")
col.itemR(scene.network_render, "priority")
col.itemR(scene.network_render, "job_name")
col.itemO("render.netclientsend", text="send job to server")
col.itemR(scene.network_render, "job_name")
col.itemR(scene.network_render, "priority")
col.itemR(scene.network_render, "chunks")
@rnaType
class SCENE_PT_network_slaves(RenderButtonsPanel):
@ -192,7 +199,7 @@ NetRenderSettings.StringProperty( attr="server_address",
name="Server address",
description="IP or name of the master render server",
maxlen = 128,
default = "127.0.0.1")
default = "[default]")
NetRenderSettings.IntProperty( attr="server_port",
name="Server port",
@ -201,6 +208,11 @@ NetRenderSettings.IntProperty( attr="server_port",
min=1,
max=65535)
NetRenderSettings.BoolProperty( attr="server_broadcast",
name="Broadcast server address",
description="broadcast server address on local network",
default = True)
NetRenderSettings.StringProperty( attr="path",
name="Path",
description="Path for temporary files",

@ -22,9 +22,12 @@ def rnaOperator(rna_op):
return rna_op
def clientConnection(scene):
netrender = scene.network_render
netsettings = scene.network_render
conn = http.client.HTTPConnection(netrender.server_address, netrender.server_port)
if netsettings.server_address == "[default]":
bpy.ops.render.netclientscan()
conn = http.client.HTTPConnection(netsettings.server_address, netsettings.server_port)
if clientVerifyVersion(conn):
return conn