netrender: edit balancing rules limits and enable/disable them from the web interface

This commit is contained in:
Martin Poirier 2009-12-31 19:11:46 +00:00
parent 09994fb444
commit 7c4e510492
4 changed files with 152 additions and 59 deletions

@ -22,14 +22,23 @@ from netrender.utils import *
import netrender.model
class RatingRule:
def __init__(self):
self.enabled = True
def rate(self, job):
return 0
class ExclusionRule:
def __init__(self):
self.enabled = True
def test(self, job):
return False
class PriorityRule:
def __init__(self):
self.enabled = True
def test(self, job):
return False
@ -39,6 +48,19 @@ class Balancer:
self.priorities = []
self.exceptions = []
def ruleByID(self, rule_id):
for rule in self.rules:
if id(rule) == rule_id:
return rule
for rule in self.priorities:
if id(rule) == rule_id:
return rule
for rule in self.exceptions:
if id(rule) == rule_id:
return rule
return None
def addRule(self, rule):
self.rules.append(rule)
@ -49,18 +71,18 @@ class Balancer:
self.exceptions.append(exception)
def applyRules(self, job):
return sum((rule.rate(job) for rule in self.rules))
return sum((rule.rate(job) for rule in self.rules if rule.enabled))
def applyPriorities(self, job):
for priority in self.priorities:
if priority.test(job):
if priority.enabled and priority.test(job):
return True # priorities are first
return False
def applyExceptions(self, job):
for exception in self.exceptions:
if exception.test(job):
if exception.enabled and exception.test(job):
return True # exceptions are last
return False
@ -82,18 +104,20 @@ class Balancer:
class RatingUsage(RatingRule):
def __str__(self):
return "Usage rating"
return "Usage per job"
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):
super().__init__()
self.getJobs = get_jobs
def __str__(self):
return "Usage per category"
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])
@ -102,28 +126,36 @@ class RatingUsageByCategory(RatingRule):
return total_category_usage / maximum_priority
class NewJobPriority(PriorityRule):
def __init__(self, limit = 1):
super().__init__()
self.limit = limit
def setLimit(self, value):
self.limit = int(value)
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 __init__(self, limit = 10):
super().__init__()
self.limit = limit
def setLimit(self, value):
self.limit = int(value)
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
@ -135,16 +167,20 @@ class ExcludeQueuedEmptyJob(ExclusionRule):
return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0
class ExcludeSlavesLimit(ExclusionRule):
def __init__(self, count_jobs, count_slaves, limit = 0.75):
super().__init__()
self.count_jobs = count_jobs
self.count_slaves = count_slaves
self.limit = limit
def setLimit(self, value):
self.limit = float(value)
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 )

@ -456,6 +456,29 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
# invalid url
self.send_head(http.client.NO_CONTENT)
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
elif self.path == "/balance_limit":
length = int(self.headers['content-length'])
info_map = eval(str(self.rfile.read(length), encoding='utf8'))
for rule_id, limit in info_map.items():
try:
rule = self.server.balancer.ruleByID(rule_id)
if rule:
rule.setLimit(limit)
except:
pass # invalid type
self.send_head()
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
elif self.path == "/balance_enable":
length = int(self.headers['content-length'])
info_map = eval(str(self.rfile.read(length), encoding='utf8'))
for rule_id, enabled in info_map.items():
rule = self.server.balancer.ruleByID(rule_id)
if rule:
rule.enabled = enabled
self.send_head()
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
elif self.path.startswith("/cancel"):
match = cancel_pattern.match(self.path)

@ -80,6 +80,9 @@ def get(handler):
def endTable():
output("</table>")
def checkbox(title, value, script=""):
return """<input type="checkbox" title="%s" %s %s>""" % (title, "checked" if value else "", ("onclick=\"%s\"" % script) if script else "")
if handler.path == "/html/netrender.js":
f = open(os.path.join(src_folder, "netrender.js"), 'rb')
@ -105,16 +108,34 @@ def get(handler):
startTable(caption = "Rules", class_style = "rules")
headerTable("type", "description", "limit")
headerTable("type", "enabled", "description", "limit")
for rule in handler.server.balancer.rules:
rowTable("rating", rule, rule.str_limit() if hasattr(rule, "limit") else "&nbsp;")
rowTable(
"rating",
checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))),
rule,
rule.str_limit() +
"""<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.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;")
rowTable(
"priority",
checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))),
rule,
rule.str_limit() +
"""<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.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;")
rowTable(
"exception",
checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))),
rule,
rule.str_limit() +
"""<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
)
endTable()
@ -132,49 +153,49 @@ def get(handler):
startTable()
headerTable(
"&nbsp;",
"id",
"name",
"category",
"chunks",
"priority",
"usage",
"wait",
"status",
"length",
"done",
"dispatched",
"error",
"first",
"exception"
)
"&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)
)
"""<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()

@ -11,6 +11,19 @@ function edit(id, info)
request("/edit_" + id, info)
}
function balance_edit(id, old_value)
{
var new_value = prompt("New limit", old_value);
if (new_value != null && new_value != "") {
request("/balance_limit", "{" + id + ":'" + new_value + "'}");
}
}
function balance_enable(id, value)
{
request("/balance_enable", "{" + id + ":" + value + "}");
}
function returnObjById( id )
{
if (document.getElementById)