More automatic stuff.
authorMartin Poirier <theeth@yahoo.com>
Tue, 15 Sep 2009 19:53:18 +0000 (19:53 +0000)
committerMartin Poirier <theeth@yahoo.com>
Tue, 15 Sep 2009 19:53:18 +0000 (19:53 +0000)
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.

release/io/netrender/client.py
release/io/netrender/master.py
release/io/netrender/operators.py
release/io/netrender/slave.py
release/io/netrender/ui.py
release/io/netrender/utils.py

index d059387cfcf72a518872c060f1dc2a54266e4aff..a6cfb4e020d50e051dfefe070ad9bee65f3d3ae6 100644 (file)
@@ -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:
index 78e9243bc9d83f656aadf800e8c1301b067e106c..13e8b399d6c386072715c92cd6e2a604d410d224 100644 (file)
@@ -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()
index 655afa6631f1860c278ceaf1a691c482b1e5a715..928c2b9efaf0a1f4e9fae01435c43f87a4e5a777 100644 (file)
@@ -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)
\ No newline at end of file
+               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)
index c12c846231d44bbe17b60e5696dc4ba09e7d4b21..1dcb608931e45c06b0f56d7edb80689686b1edff 100644 (file)
@@ -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
index eee95bdac197dc217fa077976c8c78c1041aa54f..df2b6288fb0ca2d494655140ebdd38f4f29e0e0c 100644 (file)
@@ -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",
index db6646e69162183b23b26399fbf04841493e283a..72a2947274847f23d862f15c9c4cce1b61fb1c42 100644 (file)
@@ -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