[rhmessaging-commits] rhmessaging commits: r3168 - mgmt/trunk/cumin/python/cumin.
rhmessaging-commits at lists.jboss.org
rhmessaging-commits at lists.jboss.org
Wed Mar 18 14:31:42 EDT 2009
Author: eallen
Date: 2009-03-18 14:31:42 -0400 (Wed, 18 Mar 2009)
New Revision: 3168
Modified:
mgmt/trunk/cumin/python/cumin/__init__.py
mgmt/trunk/cumin/python/cumin/charts.py
mgmt/trunk/cumin/python/cumin/model.py
mgmt/trunk/cumin/python/cumin/pool.py
mgmt/trunk/cumin/python/cumin/pool.strings
mgmt/trunk/cumin/python/cumin/stat.py
mgmt/trunk/cumin/python/cumin/stat.strings
mgmt/trunk/cumin/python/cumin/system.py
mgmt/trunk/cumin/python/cumin/system.strings
Log:
Remove old slot visualization
Added new slot visualization
Modified: mgmt/trunk/cumin/python/cumin/__init__.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/__init__.py 2009-03-18 18:31:00 UTC (rev 3167)
+++ mgmt/trunk/cumin/python/cumin/__init__.py 2009-03-18 18:31:42 UTC (rev 3168)
@@ -15,7 +15,7 @@
from model import CuminModel, ModelPage, CallPage
from demo import DemoData
from page import MainPage
-from stat import StatChartPage
+from stat import StatChartPage, SlotMapPage
from action import ActionPage
from user import LoginPage, UserSession, UserSessionExpireThread
from datetime import timedelta
@@ -49,6 +49,7 @@
self.add_page(CallPage(self, "call.xml"))
self.add_page(ActionPage(self, "actions.html"))
self.add_page(StatChartPage(self, "stats.png"))
+ self.add_page(SlotMapPage(self, "slots.png"))
unprotected = set()
Modified: mgmt/trunk/cumin/python/cumin/charts.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/charts.py 2009-03-18 18:31:00 UTC (rev 3167)
+++ mgmt/trunk/cumin/python/cumin/charts.py 2009-03-18 18:31:42 UTC (rev 3168)
@@ -3,12 +3,142 @@
from cairo import *
from random import random
from time import time
+from math import sqrt, pi, ceil
from formats import *
from util import *
log = logging.getLogger("cumin.chart")
+class HeatMapChart(object):
+ def __init__(self, width=400, height=400):
+ self.width = width # final width
+ self.height = height
+ self.max_width = width # max width
+ self.max_height = height
+ self.cols = 0 # columns
+ self.rows = 0
+ self.max_size = 28 # size of each slot
+ self.min_size = 2
+ self.min_sphere_size = 8 # use boxes if below this size
+ self.min_font = 10
+ self.min_shadow = 14
+ self.surface = None
+
+ def plot_dot(self, interior, width, height):
+ pnumbx = (115 / 128.0) * width / 2.5
+ pnumby = (102 / 128.0) * width / 2.5
+ pnumbr = (14 / 128.0) * width
+ shadox = (102 / 128.0) * width
+ shadoy = (102 / 128.0) * width
+ arc = 0.5 * width
+
+ surface = ImageSurface(FORMAT_ARGB32, width, height)
+ cr = Context(surface)
+ cr.set_line_width(1)
+
+ radial = RadialGradient(pnumbx, pnumby, pnumbr, shadox, shadoy, width)
+ radial.add_color_stop_rgba(0, 1, 1, 1, 1)
+ radial.add_color_stop_rgb(1, *interior)
+ cr.set_source(radial)
+ cr.arc(arc, arc, arc, 0, 2.0 * pi)
+ cr.fill()
+ return surface
+
+ def plot_slots(self, slots, zl, zx, zy):
+ count = len(slots)
+ slot_size = self.slot_size(count, zl)
+ x = 0
+ y = 0
+ pnumbx = (115 / 128.0) * slot_size / 2.5
+ pnumby = (102 / 128.0) * slot_size / 2.5
+ pnumbr = (14 / 128.0) * slot_size
+ shadox = (102 / 128.0) * slot_size
+ shadoy = (102 / 128.0) * slot_size
+ arc = 0.5 * slot_size
+ col = 0
+ self.rows = 1
+ # the width and height depend on the number of slots
+ self.surface = ImageSurface(FORMAT_ARGB32, self.width, self.height)
+ cr = Context(self.surface)
+ cr.set_line_width(1)
+ i = 0
+ for slot in slots:
+ i = i + 1
+ interior, state = slot[:2]
+
+ #if x + slot_size >= self.width:
+ if col >= self.cols:
+ x = 0
+ y = y + slot_size
+ col = 0
+ self.rows = self.rows + 1
+
+ # draw dots if they are big enough
+ if slot_size >= self.min_sphere_size:
+ radial = RadialGradient(x + pnumbx,
+ y + pnumby,
+ pnumbr, x + shadox,
+ y + shadoy, slot_size)
+ radial.add_color_stop_rgba(0, 1, 1, 1, 1)
+ radial.add_color_stop_rgb(1, *interior)
+ cr.set_source(radial)
+ cr.arc(x + arc, y + arc, arc, 0, 2.0 * pi)
+ cr.fill()
+ else:
+ # just draw squares
+ cr.set_source_rgb(*interior)
+ cr.rectangle(x, y, slot_size - 1, slot_size - 1)
+ cr.fill()
+
+ # draw the state if the slots are big enough
+ if state and slot_size >= self.min_font:
+ #cr.set_source_rgb(interior[0]/10, interior[1]/10, interior[2]/10)
+ cr.set_source_rgb(1, 1, 1)
+ cr.select_font_face("verdana", FONT_SLANT_NORMAL, FONT_WEIGHT_BOLD)
+ cr.set_font_size(slot_size/3)
+ width, height = cr.text_extents(state[0])[2:4]
+ dx = (slot_size / 2) - (width / 2)
+ dy = (slot_size / 2) + (height / 2)
+
+ if slot_size >= self.min_sphere_size:
+ cr.set_source_rgb(*interior)
+ else:
+ cr.set_source_rgb(1,1,1)
+ cr.move_to(x + dx, y + dy)
+ cr.show_text(state[0])
+
+ x = x + slot_size
+ col = col + 1
+ return slot_size
+
+ def slot_size(self, count, zoom=1):
+ sq = sqrt(count)
+ cols = int(sq)
+ if sq > cols:
+ cols = cols + 1
+ size = int(self.width * zoom / cols)
+ if size < 2:
+ size = 2
+ if size > self.max_size * zoom:
+ size = self.max_size * zoom
+
+ #if size * cols < self.width:
+ # if size < self.max_size:
+ # size = size + 1
+
+ self.width = (size * cols) + 1
+ #if self.width < self.max_width:
+ # self.width = self.max_width
+ self.height = (ceil(count * 1.0 / cols) * size) + 1
+ #if self.height < self.max_height:
+ # self.height = self.max_height
+ self.cols = cols
+ return size
+
+ def write(self, writer):
+ self.surface.write_to_png(writer)
+
class TimeSeriesChart(object):
def __init__(self, width, height):
self.width = width - 40
Modified: mgmt/trunk/cumin/python/cumin/model.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/model.py 2009-03-18 18:31:00 UTC (rev 3167)
+++ mgmt/trunk/cumin/python/cumin/model.py 2009-03-18 18:31:42 UTC (rev 3168)
@@ -2,7 +2,7 @@
from formats import *
from job import *
from parameters import *
-from pool import PoolSlotSet, PoolMachineSet, PoolJobStats
+from pool import PoolSlotSet, PoolJobStats
from slot import SlotStatSet
from struct import unpack, calcsize
from system import SystemSlotSet
@@ -214,6 +214,7 @@
self.summary = False
self.navigable = True
self.aggregate = False
+ self.visualization = False
self.cumin_class.add_action(self)
@@ -523,6 +524,11 @@
def get_object_name(self, object):
return object.name
+ def get_visualization_action(self):
+ for action in self.actions:
+ if action.visualization:
+ return action
+
def write_event_xml(self, writer, object):
writer.write("<events errors=\"%i\" warnings=\"%i\"/>" % (0, 0))
@@ -592,75 +598,13 @@
self.itemset = None
- def get_xml_response(self, session, object, *args):
- boxes = self.get_boxes(session, object, *args)
- fields = self.get_field_tuples(session)
+ def get_boxes(self, session, object):
+ if self.itemset:
+ box_list = self.itemset.get_items(session, object)
+ return box_list
+ else:
+ return ()
- writer = Writer()
- writer.write("<%ss>" % self.itemset.name)
- for box in boxes:
- writer.write("<%s id='%s'" % (self.itemset.name, str(box["id"])))
- for field, desc in fields:
- writer.write(" %s='%s'" % (field, box[field]))
- if fields:
- extra = self.get_extra(session, box)
- for name, value in extra:
- writer.write(" %s='%s'" % (name, value))
- writer.write(" color='%s'/>" % self.get_color(session, box))
- writer.write("</%ss>" %self.itemset.name)
- return writer.to_string()
-
- def get_boxes(self, session, object, *args):
- if args:
- self.itemset.set_args(session, *args)
- box_list = self.itemset.get_items(session, object)
- return box_list
-
- def get_color(self, session, box):
- return ""
-
- def get_extra(self, session, box):
- return list()
-
- def get_colors(self):
- return list()
-
-class SlotVisualization(Visualization):
- # list of status/colors in the order we want them displayed
- # in the legend
- load_colors = [("Idle", "clear"),
- ("Busy", "green"),
- ("Suspended", "red"),
- ("Vacating", "orange"),
- ("Killing", "blue"),
- ("Benchmarking", "yellow"),
- ("Unknown", "grey")]
-
- load_states = [("Unclaimed", "Unclaimed"),
- ("Claimed", "Claimed"),
- ("Owner", "Owner"),
- ("Matched", "Matched"),
- ("Preempting", "Preempting")]
-
- def get_field_tuples(self, session):
- return [("name", "Name"), ("machine", "Machine"), ("job_id", "Job"), ("state", "State"), ("activity", "Activity")]
-
- def get_extra(self, session, slot):
- return [("jid", slot["jid"])]
-
- def get_color(self, session, slot):
- activity = slot["activity"]
- for status, color in self.load_colors:
- if status == activity:
- return color
- return "grey"
-
- def get_colors(self):
- return self.load_colors
-
- def get_states(self):
- return self.load_states
-
class CuminSystem(RemoteClass):
def __init__(self, model):
super(CuminSystem, self).__init__(model, "system", Sysimage, SysimageStats)
@@ -712,6 +656,7 @@
action = self.SystemSlotVisualization(self, "slots")
action.navigable = False
+ action.visualization = True
#action = CuminAction(self, "ping")
#action.title = "Send Ping"
@@ -735,7 +680,7 @@
text = value and "%0.2f" % value or ""
return text
- class SystemSlotVisualization(SlotVisualization):
+ class SystemSlotVisualization(Visualization):
def __init__(self, cls, name):
super(CuminSystem.SystemSlotVisualization, self).__init__(cls, name)
@@ -1998,10 +1943,8 @@
action = self.PoolSlotVisualization(self, "slots")
action.navigable = False
+ action.visualization = True
- action = self.VisMachine(self, "machines")
- action.navigable = False
-
action = self.SeeAllPools(self, "allpools")
action.summary = True
@@ -2077,34 +2020,14 @@
return total and "%2.2f" % percent or "-"
return ""
- class PoolSlotVisualization(SlotVisualization):
+ class PoolSlotVisualization(Visualization):
def __init__(self, cls, name):
super(CuminPool.PoolSlotVisualization, self).__init__(cls, name)
self.itemset = self.ModelPoolSlotSet(cls.model.app, "slot")
self.itemset.items.path = "CuminPool.PoolSlotVisualization.slot"
- def set_machine(self, session, machine):
- self.itemset.set_machine(session, machine)
-
- def get_machine(self, session):
- return self.itemset.get_machine(session)
-
class ModelPoolSlotSet(PoolSlotSet):
- def __init__(self, app, name):
- super(CuminPool.PoolSlotVisualization.ModelPoolSlotSet, self).__init__(app, name)
-
- self.__machine = None
-
- def set_args(self, session, machine):
- self.set_machine(session, machine)
-
- def set_machine(self, session, machine):
- self.__machine= machine
-
- def get_machine(self, session):
- return self.__machine
-
def render_sql_where(self, session, pool):
elems = list()
elems.append("s.pool = %(pool)s")
@@ -2112,16 +2035,10 @@
if recent:
elems.append(recent)
- if self.__machine:
- elems.append("machine = %(machine)s")
-
return "where %s" % " and ".join(elems)
def get_sql_values(self, session, pool):
values = {"pool": pool.id}
- machine = self.__machine
- if machine:
- values["machine"] = machine
return values
def render_sql_limit(self, session, *args):
@@ -2169,36 +2086,6 @@
values = {"pool": pool.id}
return values
- class VisMachine(Visualization):
- load_colors = [("Idle", "clear"),
- ("> 0%", "green"),
- ("> 25%", "yellow"),
- ("> 50%", "blue"),
- ("> 75%", "green3")]
-
- def __init__(self, cls, name):
- super(CuminPool.VisMachine, self).__init__(cls, name)
-
- self.itemset = PoolMachineSet(cls.model.app, "machine")
- self.itemset.items.path = "CuminPool.VisMachine.machine"
-
- def get_field_tuples(self, session):
- return [("machine", "Machine"), ("busy", "Busy Slots"), ("idle", "Idle Slots")]
-
- def get_color(self, session, machine):
- total = float(machine["total"])
- busy = float(machine["busy"])
- work = total > 0.0 and busy / total or 0.0
-
- percents = [0.0, 0.25, 0.5, 0.75, 1.0]
- for i in range(len(percents)):
- if work <= percents[i]:
- return self.load_colors[i][1]
- return "grey"
-
- def get_colors(self):
- return self.load_colors
-
class CuminLimit(CuminClass):
def __init__(self, model):
super(CuminLimit, self).__init__ \
@@ -2326,9 +2213,6 @@
prop.description = "The submitter of the job"
prop.writable = False
- prop = AdProperty(self, "BufferBlockSize")
- prop.example = "32768"
-
### Condor Info Group
prop = AdProperty(self, "CondorVersion")
prop.group = "Condor Info"
@@ -2388,6 +2272,10 @@
prop.writable = False
######## Other
+ prop = AdProperty(self, "BufferBlockSize")
+ prop.example = "32768"
+ prop.group = "Other"
+
prop = AdProperty(self, "ClusterId")
prop.description = "The id of the cluster the job belongs to"
prop.group = "Other"
Modified: mgmt/trunk/cumin/python/cumin/pool.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/pool.py 2009-03-18 18:31:00 UTC (rev 3167)
+++ mgmt/trunk/cumin/python/cumin/pool.py 2009-03-18 18:31:42 UTC (rev 3168)
@@ -18,6 +18,7 @@
from negotiator import NegotiatorSet, NegotiatorFrame, NegStart, NegStop
from limits import LimitsSet, LimitsFrame
from slot import SlotSet, MachineSet, SlotStatSet
+from visualizations import SlotMap
strings = StringCatalog(__file__)
log = logging.getLogger("cumin.pool")
@@ -134,6 +135,10 @@
self.job_group_remove = JobGroupRemove(app, "jobgroupremove")
self.add_mode(self.job_group_remove)
+ def show_job_frame(self, session):
+ self.page.set_current_frame(session, self.job)
+ return self.show_mode(session, self.job)
+
def show_negs_start(self, session):
self.page.set_current_frame(session, self.__startneg)
return self.show_mode(session, self.__startneg)
@@ -171,8 +176,8 @@
stats = PoolStats(app, "stats")
self.__tabs.add_tab(stats)
- jobs = JobsAndGroupsTab(app, "jobs")
- self.__tabs.add_tab(jobs)
+ self.jobs = JobsAndGroupsTab(app, "jobs")
+ self.__tabs.add_tab(self.jobs)
self.scheds = PoolSchedulerSet(app, "scheds")
self.__tabs.add_tab(self.scheds)
@@ -284,25 +289,6 @@
def filter(self, session, system, slots):
return slots
-class PoolMachineSet(MachineSet):
- def get_args(self, session):
- return self.frame.get_args(session)
-
- def render_sql_where(self, session, pool):
- elems = list()
- elems.append("s.pool = %(pool)s")
- recent = self.get_recent_sql_where(session)
- if recent:
- elems.append(recent)
-
- return "where %s" % " and ".join(elems)
-
- def get_sql_values(self, session, pool):
- return {"pool": pool.id}
-
- def render_sql_orderby(self, session, pool):
- return "order by id asc"
-
class PoolStats(Widget):
def __init__(self, app, name):
super(PoolStats, self).__init__(app, name)
@@ -310,187 +296,19 @@
stats = PoolStatSet(app, "general", "general")
self.add_child(stats)
- self.machine_grid = self.MachineVisualization(app, "machine_grid")
- self.add_child(self.machine_grid)
-
- self.slot_grid = self.SlotVisualization(app, "slot_grid")
- self.add_child(self.slot_grid)
-
- self.all_fits = Attribute(app, "can_show_all")
- self.add_attribute(self.all_fits)
+ self.slot_map = self.PoolSlotMap(app, "pool_slot_map")
+ self.add_child(self.slot_map)
- self.machine_param = Parameter(app, "machine_param")
- self.add_parameter(self.machine_param)
-
- def process(self, session):
- super(PoolStats, self).process(session)
- all_slots = self.slot_grid.show_all_slots.get(session)
- machine = self.machine_param.get(session)
- all_fits = True
-
- if all_slots == "a":
- machine = None
-
- if not machine:
- self.slot_grid.set_machine(session, None)
- all_fits = self.slot_grid.will_it_fit(session)
- if not all_fits:
- machine = self.machine_grid.get_1st_machine(session)
- else:
- machine = ""
-
- self.all_fits.set(session, all_fits)
- self.slot_grid.set_machine(session, machine)
-
def render_title(self, session):
return "Statistics"
- def render_machine_title(self, session):
- slots_fit = self.all_fits.get(session)
- if not slots_fit:
- pool = self.frame.get_args(session)[0]
- return "Machines on %s" % pool.name
-
- def render_machine_help(self, session):
- slots_fit = self.all_fits.get(session)
- if not slots_fit:
- return "Select a Machine to view its slots"
-
- def render_slot_job_url(self, session):
- job = Identifiable("XXX")
- return self.page.main.pool.job.get_href(session, job)
-
- class SlotVisualization(StatUtilizationGrid):
- def __init__(self, app, name):
- super(PoolStats.SlotVisualization, self).__init__(app, name)
-
- self.show_all_slots = self.ShowAllSlots(app, "show_all_slots")
- self.add_child(self.show_all_slots)
-
- def render(self, session, *args):
- slots_fit = self.parent.all_fits.get(session)
- if slots_fit:
- return super(PoolStats.SlotVisualization, self).render(session, *args)
-
- def get_cells(self, session):
- pool = self.frame.get_args(session)[0]
- action = self.app.model.pool.slots
- return action.get_boxes(session, pool)
+ class PoolSlotMap(SlotMap):
+ def get_title_name(self, session, pool):
+ return pool.name
- def render_title(self, session):
- pool = self.frame.get_args(session)[0]
- machine = self.get_machine(session)
- return machine and "Slots on (%s)" % machine or "Slots on %s" % pool.name
-
- def set_machine(self, session, machine):
- action = self.app.model.pool.slots
- action.set_machine(session, machine)
-
- def get_machine(self, session):
- action = self.app.model.pool.slots
- return action.get_machine(session)
-
- def get_colors(self, session):
- action = self.app.model.pool.slots
- return action.get_colors()
-
- def get_states(self, session):
- action = self.app.model.pool.slots
- return action.get_states()
-
- def get_color(self, session, job):
- action = self.app.model.pool.slots
- return action.get_color(session, job)
+ def render_slot_clip_size(self, session, *args):
+ return 400
- def get_contents(self, session, slot):
- return ""
-
- def get_href(self, session, slot):
- return "#"
-
- def get_url(self, session):
- pool = self.parent.frame.get_args(session)[0]
- machine = self.get_machine(session)
- return "call.xml?class=pool;id=%s;method=slots;xargs=%s" % (pool.id, machine)
-
- def get_sticky_info(self, session):
- action = self.app.model.pool.slots
- return action.get_field_tuples(session)
-
- class ShowAllSlots(StateSwitch):
- def __init__(self, app, name):
- super(PoolStats.SlotVisualization.ShowAllSlots, self).__init__(app, name)
-
- self.add_state("a", "All")
- self.add_state("m", "Machine")
-
- def render(self, session):
- state = self.get(session)
- if state == "m":
- return super(PoolStats.SlotVisualization.ShowAllSlots, self).render(session)
-
- def render_href(self, session):
- state = self.get(session)
- if state == "m":
- branch = session.branch()
- self.set(branch, "a")
- return branch.marshal()
- else:
- return "#"
-
- def render_class(self, session):
- state = self.get(session)
- return state == "a" and "disabled" or "enabled"
-
- class MachineVisualization(StatUtilizationGrid):
- def render(self, session, *args):
- slots_fit = self.parent.all_fits.get(session)
- if not slots_fit:
- return super(PoolStats.MachineVisualization, self).render(session, *args)
-
- def get_cells(self, session):
- pool = self.frame.get_args(session)[0]
- action = self.app.model.pool.machines
- return action.get_boxes(session, pool)
-
- def render_title(self, session):
- return "Machines"
-
- def render_vis_help(self, session):
- return "Click on a machine to view its slots"
-
- def get_1st_machine(self, session):
- pool = self.frame.get_args(session)[0]
- action = self.app.model.pool.machines
- boxes = action.get_boxes(session, pool)
- return boxes[0]["machine"]
-
- def get_colors(self, session):
- action = self.app.model.pool.machines
- return action.get_colors()
-
- def get_color(self, session, machine):
- action = self.app.model.pool.machines
- return action.get_color(session, machine)
-
- def get_contents(self, session, machine):
- return ""
-
- def get_href(self, session, data):
- machine = data["machine"]
- branch = session.branch()
- self.parent.machine_param.set(branch, machine)
- self.parent.slot_grid.show_all_slots.set(branch, "m")
- return branch.marshal()
-
- def get_url(self, session):
- pool = self.parent.frame.get_args(session)[0]
- return "call.xml?class=pool;id=%s;method=machines" % pool.id
-
- def get_sticky_info(self, session):
- action = self.app.model.pool.machines
- return action.get_field_tuples(session)
-
class PoolStatSet(StatSet):
def render_rate_text(self, session, args):
return "Percentage"
@@ -518,12 +336,24 @@
return values
class PoolStatus(CuminStatus):
+ def __init__(self, app, name):
+ super(PoolStatus, self).__init__(app, name)
+
+ self.item_tmpl = Template(self, "status_html")
+
def render_title(self, session, pool):
return "Pool Status"
def render_status(self, session, pool):
action = self.app.model.pool.poolstatus
record = action.get_stat_record(session, pool)
+ if record and ("idl" in record) and ("all" in record):
+ writer = Writer()
+ self.item_tmpl.render(writer, session, record)
+ return writer.to_string()
- return record and "<div><span>%i</span> of <span>%i</span> slots idle</div>" % \
- (record["idl"], record["all"]) or ""
+ def render_idle(self, session, record):
+ return record["idl"]
+
+ def render_total(self, session, record):
+ return record["all"]
Modified: mgmt/trunk/cumin/python/cumin/pool.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/pool.strings 2009-03-18 18:31:00 UTC (rev 3167)
+++ mgmt/trunk/cumin/python/cumin/pool.strings 2009-03-18 18:31:42 UTC (rev 3168)
@@ -75,99 +75,26 @@
{status}
</div>
<script type="text/javascript">
-<![CDATA[
+//<![CDATA[
cumin.objectListeners["{id}"] = updatePoolStatus;
-]]>
+//]]>
</script>
-[PoolStats.css]
-div.vistats {
- margin-bottom: 2em;
-}
-div.vistats ul.radiotabs {
- margin: 1em 0 0 1em;
-}
-.machine_help {
- color: #444;
- font-size: 0.8em;
-}
+[PoolStatus.status_html]
+ <div>
+ <span>{idle}</span> of <span>{total}</span> slots idle
+ </div>
-div#legend_slot_grid { float: left; margin-right: 1.5em;}
-div#legend_slot_grid h2, div#legend1_slot_grid h2 { margin-bottom: 1em;}
-div#legend1_slot_grid .btn.Unclaimed,
-div#legend1_slot_grid .btn.Claimed,
-div#legend1_slot_grid .btn.Matched,
-div#legend1_slot_grid .btn.Owner,
-div#legend1_slot_grid .btn.Preempting { width:28px; height:28px; margin-bottom:1px;}
-
[PoolStats.html]
-<div style="width: 50%; float: left;">
+<div style="width: 40%; float: left;">
<h2>General</h2>
<div>
{general}
</div>
</div>
<div style="float: left; margin-left: 4em;">
- {machine_grid}
- {slot_grid}
+ {pool_slot_map}
</div>
<div style="clear:left;"><!-- --></div>
-<script type="text/javascript">
-var show_slot_job_url = "{slot_job_url}";
-</script>
-[SlotVisualization.html]
-{show_all_slots}
-<div class="vistats">
- <h2>{title}</h2>
- <div class="StatUtilizationGrid" id="{id}">
- <div class="visualization">
- <div id="StatGrid" class="StatGrid" style="width:{grid_width};">
- {grid}
- <div style="clear:left;"><!-- --></div>
- </div>
- </div>
- <div id="legend_{name}" class="cell_legend">
- <h2>Activities</h2>
- {grid_legend}
- </div>
- <div id="legend1_{name}" class="cell_legend">
- <h2>States</h2>
- {grid_legend1}
- </div>
- </div>
-</div>
-{grid_updater}
-
-[ShowAllSlots.html]
- <div style="margin:1em;">
- <a href="{href}"><button class="{class}" type="button" tabindex="100" >Show All Machines</button></a>
- </div>
-
-[MachineVisualization.javascript]
-function got_machine_grid(obj, id) {
- for (var cell in obj.machines.machine) {
- var machine = obj.machines.machine[cell]
- var o_Button = document.getElementById("button_"+cell);
- if (o_Button) {
- o_Button.className = "btn "+machine.color;
- o_Button.onmouseover = over_cell;
- o_Button.onmouseout = out_cell
- }
- var o_Machine = document.getElementById("cell_machine_"+cell);
- if (o_Machine) {
- o_Machine.innerHTML = machine.machine;
- }
- var o_Busy = document.getElementById("cell_busy_"+cell);
- if (o_Busy) {
- o_Busy.innerHTML = machine.busy;
- }
- var o_Idle = document.getElementById("cell_idle_"+cell);
- if (o_Idle) {
- o_Idle.innerHTML = machine.idle;
- }
- }
- setTimeout("get_machine_grid()", 1000);
-}
-
Modified: mgmt/trunk/cumin/python/cumin/stat.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/stat.py 2009-03-18 18:31:00 UTC (rev 3167)
+++ mgmt/trunk/cumin/python/cumin/stat.py 2009-03-18 18:31:42 UTC (rev 3168)
@@ -2,11 +2,14 @@
from wooly.widgets import *
from mint import *
from math import sqrt
+from random import randint
from widgets import *
from parameters import *
from util import *
from formats import *
+import tempfile
+from datetime import timedelta, datetime
strings = StringCatalog(__file__)
@@ -140,208 +143,149 @@
def render_stat_name(self, session, stat, object):
return stat.name
-class StatUtilizationGrid(Widget):
- def __init__(self, app, name):
- super(StatUtilizationGrid, self).__init__(app, name)
-
- self.cells = self.GridCells(app, "grid")
- self.add_child(self.cells)
+class ImageCache(object):
+ def __init__(self):
+ self.__files = dict() # {name: {"time": time_created, "file": file object, "cookie": (cookie values)}}
- legend = self.Legend(app, "grid_legend")
- legend.get_method = self.get_colors
- self.add_child(legend)
-
- legend1 = self.Legend(app, "grid_legend1")
- legend1.get_method = self.get_states
- self.add_child(legend1)
-
- ajax = self.Updater(app, "grid_updater")
- self.add_child(ajax)
-
- self.max_px = 480.0
- self.max_item_px = 28.0
- self.max_columns = 20.0
- self.max_background_cols = 15 # max columns with background image
+ def find_recent(self, name, max_age):
+ for cname in self.__files:
+ if cname == name:
+ age = timedelta(seconds=max_age)
+ now = datetime.now()
+ then = self.__files[cname]["time"]
+ if now - then < age:
+ file = self.__files[cname]["file"]
+ file.seek(0)
+ return (file.read(), self.__files[cname]["cookie"])
+ else:
+ break
- def render_title(self, session):
- return "Utilization"
+ return (None, None)
- def render_name(self, session):
- return self.name
-
- def render_vis_help(self, session):
- pass
-
- def will_it_fit(self, session):
- count = len(self.cells.get_items(session))
- columns = self.cells.calculate_columns(count)
- return columns <= self.max_columns
-
- def get_cell_class(self, session):
- count = len(self.cells.get_items(session))
- columns = self.cells.calculate_columns(count)
- return columns > self.max_background_cols and "small" or "big"
-
- def get_grid_width(self, session):
- count = len(self.cells.get_items(session))
- columns = self.cells.calculate_columns(count)
- # we have approx 480px to work with
- width = float(columns) * self.max_item_px
- if width > self.max_px:
- width = self.max_px
- extra = 3.0 * columns # left right border plus 1 right margin
- if columns > self.max_background_cols:
- extra = 0
- return int(width + extra)
-
- def render_grid_width(self, session):
- width = self.get_grid_width(session) or self.max_px
- return "%ipx" % int(width)
+ def create_cache_file(self, name, args):
+ if name not in self.__files:
+ file = tempfile.TemporaryFile()
+ else:
+ file = self.__files[name]["file"]
+ file.seek(0)
+ file.truncate()
- class GridCells(ItemSet):
- def __init__(self, app, name):
- super(StatUtilizationGrid.GridCells, self).__init__(app, name)
-
- self.width = Attribute(app, "width")
- self.add_attribute(self.width)
+ self.__files[name] = {"time": datetime.now(), "file": file, "cookie": args}
+ return file
- self.sticky = self.Sticky(app, "sticky_info")
- self.add_child(self.sticky)
+class SlotMapPage(Page):
+ interiors = {"Idle": (.7,.7,.7),
+ "Busy": (0.0, 0.4, 0.0),
+ "Suspended": (1,0,0),
+ "Vacating": (1,.73,.367),
+ "Killing": (0,0,1),
+ "Benchmarking": (1,.8,.8),
+ None: (.8,.8,.8)}
+
+ max_width = 400
+ max_png_age = 30
+ def __init__(self, app, name):
+ super(SlotMapPage, self).__init__(app, name)
- def render(self, session, *args):
- count = len(self.get_items(session))
- if count == 0:
- return "0 slots found"
- else:
- return super(StatUtilizationGrid.GridCells, self).render(session, *args)
+ self.class_ = CuminClassParameter(app, "class")
+ self.add_parameter(self.class_)
- def do_get_items(self, session):
- cells = self.parent.get_cells(session)
- self.width.set(session,
- self.calculate_cell_width(len(cells)))
- return cells
-
- def render_cell_id(self, session, cell):
- return cell["id"]
+ self.id = IntegerParameter(app, "id")
+ self.add_parameter(self.id)
- def render_cell_width(self, session, cell):
- return self.width.get(session)
+ self.zoom_level = IntegerParameter(app, "zl")
+ self.add_parameter(self.zoom_level)
- def render_cell_class(self, session, cell):
- return self.parent.get_cell_class(session)
-
- def calculate_columns(self, count):
- sq = sqrt(count)
- isq = int(sq)
- if sq > isq:
- isq = isq + 1
- return isq
-
- def calculate_cell_width(self, count):
- columns = self.calculate_columns(count)
- width = self.parent.max_item_px
- if columns:
- min = self.parent.max_px / float(columns)
- width = min > self.parent.max_item_px and self.parent.max_item_px or min
- return "%ipx" % int(width)
+ self.zoom_x = IntegerParameter(app, "zx")
+ self.add_parameter(self.zoom_x)
- def render_href(self, session, cell):
- return self.parent.get_href(session, cell)
-
- def render_color(self, session, cell):
- return self.parent.get_color(session, cell)
-
- def render_contents(self, session, cell):
- return self.parent.get_contents(session, cell)
-
- def get_sticky_info(self, session):
- return self.parent.get_sticky_info(session)
+ self.zoom_y = IntegerParameter(app, "zy")
+ self.add_parameter(self.zoom_y)
- def render_sticky_rows(self, session, cell):
- return self.sticky.render_items(session, cell)
-
- class Sticky(ItemSet):
- def do_get_items(self, session, cell):
- info = self.parent.get_sticky_info(session)
- return [(inf, cell["id"]) for inf in info]
-
- def render_sticky_name(self, session, item):
- return item[0][0]
-
- def render_sticky_title(self, session, item):
- return item[0][1]
-
- def render_sticky_object_id(self, session, item):
- return item[1]
+ self.dot = Parameter(app, "dot")
+ self.add_parameter(self.dot)
+
+ self.cache = ImageCache()
- def get_cells(self, session):
- """ should return a list of dictionaries """
- return list()
-
- def get_href(self, session, cell):
- return "#"
+ def get_content_type(self, session):
+ return "image/png"
- def get_color(self, session, cell):
- return "clear"
-
- def get_contents(self, session, cell):
- return ""
+ def get_cache_control(self, session):
+ return "no-cache"
- def get_colors(self, session):
- return ["clear"]
+ def get_args(self, session):
+ cls = self.class_.get(session)
+ if cls:
+ id = self.id.get(session)
+ return (cls.mint_class.get(id),)
+ else:
+ return (None,)
+
+ def render_dot(self, session, dot):
+ map = HeatMapChart()
+ if dot == "Unknown":
+ dot = None
+ surface = map.plot_dot(self.interiors[dot], 12, 12)
+ writer = Writer()
+ surface.write_to_png(writer)
+ return writer.to_string()
- def get_states(self, session):
- return ["Unclaimed"]
-
- def get_url(self, session):
- """ returns something like
- "call.xml?class=system;id=%i;method=slots" % system.id
- """
- pass
+ def do_render(self, session, object):
+ dot = self.dot.get(session)
+ if dot:
+ return self.render_dot(session, dot)
- def get_sticky_info(self, session):
- return [("Name", "Display Title")]
-
- def get_fn(self, session):
- return self.name
-
- def got_fn(self, session):
- return self.name
-
- def elem_id(self, session):
- return self.name
+ zl = self.zoom_level.get(session)
+ zx = self.zoom_x.get(session)
+ zy = self.zoom_y.get(session)
- class Legend(ItemSet):
- def render(self, session, *args):
- if len(self.parent.cells.get_items(session)) > 0:
- return super(StatUtilizationGrid.Legend, self).render(session, *args)
-
- def do_get_items(self, session, *args):
- return self.get_method(session)
-
- def render_legend_text(self, session, color):
- return color[0]
+ # determine if cached copy is recent enough
+ cached_png, args = self.get_cached(session, zl)
+ if cached_png:
+ #print "returning cached copy of png at zoom %i, x %i, y %i" % (zl, zx, zy)
+ self.set_cookie(session, args)
+ return cached_png
- def render_legend_color(self, session, color):
- return color[1]
-
- class Updater(AjaxField):
- def do_render(self, session):
- return self.render_script(session)
+ #print "starting rendering png at zoom %i, x %i, y %i" % (zl, zx, zy)
+ map = HeatMapChart(self.max_width, self.max_width)
- def get_url(self, session):
- return self.parent.get_url(session)
+ cls = self.class_.get(session)
+ action = cls.get_visualization_action()
+ slot_info = action.get_boxes(session, object)
+
+ if len(slot_info) == 0:
+ return ""
+
+ slots = [ [self.interiors[x["activity"]], x["state"]] for x in slot_info ]
+ size = map.plot_slots(slots, zl, zx, zy)
- def get_fn(self, session):
- return self.parent.get_fn(session)
+ args = (size, map.width, map.height, len(slots), map.rows, map.cols)
+ self.set_cookie(session, args)
+ self.cache_it(session, zl, map, args)
- def got_fn(self, session):
- return self.parent.got_fn(session)
+ writer = Writer()
+ map.write(writer)
+ #print "done rendering png at zoom %i, x %i, y %i" % (zl, zx, zy)
+ return writer.to_string()
+
+ def get_cached(self, session, zl):
+ filename = self.__gen_filename(session, zl)
+ return self.cache.find_recent(filename, self.max_png_age)
- def elem_id(self, session):
- return self.parent.elem_id(session)
+ def cache_it(self, session, zl, map, args):
+ filename = self.__gen_filename(session, zl)
+ writer = self.cache.create_cache_file(filename, args)
+ map.write(writer)
+
+ def __gen_filename(self, session, zl):
+ cls = self.class_.get(session)
+ id = self.id.get(session)
+ return "-".join((cls.cumin_name, str(id), str(zl)))
-
+ def set_cookie(self, session, args):
+ cookie = "|".join((str(x) for x in args))
+ session.set_cookie("slot_info", cookie)
+
class StatChartPage(Page):
def __init__(self, app, name):
super(StatChartPage, self).__init__(app, name)
Modified: mgmt/trunk/cumin/python/cumin/stat.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/stat.strings 2009-03-18 18:31:00 UTC (rev 3167)
+++ mgmt/trunk/cumin/python/cumin/stat.strings 2009-03-18 18:31:42 UTC (rev 3168)
@@ -45,7 +45,7 @@
table.StatSet {
width: 100%;
border-collapse: collapse;
- margin: 1em 0.5em;
+ margin: 1em 0;
}
table.StatSet tr {
@@ -105,15 +105,8 @@
var img = chart.elem("img")
var src = img.getattr("src");
- var sep = src.lastIndexOf(";");
- var time = new Date().getTime();
+ src = refreshTime(src);
- if (isNaN(parseInt(src.substring(sep + 1)))) {
- src = src + ";" + time;
- } else {
- src = src.substring(0, sep) + ";" + time;
- }
-
img.setattr("src", src);
/*
@@ -127,7 +120,18 @@
}
*/
}
+function refreshTime(src) {
+ var sep = src.lastIndexOf(";");
+ var time = new Date().getTime();
+ if (isNaN(parseInt(src.substring(sep + 1)))) {
+ src = src + ";" + time;
+ } else {
+ src = src.substring(0, sep) + ";" + time;
+ }
+ return src;
+}
+
[StatValueChart.css]
div.StatValueChart {
font-size: 0.9em;
@@ -165,179 +169,3 @@
<span class="ph" statname="{stat_name}" statmode="{mode}">{stat_value}</span>
</li>
-[StatUtilizationGrid.javascript]
-function over_cell() {
- this.get_left = function (o) {
- var p = o.offsetParent;
- var true_left = o.offsetLeft;
-
- while (p != null) {
- var tag = p.tagName;
- if (p.clientLeft) {
- if ( (tag != "TABLE") && (tag != "BODY") ) {
- true_left += p.clientLeft;
- }
- }
- true_left += p.offsetLeft;
- p = p.offsetParent;
- }
- return true_left;
- }
-
- var id = this.id;
- var onote = document.getElementById("note_"+id);
- if (onote) {
- if (document.all) {
- this.title = onote.innerText;
- } else {
- onote.style.visibility = "hidden";
- onote.style.display = "block";
- if (onote.style.left.indexOf("px") == -1) {
- var left = this.get_left(onote);
- var right = left + onote.offsetWidth;
- if (window.innerWidth) {
- wwidth = window.innerWidth;
- if (right > wwidth) {
- oparent = onote.offsetParent;
- pleft = this.get_left(oparent);
- onote.style.left = "-" + (onote.offsetWidth - (wwidth - pleft) + 4) + "px";
- } else {
- onote.style.left = "50%";
- }
- }
- }
- onote.style.visibility = "visible";
- }
- }
-}
-function out_cell() {
- var id = this.id;
- var onote = document.getElementById("note_"+id);
- if (onote) {
- onote.style.display = "none";
- }
-}
-
-[StatUtilizationGrid.css]
-div.StatGrid a {
- float:left;
- border:1px solid #EAEAEA;
- margin: 0 1px 1px 0;
- position: relative;
- color: black;
-}
-div.StatGrid a.small {
- float:left;
- position: relative;
- color: black;
-}
-div.StatGrid a.small:hover { border: 0px; }
-
-div.StatGrid a:hover { border: 1px solid #a00; }
-.btn.yellow { background: #ffc; }
-.btn.green { background: #cfc; }
-.btn.blue { background: #ccf; }
-.btn.red { background: #fcc; }
-.btn.orange { background: #ffbb5e; }
-.btn.black { background: #444; }
-.btn.grey { background: #ccc; }
-.btn.clear { background: white; }
-.btn.green1 { background: #9f9; }
-.btn.green2 { background: #6c6; }
-.btn.green3 { background: #393; }
-
-.btn[class] { background-position: bottom; }
-.btn.Claimed { background-image: url(resource?name=claimed1.png); }
-.btn.Unclaimed { background-image: url(resource?name=shade1.png); }
-.btn.Matched { background-image: url(resource?name=match.png); }
-.btn.Owner { background-image: url(resource?name=pwn.png); }
-.btn.Preempting { background-image: url(resource?name=preempt.png); }
-
-div.visualization {
- padding-right: 2em;
-}
-
-div.StatUtilizationGrid {
- margin-top: 1em;
- margin-left: 1em;
-}
-
-div.sticky_note {
- display: none;
- font-size: 0.8em;
- position: absolute;
- top: 120%;
- left: 50%;
- padding: 1em;
- background-color: #ffffaa;
- background-image: url(resource?name=shade.png);
- background-position: bottom right;
- background-repeat: repeat-x;
- border: 1px solid #cccc99;
- z-index: 1;
-}
-
-table.sticky_table {
- border-collapse: collapse;
-}
-td.sticky_names {
- font-weight: bold;
- line-height: 1em;
-}
-td.sticky_values {
- line-height: 1em;
-}
-div.cell_legend {
- margin: 1em 0;
- font-size: 0.8em;
- color: #000;
- position: relative;
-}
-div.cell_legend button {
- position: relative;
- top: -2px;
-}
-
-[StatUtilizationGrid.html]
-<div class="vistats">
- <h2>{title}</h2>
- <div class="vis_help">{vis_help}</div>
- <div class="StatUtilizationGrid" id="{id}">
- <div class="visualization">
- <div id="StatGrid" class="StatGrid" style="width:{grid_width};">
- {grid}
- <div style="clear:left;"><!-- --></div>
- </div>
- </div>
- <div id="legend_{name}" class="cell_legend">
- {grid_legend}
- </div>
- </div>
-</div>
-{grid_updater}
-
-[GridCells.html]
-{items}
-
-[GridCells.item_html]
- <a class="{cell_class}" href="{href}">
- <div id="button_{cell_id}" class="btn {color}" style="width:{cell_width}; height:{cell_width};">
- {contents}
- </div>
- <div id="note_button_{cell_id}" class="sticky_note">
- <table class="sticky_table">
- {sticky_rows}
- </table>
- </div>
- </a>
-
-[Sticky.item_html]
-<tr>
- <td class="sticky_names" nowrap="nowrap">{sticky_title}: </td>
- <td class="sticky_values" nowrap="nowrap"><span id="cell_{sticky_name}_{sticky_object_id}"></span></td>
-</tr>
-
-[Legend.item_html]
-<li>
- <button class="btn {legend_color}" ></button> {legend_text}
-</li>
Modified: mgmt/trunk/cumin/python/cumin/system.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/system.py 2009-03-18 18:31:00 UTC (rev 3167)
+++ mgmt/trunk/cumin/python/cumin/system.py 2009-03-18 18:31:42 UTC (rev 3168)
@@ -7,6 +7,7 @@
from parameters import *
from formats import *
from util import *
+from visualizations import SlotMap
strings = StringCatalog(__file__)
@@ -130,9 +131,6 @@
def __init__(self, app, name):
super(SystemStats, self).__init__(app, name)
- self.grid = self.SlotUtilizationGrid(app, "slot_grid")
- self.add_child(self.grid)
-
self.add_child(StatSet(app, "stats", "general"))
chart = StatValueChart(app, "freemem")
@@ -143,6 +141,9 @@
chart.stats = ("loadAverage1Min",)
self.add_child(chart)
+ vis = self.SystemSlotMap(app, "system_slot_map")
+ self.add_child(vis)
+
def render_title(self, session):
return "Statistics"
@@ -150,47 +151,13 @@
job = Identifiable("XXX")
return self.page.main.pool.job.get_href(session, job)
- class SlotUtilizationGrid(StatUtilizationGrid):
- def render(self, session):
- cells = self.get_cells(session)
- if len(cells) > 0:
- return super(SystemStats.SlotUtilizationGrid, self).render(session)
+ class SystemSlotMap(SlotMap):
+ def get_title_name(self, session, sysimage):
+ return sysimage.nodeName
- def get_cells(self, session):
- system = self.frame.get_args(session)[0]
- action = self.app.model.system.slots
- return action.get_boxes(session, system)
+ def render_slot_clip_size(self, session, *args):
+ return 120
- def render_title(self, session):
- return "Slot Utilization"
-
- def get_colors(self, session):
- action = self.app.model.system.slots
- return action.get_colors()
-
- def get_color(self, session, slot):
- action = self.app.model.system.slots
- return action.get_color(session, slot)
-
- def get_contents(self, session, slot):
- return ""
-
- def get_href(self, session, slot):
- branch = session.branch()
- try:
- job = Job.select("custom_id = '%s'" % slot.JobId)[0]
- except Exception, e:
- return "#"
- return self.page.main.pool.job.get_href(branch, job)
-
- def get_url(self, session):
- system = self.parent.frame.get_args(session)[0]
- return "call.xml?class=system;id=%i;method=slots" % system.id
-
- def get_sticky_info(self, session):
- action = self.app.model.system.slots
- return action.get_field_tuples(session)
-
class SystemView(CuminView):
def __init__(self, app, name):
super(SystemView, self).__init__(app, name)
Modified: mgmt/trunk/cumin/python/cumin/system.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/system.strings 2009-03-18 18:31:00 UTC (rev 3167)
+++ mgmt/trunk/cumin/python/cumin/system.strings 2009-03-18 18:31:42 UTC (rev 3168)
@@ -46,53 +46,6 @@
[TopSystemSet.count_sql]
--
-[SlotUtilizationGrid.javascript]
-function got_slot_grid(obj, id) {
- for (var cell in obj.slots.slot) {
- var slot = obj.slots.slot[cell]
- var oslot_Button = document.getElementById("button_"+cell);
- if (oslot_Button) {
- oslot_Button.className = "btn " + slot.color + " " + slot.state;
- oslot_Button.onmouseover = over_cell;
- oslot_Button.onmouseout = out_cell;
- if (slot.jid == "None") {
- oslot_Button.onclick = function() { return false; };
- } else {
- oslot_Button.job = slot.jid;
- oslot_Button.onclick = show_slot_job;
- }
- }
- var oslot_Name = document.getElementById("cell_name_"+cell);
- if (oslot_Name) {
- oslot_Name.innerHTML = slot.name;
- }
- var oslot_Machine = document.getElementById("cell_machine_"+cell);
- if (oslot_Machine) {
- oslot_Machine.innerHTML = slot.machine;
- }
- var oslot_Job = document.getElementById("cell_job_id_"+cell);
- if (oslot_Job) {
- oslot_Job.innerHTML = slot.job_id;
- }
- var oslot_State = document.getElementById("cell_state_"+cell);
- if (oslot_State) {
- oslot_State.innerHTML = slot.state;
- }
- var oslot_Activity = document.getElementById("cell_activity_"+cell);
- if (oslot_Activity) {
- oslot_Activity.innerHTML = slot.activity;
- }
- }
- setTimeout("get_slot_grid()", 2500);
-}
-function show_slot_job() {
- if ( typeof show_slot_job_url != "undefined" ) {
- var url = show_slot_job_url.replace("XXX", this.job);
- window.location.href = url;
- }
- return false;
-}
-
[SystemStatus.javascript]
function updateSystemStatus(id, system) {
updateStatus(id, system);
@@ -133,7 +86,7 @@
<h2>Memory/Load</h2>
{stats}
- {slot_grid}
+ {system_slot_map}
</td>
<td>
{freemem}
More information about the rhmessaging-commits
mailing list