Author: eallen Date: 2011-03-08 21:44:09 +0000 (Tue, 08 Mar 2011) New Revision: 4585
Modified: trunk/cumin/model/condor.xml trunk/cumin/model/rosemary.xml trunk/cumin/python/cumin/charts.py trunk/cumin/python/cumin/grid/dashboard.py trunk/cumin/python/cumin/grid/dashboard.strings trunk/cumin/python/cumin/objectframe.py trunk/cumin/python/cumin/objectframe.strings trunk/cumin/python/cumin/objectselector.py trunk/cumin/python/cumin/stat.py trunk/cumin/python/cumin/stat.strings Log: BZ 673194: Next iteration of the grid dashboard. This requires a reschema.
Modified: trunk/cumin/model/condor.xml =================================================================== --- trunk/cumin/model/condor.xml 2011-03-08 21:40:10 UTC (rev 4584) +++ trunk/cumin/model/condor.xml 2011-03-08 21:44:09 UTC (rev 4585) @@ -32,13 +32,13 @@ </group>
<group name="DaemonStatistics"> - <statistic name="MonitorSelfAge" type="uint32"/> - <statistic name="MonitorSelfCPUUsage" type="double"/> + <statistic name="MonitorSelfAge" type="uint32" optional="y"/> + <statistic name="MonitorSelfCPUUsage" type="double" optional="y"/>
- <statistic name="MonitorSelfImageSize" type="double" unit="KiB"/> - <statistic name="MonitorSelfRegisteredSocketCount" type="uint32"/> - <statistic name="MonitorSelfResidentSetSize" type="uint32" unit="KiB"/> - <statistic name="MonitorSelfTime" type="absTime"/> + <statistic name="MonitorSelfImageSize" type="double" unit="KiB" optional="y"/> + <statistic name="MonitorSelfRegisteredSocketCount" type="uint32" optional="y"/> + <statistic name="MonitorSelfResidentSetSize" type="uint32" unit="KiB" optional="y"/> + <statistic name="MonitorSelfTime" type="absTime" optional="y"/> </group>
<group name="GridMethods"> @@ -581,6 +581,30 @@ <group name="DaemonStatistics"/> <group name="GridMethods"/>
+ <statistic name="MatchRateSustained" type="uint32" desc="Matcher per second, since end of previous cycle" optional="y"/> + <statistic name="MatchRate" type="uint32" desc="Matches per second over current cycle" optional="y"/> + <statistic name="Matches" type="uint32" desc="Number of matches generated" optional="y"/> + <statistic name="Duration" type="uint32" desc="Negotiation cycle duration, in seconds" optional="y"/> + <statistic name="Phase1Duration" type="uint32" desc="Duration of phase 1, in seconds" optional="y"/> + <statistic name="Phase2Duration" type="uint32" desc="Duration of phase 2, in seconds" optional="y"/> + <statistic name="Phase3Duration" type="uint32" desc="Duration of phase 3, in seconds" optional="y"/> + <statistic name="Phase4Duration" type="uint32" desc="Duration of phase 4, in seconds" optional="y"/> + <statistic name="SlotShareIter" type="uint32" desc="Number of share iterations" optional="y"/> + <statistic name="NumSchedulers" type="uint32" desc="Number of submitters" optional="y"/> + <statistic name="ActiveSubmitterCount" type="uint32" desc="Number of submitters with jobs" optional="y"/> + <statistic name="SubmittersFailed" type="lstr" desc="List of submitters that encountered a failure" optional="y"/> + <statistic name="SubmittersOutOfTime" type="lstr" desc="List of submitters that hit their match time limit" optional="y"/> + <statistic name="SubmittersShareLimit" type="lstr" desc="List of submitters that hit their share limit" optional="y"/> + <statistic name="NumIdleJobs" type="uint32" desc="Number of idle jobs" optional="y"/> + <statistic name="NumJobsConsidered" type="uint32" desc="Number of jobs that were considered for matching" optional="y"/> + <statistic name="Rejections" type="uint32" desc="Number of rejections" optional="y"/> + <statistic name="TotalSlots" type="uint32" desc="Number of slots found" optional="y"/> + <statistic name="TrimmedSlots" type="uint32" desc="Number of slots after trimming claimed slots" optional="y"/> + <statistic name="CandidateSlots" type="uint32" desc="Number of slots considered for matching" optional="y"/> + <statistic name="Time" type="uint32" desc="Start of cycle, seconds since Epoch" optional="y"/> + <statistic name="End" type="uint32" desc="End of cycle, seconds since Epoch" optional="y"/> + <statistic name="Period" type="uint32" desc="Time from end of previous cycle to end of current cycle" optional="y"/> + <method name="GetLimits"> <arg name="Limits" dir="O" type="map"/> </method> @@ -625,6 +649,7 @@
<class name="Collector"> <group name="DaemonProperties"/> + <group name="DaemonStatistics"/> <!-- TODO: for now this doesn't seem to be worth the headache of the query worker socket handler interaction & crashes <group name="GridMethods"/>
Modified: trunk/cumin/model/rosemary.xml =================================================================== --- trunk/cumin/model/rosemary.xml 2011-03-08 21:40:10 UTC (rev 4584) +++ trunk/cumin/model/rosemary.xml 2011-03-08 21:44:09 UTC (rev 4585) @@ -294,6 +294,16 @@ </package>
<package name="com.redhat.grid"> + <class name="Scheduler"> + <statistic name="MonitorSelfCPUUsage"> + <title>CPU Usage</title> + </statistic> + + <statistic name="MonitorSelfResidentSetSize"> + <title>Memory Used</title> + </statistic> + </class> + <class name="Collector"> <title>Pool</title>
@@ -316,6 +326,14 @@ <statistic name="HostsTotal"> <title>Total Hosts</title> </statistic> + + <statistic name="MonitorSelfCPUUsage"> + <title>CPU Usage</title> + </statistic> + + <statistic name="MonitorSelfResidentSetSize"> + <title>Memory Used</title> + </statistic> </class>
<class name="Slot"> @@ -371,5 +389,19 @@ <title>Description</title> </property> </class> + + <class name="Negotiator"> + <statistic name="Duration"> + <title>Total Duration</title> + </statistic> + + <statistic name="MonitorSelfCPUUsage"> + <title>CPU Usage</title> + </statistic> + + <statistic name="MonitorSelfResidentSetSize"> + <title>Memory Used</title> + </statistic> + </class> </package> </model>
Modified: trunk/cumin/python/cumin/charts.py =================================================================== --- trunk/cumin/python/cumin/charts.py 2011-03-08 21:40:10 UTC (rev 4584) +++ trunk/cumin/python/cumin/charts.py 2011-03-08 21:44:09 UTC (rev 4585) @@ -27,6 +27,9 @@
wedge = cum_wedges = end = 0.0
+ # are we going to draw more than one slice + draw_border = sum([1 for x in slices if x > 0]) > 1 + for slice, color in zip(slices, colors): if slice > 0: wedge = slice / total @@ -42,10 +45,11 @@ cr.fill()
# draw a white border - self.draw_slice_path(cr, self.centerx, self.centery, - self.radius, start, end) - cr.set_source_rgb(1,1,1) - cr.stroke() + if draw_border: + self.draw_slice_path(cr, self.centerx, self.centery, + self.radius, start, end) + cr.set_source_rgb(1,1,1) + cr.stroke()
return surface
@@ -55,27 +59,6 @@ cr.arc(x, y, radius, start, end) cr.close_path()
- def test(self): - surface = ImageSurface(FORMAT_ARGB32, int(self.width), int(self.height)) - cr = Context(surface) - cr.set_line_width(1) - - cr.save() - cr.set_source_rgba(0, 0, 0, 0.15) - - centerx = int(self.width/2) - centery = int(self.height/2) - radius = int(self.width/2) - cr.new_path() - cr.move_to(centerx, centery) - cr.arc(centerx, centery, radius, 0, pi * 2) - cr.line_to(centerx, centery) - cr.close_path() - cr.fill() - cr.restore() - - return surface - class HeatMapChart(object): def __init__(self, width=400, height=400): self.width = width # final width
Modified: trunk/cumin/python/cumin/grid/dashboard.py =================================================================== --- trunk/cumin/python/cumin/grid/dashboard.py 2011-03-08 21:40:10 UTC (rev 4584) +++ trunk/cumin/python/cumin/grid/dashboard.py 2011-03-08 21:44:09 UTC (rev 4585) @@ -3,14 +3,16 @@ from wooly import Widget, Attribute from wooly.util import StringCatalog from wooly.datatable import DataAdapterOptions, DataAdapterField +from wooly.table import TableHeader +from wooly.widgets import TabbedModeSet
from cumin.sqladapter import ObjectSqlAdapter -from cumin.stat import StatSet, PieChartPage +from cumin.stat import StatSet, PieChartPage, StatFlashChart from rosemary.sqlquery import SqlQueryOptions -from cumin.objectselector import ObjectSelector, ObjectLinkColumn,\ - MonitorSelfStatColumn, ObjectTableColumn, ObjectTable -from wooly.table import TableHeader +from cumin.objectselector import ObjectSelector, MonitorSelfStatColumn, ObjectTableColumn, ObjectTable,\ + ObjectLinkColumn from cumin.util import rgb_to_string +from negotiator import NegotiatorAttribute
strings = StringCatalog(__file__) log = logging.getLogger("cumin.dashboard") @@ -30,6 +32,9 @@ capacity = DashboardCapacity(app, "capacity", collector) self.add_child(capacity)
+ stats = DashboardNegotiatorStatSet(app, "other_stats", collector) + self.add_child(stats) + def render_title(self, session): return "Dashboard"
@@ -53,19 +58,27 @@ def __init__(self, app, name, collector): super(DashboardPerformance, self).__init__(app, name)
- # scheduled vs all jobs - throughput = Widget(app, "jobs_summary") - self.add_child(throughput) + cls = app.model.com_redhat_grid.Collector + c = MonitorSelfStats(app, "ms_c", collector, cls, "Collector Performance", None) + self.add_child(c)
- #collector_perf = DashboardCollectorSelector(app, "collector_performance", collector) - #self.add_child(collector_perf) + cls = app.model.com_redhat_grid.Negotiator + frame = "main.grid.negotiator" + n = MonitorSelfStats(app, "ms_n", collector, cls, "Negotiator Performance", frame) + self.add_child(n)
- negotiator = DashboardNegotiatorSelector(app, "negotiator_performance", collector) - self.add_child(negotiator) + s = MonitorSelfSchedulerTable(app, "ms_s", collector) + self.add_child(s)
- sched = DashboardSchedulerSelector(app, "scheduler_performance", collector) - self.add_child(sched) + neg_through = NegotiatorJobThroughput(app, "negotiator_throughput", collector) + self.add_child(neg_through)
+ neg_cycles = NegotiatorCycles(app, "negotiator_cycles", collector) + self.add_child(neg_cycles) + + neg_slots = NegotiatorSlots(app, "negotiator_slots", collector) + self.add_child(neg_slots) + def render_title(self, session): return "Scheduler Performance"
@@ -80,22 +93,48 @@ return "Slot Capacity"
class DefinitionSet(StatSet): + pass + +# Displays a pie chart and a set of stats as the legend +# The legend is a definition list with the dt being the stat name +# and the dd being the value +# Supports a stat being marked as a "Totals" line, in which case +# the stat isn't put in the pie chart +class PieStatSet(DefinitionSet): radius = 60 def __init__(self, app, name, object): - super(DefinitionSet, self).__init__(app, name, object) + super(PieStatSet, self).__init__(app, name, object)
self.color_scheme = PieChartPage.RAINBOW + + # determines if a line is drawn under the legend self.has_total = False
+ # if this gets defined, this widget will have a link to display + # the chart in a popup + self.chart = None + + # don't show this if our object isn't there + def do_render(self, session): + object = self.object.get(session) + if object: + return super(PieStatSet, self).do_render(session) + + def get_pie_slices(self, session): + # don't include total lines, convert None to 0 + items = self.do_get_items(session) + return [self.render_item_value(session, x) or 0 for x in items if not self.is_total(x)] + def render_width(self, session): - return self.radius * 2 + # basically hides the pie chart if all slices are 0 + slices = self.get_pie_slices(session) + return sum(slices) and self.radius * 2 or 0
def render_height(self, session): - return self.radius * 2 + return self.render_width(session)
def render_pie_src(self, session): - items = self.do_get_items(session) - slices = [self.render_item_value(session, x) for x in items if not self.is_total(x)] + slices = self.get_pie_slices(session) return PieChartPage.get_href(slices, self.color_scheme, self.radius)
def render_legend_styles(self, session): @@ -107,19 +146,34 @@ return "\n".join(styles)
def render_class(self, session): - cls = super(DefinitionSet, self).render_class(session) + cls = super(PieStatSet, self).render_class(session) return "%s%s" % (cls, self.has_total and " hastotal" or "")
def is_total(self, item): return False
def render_legend_class(self, session, item): - return "" + classes = list() + if self.is_total(item): + classes.append("total") + if self.render_width(session) == 0: + classes.append("blank")
+ if not len(classes): + classes.append(self.get_legend_class(session, item)) + return " ".join(classes) + def render_dd_class(self, session, item): dt_class = self.render_legend_class(session, item) return "total" is dt_class and " class="total"" or ""
+ def render_span_class(self, session): + return self.chart and "chart" or "blank" + + def render_chart_href(self, session): + if self.chart: + return self.page.get_popup_url(session, self.chart) + class AliasSqlColumn(object): def __init__(self, identifier, alias): self.identifier = identifier @@ -129,21 +183,21 @@ def __init__(self, app, name, cls): super(MinimalTable, self).__init__(app, name, cls)
+ # no font or page control self.header = self.MinimalTableHeader(app, "header") self.replace_child(self.header)
+ # no footer self.footer = Widget(app, "footer") self.replace_child(self.footer)
+ # no checkboxes or id field self.adapter = self.MinimalAdapter(app, cls)
class MinimalAdapter(ObjectSqlAdapter): def get_id_field(self, cls): return None
- def get_data(self, values, options): - return super(MinimalTable.MinimalAdapter, self).get_data(values, options) - class MinimalTableHeader(TableHeader): def __init__(self, app, name): super(MinimalTable.MinimalTableHeader, self).__init__(app, name) @@ -160,6 +214,8 @@ self.offset.default = 0 self.add_attribute(self.offset)
+# The following columns and fields should probably be moved +# to a more general location class DerivedTableColumn(ObjectTableColumn): def __init__(self, app, name): super(DerivedTableColumn, self).__init__(app, name, None) @@ -256,59 +312,6 @@ field.format = format_method return field
-class DashboardObjectSelector(ObjectSelector): - def __init__(self, app, name, collector, cls, frame): - - super(DashboardObjectSelector, self).__init__(app, name, cls) - - self.pool = collector - - self.add_filter(self.pool, cls.Pool) - - col = ObjectLinkColumn(app, "name", cls.Name, cls._id, frame) - self.add_column(col) - - stat = MonitorSelfStatColumn(app, cls.MonitorSelfCPUUsage.name, cls.MonitorSelfCPUUsage) - self.add_column(stat) - - stat = MonitorSelfStatColumn(app, cls.MonitorSelfResidentSetSize.name, cls.MonitorSelfResidentSetSize) - self.add_column(stat) - - stat = MonitorSelfStatColumn(app, cls.MonitorSelfImageSize.name, cls.MonitorSelfImageSize) - self.add_column(stat) - - stat = QuotientSqlColumn(app, "used", cls.MonitorSelfResidentSetSize, - cls.MonitorSelfImageSize) - stat.title = "% Used" - self.add_column(stat) - - self.enable_csv_export(collector) - - def create_table(self, app, name, cls): - # avoid the checkboxes - return MinimalTable(app, name, cls) - -class DashboardCollectorSelector(DashboardObjectSelector): - def __init__(self, app, name, collector): - cls = app.model.com_redhat_grid.Collector - frame = "main.grid" - - super(DashboardCollectorSelector, self).__init__(app, name, collector, cls, frame) - -class DashboardNegotiatorSelector(DashboardObjectSelector): - def __init__(self, app, name, collector): - cls = app.model.com_redhat_grid.Negotiator - frame = "main.grid.negotiator" - - super(DashboardNegotiatorSelector, self).__init__(app, name, collector, cls, frame) - -class DashboardSchedulerSelector(DashboardObjectSelector): - def __init__(self, app, name, collector): - cls = app.model.com_redhat_grid.Scheduler - frame = "main.grid.scheduler" - - super(DashboardSchedulerSelector, self).__init__(app, name, collector, cls, frame) - class DashboardSumData(ObjectSqlAdapter): def __init__(self, app, columns, collector, cls): super(DashboardSumData, self).__init__(app, cls) @@ -337,13 +340,14 @@
return records[0]
-class DashboardSummarySet(DefinitionSet): +class DashboardSummarySet(PieStatSet): def __init__(self, app, name, collector, cls, columns): super(DashboardSummarySet, self).__init__(app, name, collector)
self.record = Attribute(app, "totals") self.add_attribute(self.record)
+ # XXX - consider using an ObjectSelector with SumSqlColumns as the data source self.columns = columns self.sum_columns = [x.name for x in columns] self.sum_cls = cls @@ -370,11 +374,12 @@
def render_item_value(self, session, item): record = self.record.get(session) - return record[self.sum_columns.index(item.name)] + return record[self.sum_columns.index(item.name)] or 0
- def render_legend_class(self, session, item): - if self.is_total(item): - return "total" + def render_html_title(self, session, item): + return item.description + + def get_legend_class(self, session, item): index = self.sum_columns.index(item.name) return "legend%s" % rgb_to_string(*(PieChartPage.color_schemes[self.color_scheme][index]))
@@ -399,15 +404,19 @@ self.add_filter(collector, cls.Pool)
stat = self.DiskTotalColumn(app, "disk", cls.Disk, "Disk Total") + stat.static_header = True self.add_column(stat)
stat = SumSqlColumn(app, "slot_mem_used", cls.ImageSize, "Memory Used") + stat.static_header = True self.add_column(stat) stat = SumSqlColumn(app, "memtotal", cls.Memory, "Memory Total") + stat.static_header = True self.add_column(stat) stat = QuotientSqlColumn(app, "percentmem", "sum(%s)" % cls.ImageSize.sql_column.identifier, "sum(%s)*1024" % cls.Memory.sql_column.identifier) + stat.static_header = True stat.title = "% Memory Used" self.add_column(stat)
@@ -421,7 +430,7 @@ def render_text_align(self, session): return "left"
-class GridSlotsSummary(DefinitionSet): +class GridSlotsSummary(PieStatSet): def __init__(self, app, name, collector): super(GridSlotsSummary, self).__init__(app, name, collector)
@@ -431,6 +440,10 @@ self.has_total = True self.color_scheme = PieChartPage.BLUES
+ self.chart = StatFlashChart(app, "popup", collector) + self.chart.stats = self.attrs + self.add_child(self.chart) + def render_title(self, session): return "Host Summary Info"
@@ -438,9 +451,7 @@ index = self.attrs.index(item[0].name) return index == len(self.attrs) - 1
- def render_legend_class(self, session, item): - if self.is_total(item): - return "total" + def get_legend_class(self, session, item): index = self.attrs.index(item[0].name) return "legend%s" % rgb_to_string(*(PieChartPage.color_schemes[self.color_scheme][index]))
@@ -495,9 +506,9 @@
########## dummy test data """ - records.append(("WINNT70 Used", 10)) - records.append(("WINNT70 Unused", 20)) - records.append(("WINNT70 Total", 30)) + records.append(("WINNT80 Used", 10)) + records.append(("WINNT80 Unused", 20)) + records.append(("WINNT80 Total", 30)) records.append(("SOLARIS27 Used", 12)) records.append(("SOLARIS27 Unused", 34)) records.append(("SOLARIS27 Total", 46)) @@ -511,7 +522,7 @@ def get_id_field(self, cls): return None
-class GridOSBreakdown(DefinitionSet): +class GridOSBreakdown(PieStatSet): def __init__(self, app, name, collector): super(GridOSBreakdown, self).__init__(app, name, collector)
@@ -529,6 +540,11 @@
super(GridOSBreakdown, self).do_process(session)
+ def do_render(self, session): + records = self.os_records.get(session) + if len(records): + return super(GridOSBreakdown, self).do_render(session) + def render_title(self, session): return "Slot Breakdown by OS"
@@ -547,9 +563,10 @@ _, record = item return record[1]
- def render_legend_class(self, session, this_item): - if self.is_total(this_item): - return "total" + def render_html_title(self, session, item): + return None + + def get_legend_class(self, session, this_item): items = self.do_get_items(session) real_index = 0 for item in items: @@ -559,3 +576,291 @@ real_index += 1 return "legend%s" % rgb_to_string(*(PieChartPage.color_schemes[self.color_scheme][real_index]))
+class NegotiatorDefinitionStatSet(PieStatSet): + def __init__(self, app, name, collector): + negotiator = NegotiatorAttribute(app, "neg", collector) + super(NegotiatorDefinitionStatSet, self).__init__(app, name, negotiator) + self.add_attribute(negotiator) + + self.chart = StatFlashChart(app, "popup", negotiator) + self.add_child(self.chart) + + def is_total(self, item): + if not self.has_total: + return False + index = self.attrs.index(item[0].name) + return index == len(self.attrs) - 1 + + def get_legend_class(self, session, item): + index = self.attrs.index(item[0].name) + return "legend%s" % rgb_to_string(*(PieChartPage.color_schemes[self.color_scheme][index])) + + def render_item_value(self, session, item): + _, value = item + if value is None: + return 0 + return super(NegotiatorDefinitionStatSet, self).render_item_value(session, item) + +class NegotiatorJobThroughput(NegotiatorDefinitionStatSet): + def __init__(self, app, name, collector): + super(NegotiatorJobThroughput, self).__init__(app, name, collector) + + self.attrs = ["NumJobsConsidered", "Rejections"] + self.chart.stats = self.attrs + + self.has_total = False + self.color_scheme = PieChartPage.RAINBOW + + def render_title(self, session): + return "Negotiator Job Throughput" + + +class NegotiatorCycles(NegotiatorDefinitionStatSet): + def __init__(self, app, name, collector): + super(NegotiatorCycles, self).__init__(app, name, collector) + + self.attrs = ["Phase1Duration", "Phase2Duration", "Phase3Duration", "Phase4Duration", "Duration"] + self.chart.stats = self.attrs + + self.has_total = True + self.color_scheme = PieChartPage.RAINBOW + + def render_title(self, session): + return "Negotiator Cycles" + +class NegotiatorSlots(NegotiatorDefinitionStatSet): + def __init__(self, app, name, collector): + super(NegotiatorSlots, self).__init__(app, name, collector) + + self.attrs = ["TrimmedSlots", "CandidateSlots", "TotalSlots"] + self.chart.stats = self.attrs + + self.has_total = True + + def render_title(self, session): + return "Negotiator Slots" + + def render_width(self, session): + # disable the pie chart since these stats don't add up to a total + return 0 + +class NegotiatorSlotsChart(Widget): + def __init__(self, app, name, pool): + super(NegotiatorSlotsChart, self).__init__(app, name) + + self.pool = pool + cls = app.model.com_redhat_grid.Negotiator + self.object = RosemaryPoolObjectAttribute(app, "obj", pool, cls) + self.add_attribute(self.object) + + slots = self.SlotsChart(app, "slots", self.object) + slots.stats = ("TotalSlots", "TrimmedSlots", "CandidateSlots") + self.add_child(slots) + + def do_render(self, session): + object = self.object.get(session) + if object: + return super(NegotiatorSlotsChart, self).do_render(session) + + class SlotsChart(StatFlashChart): + def render_title(self, session, *args): + return "Slots Precessed" + +class MonitorSelfSchedulerTable(Widget): + def __init__(self, app, name, pool): + super(MonitorSelfSchedulerTable, self).__init__(app, name) + + cls = app.model.com_redhat_grid.Scheduler + frame = "main.grid.scheduler" + + self.table = MonitorSelfTable(app, "current", pool, cls, frame, False) + self.add_child(self.table) + + def render_title(self, session): + return "Scheduler Performance" + + def render_table(self, session): + return self.table.render(session) + +class MonitorSelfStats(Widget): + def __init__(self, app, name, pool, cls, title, frame): + super(MonitorSelfStats, self).__init__(app, name) + + self.pool = pool + self.cls = cls + self.frame = frame + + self.object = RosemaryPoolObjectAttribute(app, "obj", pool, cls) + self.add_attribute(self.object) + + title_widget = self.MonitorSelfTitle(app, "title", self.object, title) + self.add_child(title_widget) + + tabs = TabbedModeSet(app, "tabs") + self.add_child(tabs) + + self.chart = MonitorSelfCharts(app, "history", pool, cls, self.object) + tabs.add_tab(self.chart) + tabs.add_tab(MonitorSelfTable(app, "current", pool, cls, frame, True)) + + def do_render(self, session): + if self.object.get(session): + return super(MonitorSelfStats, self).do_render(session) + + class MonitorSelfTitle(Widget): + def __init__(self, app, name, object, title): + super(MonitorSelfStats.MonitorSelfTitle, self).__init__(app, name) + + self.object = object + self.title = title + + def render_title(self, session): + return self.title + +class MonitorSelfTable(ObjectSelector): + def __init__(self, app, name, pool, cls, frame, static): + super(MonitorSelfTable, self).__init__(app, name, cls) + + self.pool = pool + self.add_filter(self.pool, cls.Pool) + + if frame: + col = ObjectLinkColumn(app, "name", cls.Name, cls._id, frame) + else: + col = ObjectTableColumn(app, cls.Name.name, cls.Name) + col.static_header = static + self.add_column(col) + + stat = MonitorSelfStatColumn(app, cls.MonitorSelfAge.name, cls.MonitorSelfAge) + stat.static_header = True + self.add_column(stat) + + stat = MonitorSelfStatColumn(app, cls.MonitorSelfRegisteredSocketCount.name, cls.MonitorSelfRegisteredSocketCount) + stat.static_header = True + self.add_column(stat) + + stat = MonitorSelfStatColumn(app, cls.MonitorSelfCPUUsage.name, cls.MonitorSelfCPUUsage) + stat.static_header = True + self.add_column(stat) + + stat = MonitorSelfStatColumn(app, cls.MonitorSelfResidentSetSize.name, cls.MonitorSelfResidentSetSize) + stat.static_header = True + self.add_column(stat) + + stat = MonitorSelfStatColumn(app, cls.MonitorSelfImageSize.name, cls.MonitorSelfImageSize) + stat.static_header = True + self.add_column(stat) + + stat = QuotientSqlColumn(app, "used", cls.MonitorSelfResidentSetSize, + cls.MonitorSelfImageSize) + stat.static_header = True + stat.title = "% Used" + self.add_column(stat) + + self.enable_csv_export(pool) + + def create_table(self, app, name, cls): + # avoid the checkboxes + return MinimalTable(app, name, cls) + + def render_title(self, session): + return "Current" + +class MonitorSelfCharts(Widget): + def __init__(self, app, name, pool, cls, object): + super(MonitorSelfCharts, self).__init__(app, name) + + self.pool = pool + self.cls = cls + + cpu = self.CPUFlashChart(app, "cpu", object) + cpu.stats = ("MonitorSelfCPUUsage",) + self.add_child(cpu) + + used = self.MemoryFlashChart(app, "used", object) + used.chart_type = "percent" + used.stats = ("MonitorSelfResidentSetSize",) + self.add_child(used) + + def render_title(self, session): + return "History" + + class CPUFlashChart(StatFlashChart): + def render_title(self, session, *args): + return "CPU Usage" + + class MemoryFlashChart(StatFlashChart): + def render_title(self, session, *args): + return "Memory Usage" + + def get_href_params(self, session): + params = super(MonitorSelfCharts.MemoryFlashChart, self).get_href_params(session) + params.append("tp=MonitorSelfImageSize") + + return params + +class RosemaryPoolObjectAttribute(Attribute): + def __init__(self, app, name, pool, cls): + super(RosemaryPoolObjectAttribute, self).__init__(app, name) + + self.pool = pool + self.cls = cls + + def get(self, session): + obj = self.pool.get(session) + return self.cls.get_object(session.cursor, Pool=obj.Pool) + +class DashboardNegotiatorStatSet(Widget): + def __init__(self, app, name, collector): + super(DashboardNegotiatorStatSet, self).__init__(app, name) + + self.stats = self.NegotiatorStatSet(app, "stats", collector) + self.add_child(self.stats) + + def do_render(self, session): + object = self.stats.object.get(session) + if object: + return super(DashboardNegotiatorStatSet, self).do_render(session) + + def render_title(self, session): + return "Other Negotiator Statistics" + + class NegotiatorStatSet(StatSet): + def __init__(self, app, name, collector): + negotiator = NegotiatorAttribute(app, "neg", collector) + super(DashboardNegotiatorStatSet.NegotiatorStatSet, self).__init__(app, name, negotiator) + self.add_attribute(negotiator) + + self.attrs = ("MatchRate", "MatchRateSustained", "Matches", + "SlotShareIter", "NumSchedulers", "ActiveSubmitterCount", "NumIdleJobs", + "SubmittersFailed", "SubmittersOutOfTime", "SubmittersShareLimit", + "Time", "End", "Period") + + cls = app.model.com_redhat_grid.Negotiator + self.popup_charts = dict() + for attr in self.attrs: + if cls._statistics_by_name[attr].type != "lstr": + chart = StatFlashChart(app, "popup%s" % attr, self.object) + chart.stats = [attr] + self.popup_charts[attr] = chart + self.add_child(chart) + + def render_chart_href(self, session, item): + stat, _ = item + try: + chart = self.popup_charts[stat.name] + except KeyError: + # some of the attrs were not added to the chart list + return + + return self.page.get_popup_url(session, chart) + + def render_chart_class(self, session, item): + stat, _ = item + return stat.name in self.popup_charts and "chart" or "blank" + + def render_span_title(self, session, item): + stat, _ = item + return stat.name in self.popup_charts and \ + "Click to show a chart of this statistic" or \ + ""
Modified: trunk/cumin/python/cumin/grid/dashboard.strings =================================================================== --- trunk/cumin/python/cumin/grid/dashboard.strings 2011-03-08 21:40:10 UTC (rev 4584) +++ trunk/cumin/python/cumin/grid/dashboard.strings 2011-03-08 21:44:09 UTC (rev 4585) @@ -1,10 +1,12 @@ [PoolDashboard.html] {overview} + <div style="clear:both;"></div> + {capacity} <div style="clear:both; padding-top:2em;"></div> {performance} - <div style="clear:both; padding-top:2em;"></div> - {capacity} - <div style="clear:both; padding-top:2em;"></div> + <div style="clear:both;"></div> + {other_stats} + <div style="clear:both;"></div>
[DashboardOverview.html] {os_breakdown} @@ -15,23 +17,42 @@ div.DashboardPerformance { width: 1000px; } + +div.DashboardPerformance div.TabbedModeSet { + min-height: 6em; +} + [DashboardPerformance.html] <div id="{id}" class="{class}"> -<!-- <h2>{title}</h2> --> -<!-- {throughput} --> - <h3>Scheduler Performance</h3> - {scheduler_performance} - <h3>Negotiator Performance</h3> - {negotiator_performance} + {ms_s} + {ms_c} + {ms_n} </div> + {negotiator_throughput} + {negotiator_cycles} + {negotiator_slots}
+[MonitorSelfTitle.html] +<h3>{title}</h3> + +[MonitorSelfSchedulerTable.css] +div.MonitorSelfSchedulerTable { + margin-bottom: 3em; +} + +[MonitorSelfSchedulerTable.html] +<div id="{id}" class="{class}"> + <h3>{title}</h3> + {table} +</div> + [DashboardCapacity.css] div.DashboardCapacity { width: 1000px; } [DashboardCapacity.html] <div id="{id}" class="{class}"> -<h2>{title}</h2> +<h3>{title}</h3> {slot_capacity} </div>
@@ -78,13 +99,17 @@ .DefinitionSet span { display: block; float: left; + width: 1em; height: 1em; margin-right: 0.33em; position: relative; top: 0.1em; - width: 1em; }
+.DefinitionSet dt.blank span { + width: 0; +} + .DefinitionSet dd { float: left; width: 3em; @@ -93,12 +118,32 @@ border-top: 1px solid #999; }
+.DefinitionSet h3 span { + display: block; + float: right; + width: 20px; + height: 15px; + background: url(resource?name=chart-20.png) repeat; + margin-right:1em; + visibility: hidden; +} + +.DefinitionSet:hover h3 span { + cursor: pointer; + visibility: visible; +} + +.DefinitionSet:hover h3 span.blank { + cursor: default; + visibility: hidden; +} + [DefinitionSet.html] <div id="{id}" class="{class}"> <style type="text/css"> {legend_styles} </style> - <h3>{title}</h3> + <h3>{title}<span class="{span_class}" title="Click to show a timeseries chart" onclick="showHistoryChart('{title}', '{chart_href}')"></span></h3> <dl> {items} </dl> @@ -106,7 +151,7 @@ </div>
[DefinitionSet.item_html] - <dt class="{legend_class}"><span></span>{item_title}</dt> + <dt title="{html_title}" class="{legend_class}"><span></span>{item_title}</dt> <dd{dd_class}>{item_value}</dd>
@@ -114,3 +159,77 @@ <thead> <tr>{headers}</tr> </thead> + +[MonitorSelfCharts.css] +div.MonitorSelfCharts div.chart { + float: left; +} + +[MonitorSelfCharts.html] +<div id="{id}" class="{class}"> + <div class="chart">{cpu}</div><div class="chart">{used}</div> + <div style="clear:both;"></div> +</div> + +[DashboardNegotiatorStatSet.css] +div.DashboardNegotiatorStatSet { + width: 1000px; +} + +div.DashboardNegotiatorStatSet th span { + display: block; + float: left; + width: 20px; + height: 15px; + background: url(resource?name=chart-20.png) repeat; + margin-right:1em; + visibility: hidden; +} + +div.DashboardNegotiatorStatSet tr.item:hover th span { + cursor: pointer; + visibility: visible; +} + +div.DashboardNegotiatorStatSet tr.item:hover th span.blank { + cursor: default; + visibility: hidden; +} +.mask { + z-index: 500; +} + +div.DashboardNegotiatorStatSet tr.item:hover { + background-color: #E7E7F7; +} + +[DashboardNegotiatorStatSet.html] +<div class="{class}"> + <h3>{title}</h3> + {stats} +</div> +<script type="text/javascript"> +//<![CDATA[ +function showHistoryChart(title, src) { + + if (src != '') { + new StickyWin.Modal.Ajax({ + url: src, + wrapWithUi: true, + caption: title, + useIframeShim: false, + allowMultiple: false, + closeOnEsc: true, + uiOptions: {width: 420} + }).update(); + } + return false; +} +//]]> +</script> + +[NegotiatorStatSet.item_html] +<tr title="{html_title}" class="item"> + <th><span class="{chart_class}" onclick="showHistoryChart('{item_title}', '{chart_href}')" title="{span_title}"></span>{item_title}</th> + <td class="ralign"> {item_value}</td> +</tr>
Modified: trunk/cumin/python/cumin/objectframe.py =================================================================== --- trunk/cumin/python/cumin/objectframe.py 2011-03-08 21:40:10 UTC (rev 4584) +++ trunk/cumin/python/cumin/objectframe.py 2011-03-08 21:44:09 UTC (rev 4585) @@ -94,16 +94,17 @@ for attr in self.get_attributes(session): name = attr.title value = obj.get_formatted_value(attr.name) + title = attr.description
- writer.write(self.entry.render(session, name, value)) + writer.write(self.entry.render(session, name, value, title))
return writer.to_string()
class ObjectAttributesEntry(Widget): - def render_name(self, session, name, value): + def render_name(self, session, name, value, description): return name
- def render_value(self, session, name, value): + def render_value(self, session, name, value, description): if value is None: return fmt_none()
@@ -112,6 +113,9 @@
return xml_escape(str(value))
+ def render_html_title(self, session, name, value, description): + return description + def break_up_long_lines(self, string): if " " in string[0:80]: return string
Modified: trunk/cumin/python/cumin/objectframe.strings =================================================================== --- trunk/cumin/python/cumin/objectframe.strings 2011-03-08 21:40:10 UTC (rev 4584) +++ trunk/cumin/python/cumin/objectframe.strings 2011-03-08 21:44:09 UTC (rev 4585) @@ -30,7 +30,7 @@ </div>
[ObjectAttributesEntry.html] -<tr><th>{name}</th><td>{value}</td></tr> +<tr><th title="{html_title}">{name}</th><td>{value}</td></tr>
[ObjectTasks.css] table.ObjectTasks { @@ -118,6 +118,7 @@ }
[ObjectView.html] +<div id="{id}" class="{class}"> {context}
{heading} @@ -125,6 +126,7 @@ {summary}
{body} +</div>
[ObjectViewHeading.html] <div class="{class}">
Modified: trunk/cumin/python/cumin/objectselector.py =================================================================== --- trunk/cumin/python/cumin/objectselector.py 2011-03-08 21:40:10 UTC (rev 4584) +++ trunk/cumin/python/cumin/objectselector.py 2011-03-08 21:44:09 UTC (rev 4585) @@ -124,7 +124,7 @@ self.selector = selector
# avoid name collisions on csv page - if self.name in self.app.export_page.modes.children_by_name: + while self.name in self.app.export_page.modes.children_by_name: self.name = self.name + ".1"
self.app.export_page.modes.add_mode(self)
Modified: trunk/cumin/python/cumin/stat.py =================================================================== --- trunk/cumin/python/cumin/stat.py 2011-03-08 21:40:10 UTC (rev 4584) +++ trunk/cumin/python/cumin/stat.py 2011-03-08 21:44:09 UTC (rev 4585) @@ -29,6 +29,8 @@
def do_get_items(self, session): object = self.object.get(session) + if not object: + return []
stats = list() for field in self.get_fields(session): @@ -40,6 +42,10 @@ stat, value = item return stat.title
+ def render_html_title(self, session, item): + stat, _ = item + return stat.description + def render_item_value(self, session, item): stat, value = item return CuminStatistic.fmt_value(value) @@ -494,8 +500,8 @@ deviated_values = list() for stat in stats: for x in samples[stat]: - d1 = nvl(x[1], 0) - d2 = nvl(x[2], 0) / 2 + d1 = float(nvl(x[1], 0)) + d2 = float(nvl(x[2], 0) / 2) dv = (d1 + d2, d1 - d2) deviated_values.append(dv)
@@ -1290,7 +1296,10 @@ min_dt = tnow - duration - end_secs for dt, value, dev in samples[stat]: if value is not None: - vals.append({"dt": secs(dt), "y": (float(value) / float(total)) * 100.0}) + if total is None or total == 0.0: + vals.append({"dt": secs(dt), "y": 0.0}) + else: + vals.append({"dt": secs(dt), "y": (float(value) / float(total)) * 100.0}) if secs(dt) < min_dt: break
Modified: trunk/cumin/python/cumin/stat.strings =================================================================== --- trunk/cumin/python/cumin/stat.strings 2011-03-08 21:40:10 UTC (rev 4584) +++ trunk/cumin/python/cumin/stat.strings 2011-03-08 21:44:09 UTC (rev 4585) @@ -46,7 +46,7 @@ </table>
[StatSet.item_html] -<tr class="item"> +<tr class="item" title="{html_title}"> <th>{item_title}</th> <td class="ralign"> {item_value}</td> </tr>
cumin-developers@lists.fedorahosted.org