r5209 - branches/noflash/cumin/resources
by croberts@fedoraproject.org
Author: croberts
Date: 2012-02-09 16:30:40 +0000 (Thu, 09 Feb 2012)
New Revision: 5209
Removed:
branches/noflash/cumin/resources/open-flash-chart.swf
Log:
Removing the open-flash-chart.swf compiled flash movie.
Deleted: branches/noflash/cumin/resources/open-flash-chart.swf
===================================================================
(Binary files differ)
12 years, 2 months
r5208 - in branches/noflash: . cumin
by croberts@fedoraproject.org
Author: croberts
Date: 2012-02-09 16:29:12 +0000 (Thu, 09 Feb 2012)
New Revision: 5208
Removed:
branches/noflash/cumin/open-flash-chart/
Modified:
branches/noflash/
Log:
Removing the open-flash-chart source files
Property changes on: branches/noflash
___________________________________________________________________
Modified: svn:ignore
- TAGS
+ TAGS
.pydevproject
.project
12 years, 2 months
r5207 - in trunk: cumin/python/cumin cumin/python/cumin/grid wooly/python/wooly wooly/resources
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-02-08 17:28:17 +0000 (Wed, 08 Feb 2012)
New Revision: 5207
Modified:
trunk/cumin/python/cumin/grid/job.py
trunk/cumin/python/cumin/objectframe.py
trunk/cumin/python/cumin/objectselector.py
trunk/wooly/python/wooly/__init__.py
trunk/wooly/python/wooly/pages.py
trunk/wooly/python/wooly/server.py
trunk/wooly/resources/wooly.js
Log:
These changes sanely handle conditions that arise from deleted
submissions when the schedd publishes QMF submission objects
(QMF_PUBLISH_SUBMISSIONS is True) while eliminating unnecessary
redirects when the jobserver process publishes QMF objects.
* Add a mechanism that optionally allows a redirect on a failed ajax update.
Redirects at this point must be specified widget by widget.
* Redirect updates of the job summary page and the job details page
to the submission list when an update fails because the submission
object is missing.
* Change redirect from "Remove job" link on the job details page
to the job list instead of the submission list.
* Remove redirect from job summary page when the last running job
in a submission is removed.
BZ783139
Modified: trunk/cumin/python/cumin/grid/job.py
===================================================================
--- trunk/cumin/python/cumin/grid/job.py 2012-02-06 19:34:41 UTC (rev 5206)
+++ trunk/cumin/python/cumin/grid/job.py 2012-02-08 17:28:17 UTC (rev 5207)
@@ -296,6 +296,17 @@
(app, "id", cls.JobId, self.ids)
self.add_column(self.checkbox_column)
+ def do_render(self, session):
+ # Set redirect if the submission object we depend on is missing
+ if self.frame.object.get(session) is None:
+ nsession = session.branch()
+ frame = self.frame.parent
+ frame.view.show(nsession)
+ submission_list_url = nsession.marshal()
+ self.page.redirect.set(session, submission_list_url)
+ else:
+ return super(JobSelectorTable, self).do_render(session)
+
class JobObjectSelectorTask(ObjectSelectorTask):
def __init__(self, app, selector, verb):
super(JobObjectSelectorTask, self).__init__(app, selector)
@@ -342,29 +353,6 @@
def get_title(self, session):
return "Remove"
- def _get_running_jobs(self, osession):
- running = []
- records = self.selector.get_qmf_results(osession)
- if records and hasattr(records, "data"):
- for rec in records.data:
- if "JobStatus" in rec and rec["JobStatus"] == "RUNNING":
- running.append("%s.%s" % (rec["ClusterId"],rec["ProcId"]))
- return running
-
- def do_enter(self, session, osession):
- super(JobSelectionRemove, self).do_enter(session, osession)
-
- # if all the running jobs in this submission are to be removed
- # return to the submission list instead of this submission page.
- running = self._get_running_jobs(osession)
- selected = self.selector.table.ids.get(osession)
- if len(set(running) - set(selected)) == 0:
- nsession = osession.branch()
- frame = self.selector.frame.parent
- frame.view.show(nsession)
- submission_list_url = nsession.marshal()
- self.form.return_url.set(session, submission_list_url)
-
class JobObjectSelectorTaskForm(ObjectSelectorTaskForm):
def __init__(self, app, name, task, verb):
super(JobObjectSelectorTaskForm, self).__init__(app, name, task)
@@ -650,6 +638,17 @@
self.defer_enabled = True
self.update_enabled = True
+ def do_render(self, session):
+ # Set redirect if the submission object we depend on is missing
+ if self.frame.object.get(session) is None:
+ nsession = session.branch()
+ frame = self.frame.parent.parent
+ frame.view.show(nsession)
+ submission_list_url = nsession.marshal()
+ self.page.redirect.set(session, submission_list_url)
+ else:
+ return super(JobAdsViewer, self).do_render(session)
+
def render_edit_button(self, session):
_, error = self.do_get_items(session)
render = True
@@ -1172,10 +1171,9 @@
nsession = osession.branch()
- # return to the submission list page
+ # return to the job list page
job_list = self.frame.parent
- submission_list = job_list.parent
- submission_list.view.show(nsession)
+ job_list.view.show(nsession)
self.form.return_url.set(session, nsession.marshal())
Modified: trunk/cumin/python/cumin/objectframe.py
===================================================================
--- trunk/cumin/python/cumin/objectframe.py 2012-02-06 19:34:41 UTC (rev 5206)
+++ trunk/cumin/python/cumin/objectframe.py 2012-02-08 17:28:17 UTC (rev 5207)
@@ -69,8 +69,20 @@
super(ObjectFrame, self).do_process(session)
+ def _allow_none(self, session):
+ try:
+ return self.page.allow_object_not_found.get(session)
+ except:
+ pass
+ return False
+
def get_object(self, session, id):
- return self.cls.get_object_by_id(session.cursor, id)
+ try:
+ return self.cls.get_object_by_id(session.cursor, id)
+ except:
+ if self._allow_none(session):
+ return None
+ raise
def get_view(self, app, name, obj):
return ObjectView(app, name, obj)
Modified: trunk/cumin/python/cumin/objectselector.py
===================================================================
--- trunk/cumin/python/cumin/objectselector.py 2012-02-06 19:34:41 UTC (rev 5206)
+++ trunk/cumin/python/cumin/objectselector.py 2012-02-08 17:28:17 UTC (rev 5207)
@@ -580,7 +580,10 @@
export_session = wooly.Session(page)
page.modes.show_child(export_session, self.exporter)
- page.set_parameters(export_session, session, self.object_attributes, self.file_name)
+ try:
+ page.set_parameters(export_session, session, self.object_attributes, self.file_name)
+ except:
+ return ""
return escape_entity(export_session.marshal())
class QmfDetails(Widget):
Modified: trunk/wooly/python/wooly/__init__.py
===================================================================
--- trunk/wooly/python/wooly/__init__.py 2012-02-06 19:34:41 UTC (rev 5206)
+++ trunk/wooly/python/wooly/__init__.py 2012-02-08 17:28:17 UTC (rev 5207)
@@ -401,6 +401,10 @@
self.redirect = Attribute(app, "redirect")
self.add_attribute(self.redirect)
+ self.allow_object_not_found = Attribute(app, "allow_object_not_found")
+ self.allow_object_not_found.required = False
+ self.add_attribute(self.allow_object_not_found)
+
self.error = Attribute(app, "error")
self.add_attribute(self.error)
@@ -502,6 +506,10 @@
class PageRedirect(Exception):
pass
+class UpdateRedirect(Exception):
+ def __init__(self, url):
+ self.url = url
+
class PageError(object):
def __init__(self, page, session):
self.page = page
Modified: trunk/wooly/python/wooly/pages.py
===================================================================
--- trunk/wooly/python/wooly/pages.py 2012-02-06 19:34:41 UTC (rev 5206)
+++ trunk/wooly/python/wooly/pages.py 2012-02-08 17:28:17 UTC (rev 5207)
@@ -110,8 +110,9 @@
value = session.request_environment.get("HTTP_USER_AGENT")
if (value and value.find("MSIE 6") != -1) or (value and value.find("MSIE 9") != -1):
- # sadly, although msie 9 handles application/xhtml+xml, the version of swfobject (2.2)
- # that we are using does not support that content type (by their design), so we fall back to text/html
+ # sadly, although msie 9 handles application/xhtml+xml, the version
+ # of swfobject (2.2) that we are using does not support that content
+ # type (by their design), so we fall back to text/html
content_type = self.html_content_type
else:
content_type = self.xhtml_content_type
@@ -215,13 +216,31 @@
def do_process(self, session):
# get the session for the main page
sess = self.session.get(session)
+
+ # Allow object frames to have None as an object
+ # during the process pass.
+ self.allow_object_not_found.set(sess, True)
+
self.html_page.process(sess)
- # if the main page set it's redirect attribute in it's session
+ # if the main page set its redirect attribute in its session
redirect = self.html_page.redirect.get(sess)
# pass that redirect on to the the update page
if redirect:
self.redirect.set(session, redirect)
+ def service(self, session):
+ self.process(session)
+
+ # Skip the render if a redirect has been set
+ if not self.redirect.get(session):
+ res = self.render(session)
+ # Check for a redirect from either process or render
+ url = self.redirect.get(session)
+ if url:
+ # Send the url to the ajax script in the update response
+ raise UpdateRedirect(url)
+ return res
+
def render_widgets(self, session):
writer = Writer()
sess = self.session.get(session)
@@ -230,7 +249,12 @@
for widget in widgets:
self.widget_tmpl.render(writer, sess, widget)
-
+ # If a widget generated a redirect url,
+ # there is no sense finishing the render
+ redirect = self.redirect.get(sess)
+ if redirect:
+ self.redirect.set(session, redirect)
+ break
return writer.to_string()
def render_widget_id(self, session, widget):
Modified: trunk/wooly/python/wooly/server.py
===================================================================
--- trunk/wooly/python/wooly/server.py 2012-02-06 19:34:41 UTC (rev 5206)
+++ trunk/wooly/python/wooly/server.py 2012-02-08 17:28:17 UTC (rev 5207)
@@ -126,6 +126,12 @@
except PageRedirect:
status = "303 See Other"
headers.append(("Location", page.redirect.get(session)))
+ except UpdateRedirect as e:
+ # This will optionally allow a failed ajax update to
+ # process a location change. Javascript code in the
+ # update handler will look for the header.
+ status = "200 OK"
+ headers.append(("Location", e.url))
except:
content = page.service_error(session)
status = "500 Internal Error"
Modified: trunk/wooly/resources/wooly.js
===================================================================
--- trunk/wooly/resources/wooly.js 2012-02-06 19:34:41 UTC (rev 5206)
+++ trunk/wooly/resources/wooly.js 2012-02-08 17:28:17 UTC (rev 5207)
@@ -459,16 +459,25 @@
function update() {
try {
- if (req.readyState == 4 && req.status == 200) {
+ // The location header provides away to recover from a
+ // failed update
+ var checkForRedirect = req.getResponseHeader("Location")
+ if (checkForRedirect){
+ if (checkForRedirect == "reload") {
+ location.reload()
+ } else {
+ location.href = checkForRedirect
+ }
+ } else if (req.readyState == 4 && req.status == 200) {
if (astext == true)
callback(req.responseText, passback);
else
callback(req.responseXML, passback);
} else {
- if(req.readyState == 4) {
- //readyState == 4 means that the call is done...a non-200 status means that something bad happened, call the callback with null for the XML
- callback(null, passback);
- }
+ if(req.readyState == 4) {
+ //readyState == 4 means that the call is done...a non-200 status means that something bad happened, call the callback with null for the XML
+ callback(null, passback);
+ }
}
} catch (e) {
log(e);
12 years, 2 months
r5206 - branches/noflash/cumin/etc
by croberts@fedoraproject.org
Author: croberts
Date: 2012-02-06 19:34:41 +0000 (Mon, 06 Feb 2012)
New Revision: 5206
Added:
branches/noflash/cumin/etc/cumin.service
Log:
New file required for fedora build.
Added: branches/noflash/cumin/etc/cumin.service
===================================================================
--- branches/noflash/cumin/etc/cumin.service (rev 0)
+++ branches/noflash/cumin/etc/cumin.service 2012-02-06 19:34:41 UTC (rev 5206)
@@ -0,0 +1,10 @@
+[Unit]
+Description=MRG Management console
+After=postgresql.target
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/cumin --syslog
+
+[Install]
+WantedBy=multi-user.target
12 years, 2 months
r5205 - branches/noflash/cumin/python/cumin
by croberts@fedoraproject.org
Author: croberts
Date: 2012-02-06 15:55:40 +0000 (Mon, 06 Feb 2012)
New Revision: 5205
Modified:
branches/noflash/cumin/python/cumin/stat.py
branches/noflash/cumin/python/cumin/widgets.py
Log:
Getting rid of code that referenced the cairo charting library.
Modified: branches/noflash/cumin/python/cumin/stat.py
===================================================================
--- branches/noflash/cumin/python/cumin/stat.py 2012-02-06 14:44:17 UTC (rev 5204)
+++ branches/noflash/cumin/python/cumin/stat.py 2012-02-06 15:55:40 UTC (rev 5205)
@@ -14,7 +14,6 @@
from cumin.model import CuminStatistic, SamplesSqlAdapter
from cumin.widgets import StateSwitch
from wooly.template import WidgetTemplate
-from cumin.charts import CuminPieChart, TimeSeriesChart, StackedValueChart
from cumin.util import calc_rate, secs, nvl
from cumin.OpenFlashChart import Element, Chart
@@ -323,20 +322,7 @@
src = "%s?%s" % (page, ";".join(tokens))
return src
- def do_render(self, session):
- radius = self.radius.get(session)
- slices = self.slices.get(session)
- color_scheme = self.color_scheme.get(session)
- colors = self.color_schemes[color_scheme]
-
- pie = CuminPieChart(radius)
- surface = pie.plot_pie(slices, colors)
-
- writer = Writer()
- surface.write_to_png(writer)
- return writer.to_string()
-
def get_content_type(self, session):
return "image/png"
@@ -478,138 +464,6 @@
rets[stat.name] = ret
return str(rets)
- def do_render(self, session):
- adapter, stats = self.get_adapter_stats(session)
- colors = ((1,0,0), (0,0,1), (0,1,0))
-
- recent = None
- if len(stats):
- recent = adapter.recent()
- else:
- return
-
- # mouseover event wants the points returned in a array
- if self.samples.get(session):
- return self.render_samples(session, recent)
-
- #if recent:
- # cached_png = self.get_cached(session, recent)
- # if cached_png:
- # return cached_png
-
- samples = dict()
- values = dict()
-
- width = self.container_width.get(session)
- height = self.container_height.get(session)
- mode = self.mode.get(session)
- duration = self.duration.get(session)
- interval = self.get_interval(session, duration, width)
-
- chart = TimeSeriesChart(width, height, interval=interval)
-
- if mode == "rate":
- #method = None # don't do averaging
- method = self.method.get(session)
- for stat in stats:
- os = adapter.samples(stat, duration, interval, method)
- ns = list()
- prev = None
-
- for sample in reversed(os):
- if prev is not None:
- rate = calc_rate(float(sample[1]), float(prev[1]),
- secs(sample[0]), secs(prev[0]))
-
- ns.insert(0, (sample[0], rate, None))
-
- prev = sample
-
- samples[stat] = ns
- else:
- method = self.method.get(session)
- for stat in stats:
- samples[stat] = adapter.samples(stat, duration, interval, method)
-
- type = self.type.get(session)
- if type == "percent":
- total_property = self.total_property.get(session)
- total = self.get_object_property(session, total_property)
- total = total and float(total) or 1.0
-
- for stat in stats:
- percents = [(x[0], (float(x[1]) / total) * 100.0, x[2])
- for x in samples[stat] if x[1] is not None]
- samples[stat] = percents
-
- # take stddev into account for max and min y values
- deviated_values = list()
- for stat in stats:
- for x in samples[stat]:
- d1 = float(nvl(x[1], 0))
- d2 = float(nvl(x[2], 0) / 2)
- dv = (d1 + d2, d1 - d2)
- deviated_values.append(dv)
-
- max_value = deviated_values and max(max(deviated_values)) or 1
- min_value = deviated_values and min(min(deviated_values)) or 0
-
- # convert from decimal to float to avoid exceptions
- max_value = float(max_value)
- min_value = float(min_value)
-
- max_value = round(max_value * 1.1 + 1)
- if type == "percent" and max_value > 100:
- # since none of our percentage graphs go above 100%
- # we limit it here to match the flash graph
- max_value = 100
-
- if min_value < 0:
- min_value = round(min_value * 1.1 - 1)
-
- chart.x_max = duration
- chart.x_min = 0
- chart.y_max = max_value
- chart.y_min = min_value
-
- x_intervals = 8
- x_step = 2
-
- if chart.x_max == 600:
- x_intervals = 10
-
- y_intervals = 6
- y_step = 2
-
- if chart.y_max < 3:
- y_step = 3
-
- chart.plot_x_axis(x_intervals, x_step)
- chart.plot_y_axis(y_intervals, y_step)
-
- points = dict()
- for stat, color in zip(stats, colors):
- points[stat] = (chart.plot_values(samples[stat], color=color), color)
-
- for stat, color in zip(stats, colors):
- chart.plot_ticks(samples[stat], color=color)
-
- chart.plot_frame()
- chart.plot_interval(interval)
-
- if mode == "rate":
- titles = ["%s / sec" % x.title for x in stats]
- else:
- titles = ["%s%s" % ((type == "percent") and "Percent " or "", x.short_title and x.short_title or x.title) for x in stats]
-
- title_xy = chart.plot_legend(titles, colors)
-
- self.cache_it(session, chart, {'recent': recent, 'samples': points, "title_xy": title_xy})
-
- writer = Writer()
- chart.write(writer)
- return writer.to_string()
-
class ModifiedAgentIdParameter(StringParameter):
def get(self, session):
agent = super(ModifiedAgentIdParameter, self).get(session)
@@ -1370,88 +1224,3 @@
def get_line_title(self, stat):
return "Percent %s" % stat.title
-class StatStackedPage(StatChartPage):
- # handles stacked.png requests
- def do_render(self, session):
- adapter, stats = self.get_adapter_stats(session)
-
- if len(stats):
- recent = adapter.recent()
- if recent:
- cached_png = self.get_cached(session, recent)
- if cached_png:
- return cached_png
-
- legend_height = len(stats) * 16 + 16
-
- width = self.container_width.get(session)
- height = self.container_height.get(session)
- chart = StackedValueChart(width, height, legend_height)
-
- duration = self.duration.get(session)
- method = self.method.get(session)
- interval = self.get_interval(session, duration, width)
-
- max_value = 1
- min_value = 999999
-
- points = dict()
- samples = dict()
- collapsed = dict()
- values = dict()
- for stat in stats:
- samples[stat] = adapter.samples(stat, duration, interval, method)
- # we sometimes get multiple samples for the same qmf_update_time
- for sample in samples[stat]:
- collapsed[sample[0]] = sample[1]
-
- for t in collapsed:
- if not t in points:
- points[t] = list()
- values[t] = 0
- points[t].append(collapsed[t])
- values[t] = values[t] + collapsed[t]
- max_value = max(values[t], max_value)
- min_value = min(values[t], min_value)
-
- max_value = round(max_value * 1.1 + 1)
-
- if min_value < 0:
- min_value = round(min_value * 1.1 - 1)
-
- chart.x_max = duration
- chart.x_min = 0
- chart.y_max = max_value
- chart.y_min = min_value
-
- x_intervals = 8
- x_step = 2
-
- if chart.x_max == 600:
- x_intervals = 10
-
- y_intervals = 6
- y_step = 2
-
- if chart.y_max < 3:
- y_step = 3
-
- chart.plot_x_axis(x_intervals, x_step)
- chart.plot_y_axis(y_intervals, y_step)
-
- colors = ((1,0,0), (0,0,1), (0,1,0), (1,0,1), (1,1,0), (0,1,1), (0,0,0))
-
- for t in points:
- chart.plot_values(t, points[t], colors)
-
- chart.plot_frame()
-
- titles = [x.title for x in stats]
-
- chart.plot_legend(reversed(titles), reversed(colors[:len(titles)]))
-
- self.cache_it(session, chart, {'recent': recent, 'samples': None, "title_xy": None})
-
- writer = Writer()
- chart.write(writer)
- return writer.to_string()
Modified: branches/noflash/cumin/python/cumin/widgets.py
===================================================================
--- branches/noflash/cumin/python/cumin/widgets.py 2012-02-06 14:44:17 UTC (rev 5204)
+++ branches/noflash/cumin/python/cumin/widgets.py 2012-02-06 15:55:40 UTC (rev 5205)
@@ -10,7 +10,6 @@
from objectselector import *
from parameters import *
from widgets import *
-from charts import *
from formats import *
from task import *
from user import *
12 years, 2 months
r5204 - branches/noflash/cumin/python/cumin
by croberts@fedoraproject.org
Author: croberts
Date: 2012-02-06 14:44:17 +0000 (Mon, 06 Feb 2012)
New Revision: 5204
Removed:
branches/noflash/cumin/python/cumin/charts.py
Log:
Removing charts.py since it is no longer used.
Deleted: branches/noflash/cumin/python/cumin/charts.py
===================================================================
--- branches/noflash/cumin/python/cumin/charts.py 2012-02-03 19:41:58 UTC (rev 5203)
+++ branches/noflash/cumin/python/cumin/charts.py 2012-02-06 14:44:17 UTC (rev 5204)
@@ -1,439 +0,0 @@
-import logging
-
-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 CuminPieChart(object):
- def __init__(self, radius=400):
- self.width = radius * 2
- self.height = self.width
- self.centerx = radius
- self.centery = radius
- self.radius = radius
-
- def plot_pie(self, slices, colors):
- surface = ImageSurface(FORMAT_ARGB32, int(self.width), int(self.height))
- cr = Context(surface)
- cr.set_line_width(2)
-
- total = float(sum(slices))
-
- if not total:
- # draw a grey placeholder circle
- cr.new_path()
- cr.arc(self.centerx, self.centery,
- self.radius, 0, 2 * pi)
- cr.close_path()
- cr.set_source_rgba(0, 0, 0, 0.1)
- cr.stroke()
- return surface
-
- 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
-
- start = end
- end = (cum_wedges + wedge) * 2 * pi
- cum_wedges += wedge
-
- # draw the wedge
- self.draw_slice_path(cr, self.centerx, self.centery,
- self.radius, start, end)
- cr.set_source_rgb(*color)
- cr.fill()
-
- # draw a white border
- 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
-
- def draw_slice_path(self, cr, x, y, radius, start, end):
- cr.new_path()
- cr.move_to(x, y)
- cr.arc(x, y, radius, start, end)
- cr.close_path()
-
-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.surface = None
- self.gap = 3
-
- def plot_colored_shape(self, interior, shape, width, height):
- surface = ImageSurface(FORMAT_ARGB32, int(width), int(height))
- cr = Context(surface)
- cr.set_line_width(1)
-
- cr.set_source_rgba(1.0, 1.0, 1.0, 1.0)
- cr.rectangle(0, 0, width - 1, height - 1)
- cr.fill()
-
- cr.set_line_width(2)
- self._plot_shape(cr, interior, shape, 0, 0, width, height)
- return surface
-
- def plot_colored_rect(self, interior, width, height):
- surface = ImageSurface(FORMAT_ARGB32, int(width), int(height))
- cr = Context(surface)
- cr.set_line_width(1)
-
- cr.set_source_rgb(*interior)
- cr.rectangle(0, 0, width - 1, height - 1)
- cr.fill()
- return surface
-
- def plot_state(self, state, width, height):
- surface = ImageSurface(FORMAT_ARGB32, int(width), int(height))
- cr = Context(surface)
- cr.set_line_width(2)
-
- self._plot_square(cr, (0.7, 0.7, 0.7), state, 0, 0, width, height)
- return surface
-
- def _plot_square(self, cr, colors, state, x, y, width, height):
- if state == "Unclaimed":
- shape = "box"
- elif state == "Claimed":
- shape = "solid"
- elif state in ("Owner", "Unavailable"):
- shape = "diagonal"
- elif state in ("Matched", "Preempting", "Preempting/Matched"):
- shape = "filled_triangle"
- else:
- shape = "box"
-
- self._plot_shape(cr, colors, shape, x, y, width, height)
-
- def _plot_shape(self, cr, colors, shape, x, y, width, height):
- cr.set_source_rgb(*colors)
- #cr.move_to(x, y)
- cr.rectangle(x, y, width - self.gap, height - self.gap)
-
- if shape == "box": # leave empty
- cr.stroke()
- elif shape == "solid": # solid fill
- cr.fill()
- elif shape == "diagonal": # diagonal line
- cr.move_to(x, y)
- cr.line_to(x + width - self.gap, y + height - self.gap)
- cr.stroke()
- elif shape == "filled_triangle": # triangle
- cr.stroke()
- cr.move_to(x + width - self.gap, y)
- cr.line_to(x, y + height - self.gap)
- cr.line_to(x + width - self.gap, y + height - self.gap)
- cr.close_path()
- cr.fill()
-
- def plot_shapes(self, shape_info, zl):
- count = len(shape_info)
- slot_size = self.slot_size(count, zl)
- x = 0
- y = 0
- col = 0
- self.rows = 1
- # the width and height depend on the number of slots
- self.surface = ImageSurface(FORMAT_ARGB32, int(self.width), int(self.height))
- cr = Context(self.surface)
-
- cr.set_line_width( zl == 1 and 1 or 2 )
-
- for slot in shape_info:
- interior = slot["color"]
- shape = slot["shape"]
-
- if col >= self.cols:
- x = 0
- y = y + slot_size
- col = 0
- self.rows = self.rows + 1
-
- self._plot_shape(cr, interior, shape, x, y, slot_size, slot_size)
-
- 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
-
- self.width = int((size * cols) + 1)
- self.height = int((ceil(count * 1.0 / cols) * size) + 1)
- self.cols = cols
- return size
-
- def write(self, writer):
- self.surface.write_to_png(writer)
-
-class TimeSeriesChart(object):
- def __init__(self, width, height, interval=0):
- self.width = width - 40
- self.height = height - 20
- real_height = height + ((interval > 10) and 12 or 0)
- self.surface = ImageSurface(FORMAT_ARGB32, int(width), int(real_height))
- self.surface.set_device_offset(1.5, 5.5)
- self.x_max = 1
- self.x_min = 0
- self.y_max = 1
- self.y_min = 0
-
- def plot_values(self, samples, color=(0, 0, 0)):
- cr = Context(self.surface)
- cr.set_line_width(1.5)
- cr.set_source_rgba(color[0], color[1], color[2], 0.66)
-
- tnow = time()
- points = list()
-
- for dt, value, dev in samples:
- if value is None:
- continue
-
- t = secs(dt)
-
- # prevent line from drawing off right side of grid
- if tnow - t < 0:
- t = tnow
-
- x = self.width - ((tnow - t) / float(self.x_max)) * self.width
- y = self.height - (value / float(self.y_max)) * self.height
- cr.line_to(x, y)
- if x < 0:
- break
- points.append([int(x), int(y), "%.2f" % value])
-
- cr.stroke()
- return points
-
- def plot_ticks(self, samples, color=(0, 0, 0)):
- cr = Context(self.surface)
- cr.set_line_width(1.5)
-
- tnow = time()
- dot_size = max(int(self.height / 100), 2)
- half_dot = int(dot_size / 2)
-
- for dt, value, dev in samples:
- if value is None:
- continue
-
- t = secs(dt)
-
- # prevent line from drawing off right side of grid
- if tnow - t < 0:
- t = tnow
-
- cr.set_source_rgba(color[0], color[1], color[2], 0.66)
- x = self.width - ((tnow - t) / float(self.x_max)) * self.width
- y = self.height - (value / float(self.y_max)) * self.height
- cr.move_to(x, y)
- cr.rectangle(x - half_dot, y - half_dot, dot_size, dot_size)
- cr.fill()
-
- if dev:
- cr.set_source_rgba(0, 0, 0, 0.1)
- half_dev = int(((float(dev) / float(self.y_max)) * self.height) / 2)
- cr.move_to(x - dot_size, y - half_dev)
- cr.line_to(x + dot_size, y - half_dev)
-
- cr.move_to(x, y - half_dev)
- cr.line_to(x, y + half_dev)
-
- cr.move_to(x - dot_size, y + half_dev)
- cr.line_to(x + dot_size, y + half_dev)
-
- cr.stroke()
-
- def plot_legend(self, titles, colors):
- cr = Context(self.surface)
- cr.set_line_width(1.5)
- title_xy = dict()
-
- for i, item in enumerate(zip(titles, colors)):
- title, color = item
- y = 16 + i * 16
-
- cr.set_source_rgba(1, 1, 1, 0.75)
- cr.rectangle(4, y - 12, 16 + 6.5 * len(title), 16)
- cr.fill()
- cr.stroke()
-
- cr.set_source_rgb(*color)
- cr.rectangle(8, y, 8, -8)
- cr.fill()
- cr.stroke()
-
- cr.move_to(20, y)
- cr.set_source_rgb(0, 0, 0)
- cr.show_text(title)
- cr.stroke()
-
- width, height = cr.text_extents(title)[2:4]
- title_xy[color] = [width, y]
- return title_xy
-
- def plot_frame(self, color=(0.8, 0.8, 0.8)):
- cr = Context(self.surface)
- cr.set_line_width(1)
- cr.set_source_rgb(*color)
-
- cr.move_to(self.width, 0)
- cr.line_to(self.width, self.height)
- cr.line_to(0, self.height)
-
- cr.stroke()
-
- def plot_interval(self, interval):
- if interval > 10:
- cr = Context(self.surface)
- cr.set_line_width(0.2)
- cr.set_source_rgb(0.2, 0.2, 0.2)
-
- cr.move_to(2, self.height + 24)
- cr.show_text("* Samples averaged over %i second interval" % interval)
- cr.stroke()
-
- def plot_x_axis(self, intervals, step):
- cr = Context(self.surface)
- cr.set_line_width(0.2)
- cr.set_source_rgb(0.6, 0.6, 0.6)
-
- interval = self.width / float(intervals)
- absmax = self.x_max - self.x_min
-
- for i in range(0, intervals + 1):
- x = self.width - (i * interval)
-
- cr.move_to(x, 0)
- cr.line_to(x, self.height + 12)
- cr.move_to(x + 2, self.height + 12)
-
- if i % step == 0:
- value = absmax - (absmax * x / float(self.width))
- text = fmt_duration_brief(value)
- if text:
- cr.show_text(text)
-
- cr.stroke()
-
- def plot_y_axis(self, intervals, step):
- cr = Context(self.surface)
- cr.set_line_width(0.2)
- cr.set_source_rgb(0.6, 0.6, 0.6)
-
- interval = self.height / float(intervals)
-
- for i in range(0, intervals + 1):
- y = self.height - (i * interval)
-
- cr.move_to(0, y)
- cr.line_to(self.width + 3, y)
- cr.move_to(self.width + 4, y + 3)
-
- if i % step == 0:
- fraction = (self.height - y) / float(self.height)
- value = fraction * self.y_max - self.y_min
- value = int(round(value + self.y_min))
-
- if value >= 1000000:
- svalue = "%.2fM" % (round(value / 1000000.0, 2))
- elif value >= 10000:
- svalue = "%ik" % int(round(value / 1000.0, -1))
- else:
- svalue = str(value)
-
- cr.show_text(svalue)
-
- cr.stroke()
-
- def write(self, writer):
- self.surface.write_to_png(writer)
-
-class StackedValueChart(TimeSeriesChart):
- def __init__(self, width, height, legend_height = 0):
- super(StackedValueChart, self).__init__(width, height)
-
- if legend_height:
- self.surface = ImageSurface(FORMAT_ARGB32, int(width), int(height + legend_height))
- self.surface.set_device_offset(1.5, 5.5)
-
- self.legend_height = legend_height
- self.bar_width = 2
-
- def plot_values(self, dt, samples, colors):
- cr = Context(self.surface)
- cr.set_line_width(self.bar_width)
-
- tnow = time()
- y = self.height
- v = 0
- for value, color in zip(samples, colors):
- if value is None:
- continue
-
- t = secs(dt)
-
- if tnow - t < 0:
- t = tnow
-
- x = self.width - ((tnow - t) / float(self.x_max)) * self.width
- cr.move_to(x, y)
- cr.set_source_rgba(color[0], color[1], color[2], 0.66)
-
- v = v + value
- y = self.height - (v / float(self.y_max)) * self.height
- cr.line_to(x, y)
- cr.stroke()
-
- def plot_legend(self, titles, colors):
- cr = Context(self.surface)
-
- for i, item in enumerate(zip(titles, colors)):
- title, color = item
- y = self.height + 26 + i * 16
-
- cr.set_source_rgba(1, 1, 1, 0.75)
- cr.rectangle(4, y - 12, 16 + 6.5 * len(title), 16)
- cr.fill()
- cr.stroke()
-
- cr.set_source_rgb(*color)
- cr.rectangle(8, y, 8, -8)
- cr.fill()
- cr.stroke()
-
- cr.move_to(20, y)
- cr.set_source_rgb(0, 0, 0)
- cr.show_text(title)
- cr.stroke()
-
12 years, 2 months
r5203 - branches/humility/cumin/python/cumin/grid
by croberts@fedoraproject.org
Author: croberts
Date: 2012-02-03 19:41:58 +0000 (Fri, 03 Feb 2012)
New Revision: 5203
Modified:
branches/humility/cumin/python/cumin/grid/reports.py
Log:
Adding extra duration options for the reporting charts....no data yet, but it's nice to have options.
Modified: branches/humility/cumin/python/cumin/grid/reports.py
===================================================================
--- branches/humility/cumin/python/cumin/grid/reports.py 2012-02-03 14:18:49 UTC (rev 5202)
+++ branches/humility/cumin/python/cumin/grid/reports.py 2012-02-03 19:41:58 UTC (rev 5203)
@@ -16,10 +16,10 @@
super(ReportSelector, self).__init__(app, name)
def render_title(self, session):
- return "Generic title:"
+ raise Exception("not implemented")
def render_item_list(self, session):
- return "<option>generic item</option>"
+ raise Exception("not implemented")
class ReportsFrame(Frame):
@@ -112,6 +112,8 @@
chart = self.JobsChart(app, "jobs", self.scheduler_object)
chart.stats = ("TotalRunningJobs", "TotalIdleJobs", "TotalHeldJobs")
+ chart.duration.add_state("2629743", "1 month")
+ chart.duration.add_state("31556926", "1 year")
chart.duration.param.default = "3600"
self.add_child(chart)
12 years, 2 months
r5202 - trunk/cumin/python/cumin/grid
by tmckay@fedoraproject.org
Author: tmckay
Date: 2012-02-03 14:18:49 +0000 (Fri, 03 Feb 2012)
New Revision: 5202
Modified:
trunk/cumin/python/cumin/grid/job.py
Log:
Compare the set of running jobs in a submission and the selection list on a "remove" operation.
If all of the running jobs are targeted for removal, return to the submission list.
(tweak to change originally made in 4679)
BZ783139
Modified: trunk/cumin/python/cumin/grid/job.py
===================================================================
--- trunk/cumin/python/cumin/grid/job.py 2012-02-02 22:05:32 UTC (rev 5201)
+++ trunk/cumin/python/cumin/grid/job.py 2012-02-03 14:18:49 UTC (rev 5202)
@@ -342,19 +342,27 @@
def get_title(self, session):
return "Remove"
+ def _get_running_jobs(self, osession):
+ running = []
+ records = self.selector.get_qmf_results(osession)
+ if records and hasattr(records, "data"):
+ for rec in records.data:
+ if "JobStatus" in rec and rec["JobStatus"] == "RUNNING":
+ running.append("%s.%s" % (rec["ClusterId"],rec["ProcId"]))
+ return running
+
def do_enter(self, session, osession):
super(JobSelectionRemove, self).do_enter(session, osession)
- # if all the jobs in this submission are to be removed
- # return to the submission list instead of this submission page
- selected_count = len(self.selector.table.ids.get(osession))
- total = self.selector.table.get_count(osession)
- if selected_count == total:
+ # if all the running jobs in this submission are to be removed
+ # return to the submission list instead of this submission page.
+ running = self._get_running_jobs(osession)
+ selected = self.selector.table.ids.get(osession)
+ if len(set(running) - set(selected)) == 0:
nsession = osession.branch()
frame = self.selector.frame.parent
frame.view.show(nsession)
submission_list_url = nsession.marshal()
-
self.form.return_url.set(session, submission_list_url)
class JobObjectSelectorTaskForm(ObjectSelectorTaskForm):
12 years, 3 months
r5201 - branches/humility/cumin/python/cumin/grid
by croberts@fedoraproject.org
Author: croberts
Date: 2012-02-02 22:05:32 +0000 (Thu, 02 Feb 2012)
New Revision: 5201
Modified:
branches/humility/cumin/python/cumin/grid/reports.py
branches/humility/cumin/python/cumin/grid/reports.strings
Log:
Work toward a generalized select box for reporting charts. Still not wired-in with JS to change the stats URL, but that should come soon.
Modified: branches/humility/cumin/python/cumin/grid/reports.py
===================================================================
--- branches/humility/cumin/python/cumin/grid/reports.py 2012-02-01 21:21:45 UTC (rev 5200)
+++ branches/humility/cumin/python/cumin/grid/reports.py 2012-02-02 22:05:32 UTC (rev 5201)
@@ -8,6 +8,20 @@
strings = StringCatalog(__file__)
log = logging.getLogger("cumin.reports")
+class ReportStatFlashChart(StatFlashChart):
+ pass
+
+class ReportSelector(Widget):
+ def __init__(self, app, name):
+ super(ReportSelector, self).__init__(app, name)
+
+ def render_title(self, session):
+ return "Generic title:"
+
+ def render_item_list(self, session):
+ return "<option>generic item</option>"
+
+
class ReportsFrame(Frame):
def __init__(self, app, name):
super(ReportsFrame, self).__init__(app, name)
@@ -42,10 +56,23 @@
class ReportsUserInfo(Widget):
def __init__(self, app, name):
super(ReportsUserInfo, self).__init__(app, name)
+ self.cls = app.model.com_redhat_grid.Submitter
+
def render_title(self, session):
return "User"
+ def getUserList(self, session):
+ sub_list = ""
+ submitters = self.cls.get_selection(session.cursor)
+ for submitter in submitters:
+ sub_list += "<option>%s</option>" % submitter.Owner
+ return sub_list
+
+ def render_user_list(self, session):
+ users = self.getUserList(session)
+ return users
+
class ReportsJobInfo(Widget):
def __init__(self, app, name):
super(ReportsJobInfo, self).__init__(app, name)
@@ -55,13 +82,31 @@
def render_title(self, session):
return "Job"
+
+
+class SchedulerSelector(ReportSelector):
+ def __init__(self, app, name):
+ super(SchedulerSelector, self).__init__(app, name)
+ self.cls = app.model.com_redhat_grid.Scheduler
+
+ def render_title(self, session):
+ return "Scheduler:"
+ def render_item_list(self, session):
+ schedList = ""
+ schedulers = self.cls.get_selection(session.cursor)
+ for scheduler in schedulers:
+ schedList += "<option>%s</option>" % scheduler.Machine
+
+ return schedList
+
class ReportsJobChart(Widget):
def __init__(self, app, name):
super(ReportsJobChart, self).__init__(app, name)
self.cls = app.model.com_redhat_grid.Scheduler
-
+
+ #this attribute gets filled-in in the process loop
self.scheduler_object = Attribute(app, "object")
self.add_attribute(self.scheduler_object)
@@ -70,30 +115,30 @@
chart.duration.param.default = "3600"
self.add_child(chart)
+
def do_process(self, session):
obj = self.getDefaultScheduler(session)
self.scheduler_object.set(session, obj)
super(ReportsJobChart, self).do_process(session)
-
- def render_scheduler_list(self, session):
- schedList = ""
- schedulers = self.cls.get_selection(session.cursor)
- for scheduler in schedulers:
- schedList += "<option>%s</option>" % scheduler.Machine
-
- return schedList
-
+
def getDefaultScheduler(self, session):
schedulers = self.cls.get_selection(session.cursor)
return schedulers[0]
-
- class JobsChart(StatFlashChart):
+
+ class JobsChart(ReportStatFlashChart):
+ def __init__(self, app, name, object):
+ super(ReportsJobChart.JobsChart, self).__init__(app, name, object)
+
+ self.selector = SchedulerSelector(app, "selector")
+ self.add_child(self.selector)
+
def render_title(self, session):
return "Jobs"
-
-
-
+
+
+
+
\ No newline at end of file
Modified: branches/humility/cumin/python/cumin/grid/reports.strings
===================================================================
--- branches/humility/cumin/python/cumin/grid/reports.strings 2012-02-01 21:21:45 UTC (rev 5200)
+++ branches/humility/cumin/python/cumin/grid/reports.strings 2012-02-02 22:05:32 UTC (rev 5201)
@@ -1,5 +1,47 @@
[ReportsJobChart.html]
<div>
{jobs}
- Scheduler: <select>{scheduler_list}</select>
</div>
+
+<!--The plan here is to use an onchange handler for the select box to adjust the chart.json URL for the stats,
+which will then load the chosen data. Not sure if the stat gathering code will need any changes to support the filter
+-->
+
+
+[ReportsUserInfo.html]
+<div>
+ User: <select>{user_list}</select>
+</div>
+
+[ReportSelector.html]
+{title} <select id="{id}">{item_list}</select>
+
+[ReportStatFlashChart.html]
+<div>
+ <div class="StatValueChart{fullpageable}" id="{id}">
+ <h2>{title}</h2>
+ <div class="duration">{duration}</div>
+ <div id="{id}_chart">
+ <img id="{id}" src="{img_href}" height="{img_height}" width="{img_width}" alt="stats" />
+ <div class="loading" style="display:none;"><span>Loading...</span></div>
+ </div>
+ <div>{selector}</div>
+ </div>
+</div>
+<script type="text/javascript">
+//<![CDATA[
+ var flashversion = swfobject.getFlashPlayerVersion();
+ if (flashversion.major < 9) {
+ wooly.addPageUpdateListener(function () {if (document.images["{id}"].className == "Loading") return; cumin.updateChart("{id}");});
+ cumin.setupChart('{id}', {width});
+ } else {
+ swfobject.embedSWF("resource?name=open-flash-chart.swf", "{id}_chart", "{width}", "{height}", "9.0.0", "",
+ {"data-file":"{href}", "id":"{id}"}, {wmode: "opaque"});
+
+ wooly.addPageUpdateListener(function () { cumin.updateFlashChart('{id}'); });
+ window.addEvent('domready',function () {
+ cumin.setFullpageHandler('{id}', '{fullpage_href}');
+ });
+ }
+//]]>
+</script>
12 years, 3 months
r5200 - branches/humility/cumin/python/cumin/grid
by croberts@fedoraproject.org
Author: croberts
Date: 2012-02-01 21:21:45 +0000 (Wed, 01 Feb 2012)
New Revision: 5200
Modified:
branches/humility/cumin/python/cumin/grid/reports.py
branches/humility/cumin/python/cumin/grid/reports.strings
Log:
More skeleton reporting work.
Modified: branches/humility/cumin/python/cumin/grid/reports.py
===================================================================
--- branches/humility/cumin/python/cumin/grid/reports.py 2012-01-31 21:38:40 UTC (rev 5199)
+++ branches/humility/cumin/python/cumin/grid/reports.py 2012-02-01 21:21:45 UTC (rev 5200)
@@ -1,6 +1,13 @@
from wooly import *
from wooly.widgets import RadioModeSet
+from cumin.stat import StatFlashChart
+
+from cumin.grid.dashboard import GridOSBreakdown
+
+strings = StringCatalog(__file__)
+log = logging.getLogger("cumin.reports")
+
class ReportsFrame(Frame):
def __init__(self, app, name):
super(ReportsFrame, self).__init__(app, name)
@@ -25,6 +32,8 @@
class ReportsHostInfo(Widget):
def __init__(self, app, name):
super(ReportsHostInfo, self).__init__(app, name)
+ os_data = GridOSBreakdown(app, "os_breakdown")
+ self.add_child(os_data)
def render_title(self, session):
return "Host"
@@ -41,5 +50,50 @@
def __init__(self, app, name):
super(ReportsJobInfo, self).__init__(app, name)
+ self.job_chart = ReportsJobChart(app, "jobchart")
+ self.add_child(self.job_chart)
+
def render_title(self, session):
- return "Job"
\ No newline at end of file
+ return "Job"
+
+
+class ReportsJobChart(Widget):
+ def __init__(self, app, name):
+ super(ReportsJobChart, self).__init__(app, name)
+ self.cls = app.model.com_redhat_grid.Scheduler
+
+ self.scheduler_object = Attribute(app, "object")
+ self.add_attribute(self.scheduler_object)
+
+ chart = self.JobsChart(app, "jobs", self.scheduler_object)
+ chart.stats = ("TotalRunningJobs", "TotalIdleJobs", "TotalHeldJobs")
+ chart.duration.param.default = "3600"
+ self.add_child(chart)
+
+ def do_process(self, session):
+ obj = self.getDefaultScheduler(session)
+ self.scheduler_object.set(session, obj)
+
+ super(ReportsJobChart, self).do_process(session)
+
+ def render_scheduler_list(self, session):
+ schedList = ""
+ schedulers = self.cls.get_selection(session.cursor)
+ for scheduler in schedulers:
+ schedList += "<option>%s</option>" % scheduler.Machine
+
+ return schedList
+
+ def getDefaultScheduler(self, session):
+ schedulers = self.cls.get_selection(session.cursor)
+ return schedulers[0]
+
+
+ class JobsChart(StatFlashChart):
+ def render_title(self, session):
+ return "Jobs"
+
+
+
+
+
\ No newline at end of file
Modified: branches/humility/cumin/python/cumin/grid/reports.strings
===================================================================
--- branches/humility/cumin/python/cumin/grid/reports.strings 2012-01-31 21:38:40 UTC (rev 5199)
+++ branches/humility/cumin/python/cumin/grid/reports.strings 2012-02-01 21:21:45 UTC (rev 5200)
@@ -0,0 +1,5 @@
+[ReportsJobChart.html]
+<div>
+ {jobs}
+ Scheduler: <select>{scheduler_list}</select>
+</div>
12 years, 3 months