Author: tmckay Date: 2011-01-24 19:06:57 +0000 (Mon, 24 Jan 2011) New Revision: 4485
Added: branches/scale_testing/cumin/bin/cumin Removed: branches/scale_testing/cumin/bin/cumin Modified: branches/scale_testing/cumin/bin/cumin-admin branches/scale_testing/cumin/bin/cumin-bench branches/scale_testing/cumin/bin/cumin-data branches/scale_testing/cumin/bin/cumin-database branches/scale_testing/cumin/bin/cumin-smoke-test branches/scale_testing/cumin/bin/cumin-web branches/scale_testing/cumin/etc/sysvinit-cumin branches/scale_testing/cumin/python/cumin/config.py branches/scale_testing/cumin/python/cumin/grid/job.py branches/scale_testing/cumin/python/cumin/grid/negotiator.py branches/scale_testing/cumin/python/cumin/grid/negotiator.strings branches/scale_testing/cumin/python/cumin/grid/pool.py branches/scale_testing/cumin/python/cumin/model.py branches/scale_testing/wooly/python/wooly/datatable.py branches/scale_testing/wooly/python/wooly/datatable.strings branches/scale_testing/wooly/python/wooly/widgets.py branches/scale_testing/wooly/python/wooly/widgets.strings Log: Bring scale testing branch into agreement with 1.3.2 before we move any further.
Deleted: branches/scale_testing/cumin/bin/cumin =================================================================== --- branches/scale_testing/cumin/bin/cumin 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/bin/cumin 2011-01-24 19:06:57 UTC (rev 4485) @@ -1,32 +0,0 @@ -#!/bin/bash - -function die { - kill "$data" "$web" -} - -trap die EXIT - -function start_data { - cumin-data & - data="$!" -} - -function start_web { - cumin-web & - web="$!" -} - -start_data -start_web - -while [ 0 ]; do - sleep 30 - - if [ ! -d "/proc/$data" ]; then - start_data - fi - - if [ ! -d "/proc/$web" ]; then - start_web - fi -done
Copied: branches/scale_testing/cumin/bin/cumin (from rev 4478, branches/1.3.2/cumin/bin/cumin) =================================================================== --- branches/scale_testing/cumin/bin/cumin (rev 0) +++ branches/scale_testing/cumin/bin/cumin 2011-01-24 19:06:57 UTC (rev 4485) @@ -0,0 +1,74 @@ +#!/usr/bin/python + +from optparse import OptionParser +from time import sleep +import subprocess +import os +import sys +import signal + +def call_sys_exit(sig,frame): + sys.exit() + +def set_exit_handler(func): + if os.name == "nt": + try: + import win32api + win32api.SetConsoleCtrlHandler(func, True) + except ImportError: + version = ".".join(map(str, sys.version_info[:2])) + raise Exception("pywin32 not installed for Python " + version) + else: + signal.signal(signal.SIGTERM, func) + +def main(): + + # tuple indices, for clarity + PROCESS = 0 + SECTION = 1 + PROGRAM = 2 + + parser = OptionParser() + + parser.add_option("--webs", dest="webs", default="web", + help="Configuration section names for cumin-web instances."\ + "\nEach value implies a separate cumin-web instance.") + + parser.add_option("--datas", dest="datas", default="data", + help="Configuration section names for cumin-data instances."\ + "\nEach value implies a separate cumin-data instance.") + + (options, args) = parser.parse_args() + + # Get our list cumin-web and data instances + # create list elements to hold the process object, section arg, and app + apps = [] + for app in options.webs.split(','): + apps.append([None, app, "cumin-web"]) + + for app in options.datas.split(','): + apps.append([None, app, "cumin-data"]) + + # Launch and babysit + try: + while True: + for app in apps: + if not app[PROCESS] or app[PROCESS].poll(): + arg = "--section=" + app[SECTION] + app[PROCESS] = subprocess.Popen([app[PROGRAM], arg]) + sleep(30) + + finally: + for app in apps: + if app[PROCESS]: + os.kill(app[PROCESS].pid, signal.SIGTERM) + +if __name__ == "__main__": + # This is necessary so that on a SIGTERM we can call sys.exit() + # and cause the finally block to be executed. Ctrl-C will + # run the finally block anyway. + set_exit_handler(call_sys_exit) + try: + main() + except KeyboardInterrupt: + pass
Modified: branches/scale_testing/cumin/bin/cumin-admin =================================================================== --- branches/scale_testing/cumin/bin/cumin-admin 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/bin/cumin-admin 2011-01-24 19:06:57 UTC (rev 4485) @@ -24,14 +24,10 @@ if uid == 0: os.setuid(file_uid)
- config = CuminConfig() - values = config.parse() - - parser = CuminOptionParser(values.data) + parser = CuminDataOptionParser() parser.set_usage(generate_usage()) + config, values, opts, args = get_configuration(parser, CuminDataConfig)
- opts, args = parser.parse_args() - try: name = args[0] except IndexError: @@ -50,7 +46,7 @@
broker_uris = [x.strip() for x in opts.brokers.split(",")]
- app = Cumin(config.home, broker_uris, opts.database) + app = Cumin(config.get_home(), broker_uris, opts.database)
app.check() app.init()
Modified: branches/scale_testing/cumin/bin/cumin-bench =================================================================== --- branches/scale_testing/cumin/bin/cumin-bench 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/bin/cumin-bench 2011-01-24 19:06:57 UTC (rev 4485) @@ -8,22 +8,17 @@ from cumin.config import *
def do_main(): - config = CuminConfig() - values = config.parse() + config, values, opts, args = get_configuration(CuminWebOptionParser(), CuminWebConfig)
- parser = CuminOptionParser(values.web) - - opts, args = parser.parse_args() - setup_logging(opts)
broker_uris = [x.strip() for x in opts.brokers.split(",")]
- cumin = Cumin(config.home, broker_uris, opts.database) + cumin = Cumin(config.get_home(), broker_uris, opts.database)
cumin.debug = True - cumin.user = values.web.user - cumin.update_interval = values.web.update_interval + cumin.user = values.user + cumin.update_interval = values.update_interval
cumin.check() cumin.init()
Modified: branches/scale_testing/cumin/bin/cumin-data =================================================================== --- branches/scale_testing/cumin/bin/cumin-data 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/bin/cumin-data 2011-01-24 19:06:57 UTC (rev 4485) @@ -11,17 +11,8 @@ from mint import *
def main(): - config = CuminConfig() - values = config.parse().data + config, values, opts, args = get_configuration(CuminDataOptionParser(), CuminDataConfig)
- parser = CuminOptionParser(values) - parser.add_option("--print-stats", action="store_true") - parser.add_option("--scale-stats", action="store_true") - parser.add_option("--object-stats", action="store_true") - parser.add_option("--print-events", type="int", default=0, metavar="LEVEL") - - opts, args = parser.parse_args() - setup_logging(opts)
model_dir = os.path.join(config.home, "model")
Modified: branches/scale_testing/cumin/bin/cumin-database =================================================================== --- branches/scale_testing/cumin/bin/cumin-database 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/bin/cumin-database 2011-01-24 19:06:57 UTC (rev 4485) @@ -151,7 +151,11 @@ check-environment || exit 1 check-initialized || exit 1
- /sbin/service postgresql restart + # The postgresql 8.1.22 init script violates LSB 3.1 Chapter 20.2, + # and returns 1 on a successful restart of a stopped service. A + # proper restart is thus simulated with condstop and start. + /sbin/service postgresql condstop + /sbin/service postgresql start }
function initialize { @@ -170,9 +174,10 @@
# On older versions of Postgres (before 8.4), initdb does not exist but # using the start option will initialize the database, then start it. - # Try initdb first, and if it fails try start followed by stop. This - # should leave the database and the service in the same state on all - # 8.x versions of Postgres. + # Try initdb first, and if it fails try start. The "start" method + # will leave the service running, which is different than initdb, + # but that is fine at this point because it is restarted after + # the call to configure anyway. echo -e "\nAttempting to use initdb option..." run "/sbin/service postgresql initdb" || { echo -e "\ninitdb option not supported, using start..."
Modified: branches/scale_testing/cumin/bin/cumin-smoke-test =================================================================== --- branches/scale_testing/cumin/bin/cumin-smoke-test 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/bin/cumin-smoke-test 2011-01-24 19:06:57 UTC (rev 4485) @@ -13,22 +13,16 @@ from cumin.util import *
def main(): - config = CuminConfig() - values = config.parse() + config, values, opts, args = get_configuration(CuminWebOptionParser(), CuminWebConfig)
- parser = CuminOptionParser(values.web) - - parser.add_option("--host", default=values.web.host) - parser.add_option("--port", default=values.web.port) - - opts, args = parser.parse_args() - setup_logging(opts)
- cumin = Cumin(config.home, opts.brokers, opts.database, + broker_uris = [x.strip() for x in opts.brokers.split(",")] + + cumin = Cumin(config.get_home(), broker_uris, opts.database, opts.host, opts.port)
- cumin.user = values.web.user + cumin.user = values.user
cumin.check() cumin.init()
Modified: branches/scale_testing/cumin/bin/cumin-web =================================================================== --- branches/scale_testing/cumin/bin/cumin-web 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/bin/cumin-web 2011-01-24 19:06:57 UTC (rev 4485) @@ -11,32 +11,24 @@ from cumin.util import *
def main(): - config = CuminConfig() - values = config.parse() + config, values, opts, args = get_configuration(CuminWebOptionParser(), CuminWebConfig)
- parser = CuminOptionParser(values.web) - - parser.add_option("--host", default=values.web.host) - parser.add_option("--port", default=values.web.port) - - opts, args = parser.parse_args() - setup_logging(opts)
broker_uris = [x.strip() for x in opts.brokers.split(",")]
- cumin = Cumin(config.home, broker_uris, opts.database, + cumin = Cumin(config.get_home(), broker_uris, opts.database, opts.host, opts.port)
cumin.debug = opts.debug - cumin.user = values.web.user - cumin.update_interval = values.web.update_interval + cumin.user = values.user + cumin.update_interval = values.update_interval
# set default values for form inputs - cumin.set_form_defaults(values.web.request_memory, - values.web.request_memory_vm, - values.web.request_disk, - values.web.request_disk_vm) + cumin.set_form_defaults(values.request_memory, + values.request_memory_vm, + values.request_disk, + values.request_disk_vm)
cumin.check() cumin.init()
Modified: branches/scale_testing/cumin/etc/sysvinit-cumin =================================================================== --- branches/scale_testing/cumin/etc/sysvinit-cumin 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/etc/sysvinit-cumin 2011-01-24 19:06:57 UTC (rev 4485) @@ -11,6 +11,9 @@ test -x /usr/bin/cumin || exit 1 test -x /usr/bin/cumin-database || exit 1
+SYSCONFIG="/etc/sysconfig/cumin" +[ -r "$SYSCONFIG" ] && source "$SYSCONFIG" + cumin-database check-started &> /dev/null || { echo "Cumin's database server is not running" echo "Run 'cumin-database start' as root" @@ -34,7 +37,7 @@
start() { echo -n $"Starting cumin: " - daemon --user cumin --check $servicename $processname & + daemon --user cumin --check $servicename $processname $CUMIN_OPTIONS & RETVAL=$? echo [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$servicename
Modified: branches/scale_testing/cumin/python/cumin/config.py =================================================================== --- branches/scale_testing/cumin/python/cumin/config.py 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/python/cumin/config.py 2011-01-24 19:06:57 UTC (rev 4485) @@ -17,9 +17,28 @@ if not os.path.isdir(self.home): raise Exception("Home path '%s' is not a directory")
- web = CuminConfigSection(self, "web") - web.log_file.default = os.path.join(self.home, "log", "web.log") + def get_home(self): + return self.home
+ def parse(self): + paths = list() + + paths.append(os.path.join(os.sep, "etc", "cumin", "cumin.conf")) + paths.append(os.path.join(self.home, "etc", "cumin.conf")) + paths.append(os.path.join(os.path.expanduser("~"), ".cumin.conf")) + + return self.parse_files(paths) + +class CuminWebConfig(CuminConfig): + def __init__(self, section_name): + super(CuminWebConfig, self).__init__() + + self.create_section(section_name) + + def create_section(self, name): + web = CuminConfigSection(self, name) + web.log_file.default = os.path.join(self.home, "log", name + ".log") + param = ConfigParameter(web, "update-interval", int) param.default = 10
@@ -42,8 +61,15 @@ param = ConfigParameter(web, "request-disk-vm", int) param.default = 5 * 1024 #MB
- data = CuminConfigSection(self, "data") - data.log_file.default = os.path.join(self.home, "log", "data.log") +class CuminDataConfig(CuminConfig): + def __init__(self, section_name): + super(CuminDataConfig, self).__init__() + + self.create_section(section_name) + + def create_section(self, name): + data = CuminConfigSection(self, name) + data.log_file.default = os.path.join(self.home, "log", name + ".log")
param = ConfigParameter(data, "packages", str)
@@ -59,15 +85,6 @@ param = ConfigParameter(data, "queue-size", int) param.default = 1000
- def parse(self): - paths = list() - - paths.append(os.path.join(os.sep, "etc", "cumin", "cumin.conf")) - paths.append(os.path.join(self.home, "etc", "cumin.conf")) - paths.append(os.path.join(os.path.expanduser("~"), ".cumin.conf")) - - return self.parse_files(paths) - class CuminConfigSection(ConfigSection): def __init__(self, config, name): super(CuminConfigSection, self).__init__(config, name) @@ -86,17 +103,61 @@ param = ConfigParameter(self, "debug", bool) param.default = False
-class CuminOptionParser(OptionParser): - def __init__(self, section): +class CuminMultiOptionParser(OptionParser,object): + def __init__(self, default_section): OptionParser.__init__(self)
- self.add_option("--database", default=section.database) - self.add_option("--brokers", default=section.brokers) - self.add_option("--log-file", default=section.log_file) - self.add_option("--log-level", default=section.log_level) - self.add_option("--debug", action="store_true", default=section.debug) + self.add_option("--section", default=default_section) + + # We don't know defaults yet... + self.add_option("--database") + self.add_option("--brokers") + self.add_option("--log-file") + self.add_option("--log-level") + self.add_option("--debug", action="store_true") self.add_option("--init-only", action="store_true")
+ def _apply_default(self, section, name): + try: + val = getattr(section, name) + self.set_default(name, val) + except: + pass + + def set_defaults_from_section(self, section): + # It is possible that a section does not contain an updated + # default value for a particular option. Skip exception. + self._apply_default(section, "database") + self._apply_default(section, "brokers") + self._apply_default(section, "log_file") + self._apply_default(section, "log_level") + self._apply_default(section, "debug") + +class CuminWebOptionParser(CuminMultiOptionParser): + def __init__(self, default_section="web"): + super(CuminWebOptionParser, self).__init__(default_section) + + # Add additional parameters for web + self.add_option("--host") + self.add_option("--port", type=int) + + def set_defaults_from_section(self, section): + super(CuminWebOptionParser, self).set_defaults_from_section(section) + + # For web, these options also have defaults in config files + self._apply_default(section, "host") + self._apply_default(section, "port") + +class CuminDataOptionParser(CuminMultiOptionParser): + def __init__(self, default_section="data"): + super(CuminDataOptionParser, self).__init__(default_section) + + # Add additional parameters for data + self.add_option("--print-stats", action="store_true") + self.add_option("--print-events", type="int", default=0, metavar="LEVEL") + self.add_option("--scale-stats", action="store_true") + self.add_option("--object-stats", action="store_true") + def setup_logging(values): modules = ("cumin", "mint", "parsley", "rosemary", "wooly")
@@ -106,3 +167,21 @@ if values.debug: for name in modules: enable_logging(name, "debug", sys.stderr) + +def get_configuration(parser, ConfigType): + # First parse should return a "section" value + opts, args = parser.parse_args() + if hasattr(opts, "section"): + # Create config object with the given section and parse config files + config = ConfigType(opts.section) + values = config.parse() + if hasattr(values, opts.section): + # Reset defaults and parse again + values = getattr(values, opts.section) + parser.set_defaults_from_section(values) + opts, args = parser.parse_args() + else: + raise Exception("Section ["+opts.section+"] not present in configuration files.") + else: + raise Exception("No '--section' value available as option or default") + return config, values, opts, args
Modified: branches/scale_testing/cumin/python/cumin/grid/job.py =================================================================== --- branches/scale_testing/cumin/python/cumin/grid/job.py 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/python/cumin/grid/job.py 2011-01-24 19:06:57 UTC (rev 4485) @@ -222,16 +222,6 @@ JobSelectionRelease(app, self, "released") JobSelectionRemove(app, self, "removed")
- def init_ids(self, app, cls): - item = Parameter(app, "item") - - self.ids = ListParameter(app, "selection", item) - self.add_parameter(self.ids) - - self.checkbox_column = ObjectCheckboxColumn \ - (app, "id", cls.JobId, self.ids) - self.add_column(self.checkbox_column) - def create_table(self, app, name, cls): return JobSelectorTable(app, name, cls)
@@ -270,6 +260,16 @@ self.qmf_response = Attribute(app, "qmf_response") self.add_attribute(self.qmf_response)
+ def init_ids(self, app, cls): + item = Parameter(app, "item") + + self.ids = ListParameter(app, "selection", item) + self.add_parameter(self.ids) + + self.checkbox_column = ObjectCheckboxColumn \ + (app, "id", cls.JobId, self.ids) + self.add_column(self.checkbox_column) + def get_data_values(self, session): values = super(JobSelectorTable, self).get_data_values(session)
@@ -300,7 +300,9 @@ qmf_response = self.qmf_response.get(session) if qmf_response.exception: writer = Writer() - msg = "The data is not available at this time. <br/>%s" % str(qmf_response.exception.message) + e = qmf_response.exception + m = len(e.args) > 0 and e.args[0] or "" + msg = "The data is not available at this time. <br/>%s" % m self.error_tmpl.render(writer, session, msg) return writer.to_string()
@@ -332,6 +334,10 @@ submission = self.selector.submission.get(osession) self.form.submission_id.set(session, submission._id)
+ def get_item_content(self, session, item): + # item here is the unicode job id + return item + class JobSelectionHold(JobObjectSelectorTask): def __init__(self, app, selector, verb): super(JobSelectionHold, self).__init__(app, selector, verb) @@ -366,9 +372,10 @@ self.submission_id = IntegerParameter(app, "sub") self.add_parameter(self.submission_id)
- self.content = ReasonField(app, "fields") - self.content.required = True - self.replace_child(self.content) + self.reason = ReasonField(app, "reason") + self.reason.required = True + self.main_fields.add_field(self.reason) + self.add_child(self.reason)
self.verb = verb
@@ -380,7 +387,7 @@
def get_reason(self, session, verb): """ returns <verb> by username[: <user input reason>] """ - reason = self.content.get(session) + reason = self.reason.get(session) if reason: reason = [reason] user = session.client_session.attributes["login_session"].user @@ -390,14 +397,18 @@ return ": ".join(reason)
def process_submit(self, session): - selection = self.selection.get(session) - reason = self.get_reason(session, self.verb) + self.validate(session)
- scheduler = self.get_scheduler(session) + errors = self.errors.get(session) + if not errors: + selection = self.selection.get(session) + reason = self.get_reason(session, self.verb)
- self.task.invoke(session, selection, scheduler, reason) - self.task.exit_with_redirect(session) + scheduler = self.get_scheduler(session)
+ self.task.invoke(session, selection, scheduler, reason) + self.task.exit_with_redirect(session) + def get_scheduler(self, session): submission_id = self.submission_id.get(session) cls = self.app.model.com_redhat_grid.Submission @@ -409,6 +420,11 @@ cls = self.app.model.com_redhat_grid.Scheduler return cls.get_object(session.cursor, _id=job_server._schedulerRef_id)
+ def render_content(self, session, *args): + content = super(JobObjectSelectorTaskForm, self).render_content(session, *args) + reason = self.reason.render(session) + return "<table class="FormFieldSet"><tbody>%s</tbody></table>%s" % (reason, content) + class JobAdsSet(PropertySet): types = {0: "expression", 1: "integer", @@ -542,7 +558,7 @@ # check for qmf error _, error = self.parent.do_get_items(session) if error: - msg = error.message + msg = len(error.args) > 0 and error.args[0] or "" if not msg: # probably a timeout exception msg = "Unable to get Job information at this time"
Modified: branches/scale_testing/cumin/python/cumin/grid/negotiator.py =================================================================== --- branches/scale_testing/cumin/python/cumin/grid/negotiator.py 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/python/cumin/grid/negotiator.py 2011-01-24 19:06:57 UTC (rev 4485) @@ -100,6 +100,8 @@ def __init__(self, app, name, negotiator, frame): super(NegotiatorOverview, self).__init__(app, name)
+ self.negotiator = negotiator + self.update_enabled = True #self.defer_enabled = True
@@ -147,10 +149,13 @@ except AssertionError: msg = "Missing negotiator" except Exception, e: - msg = e.message + msg = len(e.args) > 0 and e.args[0] or "" if msg == "Loading": tmpl = self.loading_tmpl else: + neg = self.negotiator.get(session) + neg_name = neg and " for negotiator %s" % neg.Name or "" + msg = "%s%s" % (e, neg_name) log.exception(e) else: return super(NegotiatorOverview, self).do_render(session) @@ -267,7 +272,8 @@ except TypeError: if isinstance(value, Exception): content = "<span class='QuotaError'>error</span>" - return fmt_link("#", content, "", "", value.message) + val = len(value.args) > 0 and value.args[0] or "" + return fmt_link("#", content, "", "", val)
href = self.task.get_href(session) content = "%s%%" % str(round(fval * 100.0, 2)) @@ -303,11 +309,12 @@
if not groups: if (not results.status) and (not results.exception): - raise QmfException("Loading") - #results.exception = QmfException("Request timed out. Please refresh the page to try again.") + raise Exception("Loading") if results.exception: raise results.exception else: + if not groups: + raise Exception(results.status) try: groups = self.split_group_names(groups) except Exception, e:
Modified: branches/scale_testing/cumin/python/cumin/grid/negotiator.strings =================================================================== --- branches/scale_testing/cumin/python/cumin/grid/negotiator.strings 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/python/cumin/grid/negotiator.strings 2011-01-24 19:06:57 UTC (rev 4485) @@ -53,7 +53,7 @@ <![CDATA[ (function() { window.addEvent("domready", function () { - setTimeout( function () { window.location.reload(); }, 2000 ); + setTimeout( function () { window.location.reload(); }, 5000 ); }); }()) ]]>
Modified: branches/scale_testing/cumin/python/cumin/grid/pool.py =================================================================== --- branches/scale_testing/cumin/python/cumin/grid/pool.py 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/python/cumin/grid/pool.py 2011-01-24 19:06:57 UTC (rev 4485) @@ -160,6 +160,7 @@ self.add_child(chart)
chart = self.SlotFlashChart(app, "slots", self.collector) + chart.duration.param.default = "3600" chart.stats = ("HostsClaimed", "HostsUnclaimed", "HostsOwner") chart.chart_type = "stacked" self.add_child(chart)
Modified: branches/scale_testing/cumin/python/cumin/model.py =================================================================== --- branches/scale_testing/cumin/python/cumin/model.py 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/cumin/python/cumin/model.py 2011-01-24 19:06:57 UTC (rev 4485) @@ -906,13 +906,6 @@
return "%s%s" % (conf, rect)
-class QmfException(Exception): - def __init__(self, value): - self.message = value - - def __str__(self): - return repr(self.message) - class QmfCall(object): def __init__(self, app, default=None, timeout=5): self.app = app @@ -929,7 +922,7 @@ self.data = data self.got_data = True else: - self.error = QmfException(status) + self.error = Exception(status) msg = "QMF call returned with status of %s" % str(status) log.error(msg) return completion @@ -951,7 +944,7 @@
results = self.do_wait() if not results.got_data and not results.error: - results.error = QmfException("Request timed out") + results.error = Exception("Request timed out") msg = "QMF call %s timed out" % method_name log.error(msg)
Modified: branches/scale_testing/wooly/python/wooly/datatable.py =================================================================== --- branches/scale_testing/wooly/python/wooly/datatable.py 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/wooly/python/wooly/datatable.py 2011-01-24 19:06:57 UTC (rev 4485) @@ -111,6 +111,12 @@ options.limit = self.header.limit.get(session) options.offset = self.header.offset.get(session)
+ # make sure offset is a multiple of limit + # this happens sometimes when we change limits + if options.offset % options.limit: + options.offset = (options.offset / options.limit) * options.limit + self.header.offset.set(session, options.offset) + return options
def get_count(self, session): @@ -230,6 +236,9 @@ if self.parent.selection.get(session) == option[0]: return "selected"
+ def render_title(self, session, option): + return option[2] + def edit_session(self, session, option): self.parent.selection.set(session, option[0])
@@ -238,26 +247,65 @@
class DataTableFontSelector(DataTableSelector): def get_options(self, session): - return ((0.8, "S"), (0.9, "M"), (1.0, "L")) + return ((0.8, "S", "Small"), (0.9, "M", "Medium"), (1.0, "L", "Large"))
def render_title(self, session): return "Font"
class DataTablePageSelector(DataTableSelector): + max_page_links = 16 + def get_options(self, session): + # number of items in the table count = self.table.count.get(session) + if not count: + return [] + + # number of items per page limit = self.table.header.limit.get(session)
- count = min(count, 20 * limit) + # info for each possible page of results + options = [(x[1], x[0] + 1, "Goto Page %i" % (x[0] + 1, )) for x in enumerate(range(0, count, limit))]
- return [(x[1], x[0] + 1) for x in enumerate(range(0, count, limit))] + # the starting item on the current page + selection = self.selection.get(session)
+ #find the index of the current page + cur_page = 0 + for page in options: + if selection == page[0]: + cur_page = page[1] - 1 + break + + pages = list() + + # adjust the page links shown so that the current page is in the list + first_page = max(cur_page - 1, 0) + last_page = min(first_page + self.max_page_links, len(options)) + first_page = max(last_page - self.max_page_links, 0) + + if first_page: + pages.append((0, "«", "First Page")) + prev = max(selection - limit, 0) + pages.append((prev, "‹", "Previous Page")) + + pages.extend(options[first_page : last_page]) + + if last_page < len(options): + next = options[cur_page + 1][0] + pages.append((next, "›", "Next Page")) + + last = options[len(options) - 1][0] + pages.append((last, "»", "Last Page")) + + return pages + def render_title(self, session): - return "Page" + return self.table.count.get(session) and "Page" or ""
class DataTableLimitSelector(DataTableSelector): def get_options(self, session): - return ((25, 25), (50, 50), (100, 100)) + return ((25, 25, "25 items per page"), (50, 50, "50 items per page"), (100, 100, "100 items per page"))
def render_title(self, session): return "Limit"
Modified: branches/scale_testing/wooly/python/wooly/datatable.strings =================================================================== --- branches/scale_testing/wooly/python/wooly/datatable.strings 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/wooly/python/wooly/datatable.strings 2011-01-24 19:06:57 UTC (rev 4485) @@ -9,13 +9,18 @@ margin: 0 0 0 2em; }
+table.DataTable th.controls div.paginator { + /* allow page numbers to wrap to next line */ + white-space: normal; +} + [DataTableHeader.html] <thead> <tr> <th colspan="{colspan}" class="controls"> <div class="right">{limit}</div> <div class="right">{font}</div> - {page} + <div class="paginator">{page}</div> </th> </tr> <tr>{headers}</tr>
Modified: branches/scale_testing/wooly/python/wooly/widgets.py =================================================================== --- branches/scale_testing/wooly/python/wooly/widgets.py 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/wooly/python/wooly/widgets.py 2011-01-24 19:06:57 UTC (rev 4485) @@ -131,6 +131,9 @@ self.edit_session(branch, *args) return branch.marshal()
+ def render_title(self, session, *args): + return "" + class Toggle(Link): def __init__(self, app, name): super(Toggle, self).__init__(app, name)
Modified: branches/scale_testing/wooly/python/wooly/widgets.strings =================================================================== --- branches/scale_testing/wooly/python/wooly/widgets.strings 2011-01-24 15:52:05 UTC (rev 4484) +++ branches/scale_testing/wooly/python/wooly/widgets.strings 2011-01-24 19:06:57 UTC (rev 4485) @@ -67,7 +67,7 @@ <li>{link}</li>
[Link.html] -<a class="{class}" href="{href}">{content}</a> +<a class="{class}" href="{href}" title="{title}">{content}</a>
[Toggle.css] a.Toggle.on {