# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### import os import shutil from netrender.utils import * import netrender.model import json #import rpdb2 # bitwise definition of the different files type CACHE_FILES=1 FLUID_FILES=2 OTHER_FILES=4 src_folder = os.path.split(__file__)[0] #function to return counter of different type of files # job: the job that contain files def countFiles(job): tot_cache = 0 tot_fluid = 0 tot_other = 0 for file in job.files: if file.filepath.endswith(".bphys"): tot_cache += 1 elif file.filepath.endswith((".bobj.gz", ".bvel.gz")): tot_fluid += 1 elif not file == job.files[0]: tot_other += 1 return tot_cache,tot_fluid,tot_other; def get(handler): def output(text): handler.wfile.write(bytes(text, encoding='utf8')) def head(title, refresh = False): output("") if refresh: output("") output("") output("") output(title) output("") output("") def link(text, url, script=""): return "%s" % (url, script, text) def tag(name, text, attr=""): return "<%s %s>%s" % (name, attr, text, name) def startTable(border=1, class_style = None, caption = None): output("") if caption: output("" % caption) def headerTable(*headers): output("") for c in headers: output("") output("") def rowTable(*data, id = None, class_style = None, extra = None): output("") for c in data: output("") output("") def endTable(): output("
%s
" + c + "
" + str(c) + "
") def checkbox(title, value, script=""): return """""" % (title, "checked" if value else "", ("onclick=\"%s\"" % script) if script else "") def sendjson(message): handler.send_head(content = "application/json") output(json.dumps(message,sort_keys=False)) def sendFile(filename,content_type): f = open(os.path.join(src_folder,filename), 'rb') handler.send_head(content = content_type) shutil.copyfileobj(f, handler.wfile) f.close() # return serialized version of job for html interface # job: the base job # includeFiles: boolean to indicate if we want file to be serialized too into job # includeFrames; boolean to indicate if we want frame to be serialized too into job def gethtmlJobInfo(job,includeFiles=True,includeFrames=True): if (job): results = job.framesStatus() serializedJob = job.serialize(withFiles=includeFiles, withFrames=includeFrames) serializedJob["p_rule"] = handler.server.balancer.applyPriorities(job) serializedJob["e_rule"] = handler.server.balancer.applyExceptions(job) serializedJob["wait"] = int(time.time() - job.last_dispatched) if job.status != netrender.model.JOB_FINISHED else "N/A" serializedJob["length"] = len(job); serializedJob["done"] = results[netrender.model.FRAME_DONE] serializedJob["dispatched"] = results[netrender.model.FRAME_DISPATCHED] serializedJob["error"] = results[netrender.model.FRAME_ERROR] tot_cache, tot_fluid, tot_other = countFiles(job) serializedJob["totcache"] = tot_cache serializedJob["totfluid"] = tot_fluid serializedJob["totother"] = tot_other serializedJob["wktime"] = (time.time()-job.start_time ) if job.status != netrender.model.JOB_FINISHED else (job.finish_time-job.start_time) else: serializedJob={"name":"invalid job"} return serializedJob; # return serialized files based on cumulative file_type # job_id: id of the job # message: serialized content # file_type: any combinaison of CACHE_FILE,FLUID_FILES, OTHER_FILES def getFiles(job_id,message,file_type): job=handler.server.getJobID(job_id) print ("job.files.length="+str(len(job.files))) for file in job.files: filedata=file.serialize() filedata["name"] = os.path.split(file.filepath)[1] if file.filepath.endswith(".bphys") and (file_type & CACHE_FILES): message.append(filedata); continue if file.filepath.endswith((".bobj.gz", ".bvel.gz")) and (file_type & FLUID_FILES): message.append(filedata); continue if (not file == job.files[0]) and (file_type & OTHER_FILES) and (not file.filepath.endswith((".bobj.gz", ".bvel.gz"))) and not file.filepath.endswith(".bphys"): message.append(filedata); continue if handler.path == "/html/netrender.js": sendFile("netrender.js","text/javascript") elif handler.path == "/html/netrender.css": sendFile("netrender.css","text/css") elif handler.path =="/html/newui": sendFile("newui.html","text/html") elif handler.path.startswith("/html/js"): path, filename = os.path.split(handler.path) sendFile("js/"+filename,"text/javascript") elif handler.path.startswith("/html/css/images"): path, filename = os.path.split(handler.path) sendFile("css/images/"+filename,"image/png") elif handler.path.startswith("/html/css"): path, filename = os.path.split(handler.path) sendFile("css/"+filename,"text/css") # return all master rules information elif handler.path == "/html/rules": message = [] for rule in handler.server.balancer.rules: message.append(rule.serialize()) for rule in handler.server.balancer.priorities: message.append(rule.serialize()) for rule in handler.server.balancer.exceptions: message.append(rule.serialize()) sendjson(message) #return all slaves list elif handler.path == "/html/slaves": message = [] for slave in handler.server.slaves: serializedSlave = slave.serialize() if slave.job: serializedSlave["job_name"] = slave.job.name serializedSlave["job_id"] = slave.job.id else: serializedSlave["job_name"] = "None" serializedSlave["job_id"] = "0" message.append(serializedSlave) sendjson(message) # return all job list elif handler.path == "/html/jobs": message = [] for job in handler.server.jobs: if job: message.append(gethtmlJobInfo(job, False, False)) sendjson(message) #return a job information elif handler.path.startswith("/html/job_"): job_id = handler.path[10:] job = handler.server.getJobID(job_id) message = [] if job: message.append(gethtmlJobInfo(job, includeFiles=False)) sendjson(message) # return all frames for a job elif handler.path.startswith("/html/frames_"): job_id = handler.path[13:] job = handler.server.getJobID(job_id) message = [] if job: for f in job.frames: message.append(f.serialize()) sendjson(message) # return physic cache files elif handler.path.startswith("/html/cachefiles_"): job_id = handler.path[17:] message = [] getFiles(job_id, message, CACHE_FILES); sendjson(message) #return fluid cache files elif handler.path.startswith("/html/fluidfiles_"): job_id = handler.path[17:] message = [] getFiles(job_id, message, FLUID_FILES); sendjson(message) #return list of other files ( images, sequences ...) elif handler.path.startswith("/html/otherfiles_"): job_id = handler.path[17:] message = [] getFiles(job_id, message, OTHER_FILES); sendjson(message) # return blend file info elif handler.path.startswith("/html/blendfile_"): job_id = handler.path[16:] job = handler.server.getJobID(job_id) message = [] if job: if job.files: message.append(job.files[0].serialize()) sendjson(message) # return black listed slaves for a job elif handler.path.startswith("/html/blacklist_"): job_id = handler.path[16:] job = handler.server.getJobID(job_id) message = [] if job: for slave_id in job.blacklist: slave = handler.server.slaves_map.get(slave_id, None) message.append(slave.serialize()) sendjson(message) # return all slaves currently assigned to a job elif handler.path.startswith("/html/slavesjob_"): job_id = handler.path[16:] job = handler.server.getJobID(job_id) message = [] if job: for slave in handler.server.slaves: if slave.job and slave.job == job: message.append(slave.serialize()) sendjson(message) # here begin code for simple ui elif handler.path == "/html" or handler.path == "/": handler.send_head(content = "text/html") head("NetRender", refresh = True) output("

Jobs

") startTable() headerTable( " ", "id", "name", "category", "tags", "type", "chunks", "priority", "usage", "wait", "status", "total", "done", "dispatched", "error", "priority", "exception", "started", "finished" ) handler.server.balance() for job in handler.server.jobs: results = job.framesStatus() time_finished = job.time_finished time_started = job.time_started rowTable( """""" % job.id + """""" % job.id + """""" % job.id, job.id, link(job.name, "/html/job" + job.id), job.category if job.category else "None", ";".join(sorted(job.tags)) if job.tags else "None", "%s [%s]" % (netrender.model.JOB_TYPES[job.type], netrender.model.JOB_SUBTYPES[job.subtype]), str(job.chunks) + """""" % (job.id, job.chunks + 1) + """""" % (job.id, job.chunks - 1, "disabled=True" if job.chunks == 1 else ""), str(job.priority) + """""" % (job.id, job.priority + 1) + """""" % (job.id, job.priority - 1, "disabled=True" if job.priority == 1 else ""), "%0.1f%%" % (job.usage * 100), "%is" % int(time.time() - job.last_dispatched) if job.status != netrender.model.JOB_FINISHED else "N/A", job.statusText(), len(job), results[netrender.model.FRAME_DONE], results[netrender.model.FRAME_DISPATCHED], str(results[netrender.model.FRAME_ERROR]) + """""" % (job.id, "disabled=True" if not results[netrender.model.FRAME_ERROR] else ""), "yes" if handler.server.balancer.applyPriorities(job) else "no", "yes" if handler.server.balancer.applyExceptions(job) else "no", time.ctime(time_started) if time_started else "Not Started", time.ctime(time_finished) if time_finished else "Not Finished" ) endTable() output("

Slaves

") startTable() headerTable("name", "address", "tags", "last seen", "stats", "job") for slave in handler.server.slaves: rowTable(slave.name, slave.address[0], ";".join(sorted(slave.tags)) if slave.tags else "All", time.ctime(slave.last_seen), slave.stats, link(slave.job.name, "/html/job" + slave.job.id) if slave.job else "None") endTable() output("

Configuration

") output("""""") output("
") output(link("new interface", "/html/newui")) startTable(caption = "Rules", class_style = "rules") headerTable("type", "enabled", "description", "limit") for rule in handler.server.balancer.rules: rowTable( "rating", checkbox("", rule.enabled, "balance_enable('%s', '%s')" % (rule.id(), str(not rule.enabled).lower())), rule, rule.str_limit() + """""" % (rule.id(), str(rule.limit)) if hasattr(rule, "limit") else " " ) for rule in handler.server.balancer.priorities: rowTable( "priority", checkbox("", rule.enabled, "balance_enable('%s', '%s')" % (rule.id(), str(not rule.enabled).lower())), rule, rule.str_limit() + """""" % (rule.id(), str(rule.limit)) if hasattr(rule, "limit") else " " ) for rule in handler.server.balancer.exceptions: rowTable( "exception", checkbox("", rule.enabled, "balance_enable('%s', '%s')" % (rule.id(), str(not rule.enabled).lower())), rule, rule.str_limit() + """""" % (rule.id(), str(rule.limit)) if hasattr(rule, "limit") else " " ) endTable() output("") elif handler.path.startswith("/html/job"): handler.send_head(content = "text/html") job_id = handler.path[9:] head("NetRender") output(link("Back to Main Page", "/html")) job = handler.server.getJobID(job_id) if job: output("

Job Information

") job.initInfo() startTable() rowTable("resolution", "%ix%i at %i%%" % job.resolution) rowTable("tags", ";".join(sorted(job.tags)) if job.tags else "None") rowTable("results", link("download all", resultURL(job_id))) endTable() if job.type == netrender.model.JOB_BLENDER: output("

Files

") startTable() headerTable("path") tot_cache = 0 tot_fluid = 0 tot_other = 0 rowTable(job.files[0].original_path) tot_cache, tot_fluid, tot_other = countFiles(job) if tot_cache > 0: rowTable("%i physic cache files" % tot_cache, class_style = "toggle", extra = "onclick='toggleDisplay(".cache", "none", "table-row")'") 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(".fluid", "none", "table-row")'") for file in job.files: if file.filepath.endswith((".bobj.gz", ".bvel.gz")): rowTable(os.path.split(file.filepath)[1], class_style = "fluid") if tot_other > 0: rowTable("%i other files" % tot_other, class_style = "toggle", extra = "onclick='toggleDisplay(".other", "none", "table-row")'") for file in job.files: if ( not file.filepath.endswith(".bphys") and not file.filepath.endswith((".bobj.gz", ".bvel.gz")) and not file == job.files[0] ): rowTable(file.filepath, class_style = "other") endTable() elif job.type == netrender.model.JOB_VCS: output("

Versioning

") startTable() rowTable("System", job.version_info.system.name) rowTable("Remote Path", job.version_info.rpath) rowTable("Working Path", job.version_info.wpath) rowTable("Revision", job.version_info.revision) rowTable("Render File", job.files[0].filepath) endTable() if job.blacklist: output("

Blacklist

") startTable() headerTable("name", "address") for slave_id in job.blacklist: slave = handler.server.slaves_map.get(slave_id, None) if slave: rowTable(slave.name, slave.address[0]) endTable() output("

Transitions

") startTable() headerTable("Event", "Time") for transition, time_value in job.transitions: rowTable(transition, time.ctime(time_value)) endTable() output("

Frames

") startTable() if job.hasRenderResult(): 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 " ", link("view log", logURL(job_id, frame.number)) if frame.log_path else " ", link("view result", renderURL(job_id, frame.number)) + " [" + tag("span", "show", attr="class='thumb' onclick='showThumb(%s, %i)'" % (job.id, frame.number)) + "]" if frame.status == netrender.model.FRAME_DONE else " ", "" % (frame.number, job.id, frame.number) ) else: headerTable("no", "status", "process time", "slave", "log") for frame in job.frames: rowTable( frame.number, frame.statusText(), "%.1fs" % frame.time, frame.slave.name if frame.slave else " ", link("view log", logURL(job_id, frame.number)) if frame.log_path else " " ) endTable() else: output("no such job") output(link("Back to Main Page", "/html")) output("")