[rhmessaging-commits] rhmessaging commits: r3871 - in mgmt: newdata/cumin/python/cumin and 12 other directories.

rhmessaging-commits at lists.jboss.org rhmessaging-commits at lists.jboss.org
Mon Mar 22 10:58:12 EDT 2010


Author: justi9
Date: 2010-03-22 10:58:08 -0400 (Mon, 22 Mar 2010)
New Revision: 3871

Added:
   mgmt/newdata/
   mgmt/newdata/cumin/python/cumin/messaging/subscription.py
   mgmt/newdata/cumin/python/cumin/objectform.py
   mgmt/newdata/cumin/python/cumin/objectframe.py
   mgmt/newdata/cumin/python/cumin/objectframe.strings
   mgmt/newdata/cumin/python/cumin/objectselector.py
   mgmt/newdata/cumin/python/cumin/objectselector.strings
   mgmt/newdata/cumin/python/cumin/objecttask.py
   mgmt/newdata/cumin/python/cumin/sqladapter.py
   mgmt/newdata/cumin/python/cumin/table.py
   mgmt/newdata/cumin/python/cumin/table.strings
   mgmt/newdata/mint/python/mint/newupdate.py
   mgmt/newdata/mint/sql/rosemary.sql
   mgmt/newdata/rosemary/python/rosemary/sqlquery.py
   mgmt/newdata/rosemary/xml/cumin.xml
   mgmt/newdata/wooly/python/wooly/datatable.py
   mgmt/newdata/wooly/python/wooly/datatable.strings
   mgmt/newdata/wooly/python/wooly/table.py
   mgmt/newdata/wooly/python/wooly/table.strings
Modified:
   mgmt/newdata/cumin/python/cumin/config.py
   mgmt/newdata/cumin/python/cumin/grid/collector.py
   mgmt/newdata/cumin/python/cumin/grid/job.py
   mgmt/newdata/cumin/python/cumin/grid/limit.py
   mgmt/newdata/cumin/python/cumin/grid/negotiator.py
   mgmt/newdata/cumin/python/cumin/grid/pool.py
   mgmt/newdata/cumin/python/cumin/grid/scheduler.py
   mgmt/newdata/cumin/python/cumin/grid/slot.py
   mgmt/newdata/cumin/python/cumin/grid/submission.py
   mgmt/newdata/cumin/python/cumin/grid/submitter.py
   mgmt/newdata/cumin/python/cumin/inventory/system.py
   mgmt/newdata/cumin/python/cumin/main.py
   mgmt/newdata/cumin/python/cumin/messaging/binding.py
   mgmt/newdata/cumin/python/cumin/messaging/broker.py
   mgmt/newdata/cumin/python/cumin/messaging/brokergroup.py
   mgmt/newdata/cumin/python/cumin/messaging/brokerlink.py
   mgmt/newdata/cumin/python/cumin/messaging/connection.py
   mgmt/newdata/cumin/python/cumin/messaging/exchange.py
   mgmt/newdata/cumin/python/cumin/messaging/main.py
   mgmt/newdata/cumin/python/cumin/messaging/model.py
   mgmt/newdata/cumin/python/cumin/messaging/queue.py
   mgmt/newdata/cumin/python/cumin/model.py
   mgmt/newdata/cumin/python/cumin/parameters.py
   mgmt/newdata/cumin/python/cumin/tools.py
   mgmt/newdata/cumin/python/cumin/usergrid/widgets.py
   mgmt/newdata/cumin/python/cumin/widgets.py
   mgmt/newdata/cumin/resources/app.css
   mgmt/newdata/mint/python/mint/database.py
   mgmt/newdata/mint/python/mint/demo.py
   mgmt/newdata/mint/python/mint/expire.py
   mgmt/newdata/mint/python/mint/main.py
   mgmt/newdata/mint/python/mint/model.py
   mgmt/newdata/mint/python/mint/sql.py
   mgmt/newdata/mint/python/mint/tools.py
   mgmt/newdata/mint/python/mint/update.py
   mgmt/newdata/mint/python/mint/util.py
   mgmt/newdata/mint/python/mint/vacuum.py
   mgmt/newdata/mint/sql/Makefile
   mgmt/newdata/rosemary/bin/rosemary-test
   mgmt/newdata/rosemary/python/rosemary/model.py
   mgmt/newdata/rosemary/python/rosemary/sqlmodel.py
   mgmt/newdata/rosemary/python/rosemary/sqloperation.py
   mgmt/newdata/rosemary/xml/condor.xml
   mgmt/newdata/rosemary/xml/qpid.xml
   mgmt/newdata/rosemary/xml/rosemary.xml
   mgmt/newdata/wooly/python/wooly/__init__.py
   mgmt/newdata/wooly/python/wooly/forms.py
   mgmt/newdata/wooly/python/wooly/parameters.py
   mgmt/newdata/wooly/python/wooly/profile.py
   mgmt/newdata/wooly/python/wooly/server.py
   mgmt/newdata/wooly/python/wooly/tables.py
   mgmt/newdata/wooly/python/wooly/tables.strings
   mgmt/newdata/wooly/python/wooly/template.py
   mgmt/newdata/wooly/python/wooly/widgets.py
   mgmt/newdata/wooly/python/wooly/widgets.strings
   mgmt/newdata/wooly/python/wooly/wsgiserver/__init__.py
Log:
A temporary branch for integration of data-layer changes

Copied: mgmt/newdata (from rev 3870, mgmt/trunk)

Modified: mgmt/newdata/cumin/python/cumin/config.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/config.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/config.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -51,12 +51,11 @@
         if opts:
             self.load_dict(opts)
 
-        enable_logging("cumin", self.log_level, self.log_file)
-        enable_logging("mint", self.log_level, self.log_file)
-        enable_logging("wooly", self.log_level, self.log_file)
+        modules = ("cumin", "mint", "parsley", "rosemary", "wooly")
 
-        if self.debug:
-            modules = ("cumin", "mint", "wooly", "parsley")
+        for name in modules:
+            enable_logging(name, self.log_level, self.log_file)
 
-            for module in modules:
-                enable_logging(module, "debug", sys.stderr)
+        if self.debug:
+            for name in modules:
+                enable_logging(name, "debug", sys.stderr)

Modified: mgmt/newdata/cumin/python/cumin/grid/collector.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/collector.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/grid/collector.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -45,7 +45,7 @@
         return "where %s" % sql
 
     class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Name"
 
         def render_content(self, session, data):
@@ -54,7 +54,7 @@
             return fmt_link(href, data["name"])
 
     class SystemColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "System"
 
         def render_content(self, session, data):

Modified: mgmt/newdata/cumin/python/cumin/grid/job.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/job.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/grid/job.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -87,11 +87,11 @@
         return self.phase.get_title(state)
 
     class ArgsColumn(ItemTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Arguments"
 
     class CustomIdColumn(ItemTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "ID"
 
         def render_content(self, session, data):
@@ -105,7 +105,7 @@
                 return fmt_link(href, job_id, link_title=id)
 
     class StatusColumn(ItemTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Status"
 
         def render_content(self, session, data):
@@ -113,7 +113,7 @@
             return JobStatusInfo.get_status_string(int(stat))
 
     class CommandColumn(ItemTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Command"
 
         def render_content(self, session, data):
@@ -868,7 +868,7 @@
         self.add_column(col)
 
     class GroupColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Job Group"
 
         def render_content(self, session, data):
@@ -881,7 +881,7 @@
                 return fmt_link(href, fmt_shorten(name))
 
     class JobsCountColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Jobs"
 
 class JobsAndGroupsTab(TabbedModeSet):

Modified: mgmt/newdata/cumin/python/cumin/grid/limit.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/limit.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/grid/limit.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -80,7 +80,7 @@
             return len(self.parent.get_items(session))
 
     class NameColumn(ItemTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Name"
 
         def render_content(self, session, data):
@@ -89,11 +89,11 @@
             return fmt_link(href, limit)
 
     class CurrentColumn(ItemTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Current Usage"
 
     class MaxColumn(ItemTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Max Allowance"
 
     class Limits(Attribute):

Modified: mgmt/newdata/cumin/python/cumin/grid/negotiator.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/negotiator.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/grid/negotiator.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -48,7 +48,7 @@
         return "where %s" % sql
 
     class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Name"
 
         def render_content(self, session, data):
@@ -60,7 +60,7 @@
             return fmt_olink(branch, neg, name=data["name"])
 
     class SystemColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "System"
 
         def render_content(self, session, data):

Modified: mgmt/newdata/cumin/python/cumin/grid/pool.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/pool.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/grid/pool.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -50,7 +50,7 @@
         return "Pools %s" % fmt_count(count)
 
     class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Name"
 
         def render_content(self, session, data):
@@ -59,15 +59,15 @@
             return fmt_link(href, data["name"])
 
     class JobsRunningColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Running Jobs"
 
     class JobsIdleColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Idle Jobs"
 
     class SlotsColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Slots"
 
 class PoolFrame(CuminFrame):

Modified: mgmt/newdata/cumin/python/cumin/grid/scheduler.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/scheduler.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/grid/scheduler.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -59,7 +59,7 @@
         return "Schedulers %s" % fmt_count(Scheduler.select().count())
 
     class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Name"
 
         def render_content(self, session, data):
@@ -68,19 +68,19 @@
             return fmt_link(href, data["name"])
 
     class UsersColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Users"
 
     class RunningJobsColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Running Jobs"
 
     class HeldJobsColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Held Jobs"
 
     class SystemColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "System"
 
         def render_content(self, session, data):

Modified: mgmt/newdata/cumin/python/cumin/grid/slot.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/slot.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/grid/slot.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -50,7 +50,7 @@
         return "Slots %s" % fmt_count(count)
 
     class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Name"
 
         def render_content(self, session, data):
@@ -59,15 +59,15 @@
             return fmt_link(href, data["name"])
 
     class ActivityColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Activity"
 
     class StateColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "State"
 
     class LoadAvgColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Load"
 
         def render_value(self, session, value):
@@ -77,7 +77,7 @@
                 return fmt_none_brief()
 
     class JobColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Current Job"
 
         def render_content(self, session, data):

Modified: mgmt/newdata/cumin/python/cumin/grid/submission.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/submission.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/grid/submission.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -47,7 +47,7 @@
         return "Submissions %s" % fmt_count(self.get_item_count(session))
 
     class NameColumn(TopTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Name"
 
         def render_content(self, session, data):
@@ -55,7 +55,7 @@
             return fmt_link(href, data["name"])
 
     class SchedulerColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Scheduler"
 
         def render_content(self, session, data):
@@ -64,7 +64,7 @@
             return fmt_link(href, data["scheduler_name"])
 
     class SubmitterColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Submitter"
 
         def render_content(self, session, data):
@@ -73,15 +73,15 @@
             return fmt_link(href, data["owner"])
 
     class IdleColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Idle Jobs"
 
     class RunningColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Running Jobs"
 
     class CompletedColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Completed Jobs"
 
 class SubmissionFrame(CuminFrame):
@@ -299,7 +299,7 @@
         self.add_column(col)
 
     class NameColumn(TopTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Name"
 
         def render_content(self, session, data):
@@ -313,7 +313,7 @@
             return fmt_link(branch.marshal(), data["name"], link_title=data["name"])
 
     class DurationColumn(TopTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Duration"
 
         def render_content(self, session, data):

Modified: mgmt/newdata/cumin/python/cumin/grid/submitter.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/submitter.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/grid/submitter.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -29,7 +29,7 @@
         return "Submitters %s" % fmt_count(Submitter.select().count())
 
     class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Name"
 
         def render_content(self, session, data):

Modified: mgmt/newdata/cumin/python/cumin/inventory/system.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/inventory/system.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/inventory/system.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -42,7 +42,7 @@
         return "Systems %s" % fmt_count(self.get_item_count(session, *args))
 
     class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Name"
 
         def render_content(self, session, data):
@@ -51,15 +51,15 @@
             return fmt_link(href, fmt_shorten(data["name"]))
 
     class KernelColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Kernel"
 
     class ArchColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Arch"
 
     class FreeMemoryColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Free Memory"
 
         def render_content(self, session, data):
@@ -71,7 +71,7 @@
                 return fmt_none_brief()
 
     class LoadColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Load Average"
 
         def render_content(self, session, data):
@@ -98,7 +98,7 @@
         self.add_column(col)
 
     class NameColumn(TopTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Name"
 
         def render_content(self, session, data):
@@ -107,7 +107,7 @@
             return fmt_link(href, data["name"], link_title=data["name"])
 
     class LoadColumn(TopTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Load Average"
 
         def render_content(self, session, data):

Modified: mgmt/newdata/cumin/python/cumin/main.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/main.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/main.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -5,6 +5,7 @@
 from mint import *
 from parsley.config import Config, ConfigParameter
 from parsley.loggingex import *
+from rosemary.model import RosemaryModel
 from stat import StatChartPage, StatStackedPage, \
     StatFlashPage, FlashFullPage
 from wooly import Application, Session, Page
@@ -13,6 +14,8 @@
 
 from config import *
 from model import *
+from sqladapter import *
+from table import *
 from user import *
 from widgets import *
 
@@ -49,7 +52,14 @@
 
         self.model = CuminModel(self, self.config.data)
 
+        self.rosemary = RosemaryModel()
+        self.rosemary.sql_logging_enabled = True
+        self.rosemary.load_xml_dir(os.path.join(self.home, "xml"))
+        self.rosemary.init()
+
         self.main_page = MainPage(self, "index.html")
+        self.main_page.page_html_class = "Cumin"
+
         self.add_page(self.main_page)
         self.set_default_page(self.main_page)
 
@@ -111,6 +121,9 @@
         self.set_default_frame(self.main)
 
     def render_title(self, session):
+        return self.get_title(session)
+
+    def get_title(self, session):
         return "MRG Administration"
 
 class MainView(CuminMainView):
@@ -120,6 +133,8 @@
         self.overview = OverviewFrame(app, "overview")
         self.add_tab(self.overview)
 
+# XXX Add qmf tab
+
 class OverviewFrame(CuminFrame):
     def __init__(self, app, name):
         super(OverviewFrame, self).__init__(app, name)

Modified: mgmt/newdata/cumin/python/cumin/messaging/binding.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/binding.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/messaging/binding.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -4,95 +4,61 @@
 from wooly.forms import FormInput, FormField, Form
 from wooly.parameters import DictParameter
 from wooly.resources import StringCatalog
+from cumin.formats import *
+from cumin.objectselector import *
+from cumin.sqladapter import *
 from cumin.util import sorted_by
 from cumin.widgets import *
-from cumin.formats import *
 
 import main
 
 strings = StringCatalog(__file__)
 log = logging.getLogger("cumin.messaging.exchange")
 
-class BindingSet(CuminSelectionTable):
-    def __init__(self, app, name):
-        item = BindingParameter(app, "item")
-        super(BindingSet, self).__init__(app, name, item)
+class BindingData(ObjectSqlAdapter):
+    def __init__(self, app):
+        binding = app.rosemary.org_apache_qpid_broker.Binding
+        exchange = app.rosemary.org_apache_qpid_broker.Exchange
+        queue = app.rosemary.org_apache_qpid_broker.Queue
 
-        col = self.QNameColumn(app, "q_id")
-        col.visible = False
-        self.add_column(col)
+        super(BindingData, self).__init__(app, binding)
 
-        col = self.ENameColumn(app, "e_id")
-        col.visible = False
-        self.add_column(col)
+        self.add_join(exchange, binding.exchangeRef, exchange._id)
+        self.add_join(queue, binding.queueRef, queue._id)
 
-        col = self.KeyColumn(app, "key")
-        self.add_column(col)
+class BindingSelector(ObjectSelector):
+    def __init__(self, app, name):
+        binding = app.rosemary.org_apache_qpid_broker.Binding
+        exchange = app.rosemary.org_apache_qpid_broker.Exchange
+        queue = app.rosemary.org_apache_qpid_broker.Queue
 
-        col = self.RateColumn(app, "rate")
-        col.align = "right"
-        self.add_column(col)
+        data = BindingData(app)
 
-        col = self.MatchedColumn(app, "matched")
-        col.align = "right"
-        self.add_column(col)
+        super(BindingSelector, self).__init__(app, name, binding, data)
 
-        self.phase = PhaseSwitch(app, "phase")
-        self.filters.add_child(self.phase)
+        frame = "main.messaging.broker.exchange"
+        self.exchange_column = self.Exchange \
+            (app, "exchange", exchange.name, exchange._id, frame)
 
-        task = main.module.binding_set_remove
-        button = TaskButton(app, "remove", task, self.selection)
-        self.buttons.add_child(button)
+        frame = "main.messaging.broker.queue"
+        self.queue_column = self.Queue \
+            (app, "queue", queue.name, queue._id, frame)
 
-    class QNameColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Queue"
+        self.add_attribute_column(binding.bindingKey)
+        self.add_attribute_column(binding.arguments)
+        self.add_attribute_column(binding.origin)
+        self.add_attribute_column(binding.msgMatched)
 
-        def render_content(self, session, data):
-            queue = Queue.get(data["q_id"])
-            href = self.page.main.messaging.broker.queue.get_href \
-                (session, queue)
-            return fmt_link(href, fmt_shorten(queue.name))
+        self.add_selection_task(main.module.binding_set_remove)
 
-    class ENameColumn(SqlTableColumn):
-        def render_title(self, session, data):
+    class Exchange(ObjectLinkColumn):
+        def render_header_content(self, session):
             return "Exchange"
 
-        def render_content(self, session, data):
-            exchange = Exchange.get(data["e_id"])
-            href = self.page.main.messaging.broker.exchange.get_href \
-                (session, exchange)
+    class Queue(ObjectLinkColumn):
+        def render_header_content(self, session):
+            return "Queue"
 
-            if exchange.name:
-                name = fmt_shorten(exchange.name)
-            else:
-                name = "<em>Default</em>"
-
-            return fmt_link(href, name)
-
-    class KeyColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Key"
-
-        def render_value(self, session, value):
-            return fmt_shorten(value)
-
-    class RateColumn(ItemTableColumn):
-        def render_title(self, session, data):
-            return "Message Rate"
-
-        def render_content(self, session, data):
-            binding = Binding.get(data["id"])
-            return self.app.model.binding.msgMatched.rate_html(binding)
-
-    class MatchedColumn(ItemTableColumn):
-        def render_title(self, session, data):
-            return "Messages Matched"
-
-        def render_content(self, session, data):
-            binding = Binding.get(data["id"])
-            return self.app.model.binding.msgMatched.value(binding)
-
 class ExchangeInput(Widget):
     def __init__(self, app, name):
         super(ExchangeInput, self).__init__(app, name)
@@ -112,12 +78,9 @@
             if isinstance(anc, Form):
                 self.form = anc
 
-    def get_args(self, session):
-        return (self.exchange,)
-
     def get_exchange_info(self, session, exchange):
         binding_info = self.form.bindings.dict_param.get(session)
-        if str(exchange.id) in binding_info:
+        if str(exchange._id) in binding_info:
             return binding_info[str(exchange.id)]
 
     def get_exchange_info_for(self, session, exchange, key):
@@ -142,7 +105,7 @@
         return DictParameter.sep().join((self.instance_data, "type"))
 
     def render_exchange_id(self, session, exchange):
-        return exchange.id
+        return exchange._id
 
     def render_exchange_checked(self, session, exchange):
         exchange_info = self.get_exchange_info(session, exchange)
@@ -292,10 +255,11 @@
         return self.render_dict_error(session, exchange, "mkey.3")
 
     def render_onclick(self, session, exchange):
-        return "onclick=\"toggle_row(this, 'headers_extra.%s')\"" % str(exchange.id)
+        return "onclick=\"toggle_row(this, 'headers_extra.%s')\"" % \
+            str(exchange._id)
 
     def render_headers_extra(self, session, exchange):
-        return "headers_extra.%s" % str(exchange.id)
+        return "headers_extra.%s" % str(exchange._id)
 
     def process_input(self, this_exchange, arguments):
         # x-match is a radio button, it must have a value
@@ -377,35 +341,61 @@
 
     def render_exchanges(self, session):
         vhost = self.vhost.get(session)
-        sortedExchanges = sorted_by(vhost.exchanges)
 
+        cls = self.app.rosemary.org_apache_qpid_broker.Exchange
+        
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
+
+        try:
+            exchanges = cls.select_objects(cursor, _vhostRef_id=vhost._id)
+        finally:
+            cursor.close()
+
+        print "XXX", exchanges
+
         # render each exchange we support
         writer = Writer()
-        for exchange in sortedExchanges:
-            if ExchangeInfo.is_builtin(exchange) or \
-                (not exchange._get_qmfDeleteTime() and \
-                 not (self.state.is_active(session) and not is_active(exchange))):
-                # instance_key gives us a unique path for each exchange
-                # we will be rendering
-                instance_key = self.dict_param.get_instance_key(str(exchange.id))
-                if exchange.type == "direct":
-                    if exchange.name:
-                        self.direct_input.set_instance_data(exchange, instance_key)
-                        writer.write(self.direct_input.render(session))
-                elif exchange.type == "topic":
-                    if not exchange.name == "qpid.management":
-                        self.topic_input.set_instance_data(exchange, instance_key)
-                        writer.write(self.topic_input.render(session))
-                elif exchange.type == "fanout":
-                    self.fanout_input.set_instance_data(exchange, instance_key)
-                    writer.write(self.fanout_input.render(session))
-                elif exchange.type == "xml":
-                    self.xml_input.set_instance_data(exchange, instance_key)
-                    writer.write(self.xml_input.render(session))
-                elif exchange.type == "headers":
-                    self.headers_input.set_instance_data(exchange, instance_key)
-                    writer.write(self.headers_input.render(session))
 
+        for exchange in exchanges:
+            if not ExchangeInfo.is_builtin(exchange):
+                continue
+
+            if exchange._qmf_delete_time:
+                continue
+
+            # if self.state.is_active(session) and not is_active(exchange):
+            #    continue
+
+            if exchange.name == "qpid.management":
+                continue
+
+            # XXX
+            # if ExchangeInfo.is_builtin(exchange) or \
+            #    (not exchange._qmf_delete_time and \
+            #     not (self.state.is_active(session) and not is_active(exchange))):
+
+            # instance_key gives us a unique path for each exchange
+            # we will be rendering
+
+            instance_key = self.dict_param.get_instance_key(str(exchange._id))
+
+            if exchange.type == "direct" and exchange.name:
+                self.direct_input.set_instance_data(exchange, instance_key)
+                writer.write(self.direct_input.render(session, exchange))
+            elif exchange.type == "topic":
+                self.topic_input.set_instance_data(exchange, instance_key)
+                writer.write(self.topic_input.render(session, exchange))
+            elif exchange.type == "fanout":
+                self.fanout_input.set_instance_data(exchange, instance_key)
+                writer.write(self.fanout_input.render(session, exchange))
+            elif exchange.type == "xml":
+                self.xml_input.set_instance_data(exchange, instance_key)
+                writer.write(self.xml_input.render(session, exchange))
+            elif exchange.type == "headers":
+                self.headers_input.set_instance_data(exchange, instance_key)
+                writer.write(self.headers_input.render(session, exchange))
+
         return writer.to_string()
 
     def get_binding_errors(self, session, queue_name):

Modified: mgmt/newdata/cumin/python/cumin/messaging/broker.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/broker.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/messaging/broker.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -3,10 +3,14 @@
 from wooly.widgets import *
 from random import random
 from psycopg2 import IntegrityError
-from cumin.widgets import *
-from cumin.parameters import *
+
 from cumin.formats import *
+from cumin.objectframe import *
+from cumin.parameters import *
+from cumin.sqladapter import *
+from cumin.table import *
 from cumin.util import *
+from cumin.widgets import *
 
 from queue import *
 from exchange import *
@@ -17,181 +21,110 @@
 
 strings = StringCatalog(__file__)
 
-class BrokerSet(CuminSelectionTable):
-    def __init__(self, app, name):
-        item = BrokerGroupParameter(app, "item")
-        super(BrokerSet, self).__init__(app, name, item)
+class BrokerData(ObjectSqlAdapter):
+    def __init__(self, app):
+        broker = app.rosemary.org_apache_qpid_broker.Broker
+        system = app.rosemary.org_apache_qpid_broker.System
+        cluster = app.rosemary.org_apache_qpid_cluster.Cluster
+        mapping = app.rosemary.com_redhat_cumin.BrokerGroupMapping
 
-        self.group = BrokerGroupParameter(app, "group")
-        self.add_parameter(self.group)
+        super(BrokerData, self).__init__(app, broker)
 
-        col = self.NameColumn(app, "name")
-        col.width = "40%"
-        self.add_column(col)
-        self.set_default_column(col)
+        self.add_join(system, broker.systemRef, system._id)
+        self.add_outer_join(cluster, broker._id, cluster.brokerRef)
 
-        col = self.StatusColumn(app, "status")
-        self.add_column(col)
+        subquery = SqlQuery(mapping.sql_table)
+        this = mapping.sql_table._group_id
+        that = "%(group_id)s"
 
-        col = self.ClusterColumn(app, "cluster")
-        self.add_column(col)
+        SqlComparisonFilter(subquery, this, that)
 
-        task = main.module.broker_set_engroup
-        button = TaskButton(app, "engroup", task, self.selection)
-        self.buttons.add_child(button)
+        text = subquery.emit(("1",))
 
-    def render_title(self, session):
-        count = self.get_item_count(session)
-        return "Brokers %s" % fmt_count(count)
+        self.group_filter = SqlExistenceFilter(None, text)
 
-    def render_sql_where(self, session):
-        constraints = self.get_sql_where_constraints(session)
+    def get_sql_options(self, options):
+        sql_options = super(BrokerData, self).get_sql_options(options)
 
-        if constraints:
-            return "where %s" % " and ".join(constraints)
+        if "group" in options.attributes:
+            sql_options.filters.append(self.group_filter)
 
-    def get_sql_where_constraints(self, session):
-        constraints = list()
-        group = self.group.get(session)
+        return sql_options
 
-        if group:
-            subquery = \
-                "select 1 from broker_group_mapping " + \
-                "where broker_group_id = %(group_id)r " + \
-                "and broker_id = b.id"
+class BrokerSelector(ObjectSelector):
+    def __init__(self, app, name, data):
+        broker = app.rosemary.org_apache_qpid_broker.Broker
+        system = app.rosemary.org_apache_qpid_broker.System
+        cluster = app.rosemary.org_apache_qpid_cluster.Cluster
 
-            constraints.append("exists (%s)" % subquery)
+        data = BrokerData(app)
 
-        return constraints
+        super(BrokerSelector, self).__init__(app, name, broker, data)
 
-    def get_sql_values(self, session):
-        group = self.group.get(session)
+        self.group = BrokerGroupParameter(app, "group")
+        self.add_parameter(self.group)
 
-        if group:
-            return {"group_id": group.id}
+        frame = "main.messaging.broker"
+        col = ObjectLinkColumn(app, "name", system.nodeName, broker._id, frame)
+        self.add_column(col)
 
-    class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Name"
+        self.add_attribute_column(broker.port)
+        self.add_attribute_column(cluster.clusterName)
 
-        def render_content(self, session, data):
-            broker = Identifiable(data["id"])
-            href = self.page.main.messaging.broker.get_href(session, broker)
-            return fmt_link(href, fmt_shorten(data["name"], 32, 8))
+        self.add_selection_task(main.module.broker_set_engroup)
 
-    class StatusColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Status"
+    def get_data_values(self, session):
+        values = super(BrokerSelector, self).get_data_values(session)
 
-        def render_content(self, session, data):
-            agent = self.app.model.mint.model.agents.get(data["qmf_agent_id"])
+        group = self.group.get(session)
 
-            if agent:
-                if agent.last_heartbeat is None:
-                    return fmt_none()
-                else:
-                    return fmt_datetime(agent.last_heartbeat)
+        if group:
+            values["group_id"] = group.id
 
-    class ClusterColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Cluster"
+        return values
 
-        def render_content(self, session, data):
-            return data["cluster"] or fmt_none()
+    def get_data_options(self, session):
+        options = super(BrokerSelector, self).get_data_options(session)
 
-class TopBrokerSet(CuminTable):
-    def __init__(self, app, name):
-        super(TopBrokerSet, self).__init__(app, name)
+        group = self.group.get(session)
 
-        col = self.NameColumn(app, "name")
-        self.add_column(col)
-        self.set_default_column(col)
+        if group:
+            options.attributes["group"] = True
 
-    class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Name"
+        return options
 
-        def render_content(self, session, data):
-            reg = Identifiable(data["id"])
-            href = self.page.main.messaging.broker.get_href(session, reg)
-            return fmt_link(href, fmt_shorten(data["name"]))
-
-class BrokerFrame(CuminFrame):
+class BrokerFrame(ObjectFrame):
     def __init__(self, app, name):
-        super(BrokerFrame, self).__init__(app, name)
+        cls = app.rosemary.org_apache_qpid_broker.Broker
 
-        self.object = BrokerParameter(app, "id")
-        self.add_parameter(self.object)
+        super(BrokerFrame, self).__init__(app, name, cls)
 
-        self.view = BrokerView(app, "view", self.object)
-        self.add_mode(self.view)
+        self.icon_href = "resource?name=broker-36.png"
 
-        self.queue = QueueFrame(app, "queue")
+        self.vhost = BrokerVhostAttribute(app, "vhost", self.object)
+        self.add_attribute(self.vhost)
+
+        self.queue = QueueFrame(app, "queue") # XXX pass self.vhost
         self.add_mode(self.queue)
 
         self.exchange = ExchangeFrame(app, "exchange")
         self.add_mode(self.exchange)
 
-        self.connection = ConnectionFrame(app, "conn")
+        self.connection = ConnectionFrame(app, "connection")
         self.add_mode(self.connection)
 
-        self.link = LinkFrame(app, "link")
-        self.add_mode(self.link)
+        self.brokerlink = BrokerLinkFrame(app, "link")
+        self.add_mode(self.brokerlink)
 
-class BrokerView(CuminView):
-    def __init__(self, app, name, broker):
-        super(BrokerView, self).__init__(app, name, broker)
+        self.view.add_tab(QueueSelector(app, "queues", self.vhost))
+        self.view.add_tab(ExchangeSelector(app, "exchanges", self.vhost))
+        self.view.add_tab(ConnectionSelector(app, "connections", self.vhost))
+        self.view.add_tab(BrokerLinkSelector(app, "brokerlinks", self.vhost))
 
-        self.vhost = BrokerVhostAttribute(app, "vhost", broker)
-        self.add_attribute(self.vhost)
+        self.add_summary_attribute(cls.port)
+        self.add_summary_task(main.module.exchange_add)
+        self.add_summary_task(main.module.queue_add)
 
-        self.tabs = TabbedModeSet(app, "tabs")
-        self.add_child(self.tabs)
-
-        self.tabs.add_tab(QueueSet(app, "queues", self.vhost))
-        self.tabs.add_tab(ExchangeSet(app, "exchanges", self.vhost))
-        self.tabs.add_tab(ConnectionSet(app, "conns", self.vhost))
-        self.tabs.add_tab(LinkSet(app, "links", self.vhost))
-        self.tabs.add_tab(BrokerAccessControl(app, "access", self.vhost))
-        self.tabs.add_tab(BrokerClustering(app, "cluster", self.vhost))
-        self.tabs.add_tab(BrokerDetails(app, "details", self.vhost))
-
-class BrokerDetails(Widget):
-    def __init__(self, app, name, vhost):
-        super(BrokerDetails, self).__init__(app, name)
-
-        props = self.Properties(app, "properties", vhost)
-        self.add_child(props)
-
-        tasks = self.Tasks(app, "tasks", vhost)
-        self.add_child(tasks)
-
-    def render_title(self, session):
-        return "Details"
-
-    class Properties(CuminProperties):
-        def do_get_items(self, session):
-            broker = self.object.get(session).broker
-            cls = self.app.model.get_class_by_object(broker)
-
-            props = [(x.get_title(session), x.value(session, broker))
-                     for x in cls.properties]
-
-            return props
-
-    class Tasks(CuminTasks):
-        def do_get_items(self, session):
-            broker = self.object.get(session).broker
-            cls = self.app.model.get_class_by_object(broker)
-
-            tasks = [(x.get_href(session, broker),
-                      x.get_title(session),
-                      x.get_enabled(session, broker))
-                     for x in cls.actions # XXX can't be right
-                     if x.navigable and not x.aggregate]
-
-            return tasks
-
 class ModuleNotEnabled(Widget):
     def do_render(self, session):
         return "This module is not enabled"
@@ -282,7 +215,8 @@
 
         self.group_tmpl = WidgetTemplate(self, "group_html")
 
-        self.brokers = BrokerSet(app, "brokers")
+        data = BrokerData(app)
+        self.brokers = BrokerSelector(app, "brokers", data)
         self.add_child(self.brokers)
 
     def render_title(self, session, *args):
@@ -358,3 +292,20 @@
         if len(brokers):
             self.task.invoke(session, brokers, groups)
         self.task.exit_with_redirect(session, brokers)
+
+class TopBrokerSet(CuminTable):
+    def __init__(self, app, name):
+        super(TopBrokerSet, self).__init__(app, name)
+
+        col = self.NameColumn(app, "name")
+        self.add_column(col)
+        self.set_default_column(col)
+
+    class NameColumn(SqlTableColumn):
+        def render_title(self, session):
+            return "Name"
+
+        def render_content(self, session, data):
+            reg = Identifiable(data["id"])
+            href = self.page.main.messaging.broker.get_href(session, reg)
+            return fmt_link(href, fmt_shorten(data["name"]))

Modified: mgmt/newdata/cumin/python/cumin/messaging/brokergroup.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/brokergroup.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/messaging/brokergroup.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -5,45 +5,32 @@
 from cumin.model import *
 from cumin.widgets import *
 from cumin.parameters import *
+from cumin.sqladapter import *
+from cumin.table import *
 from cumin.formats import *
 from cumin.util import *
 
+from broker import *
+
 import main
 
-from broker import BrokerSet
-
 strings = StringCatalog(__file__)
 
-class BrokerGroupSet(CuminSelectionTable):
+class BrokerGroupSelector(ObjectSelector):
     def __init__(self, app, name):
-        item = BrokerGroupParameter(app, "item")
-        super(BrokerGroupSet, self).__init__(app, name, item)
+        cls = app.rosemary.com_redhat_cumin.BrokerGroup
 
-        col = self.NameColumn(app, "name")
+        super(BrokerGroupSelector, self).__init__(app, name, cls)
+
+        frame = "main.messaging.brokergroup"
+        col = ObjectLinkColumn(app, "name", cls.name, cls._id, frame)
         self.add_column(col)
-        self.set_default_column(col)
 
-        task = main.module.broker_group_set_remove
-        button = TaskButton(self.app, "remove", task, self.selection)
-        self.buttons.add_child(button)
+        self.add_attribute_column(cls.description)
 
-        task = main.module.broker_group_add
-        link = TaskLink(self.app, "add", task, None)
-        self.links.add_child(link)
+        self.add_task(main.module.broker_group_add, None)
+        self.add_selection_task(main.module.broker_group_set_remove)
 
-    def render_title(self, session):
-        return "Broker Groups"
-
-    class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Name"
-
-        def render_content(self, session, data):
-            group = Identifiable(data["id"])
-            href = self.page.main.messaging.broker_group.get_href \
-                (session, group)
-            return fmt_link(href, xml_escape(data["name"]))
-
 class BrokerGroupInputSet(CheckboxInputSet):
     def __init__(self, app, name):
         super(BrokerGroupInputSet, self).__init__(app, name, None)
@@ -65,16 +52,12 @@
         if group in self.param.get(session):
             return "checked=\"checked\""
 
-class BrokerGroupFrame(CuminFrame):
+class BrokerGroupFrame(ObjectFrame):
     def __init__(self, app, name):
-        super(BrokerGroupFrame, self).__init__(app, name)
+        cls = app.rosemary.com_redhat_cumin.BrokerGroup
 
-        self.object = BrokerGroupParameter(app, "id")
-        self.add_parameter(self.object)
+        super(BrokerGroupFrame, self).__init__(app, name, cls)
 
-        self.view = BrokerGroupView(app, "view", self.object)
-        self.add_child(self.view)
-
 class BrokerGroupView(CuminView):
     def __init__(self, app, name, group):
         super(BrokerGroupView, self).__init__(app, name, group)
@@ -82,8 +65,8 @@
         self.tabs = TabbedModeSet(app, "tabs")
         self.add_child(self.tabs)
 
-        # XXX
-        brokers = BrokerSet(app, "brokers")
+        data = BrokerData(app)
+        brokers = BrokerSelector(app, "brokers", data)
         brokers.group = group
         self.tabs.add_tab(brokers)
 
@@ -115,7 +98,7 @@
     def __init__(self, app, name, task):
         super(BrokerGroupEditForm, self).__init__(app, name, task)
 
-        self.group = BrokerGroupParameter(app, "group")
+        self.group = NewBrokerGroupParameter(app, "group")
         self.add_parameter(self.group)
 
     def process_submit(self, session):
@@ -140,7 +123,7 @@
     def __init__(self, app, name, task):
         super(BrokerGroupRemoveForm, self).__init__(app, name, task)
 
-        self.object = BrokerGroupParameter(app, "group")
+        self.object = NewBrokerGroupParameter(app, "group")
         self.add_parameter(self.object)
 
 class BrokerGroupSetTaskForm(CuminTaskForm):

Modified: mgmt/newdata/cumin/python/cumin/messaging/brokerlink.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/brokerlink.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/messaging/brokerlink.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -15,186 +15,60 @@
 
 strings = StringCatalog(__file__)
 
-class LinkSet(CuminSelectionTable):
+class BrokerLinkSelector(ObjectSelector):
     def __init__(self, app, name, vhost):
-        item = LinkParameter(app, "item")
-        super(LinkSet, self).__init__(app, name, item)
+        cls = app.rosemary.org_apache_qpid_broker.Link
 
+        super(BrokerLinkSelector, self).__init__(app, name, cls)
+
         self.vhost = vhost
 
-        col = self.AddressColumn(app, "addr")
-        self.add_column(col)
-        self.set_default_column(col)
+        self.add_reference_filter(self.vhost, cls.vhostRef)
 
-        col = self.DurableColumn(app, "durable")
+        frame = "main.messaging.broker.brokerlink"
+        col = ObjectLinkColumn(app, "name", cls.host, cls._id, frame)
         self.add_column(col)
 
-        col = self.StateColumn(app, "state")
-        self.add_column(col)
+        self.add_attribute_column(cls.port)
+        self.add_attribute_column(cls.state)
+        self.add_attribute_column(cls.transport)
+        self.add_attribute_column(cls.durable)
 
-        col = self.LastErrorColumn(app, "last_error")
-        self.add_column(col)
+        self.add_task(main.module.link_add, self.vhost)
+        self.add_selection_task(main.module.link_set_remove)
 
-        #col = self.FromPeerColumn(app, "from_peer")
-        #self.add_column(col)
+    # Address column XXX
 
-        #col = self.ToPeerColumn(app, "to_peer")
-        #self.add_column(col)
+class BrokerLinkFrame(ObjectFrame):
+    def __init__(self, app, name):
+        cls = app.rosemary.org_apache_qpid_broker.Link
 
-        task = main.module.link_add
-        self.links.add_child(TaskLink(app, "link_add", task, vhost))
+        super(BrokerLinkFrame, self).__init__(app, name, cls)
 
-        task = main.module.link_set_remove
-        button = TaskButton(app, "remove", task, self.selection)
-        self.buttons.add_child(button)
+        routes = RouteSelector(app, "routes", self.object)
+        self.view.add_tab(routes)
 
-    def render_title(self, session):
-        count = self.get_item_count(session)
-        return "Broker Links %s" % fmt_count(count)
-
-    def get_sql_where_constraints(self, session):
-        constraints = super(LinkSet, self).get_sql_where_constraints(session)
-        constraints.append("v.id = %(vhost_id)r")
-        return constraints
-
-    def get_sql_values(self, session):
-        vhost = self.vhost.get(session)
-        return {"vhost_id": vhost.id}
-
-    def disable_closed(self, session, data):
-        return data["state"] == "Closed"
-
-    class AddressColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Address"
-
-        def render_content(self, session, data):
-            link = Identifiable(data["id"])
-            href = self.frame.link.get_href(session, link)
-            name = "%s:%i" % (data["host"], data["port"])
-            return fmt_link(href, fmt_shorten(name))
-
-        def get_order_by_sql(self, session):
-            dir = self.parent.is_reversed(session) and "desc" or "asc"
-            return "order by host, port %s" % dir
-
-    class StateColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "State"
-
-    class LastErrorColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Last Error"
-
-    class DurableColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Durable"
-
-    class FromPeerColumn(NullSortColumn, FreshDataOnlyColumn):
-        def render_title(self, session, data):
-            return "Bytes from Broker"
-
-        def render_value(self, session, value):
-            return fmt_rate(value)
-
-    class ToPeerColumn(NullSortColumn, FreshDataOnlyColumn):
-        def render_title(self, session, data):
-            return "Bytes to Broker"
-
-        def render_value(self, session, value):
-            return fmt_rate(value)
-
-class RouteSet(CuminSelectionTable):
+class RouteSelector(ObjectSelector):
     def __init__(self, app, name, link):
-        item = RouteParameter(app, "item")
-        super(RouteSet, self).__init__(app, name, item)
+        cls = app.rosemary.org_apache_qpid_broker.Bridge
 
+        super(RouteSelector, self).__init__(app, name, cls)
+
         self.link = link
 
-        #col = self.SourceColumn(app, "source")
-        #self.add_column(col)
+        self.add_reference_filter(self.link, cls.linkRef)
 
-        #col = self.DestinationColumn(app, "managed_broker")
-        #self.add_column(col)
+        self.add_attribute_column(cls.src)
+        self.add_attribute_column(cls.dest)
+        self.add_attribute_column(cls.key)
+        self.add_attribute_column(cls.tag)
+        self.add_attribute_column(cls.excludes)
 
-        col = self.ExchangeColumn(app, "exchange")
-        self.add_column(col)
-        self.set_default_column(col)
+        self.add_task(main.module.route_add, self.link)
+        self.add_selection_task(main.module.route_set_remove)
 
-        col = self.KeyColumn(app, "key")
-        self.add_column(col)
+# XXX RouteFrame
 
-        col = self.TagColumn(app, "tag")
-        self.add_column(col)
-
-        col = self.ExcludesColumn(app, "excludes")
-        self.add_column(col)
-
-        task = main.module.route_add
-        self.links.add_child(TaskLink(app, "add", task, self.link))
-
-        task = main.module.route_set_remove
-        button = TaskButton(app, "remove", task, self.selection)
-        self.buttons.add_child(button)
-
-    def render_title(self, session):
-        count = self.get_item_count(session)
-        return "Link Routes %s" % fmt_count(count)
-
-    def render_sql_where(self, session):
-        return "where l.id = %(link_id)r and b.qmf_delete_time is null"
-
-    def get_sql_values(self, session):
-        link = self.link.get(session)
-        return {"link_id": link.id}
-
-    class SourceColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Source"
-
-        def render_content(self, session, data):
-            return "%s:%i" % (data["host"], data["port"])
-
-        def get_order_by_sql(self, session):
-            dir = self.parent.is_reversed(session) and "desc" or "asc"
-            return "order by l.host, l.port %s" % dir
-
-    class DestinationColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Destination"
-
-    class ExchangeColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Exchange"
-
-    class KeyColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Routing Key"
-
-    class TagColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Tag"
-
-    class ExcludesColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Excludes"
-
-class LinkFrame(CuminFrame):
-    def __init__(self, app, name):
-        super(LinkFrame, self).__init__(app, name)
-
-        self.object = LinkParameter(app, "id")
-        self.add_parameter(self.object)
-
-        self.view = LinkView(app, "view", self.object)
-        self.add_mode(self.view)
-
-    def show_object(self, session, link):
-        if hasattr(link, "vhost"):
-            self.page.main.messaging.broker.set_object(session, link.vhost)
-
-        return super(LinkFrame, self).show_object(session, link)
-
 class LinkRemoveForm(CuminTaskForm):
     def __init__(self, app, name, task):
         super(LinkRemoveForm, self).__init__(app, name, task)

Modified: mgmt/newdata/cumin/python/cumin/messaging/connection.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/connection.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/messaging/connection.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -2,6 +2,8 @@
 from wooly.widgets import *
 from wooly.tables import *
 from datetime import datetime
+from cumin.objectframe import *
+from cumin.objectselector import *
 from cumin.stat import *
 from cumin.widgets import *
 from cumin.parameters import *
@@ -12,6 +14,55 @@
 
 strings = StringCatalog(__file__)
 
+class ConnectionSelector(ObjectSelector):
+    def __init__(self, app, name, vhost):
+        cls = app.rosemary.org_apache_qpid_broker.Connection
+
+        super(ConnectionSelector, self).__init__(app, name, cls)
+
+        self.vhost = vhost
+
+        frame = "main.messaging.broker.connection"
+        col = ObjectLinkColumn(app, "address", cls.address, cls._id, frame)
+        self.add_column(col)
+
+        col = ConnectionProcessColumn \
+            (app, "process", cls.remoteProcessName, cls.remotePid)
+        self.add_column(col)
+
+        self.add_attribute_column(cls.authIdentity)
+        self.add_attribute_column(cls.SystemConnection)
+        self.add_attribute_column(cls.federationLink)
+        self.add_attribute_column(cls.bytesFromClient)
+        self.add_attribute_column(cls.bytesToClient)
+
+class ConnectionProcessColumn(ObjectAttributeColumn):
+    def __init__(self, app, name, attr, pid_attr):
+        super(ConnectionProcessColumn, self).__init__(app, name, attr)
+
+        self.pid_attr = pid_attr
+
+    def init(self):
+        super(ConnectionProcessColumn, self).init()
+
+        try:
+            self.pid_field = self.table.adapter.fields_by_attr[self.pid_attr]
+        except KeyError:
+            self.pid_field = ObjectSqlField(self.table.adapter, self.pid_attr)
+
+    def render_header_content(self, session):
+        return "Process (PID)"
+
+    def render_cell_content(self, session, record):
+        args = (record[self.field.index], record[self.pid_field.index])
+        return "%s (%i)" % args
+
+class ConnectionFrame(ObjectFrame):
+    def __init__(self, app, name):
+        cls = app.rosemary.org_apache_qpid_broker.Connection
+
+        super(ConnectionFrame, self).__init__(app, name, cls)
+
 class ConnectionSet(CuminSelectionTable):
     def __init__(self, app, name, vhost):
         item = ConnectionParameter(app, "item")
@@ -73,7 +124,7 @@
         return {"id": vhost.id}
 
     class AddressColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Address"
 
         def render_content(self, session, data):
@@ -82,7 +133,7 @@
             return fmt_link(href, fmt_shorten(data["addr"]))
 
     class SystemConnectionColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Connect Type"
 
         def render_content(self, session, data):
@@ -92,11 +143,12 @@
                 return "Client"
 
     class AuthIdentityColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Auth Id"
 
     class FedLinkColumn(SqlTableColumn):
-        def render_title(self, session, data):
+
+        def render_title(self, session):
             return "Fed Link"
 
         def render_content(self, session, data):
@@ -106,7 +158,7 @@
                 return "No"
 
     class SentColumn(NullSortColumn, FreshDataOnlyColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "%s Sent" % self.parent.get_unit_plural(session)
 
         def get_column_key(self, session):
@@ -117,7 +169,7 @@
             return fmt_rate(value, "", "sec")
 
     class ReceivedColumn(NullSortColumn, FreshDataOnlyColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "%s Received" % self.parent.get_unit_plural(session)
 
         def get_column_key(self, session):
@@ -127,22 +179,6 @@
         def render_value(self, session, value):
             return fmt_rate(value, "", "sec")
 
-class ConnectionFrame(CuminFrame):
-    def __init__(self, app, name):
-        super(ConnectionFrame, self).__init__(app, name)
-
-        self.object = ConnectionParameter(app, "id")
-        self.add_parameter(self.object)
-
-        self.view = ConnectionView(app, "view", self.object)
-        self.add_mode(self.view)
-
-    def show_object(self, session, conn):
-        if hasattr(conn, "vhost"):
-            self.frame.set_object(session, conn.vhost.broker)
-
-        return super(ConnectionFrame, self).show_object(session, conn)
-
 class ConnectionCloseForm(CuminTaskForm):
     def __init__(self, app, name, task):
         super(ConnectionCloseForm, self).__init__(app, name, task)
@@ -245,18 +281,18 @@
         return {"id": conn.id}
 
     class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Name"
 
     class ExpiresColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Expires"
 
         def render_value(self, session, value):
             return fmt_datetime(value)
 
     class StatusColumn(SqlTableColumn):
-        def render_title(self, session, data):
+        def render_title(self, session):
             return "Attached?"
 
         def render_content(self, session, data):

Modified: mgmt/newdata/cumin/python/cumin/messaging/exchange.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/exchange.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/messaging/exchange.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -18,178 +18,84 @@
 strings = StringCatalog(__file__)
 log = logging.getLogger("cumin.messaging.exchange")
 
-class ExchangeInputSet(RadioInputSet):
-    def __init__(self, app, name):
-        super(ExchangeInputSet, self).__init__(app, name)
-
-        param = ExchangeParameter(app, "param")
-        self.add_parameter(param)
-        self.set_parameter(param)
-
-    def do_get_items(self, session, vhost):
-        return sorted_by(vhost.exchanges)
-
-    def render_item_value(self, session, exchange):
-        return exchange.id
-
-    def render_item_content(self, session, exchange):
-        return exchange.name or "<em>Default</em>"
-
-    def render_item_checked_attr(self, session, exchange):
-        return exchange is self.param.get(session) and "checked=\"checked\""
-
-class ExchangeSet(CuminSelectionTable):
+class ExchangeSelector(ObjectSelector):
     def __init__(self, app, name, vhost):
-        item = ExchangeParameter(app, "item")
-        super(ExchangeSet, self).__init__(app, name, item)
+        cls = app.rosemary.org_apache_qpid_broker.Exchange
 
+        super(ExchangeSelector, self).__init__(app, name, cls)
+
         self.vhost = vhost
 
-        col = self.NameColumn(app, "name")
+        frame = "main.messaging.broker.exchange"
+        col = ObjectLinkColumn(app, "name", cls.name, cls._id, frame)
         self.add_column(col)
-        self.set_default_column(col)
 
-        col = self.ProducersColumn(app, "name")
-        col.align = "right"
-        self.add_column(col)
+        self.add_attribute_column(cls.producerCount)
+        self.add_attribute_column(cls.bindingCount)
+        self.add_attribute_column(cls.msgRoutes)
+        self.add_attribute_column(cls.byteRoutes)
+    
+        self.add_reference_filter(vhost, cls.vhostRef)
 
-        col = self.BindingsColumn(app, "bindings")
-        col.align = "right"
-        self.add_column(col)
+        self.add_task(main.module.exchange_add, self.vhost)
+        self.add_selection_task(main.module.exchange_set_remove)
 
-        col = self.ReceivedColumn(app, "received")
-        col.align = "right"
-        self.add_column(col)
+class ExchangeFrame(ObjectFrame):
+    def __init__(self, app, name):
+        cls = app.rosemary.org_apache_qpid_broker.Exchange
 
-        col = self.RoutedColumn(app, "routed")
-        col.align = "right"
-        self.add_column(col)
+        super(ExchangeFrame, self).__init__(app, name, cls)
 
-        col = self.DroppedColumn(app, "dropped")
-        col.align = "right"
-        self.add_column(col)
+        self.icon_href = "resource?name=exchange-36.png"
 
-        self.unit = UnitSwitch(app, "unit")
-        self.switches.add_child(self.unit)
+        # XXX
+        #self.overview = ExchangeOverview(app, "overview", self.object)
+        #self.view.add_tab(self.overview)
 
-        self.phase = PhaseSwitch(app, "phase")
-        self.filters.add_child(self.phase)
+        self.bindings = ExchangeBindingSelector(app, "bindings", self.object)
+        self.view.add_tab(self.bindings)
 
-        task = main.module.exchange_add
-        self.links.add_child(TaskLink(app, "exchange_add", task, self.vhost))
+        # XXX self.add_summary_task(main.module.exchange_remove)
 
-        task = main.module.exchange_set_remove
-        button = TaskButton(app, "remove", task, self.selection)
-        self.buttons.add_child(button)
-
-    def disable_exchange(self, session, data):
-        return data["name"] in ExchangeInfo.get_builtins()
-
     def render_title(self, session):
-        vhost = self.vhost.get(session)
-        return "Exchanges %s" % fmt_count(vhost.exchanges.count())
+        exchange = self.object.get(session)
 
-    def render_sql_where(self, session):
-        vhost = self.vhost.get(session)
+        if exchange:
+            if exchange.name:
+                return super(ExchangeFrame, self).render_title(session)
+            else:
+                return "Default exchange"
 
-        elems = list()
-        elems.append("e.vhost_id = %(id)r")
-        elems.append(self.phase.get_sql_constraint(session, vhost))
+class ExchangeBindingSelector(BindingSelector):
+    def __init__(self, app, name, exchange):
+        super(ExchangeBindingSelector, self).__init__(app, name)
 
-        return "where %s" % " and ".join(elems)
+        self.exchange = exchange
 
-    def get_sql_values(self, session):
-        vhost = self.vhost.get(session)
-        return {"id": vhost.id}
+        self.add_reference_filter(self.exchange, self.cls.exchangeRef)
 
-    class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Name"
+        self.exchange_column.visible = False
 
-        def render_content(self, session, data):
-            exchange = Identifiable(data["id"])
-            href = self.frame.exchange.get_href(session, exchange)
-            name = data["name"] or "<em>Default</em>"
-            return fmt_link(href, fmt_shorten(name))
-
-    class ProducersColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Producers"
-
-        def render_content(self, session, data):
-            return data["producers"]
-
-    class BindingsColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Bindings"
-
-        def render_content(self, session, data):
-            exchange = Identifiable(data["id"])
-            href = self.frame.exchange.get_href(session, exchange)
-            return fmt_link(href, data["bindings"])
-
-    class ReceivedColumn(NullSortColumn, FreshDataOnlyColumn):
-        def render_title(self, session, data):
-            return "%s Received" % self.parent.unit.get_brief_plural(session)
-
-        def get_column_key(self, session):
-            unit = self.parent.unit.get(session)
-            return unit == "b" and "breceived" or "mreceived"
-
-        def render_value(self, session, value):
-            return fmt_rate(value, "", "sec")
-
-    class RoutedColumn(NullSortColumn, FreshDataOnlyColumn):
-        def render_title(self, session, data):
-            return "%s Routed" % self.parent.unit.get_brief_plural(session)
-
-        def get_column_key(self, session):
-            unit = self.parent.unit.get(session)
-            return unit == "b" and "brouted" or "mrouted"
-
-        def render_value(self, session, value):
-            return fmt_rate(value, "", "sec")
-
-    class DroppedColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "%s Dropped" % self.parent.unit.get_brief_plural(session)
-
-        def get_column_key(self, session):
-            unit = self.parent.unit.get(session)
-            return unit == "b" and "bdropped" or "mdropped"
-
-class ExchangeFrame(CuminFrame):
+class ExchangeInputSet(RadioInputSet):
     def __init__(self, app, name):
-        super(ExchangeFrame, self).__init__(app, name)
+        super(ExchangeInputSet, self).__init__(app, name)
 
-        self.object = ExchangeParameter(app, "id")
-        self.add_parameter(self.object)
+        param = ExchangeParameter(app, "param")
+        self.add_parameter(param)
+        self.set_parameter(param)
 
-        self.view = ExchangeView(app, "view", self.object)
-        self.add_mode(self.view)
+    def do_get_items(self, session, vhost):
+        return sorted_by(vhost.exchanges)
 
-    def show_object(self, session, exchange):
-        if isinstance(exchange, SQLObject):
-            self.page.main.messaging.broker.set_object \
-                (session, exchange.vhost.broker)
+    def render_item_value(self, session, exchange):
+        return exchange.id
 
-        return super(ExchangeFrame, self).show_object(session, exchange)
+    def render_item_content(self, session, exchange):
+        return exchange.name or "<em>Default</em>"
 
-    def render_title(self, session):
-        exchange = self.object.get(session)
+    def render_item_checked_attr(self, session, exchange):
+        return exchange is self.param.get(session) and "checked=\"checked\""
 
-        if exchange:
-            if exchange.name:
-                return super(ExchangeFrame, self).render_title(session)
-            else:
-                return "Default exchange"
-
-    def render_href(self, session):
-        exchange = self.object.get(session)
-
-        if exchange:
-            return super(ExchangeFrame, self).render_href(session, exchange)
-
 class ExchangeRemoveForm(CuminTaskForm):
     def __init__(self, app, name, task):
         super(ExchangeRemoveForm, self).__init__(app, name, task)
@@ -206,46 +112,6 @@
         self.object = ListParameter(app, "exchange", item)
         self.add_parameter(self.object)
 
-class ExchangeView(CuminView):
-    def __init__(self, app, name, exchange):
-        super(ExchangeView, self).__init__(app, name, exchange)
-
-        self.tabs = TabbedModeSet(app, "tabs")
-        self.add_child(self.tabs)
-
-        self.tabs.add_tab(ExchangeStats(app, "stats", exchange))
-
-        self.bindings = ExchangeBindingSet(app, "bindings", exchange)
-        self.tabs.add_tab(self.bindings)
-
-        self.tabs.add_tab(CuminDetails(app, "details", exchange))
-
-class ExchangeBindingSet(BindingSet):
-    def __init__(self, app, name, exchange):
-        super(ExchangeBindingSet, self).__init__(app, name)
-
-        self.exchange = exchange
-
-        self.set_default_column_name("q_id")
-
-    def get_visible_columns(self, session):
-        return self.get_request_visible_columns(session, ["q_id"])
-
-    def get_sql_values(self, session):
-        exchange = self.exchange.get(session)
-        return {"id": exchange.id}
-
-    def render_title(self, session):
-        exchange = self.exchange.get(session)
-        return "Queue Bindings %s" % fmt_count(exchange.bindings.count())
-
-    def render_sql_where(self, session):
-        exchange = self.exchange.get(session)
-        elems = list()
-        elems.append("b.exchange_id = %(id)r")
-        elems.append(self.phase.get_sql_constraint(session, exchange))
-        return "where %s" % " and ".join(elems)
-
 class ExchangeTypeField(RadioItemSetField):
     def __init__(self, app, name):
         param = SymbolParameter(app, "param")
@@ -387,9 +253,9 @@
         vhost = self.vhost.get(session)
         return self.task.get_description(session, vhost)
 
-class ExchangeStats(Widget):
+class ExchangeOverview(Widget):
     def __init__(self, app, name, exchange):
-        super(ExchangeStats, self).__init__(app, name)
+        super(ExchangeOverview, self).__init__(app, name)
 
         self.add_child(StatSet(app, "io", exchange, "io"))
         self.add_child(StatSet(app, "general", exchange, "general"))
@@ -404,7 +270,7 @@
         self.add_child(chart)
 
     def render_title(self, session):
-        return "Statistics"
+        return "Overview"
 
     class ReceiveRouteDropRateChart(StatFlashChart):
         def render_title(self, session):

Modified: mgmt/newdata/cumin/python/cumin/messaging/main.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/main.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/messaging/main.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -87,7 +87,7 @@
         self.add_mode(self.broker)
         self.add_sticky_view(self.broker)
 
-        self.broker_group = BrokerGroupFrame(app, "group")
+        self.broker_group = BrokerGroupFrame(app, "brokergroup")
         self.add_mode(self.broker_group)
 
     def render_title(self, session):
@@ -104,7 +104,7 @@
         self.add_child(self.tabs)
 
         self.tabs.add_tab(BrokerBrowser(app, "brokers"))
-        self.tabs.add_tab(BrokerGroupSet(app, "groups"))
+        self.tabs.add_tab(BrokerGroupSelector(app, "brokergroups"))
 
     class Heading(CuminHeading):
         def render_title(self, session):

Modified: mgmt/newdata/cumin/python/cumin/messaging/model.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/model.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/messaging/model.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -93,8 +93,18 @@
         return "Add queue"
 
     def get_description(self, session, vhost):
-        return "Add queue to broker '%s'" % get_vhost_name(vhost)
+        cls = self.app.rosemary.org_apache_qpid_broker.Broker
 
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
+
+        try:
+            broker = cls.get_object(cursor, vhost._brokerRef_id)
+        finally:
+            cursor.close()
+
+        return "Add queue to broker '%s'" % broker.get_title()
+
     def do_enter(self, session, vhost):
         self.form.vhost.set(session, vhost)
 
@@ -116,8 +126,6 @@
         self.app.main_page.main.messaging.broker.view.show(session)
 
     def do_invoke(self, session, queue):
-        assert isinstance(queue, Queue)
-
         session = self.app.model.get_session_by_object(queue)
         session.queue_delete(queue=queue.name)
         session.sync()
@@ -129,8 +137,8 @@
         self.form = QueueSetTaskForm(app, "queue_set_remove", self)
         self.item_task = QueueRemoveTask(app, cls)
 
-    def do_enter(self, session, queues):
-        self.form.object.set(session, queues)
+    def do_enter(self, session, selection):
+        self.form.selection.set(session, selection)
 
 class QueuePurgeTask(QmfTask):
     def __init__(self, app, cls):
@@ -144,13 +152,12 @@
     def do_enter(self, session, queue):
         self.form.queue.set(session, queue)
 
-    def do_invoke(self, completion, session, queue, count=0):
+    def do_invoke(self, completion, session, obj, count=0):
         """A count of 0 purges all"""
 
-        assert isinstance(queue, Queue)
+        agent = self.app.model.mint.model.agents[obj._qmf_agent_id]
+        agent.call_method(obj, "purge", completion, (count,))
 
-        queue.purge(completion, count)
-
 class QueueSetPurgeTask(SetTask):
     def __init__(self, app, cls):
         super(QueueSetPurgeTask, self).__init__(app, cls)
@@ -158,8 +165,8 @@
         self.form = QueueSetTaskForm(app, "queue_set_purge", self)
         self.item_task = QueuePurgeTask(app, cls)
 
-    def do_enter(self, session, queues):
-        self.form.object.set(session, queues)
+    def do_enter(self, session, ids):
+        self.form.selection.set(session, ids)
 
 class BindingAddTask(Task):
     def __init__(self, app, cls):
@@ -396,8 +403,29 @@
         return "Add broker group"
 
     def do_invoke(self, session, object, name):
-        group = BrokerGroup(name=name)
-        group.syncUpdate()
+        cls = self.app.rosemary.com_redhat_cumin.BrokerGroup
+
+        group = cls.create_object()
+        group.name = name
+        group.description = "XXX"
+
+        # XXX ugh
+        group._qmf_agent_id = "cumin"
+        group._qmf_object_id = str(uuid4())
+        group._qmf_create_time = datetime.now()
+        group._qmf_update_time = datetime.now()
+        group._qmf_class_key = "cumin"
+
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
+
+        try:
+            group.save(cursor)
+        finally:
+            cursor.close()
+
+        conn.commit()
+
         return group
 
 class BrokerGroupEditTask(Task):
@@ -413,11 +441,20 @@
         self.form.group.set(session, group)
 
     def do_invoke(self, session, group, name):
-        assert isinstance(group, BrokerGroup)
+        assert group
 
-        group.set(name=name)
-        group.syncUpdate()
+        group.name = name
 
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
+
+        try:
+            group.save(cursor)
+        finally:
+            cursor.close()
+
+        conn.commit()
+
 class BrokerGroupRemoveTask(Task):
     def __init__(self, app, cls):
         super(BrokerGroupRemoveTask, self).__init__(app, cls)
@@ -434,11 +471,18 @@
         self.app.main_page.main.messaging.view.show(session)
 
     def do_invoke(self, session, group):
-        assert isinstance(group, BrokerGroup)
+        assert group
 
-        group.destroySelf()
-        group.syncUpdate()
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
 
+        try:
+            group.delete(cursor)
+        finally:
+            cursor.close()
+
+        conn.commit()
+
 class BrokerGroupSetRemoveTask(SetTask):
     def __init__(self, app, cls):
         super(BrokerGroupSetRemoveTask, self).__init__(app, cls)

Modified: mgmt/newdata/cumin/python/cumin/messaging/queue.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/queue.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/messaging/queue.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -8,7 +8,11 @@
 from wooly.tables import *
 from datetime import datetime
 from sqlobject.sqlbuilder import LEFTJOINOn
+from cumin.objectselector import *
+from cumin.objectform import *
+from cumin.sqladapter import *
 from cumin.stat import *
+from cumin.table import *
 from cumin.widgets import *
 from cumin.parameters import *
 from cumin.formats import *
@@ -16,241 +20,73 @@
 
 from binding import *
 from exchange import ExchangeInputSet
+from subscription import *
 
 import main
 
 strings = StringCatalog(__file__)
 log = logging.getLogger("cumin.messaging.queue")
 
-class QueueSet(CuminSelectionTable):
+class QueueSelector(ObjectSelector):
     def __init__(self, app, name, vhost):
-        item = QueueParameter(app, "item")
-        super(QueueSet, self).__init__(app, name, item)
+        cls = app.rosemary.org_apache_qpid_broker.Queue
 
+        super(QueueSelector, self).__init__(app, name, cls)
+
         self.vhost = vhost
 
-        col = self.NameColumn(app, "name")
-        col.width = "30%"
+        frame = "main.messaging.broker.queue"
+        col = ObjectLinkColumn(app, "name", cls.name, cls._id, frame)
         self.add_column(col)
-        self.set_default_column(col)
 
-        col = self.ConsumersColumn(app, "consumers")
-        col.align = "right"
-        self.add_column(col)
+        self.add_attribute_column(cls.consumerCount)
+        self.add_attribute_column(cls.bindingCount)
+        self.add_attribute_column(cls.msgDepth)
+        self.add_attribute_column(cls.byteDepth)
 
-        col = self.BindingsColumn(app, "bindings")
-        col.align = "right"
-        self.add_column(col)
+        self.add_reference_filter(vhost, cls.vhostRef)
 
-        col = self.EnqueuedColumn(app, "enqueued")
-        col.align = "right"
-        self.add_column(col)
+        self.add_task(main.module.queue_add, self.vhost)
+        self.add_selection_task(main.module.queue_set_purge)
+        self.add_selection_task(main.module.queue_set_remove)
 
-        col = self.DequeuedColumn(app, "dequeued")
-        col.align = "right"
-        self.add_column(col)
-
-        col = self.DepthColumn(app, "depth")
-        col.align = "right"
-        self.add_column(col)
-
-        self.unit = UnitSwitch(app, "unit")
-        self.switches.add_child(self.unit)
-
-        self.phase = PhaseSwitch(app, "phase")
-        self.filters.add_child(self.phase)
-
-        task = main.module.queue_add
-        self.links.add_child(TaskLink(app, "add", task, vhost))
-
-        task = main.module.queue_set_purge
-        button = TaskButton(app, "purge", task, self.selection)
-        self.buttons.add_child(button)
-
-        task = main.module.queue_set_remove
-        button = TaskButton(app, "remove", task, self.selection)
-        self.buttons.add_child(button)
-
-    def render_title(self, session):
-        vhost = self.vhost.get(session)
-        return "Queues %s" % fmt_count(vhost.queues.count())
-
-    def render_sql_where(self, session):
-        vhost = self.vhost.get(session)
-
-        constraints = list()
-        constraints.append("q.vhost_id = %(id)r")
-        constraints.append(self.phase.get_sql_constraint(session, vhost))
-
-        return "where %s" % " and ".join(constraints)
-
-    def get_sql_values(self, session):
-        vhost = self.vhost.get(session)
-        return {"id": vhost.id}
-
-    class NameColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Name"
-
-        def render_content(self, session, data):
-            queue = Identifiable(data["id"])
-            href = main.module.frame.broker.queue.get_href(session, queue)
-            return fmt_link(href, data["name"], link_title=data["name"])
-
-    class ConsumersColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Consumers"
-
-        def render_value(self, session, value):
-            if value is None:
-                return fmt_none_brief()
-            else:
-                return value
-
-    class BindingsColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Bindings"
-
-        def render_content(self, session, data):
-            queue = Identifiable(data["id"])
-            branch = session.branch()
-            self.frame.queue.object.set(branch, queue)
-            self.frame.queue.view.bindings.show(branch)
-            return fmt_link(branch.marshal(), data["bindings"] or "0")
-
-    class EnqueuedColumn(NullSortColumn, FreshDataOnlyColumn):
-        def render_title(self, session, data):
-            return "%s Enqueued" % self.parent.unit.get_brief_plural(session)
-
-        def get_column_key(self, session):
-            unit = self.parent.unit.get(session)
-            return unit == "b" and "benqueued" or "menqueued"
-
-        def render_value(self, session, value):
-            return fmt_rate(value, "", "sec")
-
-    class DequeuedColumn(NullSortColumn, FreshDataOnlyColumn):
-        def render_title(self, session, data):
-            return "%s Dequeued" % self.parent.unit.get_brief_plural(session)
-
-        def get_column_key(self, session):
-            unit = self.parent.unit.get(session)
-            return unit == "b" and "bdequeued" or "mdequeued"
-
-        def render_value(self, session, value):
-            return fmt_rate(value, "", "sec")
-
-    class DepthColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "%s Depth" % self.parent.unit.get_brief_singular(session)
-
-        def get_column_key(self, session):
-            unit = self.parent.unit.get(session)
-            return unit == "b" and "bdepth" or "mdepth"
-
-        def render_value(self, session, value):
-            if value is None:
-                return fmt_none_brief()
-            else:
-                return value
-
-class TopQueueSet(TopTable):
+class QueueFrame(ObjectFrame):
     def __init__(self, app, name):
-        super(TopQueueSet, self).__init__(app, name)
+        cls = app.rosemary.org_apache_qpid_broker.Queue
 
-        col = self.NameColumn(app, "name")
-        col.width = "60%"
-        self.add_column(col)
+        super(QueueFrame, self).__init__(app, name, cls)
 
-        self.set_default_column(col)
+        self.icon_href = "resource?name=queue-36.png"
 
-        col = self.EnqueuesColumn(app, "enqueues")
-        col.width = "35%"
-        col.align = "right"
-        self.add_column(col)
+        self.bindings = QueueBindingSelector(app, "bindings", self.object)
+        self.view.add_tab(self.bindings)
 
-    class NameColumn(TopTableColumn):
-        def render_title(self, session, data):
-            return "Name"
+        self.subscriptions = SubscriptionSelector \
+            (app, "subscriptions", self.object)
+        self.view.add_tab(self.subscriptions)
 
-        def render_content(self, session, data):
-            broker = Identifiable(data["broker_id"])
-            queue = Identifiable(data["id"])
+        self.subscription = SubscriptionFrame(app, "subscription")
+        self.add_mode(self.subscription)
 
-            branch = session.branch()
-            self.page.main.messaging.broker.object.set(branch, broker)
-            self.page.main.messaging.broker.queue.object.set(branch, queue)
-            self.page.main.messaging.broker.queue.show(branch)
-            return fmt_link \
-                (branch.marshal(), data["name"], link_title=data["name"])
-
-    class EnqueuesColumn(TopTableColumn):
-        def render_title(self, session, data):
-            return "Recent Enqueues"
-
-class QueueFrame(CuminFrame):
-    def __init__(self, app, name):
-        super(QueueFrame, self).__init__(app, name)
-
-        self.object = QueueParameter(app, "id")
-        self.add_parameter(self.object)
-
-        self.view = QueueView(app, "view", self.object)
-        self.add_mode(self.view)
-
-class QueueView(CuminView):
+class QueueBindingSelector(BindingSelector):
     def __init__(self, app, name, queue):
-        super(QueueView, self).__init__(app, name, queue)
+        super(QueueBindingSelector, self).__init__(app, name)
 
-        self.tabs = TabbedModeSet(app, "tabs")
-        self.add_child(self.tabs)
-
-        self.tabs.add_tab(QueueStats(app, "stats", queue))
-
-        self.bindings = QueueBindingSet(app, "bindings", queue)
-        self.tabs.add_tab(self.bindings)
-
-        self.tabs.add_tab(CuminDetails(app, "details", queue))
-
-class QueueBindingSet(BindingSet):
-    def __init__(self, app, name, queue):
-        super(QueueBindingSet, self).__init__(app, name)
-
         self.queue = queue
 
-        self.set_default_column_name("e_id")
+        self.add_reference_filter(self.queue, self.cls.queueRef)
 
-        task = main.module.binding_add
-        self.links.add_child(TaskLink(app, "add", task, self.queue))
+        self.queue_column.visible = False
 
-    def get_visible_columns(self, session):
-        return self.get_request_visible_columns(session, ["e_id"])
-
-    def get_sql_values(self, session):
-        queue = self.queue.get(session)
-        return {"id": queue.id}
-
-    def render_sql_where(self, session):
-        queue = self.queue.get(session)
-
-        elems = list()
-        elems.append("b.queue_id = %(id)r")
-        elems.append(self.phase.get_sql_constraint(session, queue))
-
-        return "where %s" % " and ".join(elems)
-
-    def render_title(self, session):
-        queue = self.queue.get(session)
-        return "Exchange Bindings %s" % \
-            fmt_count(queue.bindings.count())
-
 class QueueAddForm(FieldSubmitForm):
     def __init__(self, app, name, task):
         super(QueueAddForm, self).__init__(app, name)
 
         self.task = task
 
-        self.vhost = VhostParameter(app, "vhost")
+        cls = self.app.rosemary.org_apache_qpid_broker.Vhost
+
+        self.vhost = RosemaryObjectParameter(app, "vhost", cls)
         self.add_parameter(self.vhost)
 
         self.namef = NameField(app, "name")
@@ -552,15 +388,12 @@
             self.task.invoke(session, queue, count)
             self.task.exit_with_redirect(session, queue)
 
-class QueueSetTaskForm(CuminTaskForm):
+class QueueSetTaskForm(SelectionTaskForm):
     def __init__(self, app, name, task):
-        super(QueueSetTaskForm, self).__init__(app, name, task)
+        cls = app.rosemary.org_apache_qpid_broker.Queue
 
-        item = QueueParameter(app, "item")
+        super(QueueSetTaskForm, self).__init__(app, name, cls, task)
 
-        self.object = ListParameter(app, "queue", item)
-        self.add_parameter(self.object)
-
 class BindSummaryPropertiesField(FormField):
     def __init__(self, app, name, queue):
         super(BindSummaryPropertiesField, self).__init__(app, name)
@@ -848,3 +681,37 @@
 
             self.task.invoke(session, queue, dest_queue, count)
             self.task.exit_with_redirect(session, queue)
+
+class TopQueueSet(TopTable):
+    def __init__(self, app, name):
+        super(TopQueueSet, self).__init__(app, name)
+
+        col = self.NameColumn(app, "name")
+        col.width = "60%"
+        self.add_column(col)
+
+        self.set_default_column(col)
+
+        col = self.EnqueuesColumn(app, "enqueues")
+        col.width = "35%"
+        col.align = "right"
+        self.add_column(col)
+
+    class NameColumn(TopTableColumn):
+        def render_title(self, session):
+            return "Name"
+
+        def render_content(self, session, data):
+            broker = Identifiable(data["broker_id"])
+            queue = Identifiable(data["id"])
+
+            branch = session.branch()
+            self.page.main.messaging.broker.object.set(branch, broker)
+            self.page.main.messaging.broker.queue.object.set(branch, queue)
+            self.page.main.messaging.broker.queue.show(branch)
+            return fmt_link \
+                (branch.marshal(), data["name"], link_title=data["name"])
+
+    class EnqueuesColumn(TopTableColumn):
+        def render_title(self, session):
+            return "Recent Enqueues"

Added: mgmt/newdata/cumin/python/cumin/messaging/subscription.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/messaging/subscription.py	                        (rev 0)
+++ mgmt/newdata/cumin/python/cumin/messaging/subscription.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,25 @@
+from cumin.objectframe import *
+from cumin.objectselector import *
+from cumin.util import *
+
+class SubscriptionSelector(ObjectSelector):
+    def __init__(self, app, name, queue):
+        cls = app.rosemary.org_apache_qpid_broker.Subscription
+
+        super(SubscriptionSelector, self).__init__(app, name, cls)
+
+        self.queue = queue
+
+        frame = "main.messaging.broker.queue.subscription"
+        col = ObjectLinkColumn(app, "name", cls.name, cls._id, frame)
+        self.add_column(col)
+
+        self.add_attribute_column(cls.creditMode)
+        self.add_attribute_column(cls.arguments)
+        self.add_attribute_column(cls.delivered)
+
+class SubscriptionFrame(ObjectFrame):
+    def __init__(self, app, name):
+        cls = app.rosemary.org_apache_qpid_broker.Subscription
+
+        super(SubscriptionFrame, self).__init__(app, name, cls)

Modified: mgmt/newdata/cumin/python/cumin/model.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/model.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/model.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -124,9 +124,9 @@
     def get_session_by_object(self, object):
         assert object
 
-        agent = self.mint.model.agents[object.qmfAgentId]
+        agent = self.mint.model.agents[object._qmf_agent_id]
 
-        return agent.agent.getBroker().getAmqpSession()
+        return agent.qmf_agent.getBroker().getAmqpSession()
 
     def get_negotiator_limits(self, negotiator):
         assert negotiator
@@ -1607,11 +1607,6 @@
         stat.unit = "byte"
         stat.category = "io"
 
-    def init(self):
-        super(CuminLink, self).init()
-
-        self.frame = self.model.frame.messaging.broker.link
-
     def get_title(self, session):
         return "Broker Link"
 

Added: mgmt/newdata/cumin/python/cumin/objectform.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/objectform.py	                        (rev 0)
+++ mgmt/newdata/cumin/python/cumin/objectform.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,97 @@
+from wooly import *
+from wooly.forms import *
+from wooly.parameters import *
+
+from util import *
+
+class ObjectForm(SubmitForm, Frame):
+    def __init__(self, app, name, cls):
+        super(ObjectForm, self).__init__(app, name)
+
+        self.cls = cls
+
+        self.id = IntegerParameter(app, "id")
+        self.add_parameter(self.id)
+
+        self.object = Attribute(app, "object")
+
+    def init(self):
+        super(ObjectForm, self).init()
+
+        assert self.cls, self
+
+    def do_process(self, session):
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
+
+        try:
+            obj = self.cls.get_object(cursor, id)
+            self.object.set(session, obj)
+        finally:
+            cursor.close()
+
+        super(ObjectForm, self).do_process(session)
+
+class SelectionForm(SubmitForm, Frame):
+    def __init__(self, app, name, cls):
+        super(SelectionForm, self).__init__(app, name)
+
+        self.cls = cls
+
+        item = IntegerParameter(app, "item")
+
+        self.selection = ListParameter(app, "id", item)
+        self.add_parameter(self.selection)
+
+        self.objects = Attribute(app, "objects")
+
+    def init(self):
+        super(SelectionForm, self).init()
+
+        assert self.cls, self
+
+    def do_process(self, session):
+        objects = list()
+        
+        self.objects.set(session, objects)
+
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
+
+        try:
+            for id in self.selection.get(session):
+                obj = self.cls.get_object(cursor, id)
+                objects.append(obj)
+        finally:
+            cursor.close()
+
+        super(SelectionForm, self).do_process(session)
+
+class SelectionTaskForm(SelectionForm):
+    def __init__(self, app, name, cls, task):
+        super(SelectionTaskForm, self).__init__(app, name, cls)
+
+        self.cls = cls
+        self.task = task
+
+    def init(self):
+        super(SelectionForm, self).init()
+
+        assert self.task, self
+
+    def process_submit(self, session):
+        objects = self.objects.get(session)
+
+        self.task.invoke(session, objects)
+        self.task.exit_with_redirect(session, objects)
+
+    def render_submit_content(self, session):
+        return self.task.get_title(session)
+
+    def render_title(self, session):
+        return "Confirm"
+
+    def render_content(self, session):
+        objects = self.objects.get(session)
+
+        return "%s?" % self.task.get_description(session, objects)

Added: mgmt/newdata/cumin/python/cumin/objectframe.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/objectframe.py	                        (rev 0)
+++ mgmt/newdata/cumin/python/cumin/objectframe.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,276 @@
+from wooly import *
+from wooly.resources import *
+from wooly.widgets import *
+
+from formats import *
+from util import *
+from widgets import *
+
+log = logging.getLogger("cumin.objectframe")
+strings = StringCatalog(__file__)
+
+class ObjectFrame(Frame, ModeSet):
+    def __init__(self, app, name, cls):
+        super(ObjectFrame, self).__init__(app, name)
+
+        self.cls = cls
+        
+        self.id = IntegerParameter(app, "id")
+        self.add_parameter(self.id)
+
+        self.object = Attribute(app, "object")
+        self.add_attribute(self.object)
+
+        self.view = ObjectView(app, name, self.object)
+        self.add_child(self.view)
+
+        self.icon_href = "resource?name=action-36.png"
+
+        self.summary_attributes = list()
+        self.summary_tasks = list()
+
+        self.add_summary_attribute(cls._qmf_update_time)
+
+    def add_summary_attribute(self, attr):
+        assert attr not in self.summary_attributes, attr
+
+        self.summary_attributes.append(attr)
+
+    def add_summary_task(self, task):
+        assert task not in self.summary_tasks, task
+
+        self.summary_tasks.append(task)
+
+    def init(self):
+        super(ObjectFrame, self).init()
+
+        assert self.cls, self
+
+    def get_href(self, session, id):
+        branch = session.branch()
+
+        self.id.set(branch, id)
+        self.view.show(branch)
+
+        return branch.marshal()
+
+    def get_title(self, session):
+        obj = self.object.get(session)
+
+        return "%s '%s'" % (obj._class._title, obj.get_title())
+
+    def do_process(self, session):
+        # XXX don't process if this frame is invisible
+
+        id = self.id.get(session)
+
+        assert id
+
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
+
+        try:
+            obj = self.cls.get_object(cursor, id)
+        finally:
+            cursor.close()
+
+        self.object.set(session, obj)
+
+        super(ObjectFrame, self).do_process(session)
+
+class ObjectAttributes(Widget):
+    def __init__(self, app, name, object):
+        super(ObjectAttributes, self).__init__(app, name)
+
+        self.object = object
+
+        self.entry = ObjectAttributesEntry(app, "entry")
+        self.add_child(self.entry)
+
+    def get_attributes(self, session):
+        return ()
+
+    def render_attributes(self, session):
+        writer = Writer()
+
+        obj = self.object.get(session)
+
+        for attr in self.get_attributes(session):
+            name = attr.title
+            value = getattr(obj, attr.name, fmt_none())
+
+            writer.write(self.entry.render(session, name, value))
+    
+        return writer.to_string()
+
+class ObjectAttributesEntry(Widget):
+    def render_name(self, session, name, value):
+        return name
+
+    def render_value(self, session, name, value):
+        return value
+
+class ObjectTaskLinks(WidgetSet):
+    def __init__(self, app, name, object):
+        super(ObjectTaskLinks, self).__init__(app, name)
+
+        self.object = object
+
+class SummaryAttributes(ObjectAttributes):
+    pass
+
+class SummaryLinks(ObjectTaskLinks):
+    pass
+
+class ObjectView(Widget):
+    def __init__(self, app, name, object):
+        super(ObjectView, self).__init__(app, name)
+
+        self.object = object
+
+        self.context = ObjectViewContext(app, "context", self.object)
+        self.add_child(self.context)
+
+        self.heading = ObjectViewHeading(app, "heading", self.object)
+        self.add_child(self.heading)
+
+        self.summary = ObjectViewSummary(app, "summary", self.object)
+        self.add_child(self.summary)
+
+        self.body = TabbedModeSet(app, "body")
+        self.add_child(self.body)
+
+    def init(self):
+        self.add_tab(ObjectDetails(self.app, "details", self.object))
+
+        super(ObjectView, self).init()
+
+    def add_tab(self, widget):
+        self.body.add_tab(widget)
+
+    def render_title(self, session):
+        obj = self.object.get(session)
+        return obj.get_title()
+
+class ObjectViewChild(Widget):
+    def __init__(self, app, name, object):
+        super(ObjectViewChild, self).__init__(app, name)
+
+        self.object = object
+
+class ObjectViewContext(ObjectViewChild):
+    def __init__(self, app, name, object):
+        super(ObjectViewContext, self).__init__(app, name, object)
+
+        self.link = ObjectViewContextLink(app, "link")
+        self.add_child(self.link)
+
+    def render_links(self, session):
+        links = list()
+
+        for frame in self.parent.ancestors:
+            if not isinstance(frame, Frame):
+                break
+
+            links.append(self.link.render(session, frame))
+
+        return " &gt; ".join(reversed(links))
+
+class ObjectViewContextLink(Link):
+    def __init__(self, app, name):
+        super(ObjectViewContextLink, self).__init__(app, name)
+
+    def edit_session(self, session, frame):
+        frame.view.show(session)
+
+    def render_class(self, session, frame):
+        cls = super(ObjectViewContextLink, self).render_class(session)
+
+        if self.frame is frame:
+            cls = "%s selected" % cls
+
+        return cls
+
+    def render_content(self, session, frame):
+        return frame.get_title(session)
+
+class ObjectViewHeading(ObjectViewChild):
+    def __init__(self, app, name, object):
+        super(ObjectViewHeading, self).__init__(app, name, object)
+
+    def render_icon_href(self, session):
+        return self.frame.icon_href
+
+    def render_title(self, session):
+        return self.parent.render_title(session)
+
+class ObjectViewSummary(ObjectViewChild):
+    def __init__(self, app, name, object):
+        super(ObjectViewSummary, self).__init__(app, name, object)
+
+        attributes = self.Attributes(app, "attributes", self.object)
+        self.add_child(attributes)
+
+        links = self.Links(app, "links", self.object)
+        self.add_child(links)
+
+    class Attributes(SummaryAttributes):
+        def get_attributes(self, session):
+            return self.frame.summary_attributes
+
+    class Links(SummaryLinks):
+        def init(self):
+            super(ObjectViewSummary.Links, self).init()
+
+            for task in self.frame.summary_tasks:
+                name = task.__class__.__name__
+                link = TaskLink(self.app, name, task, self.object)
+
+                self.add_child(link)
+
+                link.init()
+
+class ObjectDetails(Widget):
+    def __init__(self, app, name, object):
+        super(ObjectDetails, self).__init__(app, name)
+
+        self.object = object
+
+        props = self.Headers(app, "headers", self.object)
+        props.update_enabled = True
+        self.add_child(props)
+
+        props = self.Properties(app, "props", self.object)
+        props.update_enabled = True
+        self.add_child(props)
+
+        stats = self.Statistics(app, "stats", self.object)
+        stats.update_enabled = True
+        self.add_child(stats)
+
+    def render_title(self, session):
+        return "Details"
+
+    class Headers(ObjectAttributes):
+        def get_attributes(self, session):
+            obj = self.object.get(session)
+            return obj._class._headers
+
+        def render_title(self, session):
+            return "QMF Headers"
+
+    class Properties(ObjectAttributes):
+        def get_attributes(self, session):
+            obj = self.object.get(session)
+            return obj._class._properties
+
+        def render_title(self, session):
+            return "Properties"
+
+    class Statistics(ObjectAttributes):
+        def get_attributes(self, session):
+            obj = self.object.get(session)
+            return obj._class._statistics
+
+        def render_title(self, session):
+            return "Statistics"

Added: mgmt/newdata/cumin/python/cumin/objectframe.strings
===================================================================
--- mgmt/newdata/cumin/python/cumin/objectframe.strings	                        (rev 0)
+++ mgmt/newdata/cumin/python/cumin/objectframe.strings	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,151 @@
+[ObjectAttributes.css]
+div.ObjectAttributes h2 {
+    margin: 0 0 1em 0;
+    font-size: 0.9em;
+}
+
+div.ObjectAttributes table {
+    width: 100%;
+    margin: 0 0 1em 0;
+    border-collapse: collapse;
+    font-size: 0.9em;
+}
+
+div.ObjectAttributes th, div.ObjectAttributes td {
+    padding: 0.35em 0.5em 0.35em 0;
+    text-align: left;
+    vertical-align: top;
+}
+
+div.ObjectAttributes thead th {
+    font-weight: normal;
+    font-style: italic;
+    font-size: 0.9em;
+    color: #666;
+}
+
+div.ObjectAttributes tbody tr {
+    border-top: 1px dotted #ccc;
+}
+
+div.ObjectAttributes tbody th {
+    width: 40%;
+    font-weight: normal;
+}
+
+div.ObjectAttributes tbody td {
+    width: 60%;
+}
+
+[ObjectAttributes.html]
+<div id="{id}" class="{class}">
+  <h2>{title}</h2>
+
+  <table>
+    <thead>
+      <tr>
+        <th>Name</th>
+        <th>Value</th>
+      </tr>
+    </thead>
+    <tbody>{attributes}</tbody>
+  </table>
+</div>
+
+[ObjectAttributesEntry.html]
+<tr><th>{name}</th><td>{value}</td></tr>
+
+[SummaryAttributes.css]
+div.SummaryAttributes {
+    width: 20em;
+}
+
+div.SummaryAttributes tbody tr {
+    border-top: none;
+}
+
+div.SummaryAttributes tbody th, div.SummaryAttributes tbody td {
+    padding: 0.125em 0;
+}
+
+div.SummaryAttributes tbody th {
+    color: #666;
+}
+
+[SummaryAttributes.html]
+<div id="{id}" class="{class}">
+  <table>
+    <tbody>{attributes}</tbody>
+  </table>
+</div>
+
+[SummaryLinks.css]
+ul.SummaryLinks {
+    width: 15em;
+    float: right;
+    list-style: none;
+    margin: 0;
+}
+
+[ObjectView.css]
+div.ObjectViewChild {
+    margin: 0 0 0.5em 0;
+}
+
+div.ObjectViewContext {
+    color: #666;
+    font-size: 0.9em;
+}
+
+div.ObjectViewContext a.selected {
+    color: #666;
+}
+
+div.ObjectViewHeading h1 {
+    margin: 0;
+    display: inline;
+}
+
+div.ObjectViewHeading img {
+    vertical-align: -70%;
+    margin: 0 0.25em 0 0;
+}
+
+div.ObjectViewSummary {
+    border-top: 1px dotted #ccc;
+    padding: 0.5em 0 0 0;
+    width: 40em;    
+    font-size: 0.9em;
+}
+
+[ObjectView.html]
+{context}
+
+{heading}
+
+{summary}
+
+{body}
+
+[ObjectViewHeading.html]
+<div class="{class}">
+  <img src="{icon_href}" alt="icon"/>
+
+  <h1>{title}</h1>
+</div>
+
+[ObjectViewSummary.html]
+<div class="{class}">
+  {links}
+  {attributes}
+</div>
+
+[ObjectViewContext.html]
+<div class="{class}">{links}</div>
+
+[ObjectDetails.html]
+{headers}
+
+{props}
+
+{stats}

Added: mgmt/newdata/cumin/python/cumin/objectselector.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/objectselector.py	                        (rev 0)
+++ mgmt/newdata/cumin/python/cumin/objectselector.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,184 @@
+from wooly import *
+from wooly.datatable import *
+from wooly.forms import *
+from wooly.widgets import *
+
+from objectframe import *
+from sqladapter import *
+from util import *
+
+strings = StringCatalog(__file__)
+
+# XXX ObjectTable
+
+class ObjectSelector(DataTable, Form):
+    def __init__(self, app, name, cls, adapter=None):
+        assert isinstance(cls, RosemaryClass), cls
+
+        if not adapter:
+            adapter = ObjectSqlAdapter(app, cls)
+
+        assert isinstance(adapter, ObjectSqlAdapter), adapter
+
+        super(ObjectSelector, self).__init__(app, name, adapter)
+
+        self.cls = cls
+
+        item = IntegerParameter(app, "item")
+
+        self.selection = ListParameter(app, "selection", item)
+        self.add_parameter(self.selection)
+
+        self.checkbox_column = ObjectCheckboxColumn \
+            (app, "id", cls._id, self.selection)
+        self.add_column(self.checkbox_column)
+
+        self.links = ObjectSelectorLinks(app, "links")
+        self.add_child(self.links)
+
+        self.switches = ObjectSelectorSwitches(app, "switches")
+        self.add_child(self.switches)
+
+        self.filters = ObjectSelectorFilters(app, "filters")
+        self.add_child(self.filters)
+
+        self.buttons = ObjectSelectorButtons(app, "buttons")
+        self.add_child(self.buttons)
+
+        # (RosemaryAttribute this, RosemaryAttribute that, Attribute object)
+        self.filter_specs = list()
+
+    def init(self):
+        assert self.cls, self
+        assert self.adapter, self
+        assert self.adapter.id_field, self
+
+        super(ObjectSelector, self).init()
+
+    def add_attribute_column(self, attr):
+        assert isinstance(attr, RosemaryAttribute), attr
+
+        col = ObjectAttributeColumn(self.app, attr.name, attr)
+        self.add_column(col)
+
+    def add_task(self, task, parameter):
+        link = TaskLink(self.app, task.__class__.__name__, task, parameter)
+
+        self.links.add_child(link)
+
+        return link
+
+    def add_selection_task(self, task):
+        name = task.__class__.__name__
+        button = TaskButton(self.app, name, task, self.selection)
+
+        self.buttons.add_child(button)
+
+        return button
+
+    def add_filter(self, object, this, that=None):
+        if not that:
+            that = this
+
+        assert isinstance(object, Attribute), object
+        assert isinstance(this, RosemaryAttribute), this
+        assert isinstance(that, RosemaryAttribute), that
+
+        self.filter_specs.append((this, that, object))
+        self.adapter.add_value_filter(this)
+
+    def add_reference_filter(self, object, ref):
+        assert isinstance(ref, RosemaryReference), ref
+
+        this = ref
+        that = ref.that_cls._id
+
+        self.add_filter(object, this, that)
+
+    def get_data_values(self, session):
+        values = dict()
+
+        for this, that, object in self.filter_specs:
+            obj = object.get(session)
+
+            values[this.name] = getattr(obj, that.name)
+
+        return values
+
+    def render_title(self, session):
+        return "%ss" % self.cls._title
+
+class ObjectAttributeColumn(DataTableColumn):
+    def __init__(self, app, name, attr):
+        super(ObjectAttributeColumn, self).__init__(app, name, None)
+
+        self.attr = attr
+
+    def init(self):
+        super(ObjectAttributeColumn, self).init()
+
+        try:
+            self.field = self.table.adapter.fields_by_attr[self.attr]
+        except KeyError:
+            self.field = ObjectSqlField(self.table.adapter, self.attr)
+
+class ObjectCheckboxColumn(ObjectAttributeColumn):
+    def __init__(self, app, name, attr, selection):
+        super(ObjectCheckboxColumn, self).__init__(app, name, attr)
+
+        self.selection = selection
+
+        self.header = CheckboxColumnHeader(app, "header")
+        self.replace_child(self.header)
+
+        self.cell = CheckboxColumnCell(app, "cell", selection)
+        self.replace_child(self.cell)
+
+        self.width = "1%"
+
+    def render_cell_value(self, session, record):
+        return record[self.field.index]
+
+class ObjectLinkColumn(ObjectAttributeColumn, LinkColumn):
+    def __init__(self, app, name, attr, id_attr, frame_path):
+        super(ObjectLinkColumn, self).__init__(app, name, attr)
+
+        self.id_attr = id_attr
+        self.frame_path = frame_path
+
+    def init(self):
+        super(ObjectLinkColumn, self).init()
+ 
+        try:
+            self.id_field = self.table.adapter.fields_by_attr[self.id_attr]
+        except KeyError:
+            self.id_field = ObjectSqlField(self.table.adapter, self.id_attr)
+
+    def render_cell_href(self, session, record):
+        id = record[self.id_field.index]
+        frame = self.page.page_widgets_by_path[self.frame_path]
+
+        if isinstance(frame, ObjectFrame):
+            return frame.get_href(session, id)
+        else:
+            return frame.get_href(session, Identifiable(id)) # XXX
+
+    def render_cell_content(self, session, record):
+        return record[self.field.index]
+
+class ObjectSelectorControl(WidgetSet):
+    def do_render(self, session):
+        if len(self.children):
+            return super(ObjectSelectorControl, self).do_render(session)
+
+class ObjectSelectorLinks(ObjectSelectorControl):
+    pass
+
+class ObjectSelectorSwitches(ObjectSelectorControl):
+    pass
+
+class ObjectSelectorFilters(ObjectSelectorControl):
+    pass
+
+class ObjectSelectorButtons(ObjectSelectorControl):
+    pass

Added: mgmt/newdata/cumin/python/cumin/objectselector.strings
===================================================================
--- mgmt/newdata/cumin/python/cumin/objectselector.strings	                        (rev 0)
+++ mgmt/newdata/cumin/python/cumin/objectselector.strings	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,75 @@
+[ObjectSelector.css]
+//div.ObjectSelector form {
+//    clear: both;
+//}
+
+div.ObjectSelectorControl ul {
+    list-style: none;
+    display: inline;
+    padding: 0;
+}
+
+div.ObjectSelectorLinks {
+    margin: 0 0 1em 0;
+    font-size: 0.9em;
+}
+
+div.ObjectSelectorFilters {
+    float: right;
+}
+
+div.ObjectSelectorButtons {
+    background-color: #e7e7f7;
+    padding: 0.35em 0.75em;
+}
+
+div.ObjectSelectorButtons span {
+    font-size: 0.9em;
+    margin: 0 0.5em 0 0;
+}
+
+div.ObjectSelectorButtons ul {
+    padding: 0;
+    margin: 0;
+}
+
+div.ObjectSelectorButtons ul li {
+    margin: 0 0.4em 0 0;
+    display: inline;
+}
+
+[ObjectSelector.html]
+<div id="{id}" class="{class}">
+  {links}
+
+  {filters}
+
+  {switches}
+
+  <form method="post" action="?">
+    {buttons}
+
+    <table id="{id}" class="{class}">
+      <style type="text/css">
+        {css}
+      </style>
+      {header}
+      {footer}
+      <tbody>{rows}</tbody>
+    </table>
+
+    <div>{hidden_inputs}</div>
+  </form>
+</div>
+
+[ObjectSelectorControl.html]
+<div class="{class}">
+  <ul>{widgets}</ul>
+</div>
+
+[ObjectSelectorButtons.html]
+<div class="{class}">
+  <span>Act on selection:</span>
+
+  <ul>{widgets}</ul>
+</div>

Added: mgmt/newdata/cumin/python/cumin/objecttask.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/objecttask.py	                        (rev 0)
+++ mgmt/newdata/cumin/python/cumin/objecttask.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,5 @@
+from util import *
+
+class ObjectTaskLink(object):
+    def __init__(self, app, cls):
+        self.cls = cls

Modified: mgmt/newdata/cumin/python/cumin/parameters.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/parameters.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/parameters.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -37,6 +37,30 @@
     def do_marshal(self, cls):
         return cls.cumin_name
 
+class RosemaryObjectParameter(Parameter):
+    def __init__(self, app, name, cls):
+        super(RosemaryObjectParameter, self).__init__(app, name)
+
+        self.cls = cls
+
+    def do_unmarshal(self, string):
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
+
+        try:
+            return self.cls.get_object(cursor, int(string))
+        finally:
+            cursor.close()
+
+    def do_marshal(self, obj):
+        return str(obj._id)
+
+class VhostParameter(RosemaryObjectParameter):
+    def __init__(self, app, name):
+        cls = app.rosemary.org_apache_qpid_broker.Vhost
+
+        super(VhostParameter, self).__init__(app, name, cls)
+
 class BindingParameter(Parameter):
     def do_unmarshal(self, string):
         return Binding.get(int(string))
@@ -59,6 +83,19 @@
     def do_marshal(self, group):
         return str(group.id)
 
+class NewBrokerGroupParameter(Parameter):
+    def do_unmarshal(self, string):
+        id = int(string)
+        cls = self.app.rosemary.com_redhat_cumin.BrokerGroup
+
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
+
+        try:
+            return cls.get_object(cursor, id)
+        finally:
+            cursor.close()
+
 class BrokerParameter(Parameter):
     def do_unmarshal(self, string):
         return Broker.get(int(string))
@@ -68,9 +105,19 @@
 
 class BrokerVhostAttribute(ObjectAssociateAttribute):
     def get_associate(self, session, broker):
-        for vhost in Vhost.selectBy(broker=broker, name="/"):
-            return vhost
+        cls = self.app.rosemary.org_apache_qpid_broker.Vhost
 
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
+
+        try:
+            kwargs = {"_brokerRef_id": broker._id, "name": "'/'"}
+
+            for obj in cls.select_objects(cursor, **kwargs):
+                return obj
+        finally:
+            cursor.close()
+
 class ConnectionParameter(Parameter):
     def do_unmarshal(self, string):
         return ClientConnection.get(int(string))
@@ -188,13 +235,6 @@
     def do_marshal(self, sub):
         return str(sub.id)
 
-class VhostParameter(Parameter):
-    def do_unmarshal(self, string):
-        return Vhost.get(int(string))
-
-    def do_marshal(self, vhost):
-        return str(vhost.id)
-
 class CollectorParameter(Parameter):
     def do_unmarshal(self, string):
         return Collector.get(int(string))

Added: mgmt/newdata/cumin/python/cumin/sqladapter.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/sqladapter.py	                        (rev 0)
+++ mgmt/newdata/cumin/python/cumin/sqladapter.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,147 @@
+from rosemary.sqlquery import *
+from wooly.datatable import *
+
+from util import *
+
+class SqlAdapter(DataAdapter):
+    def __init__(self, app, table):
+        super(SqlAdapter, self).__init__()
+
+        self.app = app
+        self.table = table
+        
+        self.query = SqlQuery(self.table)
+        self.columns = list()
+
+    def init(self):
+        for field in self.fields:
+            field.init()
+
+    def get_count(self, values):
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
+
+        try:
+            self.query.execute(cursor, ("count(1)",), values)
+            
+            return cursor.fetchone()[0]
+        finally:
+            cursor.close()
+
+    def get_sql_options(self, options):
+        sql_options = SqlQueryOptions()
+
+        if options.sort_field:
+            sql_options.sort_column = options.sort_field.column
+
+        sql_options.sort_ascending = options.sort_ascending
+        sql_options.limit = options.limit
+        sql_options.offset = options.offset
+
+        return sql_options
+
+    def get_data(self, values, options):
+        sql_options = self.get_sql_options(options)
+
+        conn = self.app.model.get_sql_connection()
+        cursor = conn.cursor()
+
+        try:
+            self.query.execute(cursor, self.columns, values, sql_options)
+
+            return cursor.fetchall()
+        finally:
+            cursor.close()
+
+class SqlField(DataAdapterField):
+    def __init__(self, adapter, column):
+        python_type = column.type.python_type
+
+        super(SqlField, self).__init__(adapter, column.name, python_type)
+
+        self.column = column
+
+        self.adapter.columns.append(column)
+
+class ObjectSqlAdapter(SqlAdapter):
+    def __init__(self, app, cls):
+        super(ObjectSqlAdapter, self).__init__(app, cls.sql_table)
+
+        self.cls = cls
+
+        self.fields_by_attr = dict()
+
+        # XXX eliminate this?
+        self.id_field = ObjectSqlField(self, self.cls._id)
+
+    def add_join(self, cls, this, that):
+        assert cls
+        assert this
+        assert that
+
+        SqlInnerJoin(self.query, cls.sql_table,
+                     this.sql_column, that.sql_column)
+
+    def add_outer_join(self, cls, this, that):
+        assert cls
+        assert this
+        assert that
+
+        SqlOuterJoin(self.query, cls.sql_table,
+                     this.sql_column, that.sql_column)
+
+    def add_value_filter(self, attr):
+        assert attr
+        value = "%%(%s)s" % attr.name
+
+        SqlComparisonFilter(self.query, attr.sql_column, value)
+
+class ObjectSqlField(SqlField):
+    def __init__(self, adapter, attr):
+        assert isinstance(adapter, ObjectSqlAdapter), adapter
+
+        super(ObjectSqlField, self).__init__(adapter, attr.sql_column)
+
+        self.attr = attr
+
+        self.adapter.fields_by_attr[self.attr] = self
+
+    def get_title(self, session):
+        return self.attr.title or self.attr.name
+
+class HeartbeatField(SqlField):
+    def get_title(self, session):
+        return "Status"
+
+    def get_content(self, session, record):
+        id = record[self.index]
+
+        try:
+            last = self.adapter.app.model.mint.model.agents[id].last_heartbeat
+        except KeyError:
+            last = None
+
+        if last is None:
+            return "Unknown"
+
+        if last < datetime.now() - timedelta(seconds=10):
+            return "Stale"
+
+        return "Fresh"
+
+class TestData(ObjectSqlAdapter):
+    def __init__(self, app):
+        broker = app.rosemary.org_apache_qpid_broker.Broker
+        system = app.rosemary.org_apache_qpid_broker.System
+        cluster = app.rosemary.org_apache_qpid_cluster.Cluster
+
+        super(TestData, self).__init__(app, broker)
+
+        self.add_join(system, broker.systemRef, system.id)
+        self.add_outer_join(cluster, broker.id, cluster.brokerRef)
+
+        col = self.table._qmf_agent_id
+        field = HeartbeatField(self, col)
+
+        for prop in broker._properties + system._properties:
+            field = ObjectSqlField(self, prop)

Added: mgmt/newdata/cumin/python/cumin/table.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/table.py	                        (rev 0)
+++ mgmt/newdata/cumin/python/cumin/table.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,109 @@
+from wooly import *
+from wooly.datatable import *
+from wooly.forms import *
+from wooly.widgets import *
+
+from util import *
+from widgets import *
+
+strings = StringCatalog(__file__)
+
+# XXX delete this
+
+class SelectionTable(Form):
+    def __init__(self, app, name, data):
+        super(SelectionTable, self).__init__(app, name)
+
+        self.table = self.SelectionDataTable(app, "table", data)
+        self.table.update_enabled = True
+        self.add_child(self.table)
+
+        item = IntegerParameter(app, "item")
+
+        self.selection = ListParameter(app, "selection", item)
+        self.add_parameter(self.selection)
+
+        self.links = SelectionTableLinks(app, "links")
+        self.add_child(self.links)
+
+        self.switches = SelectionTableSwitches(app, "switches")
+        self.add_child(self.switches)
+
+        self.filters = SelectionTableFilters(app, "filters")
+        self.add_child(self.filters)
+
+        self.buttons = SelectionTableButtons(app, "buttons")
+        self.add_child(self.buttons)
+
+        col = self.Checkbox(app, "checkbox", self.table, self.selection)
+        self.table.add_column(col)
+
+        col = self.Name(app, "name", self.table)
+        self.table.add_column(col)
+
+        self.frame_path = None
+
+    def init(self):
+        super(SelectionTable, self).init()
+
+        assert self.frame_path, self
+
+    # XXX this isn't what we do now
+    def do_get_data(self, session, sort, ascending, limit, offset):
+        return self.table.adapter.get_data(sort, ascending, limit, offset)
+
+    def add_task(self, task):
+        link = TaskLink(self.app, task.__class__.__name__, task, None)
+        self.links.add_child(link)
+
+    def add_selection_task(self, task):
+        name = task.__class__.__name__
+        button = TaskButton(self.app, name, task, self.selection)
+        self.buttons.add_child(button)
+
+    class Checkbox(NewCheckboxColumn):
+        def render_cell_value(self, session, record):
+            return record[self.table.adapter.id_field.index]
+
+    class Name(LinkColumn):
+        def render_header_content(self, session):
+            return "Name"
+
+        def render_cell_href(self, session, record):
+            branch = session.branch()
+
+            path = self.table.parent.frame_path
+            id = record[self.table.adapter.id_field.index]
+
+            frame = self.page.page_widgets_by_path[path]
+
+            return frame.get_href(session, id)
+
+        def render_cell_content(self, session, record):
+            return record[self.table.adapter.name_field.index]
+
+    class SelectionDataTable(DataTable):
+        def do_get_data(self, session, sort, ascending, limit, offset):
+            return self.parent.do_get_data \
+                (session, sort, ascending, limit, offset)
+
+class SelectionTableControl(WidgetSet):
+    def do_render(self, session):
+        if len(self.children):
+            return super(SelectionTableControl, self).do_render(session)
+
+class SelectionTableLinks(SelectionTableControl):
+    def render_title(self, session):
+        return "Links"
+
+class SelectionTableSwitches(SelectionTableControl):
+    def render_title(self, session):
+        return "Switches"
+
+class SelectionTableFilters(SelectionTableControl):
+    def render_title(self, session):
+        return "Filters"
+
+class SelectionTableButtons(SelectionTableControl):
+    def render_title(self, session):
+        return "Act on selection:"

Added: mgmt/newdata/cumin/python/cumin/table.strings
===================================================================
--- mgmt/newdata/cumin/python/cumin/table.strings	                        (rev 0)
+++ mgmt/newdata/cumin/python/cumin/table.strings	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,73 @@
+[SelectionTable.css]
+div.SelectionTable div.switches ul,
+div.SelectionTable div.filters ul,
+div.SelectionTable div.buttons ul {
+    list-style: none;
+    display: inline;
+    padding: 0;
+    margin: 0;
+}
+
+div.SelectionTable div.filters {
+    float: right;
+}
+
+div.SelectionTable div.buttons ul li {
+    margin: 0 0.4em 0 0;
+    display: inline;
+}
+
+div.SelectionTable form {
+    clear: both;
+}
+
+[SelectionTable.html]
+<div id="{id}" class="{class}">
+  {links}
+
+  {filters}
+
+  {switches}
+
+  <form method="post" action="?">
+    {buttons}
+
+    {table}
+
+    <div>{hidden_inputs}</div>
+  </form>
+</div>
+
+[SelectionTableControl.css]
+div.SelectionTableControl {
+    padding: 0.35em 0.75em;
+}
+
+div.SelectionTableControl ul {
+    list-style: none;
+    display: inline;
+    padding: 0;
+    margin: 0;
+}
+
+[SelectionTableControl.html]
+<div class="{class}">
+  <span>{title}</span>
+
+  <ul>{widgets}</ul>
+</div>
+
+[SelectionTableButtons.css]
+div.SelectionTableButtons {
+    background-color: #e7e7f7;
+}
+
+div.SelectionTableButtons span {
+    font-size: 0.9em;
+    margin: 0 0.5em 0 0;
+}
+
+div.SelectionTableButtons ul li {
+    margin: 0 0.4em 0 0;
+    display: inline;
+}

Modified: mgmt/newdata/cumin/python/cumin/tools.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/tools.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/tools.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -146,8 +146,13 @@
         app.start()
 
         try:
+            server.start()
+
             try:
-                server.start() # XXX fix the weird api here
+                while True:
+                    print "Sleeping!"
+
+                    sleep(1)
             finally:
                 server.stop()
         finally:

Modified: mgmt/newdata/cumin/python/cumin/usergrid/widgets.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/usergrid/widgets.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/usergrid/widgets.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -29,6 +29,9 @@
         self.set_default_frame(self.main)
 
     def render_title(self, session):
+        return self.get_title(session)
+
+    def get_title(self, session):
         return "User Grid"
 
 class MainView(CuminMainView):

Modified: mgmt/newdata/cumin/python/cumin/widgets.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/widgets.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/python/cumin/widgets.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -1,10 +1,10 @@
 from datetime import datetime, timedelta
 from wooly import *
 from wooly.pages import *
+from wooly.datatable import *
 from wooly.widgets import *
 from wooly.forms import *
 from wooly.sql import *
-from wooly.tables import *
 from mint.schema import *
 
 from action import *
@@ -19,6 +19,7 @@
 
 from wooly import Session
 from wooly.widgets import Link
+from wooly.tables import SqlTable
 
 strings = StringCatalog(__file__)
 
@@ -73,17 +74,21 @@
         return lsess.marshal()
 
     def render_tab_href(self, session, tab):
-        if tab.sticky_view:
-            try:
-                obj = session.client_session.attributes["sticky_%s" % tab.name]
-                return tab.sticky_view.frame.get_href(session, obj)
-            except KeyError:
-                pass
+        if hasattr(tab, "sticky_view"):
+            if tab.sticky_view:
+                try:
+                    name = "sticky_%s" % tab.name
+                    obj = session.client_session.attributes[name]
+                    return tab.sticky_view.frame.get_href(session, obj)
+                except KeyError:
+                    pass
 
-        branch = session.branch()
-        tab.mode.set(branch, None)
-        tab.show(branch)
-        return branch.marshal()
+            branch = session.branch()
+            tab.mode.set(branch, None)
+            tab.show(branch)
+            return branch.marshal()
+        else:
+            return super(CuminMainView, self).render_tab_href(session, tab)
 
 class CuminPageLinks(ItemSet):
     def __init__(self, app, name):
@@ -135,13 +140,18 @@
     def __init__(self, app, name):
         super(CuminFrame, self).__init__(app, name)
 
-        self.sticky_view = None
         self.object = None
+
         self.__view = None
         self.__add = None
         self.__edit = None
         self.__remove = None
 
+        self.sticky_view = None
+
+    def init(self):
+        super(CuminFrame, self).init()
+
     def show_object(self, session, object):
         if self.object:
             self.object.set(session, object)
@@ -151,12 +161,6 @@
 
         return self
 
-    def get_args(self, session):
-        if self.object:
-            return (self.get_object(session),)
-        else:
-            return ()
-
     def get_object(self, session):
         if self.object:
             return self.object.get(session)
@@ -188,6 +192,10 @@
         if cls:
             return cls.get_object_title(session, obj)
 
+    # XXX
+    def get_title(self, session):
+        return self.render_title(session)
+
 class CuminView(Widget):
     def __init__(self, app, name, object):
         super(CuminView, self).__init__(app, name)
@@ -492,6 +500,8 @@
 
         if cls:
             return xml_escape(cls.get_object_name(obj))
+        elif obj:
+            return obj.get_title()
 
     def render_icon_href(self, session):
         obj = self.object.get(session)

Modified: mgmt/newdata/cumin/resources/app.css
===================================================================
--- mgmt/trunk/cumin/resources/app.css	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/cumin/resources/app.css	2010-03-22 14:58:08 UTC (rev 3871)
@@ -91,7 +91,7 @@
     border-left: 1px solid #ddd;
     border-bottom: 1px solid #bbb;
     border-right: 1px solid #bbb;
-    padding: 0.25em 0.5em 0.30em 0.5em;
+    padding: 0.2em 0.4em;
     background: url(resource?name=button-background.png) repeat;
     color: #000;
     font-size: 0.9em;
@@ -586,6 +586,35 @@
     position: absolute;
     z-index: 1000;
 }
+
+table.DataTable.Cumin tr {
+    border-top: 1px dotted #ccc;
+}
+
+table.DataTable.Cumin td, table.DataTable.Cumin th {
+    padding: 0.35em 0.5em;
+}
+
+table.DataTable.Cumin th {
+    font-weight: normal;
+}
+
+table.DataTable.Cumin thead, table.DataTable.Cumin tfoot {
+    background-color: #f7f7f7;
+}
+
+table.DataTable.Cumin tfoot {
+    text-align: center;
+    font-style: italic;
+    font-size: 0.9em;
+}
+
+a.TaskLink.Cumin:before {
+    content: "\00BB \0020";
+    font-weight: bold;
+    color: #dc9f2e;
+}
+
 .autocomplete{
     cursor: pointer;
     border: 1px solid #999;
@@ -597,4 +626,3 @@
 .autocomplete .normal{border-top: 1px solid #fefefe;}
 .autocomplete .selected{background: #ddf;}
 .autocomplete .highlited{font-weight: bold; color: #008;}
-

Modified: mgmt/newdata/mint/python/mint/database.py
===================================================================
--- mgmt/trunk/mint/python/mint/database.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/mint/python/mint/database.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -83,6 +83,7 @@
         paths.append(os.path.join(self.app.config.home, "sql", "schema.sql"))
         paths.append(os.path.join(self.app.config.home, "sql", "indexes.sql"))
         paths.append(os.path.join(self.app.config.home, "sql", "triggers.sql"))
+        paths.append(os.path.join(self.app.config.home, "sql", "rosemary.sql"))
 
         scripts = list()
 

Modified: mgmt/newdata/mint/python/mint/demo.py
===================================================================
--- mgmt/trunk/mint/python/mint/demo.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/mint/python/mint/demo.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -1,11 +1,33 @@
 from mint import *
 
 class DemoData(object):
+    def __init__(self, app):
+        self.app = app
+
     def load(self):
-        for name in ("Engineering", "Marketing", "Sales"):
-            group = BrokerGroup(name=name)
-            group.syncUpdate()
+        cls = self.app.model.rosemary.com_redhat_cumin.BrokerGroup
 
+        conn = self.app.database.get_connection()
+        cursor = conn.cursor()
+
+        try:
+            for name in ("Engineering", "Marketing", "Sales"):
+                group = cls.create_object()
+                group.name = name
+
+                # XXX ugh
+                group._qmf_agent_id = "cumin"
+                group._qmf_object_id = str(datetime.now())
+                group._qmf_create_time = datetime.now()
+                group._qmf_update_time = datetime.now()
+                group._qmf_class_key = "cumin"
+
+                group.save(cursor)
+        finally:
+            cursor.close()
+            conn.commit()
+            conn.close()
+
 def main():
     config = MintConfig()
     config.init()
@@ -14,7 +36,7 @@
     app.check()
     app.init()
 
-    data = DemoData()
+    data = DemoData(app)
     data.load()
 
 if __name__ == "__main__":

Modified: mgmt/newdata/mint/python/mint/expire.py
===================================================================
--- mgmt/trunk/mint/python/mint/expire.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/mint/python/mint/expire.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -1,3 +1,4 @@
+from newupdate import *
 from schema import *
 from sql import *
 from update import *
@@ -34,7 +35,6 @@
 
         log.debug("Expiring database records older than %d %s, every %d %s" \
                     % args)
-               
 
     def run(self):
         frequency = self.app.expire_frequency
@@ -46,6 +46,9 @@
             up = ExpireUpdate()
             self.app.update_thread.enqueue(up)
 
+            up = NewExpireUpdate(self.app.model)
+            self.app.new_update_thread.enqueue(up)
+
             sleep(frequency)
 
     def __convertTimeUnits(self, t):
@@ -84,3 +87,31 @@
         log.debug("%i total records expired", total)
 
         stats.expired += 1
+
+class NewExpireUpdate(NewUpdate):
+    def do_process(self, conn, stats):
+        seconds = self.model.app.expire_threshold
+
+        log.info("Expiring stats older than %i seconds", seconds)
+
+        count = 0
+
+        for pkg in self.model.rosemary._packages:
+            for cls in pkg._classes:
+                count += self.delete_stats(conn, cls, seconds)
+
+                conn.commit()
+
+        log.info("Expired %i stat records", count)
+
+    def delete_stats(self, conn, cls, seconds):
+        cursor = conn.cursor()
+
+        try:
+            cls.sql_stats_delete.execute(cursor, (), {"seconds": seconds})
+
+            log.debug("Deleted %i %s", cursor.rowcount, cls)
+
+            return cursor.rowcount
+        finally:
+            cursor.close()

Modified: mgmt/newdata/mint/python/mint/main.py
===================================================================
--- mgmt/trunk/mint/python/mint/main.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/mint/python/mint/main.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -1,17 +1,12 @@
-import sys
-import os
-import logging
-
-from parsley.config import Config, ConfigParameter
-from parsley.loggingex import enable_logging
-from parsley.threadingex import Lifecycle
-
 from database import MintDatabase
 from expire import ExpireThread
 from model import MintModel
+from newupdate import NewUpdateThread
 from update import UpdateThread
 from vacuum import VacuumThread
 
+from util import *
+
 log = logging.getLogger("mint.main")
 
 class Mint(Lifecycle):
@@ -24,6 +19,7 @@
 
         self.update_enabled = True
         self.update_thread = UpdateThread(self)
+        self.new_update_thread = NewUpdateThread(self)
 
         self.expire_enabled = True
         self.expire_frequency = self.config.expire_frequency
@@ -48,6 +44,7 @@
         log.info("Expiration is %s", state(self.expire_enabled))
 
         self.update_thread.init()
+        self.new_update_thread.init()
         self.expire_thread.init()
         self.vacuum_thread.init()
 
@@ -55,7 +52,8 @@
         self.model.start()
 
         if self.update_enabled:
-            self.update_thread.start()
+            # XXX self.update_thread.start()
+            self.new_update_thread.start()
 
         if self.expire_enabled:
             self.expire_thread.start()
@@ -68,6 +66,7 @@
 
         if self.update_enabled:
             self.update_thread.stop()
+            self.new_update_thread.stop()
 
         if self.expire_enabled:
             self.expire_thread.stop()

Modified: mgmt/newdata/mint/python/mint/model.py
===================================================================
--- mgmt/trunk/mint/python/mint/model.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/mint/python/mint/model.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -1,12 +1,12 @@
-from parsley.collectionsex import defaultdict
-from parsley.threadingex import Lifecycle
+from rosemary.model import *
 from sqlobject import *
 
-from mint.cache import MintCache
-from mint.schema import *
-from mint.schemalocal import *
-from mint.update import *
-from mint.util import *
+from cache import MintCache
+from newupdate import *
+from schema import *
+from schemalocal import *
+from update import *
+from util import *
 
 import mint.schema
 
@@ -17,12 +17,12 @@
 class MintModel(Lifecycle):
     def __init__(self, app):
         self.log = log
+        self.app = app
 
         assert mint.schema.model is None
         mint.schema.model = self
 
-        self.app = app
-
+        self.rosemary = RosemaryModel()
         self.qmf_session = None
         self.qmf_brokers = list()
 
@@ -44,6 +44,9 @@
                                    manageConnections=True,
                                    rcvObjects=self.app.update_enabled)
 
+        self.rosemary.load_xml_dir(os.path.join(self.app.config.home, "xml"))
+        self.rosemary.init()
+
     def do_start(self):
         # Clean up any transient objects that a previous instance may
         # have left behind in the DB; it's basically an unconstrained
@@ -72,18 +75,21 @@
             self.lock.release()
 
 class MintAgent(object):
-    def __init__(self, model, agent):
+    def __init__(self, model, qmf_agent):
         self.model = model
-        self.agent = agent
+        self.qmf_agent = qmf_agent
 
-        self.id = str(QmfAgentId.fromAgent(agent))
+        self.id = str(QmfAgentId.fromAgent(self.qmf_agent))
 
         self.last_heartbeat = None
 
         # qmfObjectId => int database id
         self.database_ids = MintCache()
 
+        self.objects_by_id = dict()
+
         # qmfObjectId => list of ModelUpdate objects
+        # XXX we're no longer using this; remove it
         self.deferred_updates = defaultdict(list)
 
         self.model.lock.acquire()
@@ -93,16 +99,18 @@
         finally:
             self.model.lock.release()
 
-    def call_method(self, object, name, callback, args):
-        classKey = ClassKey(object.qmfClassKey)
-        objectId = QmfObjectId.fromString(object.qmfObjectId).toObjectId()
+    def call_method(self, obj, name, callback, args):
+        assert isinstance(obj, RosemaryObject)
+        
+        class_key = ClassKey(obj._qmf_class_key)
+        oid = QmfObjectId.fromString(obj._qmf_object_id).toObjectId()
 
         self.model.lock.acquire()
         try:
-            broker = self.agent.getBroker()
+            broker = self.qmf_agent.getBroker()
 
             seq = self.model.qmf_session._sendMethodRequest \
-                    (broker, classKey, objectId, name, args)
+                    (broker, class_key, oid, name, args)
 
             if seq is not None:
                 self.model.outstanding_method_calls[seq] = callback
@@ -147,8 +155,8 @@
         log.info("Creating %s", qagent)
 
         # Some agents come down without a brokerId, meaning we can't
-        # generate a fully qualified agent ID for them.    Those we handle
-        # in brokerInfo.
+        # generate a fully qualified agent ID for them.  Those we
+        # handle in brokerInfo.
 
         if qagent.getBroker().brokerId:
             agent = MintAgent(self.model, qagent)
@@ -182,7 +190,11 @@
 
         id = str(QmfAgentId.fromAgent(qagent))
 
-        agent = self.model.agents[id]
+        try:
+            agent = self.model.agents[id]
+        except KeyError:
+            return
+
         agent.last_heartbeat = datetime.fromtimestamp(timestamp)
 
     def newPackage(self, name):
@@ -191,9 +203,15 @@
     def newClass(self, kind, classKey):
         log.info("New class %s", classKey)
 
+        # XXX I want to store class keys using this, but I can't,
+        # because I don't get any agent info; instead
+
     def objectProps(self, broker, object):
-        if not self.model.app.update_thread.isAlive():
-            return
+        self.model.lock.acquire()
+        try:
+            pass
+        finally:
+            self.model.lock.release()
 
         self.model.lock.acquire()
         try:
@@ -207,15 +225,19 @@
         finally:
             self.model.lock.release()
 
-        up = PropertyUpdate(agent, object)
-        self.model.app.update_thread.enqueue(up)
+        if self.model.app.update_thread.isAlive():
+            up = PropertyUpdate(agent, object)
+            self.model.app.update_thread.enqueue(up)
 
-    def objectStats(self, broker, object):
-        """ Invoked when an object is updated. """
+        if self.model.app.new_update_thread.isAlive():
+            if object.getTimestamps()[2]:
+                up = NewObjectDelete(self.model, agent, object)
+            else:
+                up = NewObjectUpdate(self.model, agent, object)
 
-        if not self.model.app.update_thread.isAlive():
-            return
+            self.model.app.new_update_thread.enqueue(up)
 
+    def objectStats(self, broker, object):
         self.model.lock.acquire()
         try:
             id = str(QmfAgentId.fromObject(object))
@@ -228,9 +250,14 @@
         finally:
             self.model.lock.release()
 
-        up = StatisticUpdate(agent, object)
-        self.model.app.update_thread.enqueue(up)
+        if self.model.app.update_thread.isAlive():
+            up = StatisticUpdate(agent, object)
+            self.model.app.update_thread.enqueue(up)
 
+        if self.model.app.new_update_thread.isAlive():
+            up = NewStatsUpdate(self.model, agent, object)
+            self.model.app.new_update_thread.enqueue(up)
+
     def event(self, broker, event):
         """ Invoked when an event is raised. """
         pass

Added: mgmt/newdata/mint/python/mint/newupdate.py
===================================================================
--- mgmt/newdata/mint/python/mint/newupdate.py	                        (rev 0)
+++ mgmt/newdata/mint/python/mint/newupdate.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,369 @@
+import pickle
+
+from psycopg2 import IntegrityError, TimestampFromTicks
+from rosemary.model import *
+from util import *
+
+log = logging.getLogger("mint.newupdate")
+
+class NewUpdateThread(MintDaemonThread):
+    def __init__(self, app):
+        super(NewUpdateThread, self).__init__(app)
+
+        self.conn = None
+        self.stats = None
+
+        self.updates = ConcurrentQueue()
+
+        self.halt_on_error = True
+
+    def init(self):
+        self.conn = self.app.database.get_connection()
+        self.stats = NewUpdateStats()
+
+    def enqueue(self, update):
+        update.thread = self
+
+        self.updates.put(update)
+
+        if self.stats:
+            self.stats.enqueued += 1
+
+        # This is an attempt to yield from the enqueueing thread (this
+        # method's caller) to the update thread
+
+        if self.updates.qsize() > 1000:
+            sleep(0.1)
+
+    def run(self):
+        while True:
+            if self.stop_requested:
+                break
+
+            try:
+                update = self.updates.get(True, 1)
+            except Empty:
+                continue
+
+            if self.stats:
+                self.stats.dequeued += 1
+
+            update.process(self.conn, self.stats)
+
+class NewUpdateStats(object):
+    def __init__(self):
+        self.enqueued = 0
+        self.dequeued = 0
+
+        self.updated = 0
+        self.deleted = 0
+        self.dropped = 0
+
+        self.stats_updated = 0
+        self.stats_expired = 0
+        self.stats_dropped = 0
+
+class NewUpdate(object):
+    def __init__(self, model):
+        self.model = model
+
+    def process(self, conn, stats):
+        log.debug("Processing %s", self)
+
+        try:
+            self.do_process(conn, stats)
+
+            conn.commit()
+        except UpdateException, e:
+            log.info("Update could not be completed; %s", e)
+
+            conn.rollback()
+        except:
+            log.exception("Update failed")
+
+            conn.rollback()
+
+            if self.model.app.new_update_thread.halt_on_error:
+                raise
+
+    def do_process(self, conn, stats):
+        raise Exception("Not implemented")
+
+    def __repr__(self):
+        return self.__class__.__name__
+
+class NewObjectUpdate(NewUpdate):
+    def __init__(self, model, agent, object):
+        super(NewObjectUpdate, self).__init__(model)
+
+        self.agent = agent
+        self.object = object
+        self.object_id = str(QmfObjectId.fromObject(object))
+        self.session_id = None
+
+        sequence = object.getObjectId().getSequence()
+
+        if sequence != 0:
+            self.session_id = str(sequence)
+
+        update_time, create_time, delete_time = self.object.getTimestamps()
+
+        self.update_time = datetime.fromtimestamp(update_time / 1000000000)
+        self.create_time = datetime.fromtimestamp(create_time / 1000000000)
+        self.delete_time = datetime.fromtimestamp(delete_time / 1000000000)
+
+    def do_process(self, conn, stats):
+        cls = self.get_class()
+        obj = self.get_object(cls, self.object_id)
+
+        columns = list()
+
+        update_time, create_time, delete_time = self.object.getTimestamps()
+
+        if obj._id:
+            # This object is already in the database
+
+            obj._qmf_update_time = self.update_time
+            columns.append(cls.sql_table._qmf_update_time)
+
+            # XXX session_id may have changed too?
+        else:
+            obj._qmf_agent_id = self.agent.id
+            obj._qmf_object_id = self.object_id
+            obj._qmf_session_id = self.session_id
+            obj._qmf_class_key = str(self.object.getClassKey())
+            obj._qmf_update_time = self.update_time
+            obj._qmf_create_time = self.create_time
+
+            columns.append(cls.sql_table._qmf_agent_id)
+            columns.append(cls.sql_table._qmf_object_id)
+            columns.append(cls.sql_table._qmf_session_id)
+            columns.append(cls.sql_table._qmf_class_key)
+            columns.append(cls.sql_table._qmf_update_time)
+            columns.append(cls.sql_table._qmf_create_time)
+
+        self.process_references(obj, columns)
+        self.process_properties(obj, columns)
+
+        cursor = conn.cursor()
+
+        try:
+            obj.save(cursor, columns)
+        finally:
+            cursor.close()
+
+        stats.updated += 1
+
+    def get_class(self):
+        class_key = self.object.getClassKey()
+
+        name = class_key.getPackageName()
+
+        try:
+            pkg = self.model.rosemary._packages_by_name[name]
+        except KeyError:
+            raise PackageUnknown(name)
+
+        name = class_key.getClassName()
+        name = name[0].upper() + name[1:]
+
+        try:
+            cls = pkg._classes_by_name[name]
+        except KeyError:
+            raise ClassUnknown(name)
+        
+        return cls
+
+    def get_object(self, cls, object_id):
+        try:
+            return self.agent.objects_by_id[object_id]
+        except KeyError:
+            obj = cls.create_object()
+            obj._qmf_agent_id = self.agent.id
+            obj._qmf_object_id = object_id
+
+            conn = self.model.app.database.get_connection()
+            cursor = conn.cursor()
+
+            try:
+                try:
+                    cls.load_object(cursor, obj)
+                except RosemaryNotFound:
+                    pass
+            finally:
+                cursor.close()
+
+            self.agent.objects_by_id[object_id] = obj
+
+            return obj
+
+    def process_references(self, obj, columns):
+        for prop, value in self.object.getProperties():
+            if prop.type != 10:
+                continue
+
+            try:
+                ref = obj._class._references_by_name[prop.name]
+            except KeyError:
+                log.debug("Reference %s is unknown", prop.name)
+
+                continue
+
+            if not ref.sql_column:
+                log.warn("Reference %s has no column; skipping it", ref.name)
+
+                continue
+
+            col = ref.sql_column
+
+            if value:
+                that_id = str(QmfObjectId(value.first, value.second))
+                that = self.get_object(ref.that_cls, that_id)
+
+                if not that._id:
+                    continue
+
+                value = that._id
+
+            columns.append(col)
+
+            setattr(obj, col.name, value)
+
+    def process_properties(self, obj, columns):
+        for prop, value in self.object.getProperties():
+            if prop.type == 10:
+                continue
+
+            try:
+                col = obj._class._properties_by_name[prop.name].sql_column
+            except KeyError:
+                log.debug("Property %s is unknown", prop)
+
+                continue
+
+            if value is not None:
+                value = self.transform_value(prop, value)
+
+            # XXX This optimization will be obsolete when QMF does it
+            # instead
+
+            if value == getattr(obj, col.name):
+                continue
+
+            columns.append(col)
+
+            setattr(obj, col.name, value)
+
+    def transform_value(self, attr, value):
+        if attr.type == 8: # absTime
+            if value == 0:
+                value = None
+            else:
+                value = datetime.fromtimestamp(value / 1000000000)
+                # XXX value = TimestampFromTicks(value / 1000000000)
+        elif attr.type == 15: # map
+            value = pickle.dumps(value)
+        elif attr.type == 10: # objId
+            value = str(value)
+        elif attr.type == 14: # uuid
+            value = str(value)
+
+        return value
+
+    def __repr__(self):
+        name = self.__class__.__name__
+        cls = self.object.getClassKey().getClassName()
+
+        return "%s(%s,%s,%s)" % (name, self.agent.id, cls, self.object_id)
+
+class NewObjectDelete(NewObjectUpdate):
+    def do_process(self, conn, stats):
+        cls = self.get_class()
+        obj = self.get_object(cls, self.object_id)
+
+        cursor = conn.cursor()
+
+        try:
+            cls.sql_delete.execute(cursor, (), obj.__dict__)
+        finally:
+            cursor.close()
+
+        try:
+            del self.agent.objects_by_id[self.object_id]
+        except KeyError:
+            pass
+
+        stats.deleted += 1
+
+class NewStatsUpdate(NewObjectUpdate):
+    def do_process(self, conn, stats):
+        cls = self.get_class()
+        obj = self.get_object(cls, self.object_id)
+
+        if not cls._statistics:
+            stats.stats_dropped += 1; return
+
+        if not obj._id:
+            stats.stats_dropped += 1; return
+
+        if stats.enqueued - stats.dequeued > 100:
+            if obj._qmf_update_time > datetime.now() - timedelta(seconds=60):
+                stats.stats_dropped += 1; return
+
+        update_columns = list()
+        update_columns.append(cls.sql_table._qmf_update_time)
+
+        insert_columns = list()
+        insert_columns.append(cls.sql_table._qmf_update_time)
+
+        obj._qmf_update_time = self.update_time
+
+        self.process_statistics(obj, update_columns, insert_columns)
+
+        cursor = conn.cursor()
+
+        try:
+            cls.sql_update.execute(cursor, update_columns, obj.__dict__)
+            cls.sql_stats_insert.execute(cursor, insert_columns, obj.__dict__)
+        finally:
+            cursor.close()
+
+        stats.stats_updated += 1
+            
+    def process_statistics(self, obj, update_columns, insert_columns):
+        for stat, value in self.object.getStatistics():
+            try:
+                col = obj._class._statistics_by_name[stat.name].sql_column
+            except KeyError:
+                log.debug("Statistic %s is unknown", stat)
+
+                continue
+
+            if value is not None:
+                value = self.transform_value(stat, value)
+
+            # XXX This optimization will be obsolete when QMF does it
+            # instead
+
+            if value != getattr(obj, col.name):
+                update_columns.append(col)
+
+            insert_columns.append(col)
+
+            setattr(obj, col.name, value)
+
+class UpdateException(Exception):
+    def __init__(self, name):
+        self.name = name
+
+    def __str__(self):
+        return "%s(%s)" % (self.__class__.__name__, self.name)
+
+class PackageUnknown(UpdateException):
+    pass
+
+class ClassUnknown(UpdateException):
+    pass
+
+class ObjectUnknown(UpdateException):
+    pass

Modified: mgmt/newdata/mint/python/mint/sql.py
===================================================================
--- mgmt/trunk/mint/python/mint/sql.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/mint/python/mint/sql.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -60,7 +60,7 @@
 
             if values:
                 for item in values.items():
-                    log.warn("    %-20s    %r", *item)
+                    log.warn("    %-20s  %r", *item)
 
             raise
 

Modified: mgmt/newdata/mint/python/mint/tools.py
===================================================================
--- mgmt/trunk/mint/python/mint/tools.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/mint/python/mint/tools.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -72,6 +72,7 @@
 
         if self.config.debug:
             self.config.prt()
+            enable_logging("rosemary", "debug", sys.stderr)
             enable_logging("mint", "debug", sys.stderr)
 
         self.do_run(opts, args)
@@ -439,10 +440,10 @@
         app.init()
         app.start()
 
-        head = "%6s %6s %6s %6s %6s %6s %6s %6s" % \
-            ("enqs", "deqs", "depth", "drop", "defer",
-             "prop", "stat", "exp")
-        row = "%6i %6i %6i %6i %6i %6i %6i %6i"
+        head = "%8s %8s %8s %8s %8s %8s %8s %8s %8s" % \
+            ("enqs", "deqs", "depth", "update", "delete", "drop",
+             "s.update", "s.expire", "s.drop")
+        row = "%8i %8i %8i %8i %8i %8i %8i %8i %8i"
 
         try:
             for arg in args[1:]:
@@ -454,13 +455,15 @@
             try:
                 enq_last = 0
                 deq_last = 0
+
+                upd_last = 0
+                dlt_last = 0
                 drp_last = 0
-                dfr_last = 0
-                prop_last = 0
-                stat_last = 0
-                meth_last = 0
-                exp_last = 0
 
+                supd_last = 0
+                sexp_last = 0
+                sdrp_last = 0
+
                 samples = 0
 
                 while True:
@@ -471,45 +474,51 @@
 
                     sleep(1)
 
-                    stats = app.update_thread.stats
+                    stats = app.new_update_thread.stats
 
                     enq = stats.enqueued
                     deq = stats.dequeued
+
+                    upd = stats.updated
+                    dlt = stats.deleted
                     drp = stats.dropped
-                    dfr = stats.deferred
 
-                    prop = stats.prop_updated
-                    stat = stats.stat_updated
-                    exp = stats.expired
+                    supd = stats.stats_updated
+                    sexp = stats.stats_expired
+                    sdrp = stats.stats_dropped
 
                     print row % (enq - enq_last,
                                  deq - deq_last,
                                  enq - deq,
+                                 upd - upd_last,
+                                 dlt - dlt_last,
                                  drp - drp_last,
-                                 dfr - dfr_last,
-                                 prop - prop_last,
-                                 stat - stat_last,
-                                 exp - exp_last)
+                                 supd - supd_last,
+                                 sexp - sexp_last,
+                                 sdrp - sdrp_last)
 
                     enq_last = enq
                     deq_last = deq
+
+                    upd_last = upd
+                    dlt_last = dlt
                     drp_last = drp
-                    dfr_last = dfr
 
-                    prop_last = prop
-                    stat_last = stat
-                    exp_last = exp
+                    supd_last = supd
+                    sexp_last = sexp
+                    sdrp_last = sdrp
             finally:
                 print "Totals:"
 
                 print row % (enq,
                              deq,
                              enq - deq,
+                             upd,
+                             dlt,
                              drp,
-                             dfr,
-                             prop,
-                             stat,
-                             exp)
+                             supd,
+                             sexp,
+                             sdrp)
         finally:
             #from threading import enumerate
             #for item in enumerate():

Modified: mgmt/newdata/mint/python/mint/update.py
===================================================================
--- mgmt/trunk/mint/python/mint/update.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/mint/python/mint/update.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -87,6 +87,9 @@
         try:
             self.do_process(conn, stats)
 
+            for notice in conn.notices:
+                log.debug("Database: %s", notice)
+
             conn.commit()
         except:
             log.exception("Update failed")
@@ -362,44 +365,3 @@
 
         op = SqlAgentDisconnect(self.agent)
         op.execute(cursor, args)
-
-class UpdateQueue(ConcurrentQueue):
-    def __init__(self, maxsize=0, slotCount=1):
-        self.slotCount = slotCount
-        ConcurrentQueue.__init__(self, maxsize)
-
-    def _init(self, maxsize):
-        self.maxsize = maxsize
-        self.slots = []
-
-        for i in range(self.slotCount):
-            self.slots.append(deque())
-
-    def _qsize(self):
-        size = 0
-
-        for i in range(self.slotCount):
-            size += len(self.slots[i])
-
-        return size
-
-    def _empty(self):
-        return self._qsize() == 0
-
-    def _full(self):
-        return self.maxsize > 0 and self._qsize() == self.maxsize
-
-    def _put(self, update):
-        slot = update.priority
-
-        if slot in range(self.slotCount):
-            self.slots[slot].append(update)
-        else:
-            raise ValueError("Invalid priority slot")
-
-    def _get(self):
-        for slot in range(self.slotCount):
-            if len(self.slots[slot]) > 0:
-                return self.slots[slot].popleft()
-
-        return None

Modified: mgmt/newdata/mint/python/mint/util.py
===================================================================
--- mgmt/trunk/mint/python/mint/util.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/mint/python/mint/util.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -1,5 +1,6 @@
 import logging
 import os
+import random
 import sys
 
 from Queue import Queue as ConcurrentQueue, Full, Empty
@@ -12,6 +13,11 @@
 from time import clock, sleep
 from traceback import print_exc
 
+from parsley.collectionsex import *
+from parsley.config import *
+from parsley.loggingex import *
+from parsley.threadingex import *
+
 log = logging.getLogger("mint.util")
 
 class MintDaemonThread(Thread):
@@ -113,6 +119,47 @@
     def __str__(self):
         return "%i.%i" % (self.first, self.second)
 
+class UpdateQueue(ConcurrentQueue):
+    def __init__(self, maxsize=0, slotCount=1):
+        self.slotCount = slotCount
+        ConcurrentQueue.__init__(self, maxsize)
+
+    def _init(self, maxsize):
+        self.maxsize = maxsize
+        self.slots = []
+
+        for i in range(self.slotCount):
+            self.slots.append(deque())
+
+    def _qsize(self):
+        size = 0
+
+        for i in range(self.slotCount):
+            size += len(self.slots[i])
+
+        return size
+
+    def _empty(self):
+        return self._qsize() == 0
+
+    def _full(self):
+        return self.maxsize > 0 and self._qsize() == self.maxsize
+
+    def _put(self, update):
+        slot = update.priority
+
+        if slot in range(self.slotCount):
+            self.slots[slot].append(update)
+        else:
+            raise ValueError("Invalid priority slot")
+
+    def _get(self):
+        for slot in range(self.slotCount):
+            if len(self.slots[slot]) > 0:
+                return self.slots[slot].popleft()
+
+        return None
+
 password_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
 
 def crypt_password(password, salt=None):

Modified: mgmt/newdata/mint/python/mint/vacuum.py
===================================================================
--- mgmt/trunk/mint/python/mint/vacuum.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/mint/python/mint/vacuum.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -1,3 +1,4 @@
+from newupdate import *
 from update import *
 from util import *
 
@@ -4,26 +5,56 @@
 log = logging.getLogger("mint.vacuum")
 
 class VacuumThread(MintDaemonThread):
-  def run(self):
-    while True:
-      if self.stop_requested:
-        break
+    def run(self):
+        while True:
+            if self.stop_requested:
+                break
 
-      up = VacuumUpdate()
-      self.app.update_thread.enqueue(up)
+            up = VacuumUpdate()
+            self.app.update_thread.enqueue(up)
 
-      sleep(60 * 10)
+            up = NewVacuumUpdate(self.app.model)
+            self.app.new_update_thread.enqueue(up)
 
+            sleep(60 * 60 * 10)
+
 class VacuumUpdate(Update):
-  def do_process(self, conn, stats):
-    log.info("Vacuuming")
+    def do_process(self, conn, stats):
+        log.info("Vacuuming")
 
-    conn.commit()
+        conn.commit()
 
-    level = conn.isolation_level
-    conn.set_isolation_level(0)
+        level = conn.isolation_level
+        conn.set_isolation_level(0)
 
-    cursor = conn.cursor()
-    cursor.execute("vacuum")
+        cursor = conn.cursor()
+        cursor.execute("vacuum")
 
-    conn.set_isolation_level(level)
+        conn.set_isolation_level(level)
+
+class NewVacuumUpdate(NewUpdate):
+    def do_process(self, conn, stats):
+        log.info("Vacumming tables")
+
+        level = conn.isolation_level
+        conn.set_isolation_level(0)
+
+        for pkg in self.model.rosemary._packages:
+            for cls in pkg._classes:
+                self.vacuum(conn, cls)
+
+        conn.set_isolation_level(level)
+
+        log.info("Vacuumed tables")
+
+    def vacuum(self, conn, cls):
+        cursor = conn.cursor()
+
+        try:
+            cursor.execute("vacuum verbose %s" % cls.sql_table.identifier)
+
+            for notice in conn.notices:
+                log.debug("Database: %s", notice.replace("\n", " "))
+        finally:
+            cursor.close()
+

Modified: mgmt/newdata/mint/sql/Makefile
===================================================================
--- mgmt/trunk/mint/sql/Makefile	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/mint/sql/Makefile	2010-03-22 14:58:08 UTC (rev 3871)
@@ -2,10 +2,13 @@
 
 dsn := "postgresql://localhost/"
 
-schema: schema.sql	
+schema: schema.sql rosemary.sql
 
 schema.sql: ../python/mint/*.py
 	sqlobject-admin sql -m mint.schema -m mint.schemalocal -c ${dsn} | sed -e '1,2d' > schema.sql
 
+rosemary.sql:
+	rosemary-test ddl > rosemary.sql
+
 clean:
-	rm -f schema.sql
+	rm -f schema.sql rosemary.sql

Added: mgmt/newdata/mint/sql/rosemary.sql
===================================================================
--- mgmt/newdata/mint/sql/rosemary.sql	                        (rev 0)
+++ mgmt/newdata/mint/sql/rosemary.sql	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,1150 @@
+drop schema "org.apache.qpid.cluster" cascade;
+drop schema "org.apache.qpid.broker" cascade;
+drop schema "com.redhat.sesame" cascade;
+drop schema "org.apache.qpid.acl" cascade;
+drop schema "com.redhat.cumin" cascade;
+drop schema "com.redhat.rhm.store" cascade;
+drop schema "mrg.grid" cascade;
+create schema "org.apache.qpid.cluster"
+    create table "Cluster" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_brokerRef_id" int8,
+        "clusterName" text not null,
+        "clusterID" text not null,
+        "memberID" text not null,
+        "publishedURL" text not null,
+        "clusterSize" int4 not null,
+        "status" text not null,
+        "members" text not null,
+        "memberIDs" text not null,
+        constraint "Cluster_pk" primary key ("_id"),
+        constraint "Cluster_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Cluster_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        constraint "Cluster_stats_pk" primary key ("_id")
+        )
+    ;
+create schema "org.apache.qpid.broker"
+    create table "System" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "systemId" text not null,
+        "osName" text not null,
+        "nodeName" text not null,
+        "release" text not null,
+        "version" text not null,
+        "machine" text not null,
+        constraint "System_pk" primary key ("_id"),
+        constraint "System_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "System_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        constraint "System_stats_pk" primary key ("_id")
+        )
+    create table "Broker" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_systemRef_id" int8,
+        "port" int4 not null,
+        "workerThreads" int4 not null,
+        "maxConns" int4 not null,
+        "connBacklog" int4 not null,
+        "stagingThreshold" int8 not null,
+        "mgmtPubInterval" int4 not null,
+        "version" text not null,
+        "dataDir" text,
+        "uptime" numeric(19),
+        constraint "Broker_pk" primary key ("_id"),
+        constraint "Broker_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Broker_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "uptime" numeric(19) not null,
+        constraint "Broker_stats_pk" primary key ("_id")
+        )
+    create table "Agent" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_connectionRef_id" int8,
+        "_registeredTo_id" int8,
+        "label" text not null,
+        "systemId" text not null,
+        "brokerBank" int8 not null,
+        "agentBank" int8 not null,
+        constraint "Agent_pk" primary key ("_id"),
+        constraint "Agent_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Agent_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        constraint "Agent_stats_pk" primary key ("_id")
+        )
+    create table "Vhost" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_brokerRef_id" int8,
+        "name" text not null,
+        "federationTag" text not null,
+        constraint "Vhost_pk" primary key ("_id"),
+        constraint "Vhost_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Vhost_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        constraint "Vhost_stats_pk" primary key ("_id")
+        )
+    create table "Queue" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_vhostRef_id" int8,
+        "_altExchange_id" int8,
+        "name" text not null,
+        "durable" bool not null,
+        "autoDelete" bool not null,
+        "exclusive" bool not null,
+        "arguments" text not null,
+        "msgTotalEnqueues" numeric(19),
+        "msgTotalDequeues" numeric(19),
+        "msgTxnEnqueues" numeric(19),
+        "msgTxnDequeues" numeric(19),
+        "msgPersistEnqueues" numeric(19),
+        "msgPersistDequeues" numeric(19),
+        "msgDepth" int8,
+        "byteDepth" int8,
+        "byteTotalEnqueues" numeric(19),
+        "byteTotalDequeues" numeric(19),
+        "byteTxnEnqueues" numeric(19),
+        "byteTxnDequeues" numeric(19),
+        "bytePersistEnqueues" numeric(19),
+        "bytePersistDequeues" numeric(19),
+        "consumerCount" int8,
+        "bindingCount" int8,
+        "unackedMessages" int8,
+        "messageLatency" numeric(19),
+        constraint "Queue_pk" primary key ("_id"),
+        constraint "Queue_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Queue_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "msgTotalEnqueues" numeric(19) not null,
+        "msgTotalDequeues" numeric(19) not null,
+        "msgTxnEnqueues" numeric(19) not null,
+        "msgTxnDequeues" numeric(19) not null,
+        "msgPersistEnqueues" numeric(19) not null,
+        "msgPersistDequeues" numeric(19) not null,
+        "msgDepth" int8 not null,
+        "byteDepth" int8 not null,
+        "byteTotalEnqueues" numeric(19) not null,
+        "byteTotalDequeues" numeric(19) not null,
+        "byteTxnEnqueues" numeric(19) not null,
+        "byteTxnDequeues" numeric(19) not null,
+        "bytePersistEnqueues" numeric(19) not null,
+        "bytePersistDequeues" numeric(19) not null,
+        "consumerCount" int8 not null,
+        "bindingCount" int8 not null,
+        "unackedMessages" int8 not null,
+        "messageLatency" numeric(19),
+        constraint "Queue_stats_pk" primary key ("_id")
+        )
+    create table "Exchange" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_vhostRef_id" int8,
+        "_altExchange_id" int8,
+        "name" text not null,
+        "type" text not null,
+        "durable" bool not null,
+        "autoDelete" bool not null,
+        "arguments" text not null,
+        "producerCount" int8,
+        "bindingCount" int8,
+        "msgReceives" numeric(19),
+        "msgDrops" numeric(19),
+        "msgRoutes" numeric(19),
+        "byteReceives" numeric(19),
+        "byteDrops" numeric(19),
+        "byteRoutes" numeric(19),
+        constraint "Exchange_pk" primary key ("_id"),
+        constraint "Exchange_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Exchange_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "producerCount" int8 not null,
+        "bindingCount" int8 not null,
+        "msgReceives" numeric(19) not null,
+        "msgDrops" numeric(19) not null,
+        "msgRoutes" numeric(19) not null,
+        "byteReceives" numeric(19) not null,
+        "byteDrops" numeric(19) not null,
+        "byteRoutes" numeric(19) not null,
+        constraint "Exchange_stats_pk" primary key ("_id")
+        )
+    create table "Binding" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_exchangeRef_id" int8,
+        "_queueRef_id" int8,
+        "bindingKey" text not null,
+        "arguments" text not null,
+        "origin" text,
+        "msgMatched" numeric(19),
+        constraint "Binding_pk" primary key ("_id"),
+        constraint "Binding_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Binding_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "msgMatched" numeric(19) not null,
+        constraint "Binding_stats_pk" primary key ("_id")
+        )
+    create table "Subscription" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_sessionRef_id" int8,
+        "_queueRef_id" int8,
+        "name" text not null,
+        "browsing" bool not null,
+        "acknowledged" bool not null,
+        "exclusive" bool not null,
+        "creditMode" text not null,
+        "arguments" text not null,
+        "delivered" numeric(19),
+        constraint "Subscription_pk" primary key ("_id"),
+        constraint "Subscription_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Subscription_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "delivered" numeric(19) not null,
+        constraint "Subscription_stats_pk" primary key ("_id")
+        )
+    create table "Connection" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_vhostRef_id" int8,
+        "address" text not null,
+        "incoming" bool not null,
+        "SystemConnection" bool not null,
+        "federationLink" bool not null,
+        "authIdentity" text not null,
+        "remoteProcessName" text,
+        "remotePid" int8,
+        "remoteParentPid" int8,
+        "shadow" bool not null,
+        "closing" bool,
+        "framesFromClient" numeric(19),
+        "framesToClient" numeric(19),
+        "bytesFromClient" numeric(19),
+        "bytesToClient" numeric(19),
+        constraint "Connection_pk" primary key ("_id"),
+        constraint "Connection_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Connection_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "closing" bool not null,
+        "framesFromClient" numeric(19) not null,
+        "framesToClient" numeric(19) not null,
+        "bytesFromClient" numeric(19) not null,
+        "bytesToClient" numeric(19) not null,
+        constraint "Connection_stats_pk" primary key ("_id")
+        )
+    create table "Link" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_vhostRef_id" int8,
+        "host" text not null,
+        "port" int4 not null,
+        "transport" text not null,
+        "durable" bool not null,
+        "state" text,
+        "lastError" text,
+        constraint "Link_pk" primary key ("_id"),
+        constraint "Link_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Link_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "state" text not null,
+        "lastError" text not null,
+        constraint "Link_stats_pk" primary key ("_id")
+        )
+    create table "Bridge" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_linkRef_id" int8,
+        "channelId" int4 not null,
+        "durable" bool not null,
+        "src" text not null,
+        "dest" text not null,
+        "key" text not null,
+        "srcIsQueue" bool not null,
+        "srcIsLocal" bool not null,
+        "tag" text not null,
+        "excludes" text not null,
+        "dynamic" bool not null,
+        "sync" int4 not null,
+        constraint "Bridge_pk" primary key ("_id"),
+        constraint "Bridge_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Bridge_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        constraint "Bridge_stats_pk" primary key ("_id")
+        )
+    create table "Session" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_vhostRef_id" int8,
+        "_connectionRef_id" int8,
+        "name" text not null,
+        "channelId" int4 not null,
+        "detachedLifespan" int8 not null,
+        "attached" bool not null,
+        "expireTime" timestamp,
+        "maxClientRate" int8,
+        "framesOutstanding" int8,
+        "TxnStarts" numeric(19),
+        "TxnCommits" numeric(19),
+        "TxnRejects" numeric(19),
+        "TxnCount" int8,
+        "clientCredit" int8,
+        constraint "Session_pk" primary key ("_id"),
+        constraint "Session_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Session_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "framesOutstanding" int8 not null,
+        "TxnStarts" numeric(19) not null,
+        "TxnCommits" numeric(19) not null,
+        "TxnRejects" numeric(19) not null,
+        "TxnCount" int8 not null,
+        "clientCredit" int8 not null,
+        constraint "Session_stats_pk" primary key ("_id")
+        )
+    create table "ManagementSetupState" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "objectNum" numeric(19) not null,
+        "bootSequence" int4 not null,
+        constraint "ManagementSetupState_pk" primary key ("_id"),
+        constraint "ManagementSetupState_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "ManagementSetupState_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        constraint "ManagementSetupState_stats_pk" primary key ("_id")
+        )
+    ;
+create schema "com.redhat.sesame"
+    create table "Sysimage" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "uuid" text not null,
+        "osName" text not null,
+        "nodeName" text not null,
+        "release" text not null,
+        "version" text not null,
+        "machine" text not null,
+        "distro" text,
+        "memTotal" int8 not null,
+        "swapTotal" int8 not null,
+        "memFree" int8,
+        "swapFree" int8,
+        "loadAverage1Min" float4,
+        "loadAverage5Min" float4,
+        "loadAverage10Min" float4,
+        "procTotal" int8,
+        "procRunning" int8,
+        constraint "Sysimage_pk" primary key ("_id"),
+        constraint "Sysimage_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Sysimage_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "memFree" int8 not null,
+        "swapFree" int8 not null,
+        "loadAverage1Min" float4 not null,
+        "loadAverage5Min" float4 not null,
+        "loadAverage10Min" float4 not null,
+        "procTotal" int8 not null,
+        "procRunning" int8 not null,
+        constraint "Sysimage_stats_pk" primary key ("_id")
+        )
+    ;
+create schema "org.apache.qpid.acl"
+    create table "Acl" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_brokerRef_id" int8,
+        "policyFile" text not null,
+        "enforcingAcl" bool not null,
+        "transferAcl" bool not null,
+        "lastAclLoad" timestamp not null,
+        "aclDenyCount" numeric(19),
+        constraint "Acl_pk" primary key ("_id"),
+        constraint "Acl_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Acl_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "aclDenyCount" numeric(19) not null,
+        constraint "Acl_stats_pk" primary key ("_id")
+        )
+    ;
+create schema "com.redhat.cumin"
+    create table "BrokerGroup" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "name" text not null,
+        "description" text,
+        constraint "BrokerGroup_pk" primary key ("_id"),
+        constraint "BrokerGroup_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "BrokerGroup_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        constraint "BrokerGroup_stats_pk" primary key ("_id")
+        )
+    create table "BrokerGroupMapping" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_broker_id" int8,
+        "_group_id" int8,
+        constraint "BrokerGroupMapping_pk" primary key ("_id"),
+        constraint "BrokerGroupMapping_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "BrokerGroupMapping_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        constraint "BrokerGroupMapping_stats_pk" primary key ("_id")
+        )
+    ;
+create schema "com.redhat.rhm.store"
+    create table "Store" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_brokerRef_id" int8,
+        "location" text not null,
+        "defaultInitialFileCount" int4 not null,
+        "defaultDataFileSize" int8 not null,
+        "tplIsInitialized" bool not null,
+        "tplDirectory" text not null,
+        "tplWritePageSize" int8 not null,
+        "tplWritePages" int8 not null,
+        "tplInitialFileCount" int4 not null,
+        "tplDataFileSize" int8 not null,
+        "tplCurrentFileCount" int8 not null,
+        "tplTransactionDepth" int8,
+        "tplTxnPrepares" numeric(19),
+        "tplTxnCommits" numeric(19),
+        "tplTxnAborts" numeric(19),
+        "tplOutstandingAIOs" int8,
+        constraint "Store_pk" primary key ("_id"),
+        constraint "Store_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Store_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "tplTransactionDepth" int8 not null,
+        "tplTxnPrepares" numeric(19) not null,
+        "tplTxnCommits" numeric(19) not null,
+        "tplTxnAborts" numeric(19) not null,
+        "tplOutstandingAIOs" int8 not null,
+        constraint "Store_stats_pk" primary key ("_id")
+        )
+    create table "Journal" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_queueRef_id" int8,
+        "name" text not null,
+        "directory" text not null,
+        "baseFileName" text not null,
+        "writePageSize" int8 not null,
+        "writePages" int8 not null,
+        "readPageSize" int8 not null,
+        "readPages" int8 not null,
+        "initialFileCount" int4 not null,
+        "autoExpand" bool not null,
+        "currentFileCount" int4 not null,
+        "maxFileCount" int4 not null,
+        "dataFileSize" int8 not null,
+        "recordDepth" int8,
+        "enqueues" numeric(19),
+        "dequeues" numeric(19),
+        "txn" int8,
+        "txnEnqueues" numeric(19),
+        "txnDequeues" numeric(19),
+        "txnCommits" numeric(19),
+        "txnAborts" numeric(19),
+        "outstandingAIOs" int8,
+        "freeFileCount" int8,
+        "availableFileCount" int8,
+        "writeWaitFailures" numeric(19),
+        "writeBusyFailures" numeric(19),
+        "readRecordCount" numeric(19),
+        "readBusyFailures" numeric(19),
+        "writePageCacheDepth" int8,
+        "readPageCacheDepth" int8,
+        constraint "Journal_pk" primary key ("_id"),
+        constraint "Journal_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Journal_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "recordDepth" int8 not null,
+        "enqueues" numeric(19) not null,
+        "dequeues" numeric(19) not null,
+        "txn" int8 not null,
+        "txnEnqueues" numeric(19) not null,
+        "txnDequeues" numeric(19) not null,
+        "txnCommits" numeric(19) not null,
+        "txnAborts" numeric(19) not null,
+        "outstandingAIOs" int8 not null,
+        "freeFileCount" int8 not null,
+        "availableFileCount" int8 not null,
+        "writeWaitFailures" numeric(19) not null,
+        "writeBusyFailures" numeric(19) not null,
+        "readRecordCount" numeric(19) not null,
+        "readBusyFailures" numeric(19) not null,
+        "writePageCacheDepth" int8 not null,
+        "readPageCacheDepth" int8 not null,
+        constraint "Journal_stats_pk" primary key ("_id")
+        )
+    ;
+create schema "mrg.grid"
+    create table "Slot" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "Pool" text not null,
+        "System" text not null,
+        "AccountingGroup" text,
+        "Activity" text not null,
+        "Arch" text not null,
+        "CheckpointPlatform" text not null,
+        "ClientMachine" text,
+        "ConcurrencyLimits" text,
+        "Cpus" int8 not null,
+        "CurrentRank" float8,
+        "Disk" int8 not null,
+        "EnteredCurrentActivity" timestamp not null,
+        "EnteredCurrentState" timestamp not null,
+        "FileSystemDomain" text not null,
+        "GlobalJobId" text,
+        "IsValidCheckpointPlatform" text not null,
+        "JobId" text,
+        "JobStart" timestamp,
+        "KFlops" int8 not null,
+        "LastBenchmark" timestamp not null,
+        "LastFetchWorkCompleted" timestamp,
+        "LastFetchWorkSpawned" timestamp,
+        "LastPeriodicCheckpoint" timestamp,
+        "Machine" text not null,
+        "MaxJobRetirementTime" text not null,
+        "Memory" int8 not null,
+        "Mips" int8 not null,
+        "MyAddress" text not null,
+        "Name" text not null,
+        "NextFetchWorkDelay" int4 not null,
+        "OpSys" text not null,
+        "PreemptingConcurrencyLimits" text,
+        "PreemptingOwner" text,
+        "PreemptingUser" text,
+        "PreemptingRank" float8,
+        "RemoteOwner" text,
+        "RemoteUser" text,
+        "Requirements" text not null,
+        "Rank" text not null,
+        "SlotID" int8 not null,
+        "Start" text not null,
+        "StarterAbilityList" text not null,
+        "State" text not null,
+        "TimeToLive" int8 not null,
+        "TotalClaimRunTime" int8,
+        "TotalClaimSuspendTime" int8,
+        "TotalCpus" int8 not null,
+        "TotalDisk" int8 not null,
+        "TotalJobRunTime" int8,
+        "TotalJobSuspendTime" int8,
+        "TotalMemory" int8 not null,
+        "TotalSlots" int8 not null,
+        "TotalVirtualMemory" int8 not null,
+        "UidDomain" text not null,
+        "VirtualMemory" int8 not null,
+        "WindowsBuildNumber" int8 not null,
+        "WindowsMajorVersion" int8 not null,
+        "WindowsMinorVersion" int8 not null,
+        "ClockDay" int8,
+        "ClockMin" int8,
+        "CondorLoadAvg" float8,
+        "ConsoleIdle" int8,
+        "ImageSize" int8,
+        "KeyboardIdle" int8,
+        "LoadAvg" float8,
+        "MyCurrentTime" timestamp,
+        "TotalCondorLoadAvg" float8,
+        "TotalLoadAvg" float8,
+        "TotalTimeBackfillBusy" int8,
+        "TotalTimeBackfillIdle" int8,
+        "TotalTimeBackfillKilling" int8,
+        "TotalTimeClaimedBusy" int8,
+        "TotalTimeClaimedIdle" int8,
+        "TotalTimeClaimedRetiring" int8,
+        "TotalTimeClaimedSuspended" int8,
+        "TotalTimeMatchedIdle" int8,
+        "TotalTimeOwnerIdle" int8,
+        "TotalTimePreemptingKilling" int8,
+        "TotalTimePreemptingVacating" int8,
+        "TotalTimeUnclaimedBenchmarking" int8,
+        "TotalTimeUnclaimedIdle" int8,
+        constraint "Slot_pk" primary key ("_id"),
+        constraint "Slot_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Slot_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "ClockDay" int8 not null,
+        "ClockMin" int8 not null,
+        "CondorLoadAvg" float8 not null,
+        "ConsoleIdle" int8 not null,
+        "ImageSize" int8 not null,
+        "KeyboardIdle" int8 not null,
+        "LoadAvg" float8 not null,
+        "MyCurrentTime" timestamp not null,
+        "TotalCondorLoadAvg" float8 not null,
+        "TotalLoadAvg" float8 not null,
+        "TotalTimeBackfillBusy" int8 not null,
+        "TotalTimeBackfillIdle" int8 not null,
+        "TotalTimeBackfillKilling" int8 not null,
+        "TotalTimeClaimedBusy" int8 not null,
+        "TotalTimeClaimedIdle" int8 not null,
+        "TotalTimeClaimedRetiring" int8 not null,
+        "TotalTimeClaimedSuspended" int8 not null,
+        "TotalTimeMatchedIdle" int8 not null,
+        "TotalTimeOwnerIdle" int8 not null,
+        "TotalTimePreemptingKilling" int8 not null,
+        "TotalTimePreemptingVacating" int8 not null,
+        "TotalTimeUnclaimedBenchmarking" int8 not null,
+        "TotalTimeUnclaimedIdle" int8 not null,
+        constraint "Slot_stats_pk" primary key ("_id")
+        )
+    create table "Scheduler" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "Pool" text not null,
+        "System" text not null,
+        "JobQueueBirthdate" timestamp not null,
+        "MaxJobsRunning" int8 not null,
+        "Machine" text not null,
+        "MyAddress" text not null,
+        "Name" text not null,
+        "NumUsers" int8,
+        "TotalHeldJobs" int8,
+        "TotalIdleJobs" int8,
+        "TotalJobAds" int8,
+        "TotalRemovedJobs" int8,
+        "TotalRunningJobs" int8,
+        constraint "Scheduler_pk" primary key ("_id"),
+        constraint "Scheduler_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Scheduler_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "NumUsers" int8 not null,
+        "TotalHeldJobs" int8 not null,
+        "TotalIdleJobs" int8 not null,
+        "TotalJobAds" int8 not null,
+        "TotalRemovedJobs" int8 not null,
+        "TotalRunningJobs" int8 not null,
+        constraint "Scheduler_stats_pk" primary key ("_id")
+        )
+    create table "Submitter" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_schedulerRef_id" int8,
+        "JobQueueBirthdate" timestamp not null,
+        "Machine" text not null,
+        "Name" text not null,
+        "ScheddName" text not null,
+        "HeldJobs" int8,
+        "IdleJobs" int8,
+        "RunningJobs" int8,
+        constraint "Submitter_pk" primary key ("_id"),
+        constraint "Submitter_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Submitter_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "HeldJobs" int8 not null,
+        "IdleJobs" int8 not null,
+        "RunningJobs" int8 not null,
+        constraint "Submitter_stats_pk" primary key ("_id")
+        )
+    create table "Negotiator" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "Pool" text not null,
+        "System" text not null,
+        "Name" text not null,
+        "Machine" text not null,
+        "MyAddress" text not null,
+        constraint "Negotiator_pk" primary key ("_id"),
+        constraint "Negotiator_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Negotiator_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        constraint "Negotiator_stats_pk" primary key ("_id")
+        )
+    create table "Collector" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "Pool" text not null,
+        "System" text not null,
+        "CondorPlatform" text not null,
+        "CondorVersion" text not null,
+        "Name" text not null,
+        "MyAddress" text not null,
+        "RunningJobs" int8,
+        "IdleJobs" int8,
+        "HostsTotal" int8,
+        "HostsClaimed" int8,
+        "HostsUnclaimed" int8,
+        "HostsOwner" int8,
+        constraint "Collector_pk" primary key ("_id"),
+        constraint "Collector_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Collector_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "RunningJobs" int8 not null,
+        "IdleJobs" int8 not null,
+        "HostsTotal" int8 not null,
+        "HostsClaimed" int8 not null,
+        "HostsUnclaimed" int8 not null,
+        "HostsOwner" int8 not null,
+        constraint "Collector_stats_pk" primary key ("_id")
+        )
+    create table "Master" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "Pool" text not null,
+        "System" text not null,
+        "Name" text not null,
+        "Machine" text not null,
+        "MyAddress" text not null,
+        "RealUid" int4 not null,
+        constraint "Master_pk" primary key ("_id"),
+        constraint "Master_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Master_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        constraint "Master_stats_pk" primary key ("_id")
+        )
+    create table "Grid" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "Pool" text not null,
+        "Name" text not null,
+        "ScheddName" text not null,
+        "Owner" text not null,
+        "JobLimit" int8 not null,
+        "SubmitLimit" int8 not null,
+        "GridResourceUnavailableTime" timestamp,
+        "NumJobs" int8,
+        "SubmitsInProgress" int8,
+        "SubmitsQueued" int8,
+        "SubmitsAllowed" int8,
+        "SubmitsWanted" int8,
+        "RunningJobs" int8,
+        "IdleJobs" int8,
+        constraint "Grid_pk" primary key ("_id"),
+        constraint "Grid_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Grid_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "NumJobs" int8 not null,
+        "SubmitsInProgress" int8 not null,
+        "SubmitsQueued" int8 not null,
+        "SubmitsAllowed" int8 not null,
+        "SubmitsWanted" int8 not null,
+        "RunningJobs" int8 not null,
+        "IdleJobs" int8 not null,
+        constraint "Grid_stats_pk" primary key ("_id")
+        )
+    create table "Submission" (
+        "_id" serial8 not null,
+        "_qmf_agent_id" text not null,
+        "_qmf_object_id" text not null,
+        "_qmf_session_id" text,
+        "_qmf_class_key" text not null,
+        "_qmf_create_time" timestamp not null,
+        "_qmf_update_time" timestamp not null,
+        "_qmf_delete_time" timestamp,
+        "_schedulerRef_id" int8,
+        "Name" text not null,
+        "Owner" text not null,
+        "Idle" int8,
+        "Running" int8,
+        "Removed" int8,
+        "Completed" int8,
+        "Held" int8,
+        constraint "Submission_pk" primary key ("_id"),
+        constraint "Submission_qmf_ids_uq" unique ("_qmf_agent_id", "_qmf_object_id")
+        )
+    create table "Submission_stats" (
+        "_id" serial8 not null,
+        "_parent_id" int8 not null,
+        "_qmf_update_time" timestamp not null,
+        "Idle" int8 not null,
+        "Running" int8 not null,
+        "Removed" int8 not null,
+        "Completed" int8 not null,
+        "Held" int8 not null,
+        constraint "Submission_stats_pk" primary key ("_id")
+        )
+    ;
+alter table "org.apache.qpid.cluster"."Cluster"
+    add constraint "_brokerRef_id_fk" foreign key ("_brokerRef_id") references "org.apache.qpid.broker"."Broker" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.cluster"."Cluster_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.cluster"."Cluster" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."System_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."System" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."Broker"
+    add constraint "_systemRef_id_fk" foreign key ("_systemRef_id") references "org.apache.qpid.broker"."System" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.broker"."Broker_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."Broker" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."Agent"
+    add constraint "_connectionRef_id_fk" foreign key ("_connectionRef_id") references "org.apache.qpid.broker"."Connection" ("_id") on update cascade on delete set null,
+    add constraint "_registeredTo_id_fk" foreign key ("_registeredTo_id") references "org.apache.qpid.broker"."Broker" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.broker"."Agent_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."Agent" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."Vhost"
+    add constraint "_brokerRef_id_fk" foreign key ("_brokerRef_id") references "org.apache.qpid.broker"."Broker" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.broker"."Vhost_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."Vhost" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."Queue"
+    add constraint "_vhostRef_id_fk" foreign key ("_vhostRef_id") references "org.apache.qpid.broker"."Vhost" ("_id") on update cascade on delete set null,
+    add constraint "_altExchange_id_fk" foreign key ("_altExchange_id") references "org.apache.qpid.broker"."Exchange" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.broker"."Queue_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."Queue" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."Exchange"
+    add constraint "_vhostRef_id_fk" foreign key ("_vhostRef_id") references "org.apache.qpid.broker"."Vhost" ("_id") on update cascade on delete set null,
+    add constraint "_altExchange_id_fk" foreign key ("_altExchange_id") references "org.apache.qpid.broker"."Exchange" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.broker"."Exchange_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."Exchange" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."Binding"
+    add constraint "_exchangeRef_id_fk" foreign key ("_exchangeRef_id") references "org.apache.qpid.broker"."Exchange" ("_id") on update cascade on delete set null,
+    add constraint "_queueRef_id_fk" foreign key ("_queueRef_id") references "org.apache.qpid.broker"."Queue" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.broker"."Binding_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."Binding" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."Subscription"
+    add constraint "_sessionRef_id_fk" foreign key ("_sessionRef_id") references "org.apache.qpid.broker"."Session" ("_id") on update cascade on delete set null,
+    add constraint "_queueRef_id_fk" foreign key ("_queueRef_id") references "org.apache.qpid.broker"."Queue" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.broker"."Subscription_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."Subscription" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."Connection"
+    add constraint "_vhostRef_id_fk" foreign key ("_vhostRef_id") references "org.apache.qpid.broker"."Vhost" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.broker"."Connection_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."Connection" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."Link"
+    add constraint "_vhostRef_id_fk" foreign key ("_vhostRef_id") references "org.apache.qpid.broker"."Vhost" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.broker"."Link_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."Link" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."Bridge"
+    add constraint "_linkRef_id_fk" foreign key ("_linkRef_id") references "org.apache.qpid.broker"."Link" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.broker"."Bridge_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."Bridge" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."Session"
+    add constraint "_vhostRef_id_fk" foreign key ("_vhostRef_id") references "org.apache.qpid.broker"."Vhost" ("_id") on update cascade on delete set null,
+    add constraint "_connectionRef_id_fk" foreign key ("_connectionRef_id") references "org.apache.qpid.broker"."Connection" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.broker"."Session_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."Session" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.broker"."ManagementSetupState_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.broker"."ManagementSetupState" ("_id") on update cascade on delete cascade
+    ;
+alter table "com.redhat.sesame"."Sysimage_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "com.redhat.sesame"."Sysimage" ("_id") on update cascade on delete cascade
+    ;
+alter table "org.apache.qpid.acl"."Acl"
+    add constraint "_brokerRef_id_fk" foreign key ("_brokerRef_id") references "org.apache.qpid.broker"."Broker" ("_id") on update cascade on delete set null
+    ;
+alter table "org.apache.qpid.acl"."Acl_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "org.apache.qpid.acl"."Acl" ("_id") on update cascade on delete cascade
+    ;
+alter table "com.redhat.cumin"."BrokerGroup_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "com.redhat.cumin"."BrokerGroup" ("_id") on update cascade on delete cascade
+    ;
+alter table "com.redhat.cumin"."BrokerGroupMapping"
+    add constraint "_broker_id_fk" foreign key ("_broker_id") references "org.apache.qpid.broker"."Broker" ("_id") on update cascade on delete set null,
+    add constraint "_group_id_fk" foreign key ("_group_id") references "com.redhat.cumin"."BrokerGroup" ("_id") on update cascade on delete set null
+    ;
+alter table "com.redhat.cumin"."BrokerGroupMapping_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "com.redhat.cumin"."BrokerGroupMapping" ("_id") on update cascade on delete cascade
+    ;
+alter table "com.redhat.rhm.store"."Store"
+    add constraint "_brokerRef_id_fk" foreign key ("_brokerRef_id") references "org.apache.qpid.broker"."Broker" ("_id") on update cascade on delete set null
+    ;
+alter table "com.redhat.rhm.store"."Store_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "com.redhat.rhm.store"."Store" ("_id") on update cascade on delete cascade
+    ;
+alter table "com.redhat.rhm.store"."Journal"
+    add constraint "_queueRef_id_fk" foreign key ("_queueRef_id") references "org.apache.qpid.broker"."Queue" ("_id") on update cascade on delete set null
+    ;
+alter table "com.redhat.rhm.store"."Journal_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "com.redhat.rhm.store"."Journal" ("_id") on update cascade on delete cascade
+    ;
+alter table "mrg.grid"."Slot_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "mrg.grid"."Slot" ("_id") on update cascade on delete cascade
+    ;
+alter table "mrg.grid"."Scheduler_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "mrg.grid"."Scheduler" ("_id") on update cascade on delete cascade
+    ;
+alter table "mrg.grid"."Submitter"
+    add constraint "_schedulerRef_id_fk" foreign key ("_schedulerRef_id") references "mrg.grid"."Scheduler" ("_id") on update cascade on delete set null
+    ;
+alter table "mrg.grid"."Submitter_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "mrg.grid"."Submitter" ("_id") on update cascade on delete cascade
+    ;
+alter table "mrg.grid"."Negotiator_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "mrg.grid"."Negotiator" ("_id") on update cascade on delete cascade
+    ;
+alter table "mrg.grid"."Collector_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "mrg.grid"."Collector" ("_id") on update cascade on delete cascade
+    ;
+alter table "mrg.grid"."Master_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "mrg.grid"."Master" ("_id") on update cascade on delete cascade
+    ;
+alter table "mrg.grid"."Grid_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "mrg.grid"."Grid" ("_id") on update cascade on delete cascade
+    ;
+alter table "mrg.grid"."Submission"
+    add constraint "_schedulerRef_id_fk" foreign key ("_schedulerRef_id") references "mrg.grid"."Scheduler" ("_id") on update cascade on delete set null
+    ;
+alter table "mrg.grid"."Submission_stats"
+    add constraint "_parent_id_fk" foreign key ("_parent_id") references "mrg.grid"."Submission" ("_id") on update cascade on delete cascade
+    ;

Modified: mgmt/newdata/rosemary/bin/rosemary-test
===================================================================
--- mgmt/trunk/rosemary/bin/rosemary-test	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/rosemary/bin/rosemary-test	2010-03-22 14:58:08 UTC (rev 3871)
@@ -4,22 +4,25 @@
 from rosemary.sqloperation import *
 
 def do_model(args):
-    for package in model.packages:
-        print "package %s" % package.name
+    for pkg in model._packages:
+        print "package %s" % pkg._name
 
-        for cls in package.classes:
-            print "  class %s" % cls.name
+        for cls in pkg._classes:
+            print "  class %s" % cls._name
 
-            for ref in cls.references:
+            for hdr in cls._headers:
+                print "    hdr %s %s" % (hdr.name, hdr.title or "")
+
+            for ref in cls._references:
                 print "    ref %s %s" % (ref.name, ref.title or "")
 
-            for prop in cls.properties:
+            for prop in cls._properties:
                 print "    prop %s %s" % (prop.name, prop.title or "")
 
-            for stat in cls.statistics:
+            for stat in cls._statistics:
                 print "    stat %s %s" % (stat.name, stat.title or "")
 
-            for meth in cls.methods:
+            for meth in cls._methods:
                 print "    meth %s" % meth.name
 
                 for arg in meth.arguments:

Modified: mgmt/newdata/rosemary/python/rosemary/model.py
===================================================================
--- mgmt/trunk/rosemary/python/rosemary/model.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/rosemary/python/rosemary/model.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -1,5 +1,6 @@
 from sqloperation import *
 from sqlmodel import *
+from sqlquery import *
 from sqltype import *
 from util import *
 
@@ -7,11 +8,13 @@
 
 class RosemaryModel(object):
     def __init__(self):
-        self.packages = list()
-        self.packages_by_name = dict()
+        self._packages = list()
+        self._packages_by_name = dict()
 
-        self.sql_model = SqlModel()
+        self.sql_model = SqlModel(self)
 
+        self.sql_logging_enabled = False
+
     def load_xml_dir(self, path):
         assert os.path.isdir(path)
 
@@ -52,37 +55,40 @@
         finally:
             file.close()
 
+    # XXX Change to load_elem, load_extended_elem
     def load(self, elem):
         pkg = RosemaryPackage(self, elem.get("package"))
         pkg.load(elem)
 
     def extend(self, elem):
         for child in elem.findall("package"):
-            pkg = self.packages_by_name[child.get("name")]
+            pkg = self._packages_by_name[child.get("name")]
             pkg.extend(child)
 
     def init(self):
-        for pkg in self.packages:
+        for pkg in self._packages:
             pkg.init()
 
 class RosemaryPackage(object):
     def __init__(self, model, name):
-        self.model = model
-        self.name = name
+        self._model = model
+        self._name = name
 
-        self.model.packages.append(self)
-        self.model.packages_by_name[self.name] = self
+        self._model._packages.append(self)
+        self._model._packages_by_name[self._name] = self
 
-        mangled = self.name.replace(".", "_")
+        mangled = self._name.replace(".", "_")
 
-        if not hasattr(self.model, mangled):
-            setattr(self.model, mangled, self)
+        if hasattr(self._model, mangled):
+            raise Exception("Collision")
 
-        self.classes = list()
-        self.classes_by_name = dict()
+        setattr(self._model, mangled, self)
 
-        self.sql_schema = SqlSchema(self.model.sql_model, self.name)
+        self._classes = list()
+        self._classes_by_name = dict()
 
+        self.sql_schema = SqlSchema(self._model.sql_model, self._name)
+
     def load(self, elem):
         for child in elem.findall("class"):
             cls = RosemaryClass(self, child.get("name"))
@@ -90,63 +96,139 @@
 
     def extend(self, elem):
         for child in elem.findall("class"):
-            cls = self.classes_by_name[child.get("name")]
+            cls = self._classes_by_name[child.get("name")]
             cls.extend(child)
 
     def init(self):
-        for cls in self.classes:
+        for cls in self._classes:
             cls.init()
 
     def __repr__(self):
-        args = (self.__class__.__name__, self.name)
+        args = (self.__class__.__name__, self._name)
         return "%s(%s)" % args
 
 class RosemaryClass(object):
     def __init__(self, package, name):
-        self.package = package
-        self.name = name
+        self._package = package
+        self._name = name
 
-        self.package.classes.append(self)
-        self.package.classes_by_name[self.name] = self
+        self._package._classes.append(self)
+        self._package._classes_by_name[self._name] = self
 
-        if not hasattr(self.package, self.name):
-            setattr(self.package, self.name, self)
+        assert not hasattr(self._package, self._name), self.name
 
-        self.references = list()
-        self.references_by_name = dict()
+        setattr(self._package, self._name, self)
 
-        self.inbound_references = list()
+        self._title = None
+        self._object_title = None
 
-        self.properties = list()
-        self.properties_by_name = dict()
+        self._headers = list()
+        self._headers_by_name = dict()
 
-        self.statistics = list()
-        self.statistics_by_name = dict()
+        self._references = list()
+        self._references_by_name = dict()
 
-        self.methods = list()
-        self.methods_by_name = dict()
+        self._inbound_references = list()
 
-        self.sql_table = SqlTable(self.package.sql_schema, self.name)
+        self._properties = list()
+        self._properties_by_name = dict()
 
-        name = "%sStats" % self.name
+        self._statistics = list()
+        self._statistics_by_name = dict()
 
-        self.sql_stats_table = SqlTable(self.package.sql_schema, name)
+        self._methods = list()
+        self._methods_by_name = dict()
 
-        self.add_qmf_columns()
-        self.add_id_columns()
+        self.add_sql()
 
-        self.id = RosemaryAttribute(self, "id")
-        self.id.sql_column = self.sql_table.key_column
+        self._id = RosemaryAttribute(self, "_id")
+        self._id.sql_column = self.sql_table.key_column
 
-        self.sql_qmf_columns = None
+        self.add_headers()
 
-        self.sql_select = SqlSelectItem(self.sql_table)
+    def add_sql(self):
+        # Main table
+
+        self.sql_table = SqlTable(self._package.sql_schema, self._name)
+
+        id_col = SqlColumn(self.sql_table, "_id", sql_serial8)
+        self.sql_table.key_column = id_col
+
+        name = "%s_pk" % self._name
+        SqlPrimaryKeyConstraint(self.sql_table, name, (id_col,))
+
+        self.sql_select = SqlQuery(self.sql_table)
+        self.sql_select_by_id = SqlSelectItem(self.sql_table)
+        self.sql_select_by_qmf_id = SqlSelectItemByQmfId(self.sql_table)
+
         self.sql_insert = SqlInsertItem(self.sql_table)
         self.sql_update = SqlUpdateItem(self.sql_table)
         self.sql_delete = SqlDeleteItem(self.sql_table)
 
-        self.sql_select_by_object_id = SqlSelectItemByObjectId(self.sql_table)
+        # Stats table
 
+        name = "%s_stats" % self._name
+        self.sql_stats_table = SqlTable(self._package.sql_schema, name)
+
+        stats_id_col = SqlColumn(self.sql_stats_table, "_id", sql_serial8)
+        self.sql_stats_table.key_column = stats_id_col
+
+        name = "%s_pk" % self.sql_stats_table._name
+        SqlPrimaryKeyConstraint(self.sql_stats_table, name, (stats_id_col,))
+
+        name = "_parent_id"
+        parent_col = SqlColumn(self.sql_stats_table, name, sql_int8)
+        parent_col.foreign_key_column = id_col
+
+        table = self.sql_stats_table
+        name = "%s_fk" % parent_col.name
+        constraint = SqlForeignKeyConstraint(table, name, parent_col, id_col)
+        constraint.on_delete = "cascade"
+
+        name = "_qmf_update_time"
+        SqlColumn(self.sql_stats_table, name, sql_timestamp)
+
+        self.sql_stats_insert = SqlInsertItemStats(self.sql_stats_table)
+        self.sql_stats_delete = SqlDeleteItemStats(self.sql_stats_table)
+
+    def add_headers(self):
+        name = "_qmf_agent_id"
+        self._qmf_agent_id = RosemaryHeader(self, name, sql_text)
+        self._qmf_agent_id.title = "Agent ID"
+
+        name = "_qmf_object_id"
+        self._qmf_object_id = RosemaryHeader(self, name, sql_text)
+        self._qmf_object_id.title = "Object ID"
+
+        name = "_qmf_session_id"
+        self._qmf_session_id = RosemaryHeader(self, name, sql_text)
+        self._qmf_session_id.title = "Session ID"
+
+        name = "_qmf_class_key"
+        self._qmf_class_key = RosemaryHeader(self, name, sql_text)
+        self._qmf_class_key.title = "Class Key"
+
+        name = "_qmf_create_time"
+        self._qmf_create_time = RosemaryHeader(self, name, sql_timestamp)
+        self._qmf_create_time.title = "Create Time"
+
+        name = "_qmf_update_time"
+        self._qmf_update_time = RosemaryHeader(self, name, sql_timestamp)
+        self._qmf_update_time.title = "Update Time"
+
+        name = "_qmf_delete_time"
+        self._qmf_delete_time = RosemaryHeader(self, name, sql_timestamp)
+        self._qmf_delete_time.title = "Delete Time"
+
+        self._qmf_session_id.optional = True
+        self._qmf_delete_time.optional = True
+
+    def add_constraints(self):
+        name = "%s_qmf_ids_uq" % self._name
+        cols = (self._qmf_agent_id.sql_column, self._qmf_object_id.sql_column)
+
+        SqlUniqueConstraint(self.sql_table, name, cols)
+
     def load(self, elem):
         log.debug("Loading %s", self)
 
@@ -171,93 +253,155 @@
     def extend(self, elem):
         log.debug("Extending %s", self)
 
+        self._title = elem.find("title")
+        self._object_title = elem.findtext("object/title")
+
         for child in elem.findall("property"):
-            prop = self.properties_by_name[child.get("name")]
+            prop = self._properties_by_name[child.get("name")]
             prop.extend(child)
 
         for child in elem.findall("statistic"):
-            stat = self.statistics_by_name[child.get("name")]
+            stat = self._statistics_by_name[child.get("name")]
             stat.extend(child)
 
         for child in elem.findall("method"):
-            meth = self.methods_by_name[child.get("name")]
+            meth = self._methods_by_name[child.get("name")]
             meth.extend(child)
 
     def init(self):
         log.debug("Initializing %s", self)
 
-        for ref in self.references:
+        if not self._title:
+            self._title = self._name
+
+        for hdr in self._headers:
+            hdr.init()
+
+        for ref in self._references:
             ref.init()
 
-        for prop in self.properties:
+        for prop in self._properties:
             prop.init()
 
-        for stat in self.statistics:
+        for stat in self._statistics:
             stat.init()
 
-        for meth in self.methods:
+        for meth in self._methods:
             meth.init()
 
-    def add_id_columns(self):
-        id_col = SqlColumn(self.sql_table, "_id", sql_serial8)
-        self.sql_table.key_column = id_col
+        self.add_constraints()
+            
+#    def add_id_columns(self):
+        # id_col = SqlColumn(self.sql_table, "_id", sql_serial8)
+        # self.sql_table.key_column = id_col
 
-        name = "%s_pk" % self.name
-        SqlPrimaryKeyConstraint(self.sql_table, name, (id_col,))
+#        name = "%s_pk" % self._name
+#        SqlPrimaryKeyConstraint(self.sql_table, name, (id_col,))
 
-        stats_id_col = SqlColumn(self.sql_stats_table, "_id", sql_serial8)
-        self.sql_stats_table.key_column = stats_id_col
 
-        name = "%s_pk" % self.sql_stats_table.name
-        SqlPrimaryKeyConstraint(self.sql_stats_table, name, (stats_id_col,))
+#    def add_qmf_columns(self):
+#        agent = SqlColumn(self.sql_table, "_qmf_agent_id", sql_text)
+#        object = SqlColumn(self.sql_table, "_qmf_object_id", sql_text)
 
-        parent_col = SqlColumn(self.sql_stats_table, "_parent_id", sql_int8)
-        parent_col.foreign_key_column = id_col
+#        name = "%s_qmf_ids_uq" % self._name
 
-        name = "%s_fk" % parent_col.name
-        SqlForeignKeyConstraint(self.sql_stats_table, name, parent_col, id_col)
+        #SqlUniqueConstraint(self.sql_table, name, (agent, object))
 
-    def add_qmf_columns(self):
-        agent = SqlColumn(self.sql_table, "_qmf_agent_id", sql_text)
-        object = SqlColumn(self.sql_table, "_qmf_object_id", sql_text)
+        #col = SqlColumn(self.sql_table, "_qmf_session_id", sql_text)
+        #col.nullable = True
 
-        name = "%s_qmf_ids_uq" % self.name
+        #SqlColumn(self.sql_table, "_qmf_class_key", sql_text)
 
-        SqlUniqueConstraint(self.sql_table, name, (agent, object))
+        #SqlColumn(self.sql_table, "_qmf_update_time", sql_timestamp)
+        #SqlColumn(self.sql_table, "_qmf_create_time", sql_timestamp)
 
-        session = SqlColumn(self.sql_table, "_qmf_session_id", sql_text)
-        session.nullable = True
+        #col = SqlColumn(self.sql_table, "_qmf_delete_time", sql_timestamp)
+        #col.nullable = True
 
-        update = SqlColumn(self.sql_table, "_qmf_update_time", sql_timestamp)
-        create = SqlColumn(self.sql_table, "_qmf_create_time", sql_timestamp)
+        #SqlColumn(self.sql_stats_table, "_qmf_update_time", sql_timestamp)
 
-        delete = SqlColumn(self.sql_table, "_qmf_delete_time", sql_timestamp)
-        delete.nullable = True
+    def select_objects(self, cursor, **kwargs):
+        objects = list()
 
-        self.sql_qmf_columns = (agent, object, session, update, create, delete)
+        columns = self.sql_table._columns
 
-    def load_object(self, cursor, id):
-        self.sql_select.execute(cursor, self.sql_table.columns, {"id": id})
+        options = SqlQueryOptions()
 
-        return object # XXX
+        for name, value in kwargs.items():
+            column = self.sql_table._columns_by_name[name]
+            filter = SqlComparisonFilter(None, column, value)
+            options.filters.append(filter)
 
-    def save_object(self, cursor, object):
-        assert isinstance(object, RosemaryObject)
+        self.sql_select.execute(cursor, columns, kwargs, options)
 
-        values = object.__dict__
+        for record in cursor.fetchall():
+            obj = RosemaryObject(self, None)
 
-        try:
-            self.sql_update.execute(cursor, self.sql_table.columns, values)
-        except: # XXX need better exception
-            self.sql_insert.execute(cursor, self.sql_table.columns, values)
+            self.set_object_attributes(obj, columns, record)
 
-    def delete_object(self, cursor, object):
-        assert isinstance(object, RosemaryObject)
+            objects.append(obj)
 
-        self.sql_delete.execute(cursor, (), {"id": object.id})
+        return objects
 
+    def get_object(self, cursor, id):
+        assert id
+
+        object = RosemaryObject(self, id)
+
+        self.load_object(cursor, object)
+
+        return object
+
+    def create_object(self):
+        return RosemaryObject(self, None)
+
+    def load_object(self, cursor, obj):
+        assert isinstance(obj, RosemaryObject)
+
+        columns = self.sql_table._columns
+
+        if obj._id:
+            self.sql_select_by_id.execute(cursor, columns, obj.__dict__)
+        elif obj._qmf_agent_id and obj._qmf_object_id:
+            self.sql_select_by_qmf_id.execute(cursor, columns, obj.__dict__)
+        else:
+            raise Exception("No id attributes are set")
+
+        values = cursor.fetchone()
+
+        if not values:
+            raise RosemaryNotFound()
+
+        self.set_object_attributes(obj, columns, values)
+
+    def set_object_attributes(self, obj, columns, values):
+        assert isinstance(obj, RosemaryObject)
+
+        for column, value in zip(columns, values):
+            setattr(obj, column.name, value)
+
+    def save_object(self, cursor, obj, columns=None):
+        assert isinstance(obj, RosemaryObject)
+
+        if not columns:
+            columns = list(self.sql_table._columns)
+            columns.remove(self.sql_table._id)
+
+        if obj._id is None:
+            self.sql_insert.execute(cursor, columns, obj.__dict__)
+
+            obj._id = cursor.fetchone()[0]
+        else:
+            self.sql_update.execute(cursor, columns, obj.__dict__)
+
+    def delete_object(self, cursor, obj):
+        assert isinstance(obj, RosemaryObject)
+        assert obj._id
+
+        self.sql_delete.execute(cursor, (), obj.__dict__)
+
     def __repr__(self):
-        args = (self.__class__.__name__, self.package.name, self.name)
+        args = (self.__class__.__name__, self._package._name, self._name)
         return "%s(%s,%s)" % args
 
 class RosemaryAttribute(object):
@@ -265,15 +409,16 @@
         self.cls = cls
         self.name = name
 
-        if not hasattr(self.cls, self.name):
-            setattr(self.cls, self.name, self)
+        assert not hasattr(self.cls, self.name), self.name
 
+        setattr(self.cls, self.name, self)
+
         self.type = None
         self.references = None
         self.access = None
         self.unit = None
-        self.is_index = None
-        self.is_optional = None
+        self.index = None
+        self.optional = None
 
         self.title = None
         self.description = None
@@ -285,41 +430,64 @@
         self.references = elem.get("references")
         self.access = elem.get("access")
         self.unit = elem.get("unit")
-        self.is_index = elem.get("index", "n") == "y" and True
-        self.is_optional = elem.get("optional", "n") == "y" and True
+        self.index = elem.get("index", "n") == "y" and True
+        self.optional = elem.get("optional", "n") == "y" and True
         self.description = elem.get("desc")
 
     def extend(self, elem):
         self.title = elem.findtext("title")
 
+    def init(self):
+        if not self.title:
+            self.title = self.name
+
     def __repr__(self):
-        args = (self.__class__.__name__, self.cls.name, self.name)
+        args = (self.__class__.__name__, self.cls._name, self.name)
         return "%s(%s,%s)" % args
 
+class RosemaryHeader(RosemaryAttribute):
+    def __init__(self, cls, name, type):
+        super(RosemaryHeader, self).__init__(cls, name)
+
+        self.type = type
+
+        self.cls._headers.append(self)
+        self.cls._headers_by_name[self.name] = self
+
+    def init(self):
+        super(RosemaryHeader, self).init()
+
+        assert not self.references
+
+        self.sql_column = SqlColumn(self.cls.sql_table, self.name, self.type)
+        self.sql_column.nullable = self.optional
+
 class RosemaryReference(RosemaryAttribute):
     def __init__(self, cls, name):
         super(RosemaryReference, self).__init__(cls, name)
 
-        self.cls.references.append(self)
-        self.cls.references_by_name[self.name] = self
+        self.cls._references.append(self)
+        self.cls._references_by_name[self.name] = self
 
         self.that_cls = None
 
     def init(self):
+        super(RosemaryReference, self).init()
+
         assert self.references
 
         tokens = self.references.split(":", 1)
 
         if len(tokens) == 2:
-            pkg = self.cls.package.model.packages_by_name[tokens[0]]
+            pkg = self.cls._package._model._packages_by_name[tokens[0]]
             cls = tokens[1]
         else:
-            pkg = self.cls.package
+            pkg = self.cls._package
             cls = tokens[0]
 
         try:
-            self.that_cls = pkg.classes_by_name[cls]
-            self.that_cls.inbound_references.append(self)
+            self.that_cls = pkg._classes_by_name[cls]
+            self.that_cls._inbound_references.append(self)
         except KeyError:
             log.error("Reference to '%s' invalid", self.references)
 
@@ -327,11 +495,11 @@
 
         name = "_%s_id" % self.name
 
-        col = SqlColumn(self.cls.sql_table, name, sql_int4)
+        col = SqlColumn(self.cls.sql_table, name, sql_int8)
         col.foreign_key_column = self.that_cls.sql_table.key_column
+        col.nullable = True
 
         self.sql_column = col
-        self.sql_column.nullable = self.is_optional
 
         name = "%s_fk" % col.name
 
@@ -341,38 +509,46 @@
     def __init__(self, cls, name):
         super(RosemaryProperty, self).__init__(cls, name)
 
-        self.cls.properties.append(self)
-        self.cls.properties_by_name[self.name] = self
+        self.cls._properties.append(self)
+        self.cls._properties_by_name[self.name] = self
 
     def init(self):
+        super(RosemaryProperty, self).init()
+
         assert not self.references
 
         type = sql_types_by_qmf_type_string[self.type]
 
         self.sql_column = SqlColumn(self.cls.sql_table, self.name, type)
-        self.sql_column.nullable = self.is_optional
+        self.sql_column.nullable = self.optional
 
 class RosemaryStatistic(RosemaryAttribute):
     def __init__(self, cls, name):
         super(RosemaryStatistic, self).__init__(cls, name)
 
-        self.cls.statistics.append(self)
-        self.cls.statistics_by_name[self.name] = self
+        self.cls._statistics.append(self)
+        self.cls._statistics_by_name[self.name] = self
 
     def init(self):
+        super(RosemaryStatistic, self).init()
+
         assert not self.references
 
         type = sql_types_by_qmf_type_string[self.type]
 
-        self.sql_column = SqlColumn(self.cls.sql_stats_table, self.name, type)
+        self.sql_column = SqlColumn(self.cls.sql_table, self.name, type)
+        self.sql_column.nullable = True
 
+        col = SqlColumn(self.cls.sql_stats_table, self.name, type)
+        col.nullable = self.optional
+
 class RosemaryMethod(object):
     def __init__(self, cls, name):
         self.cls = cls
         self.name = name
 
-        self.cls.methods.append(self)
-        self.cls.methods_by_name[self.name] = self
+        self.cls._methods.append(self)
+        self.cls._methods_by_name[self.name] = self
 
         self.description = None
 
@@ -393,9 +569,6 @@
         for arg in self.arguments:
             arg.init()
 
-    def call(self, console, object, callback, **kwargs):
-        pass
-
 class RosemaryArgument(object):
     def __init__(self, meth, name):
         self.meth = meth
@@ -420,20 +593,30 @@
         pass
 
 class RosemaryObject(object):
-    def __init__(self, cls):
-        self.cls = cls
+    def __init__(self, cls, id):
+        for column in cls.sql_table._columns:
+            setattr(self, column.name, None)
 
-        self.id = None
-        self.qmf_agent_id = None
-        self.qmf_object_id = None
+        self._class = cls
+        self._id = id
 
-        for name in self.cls.properties:
-            setattr(name, None)
+    # XXX prefix these with _
+    def load(self, cursor):
+        self._class.load_object(cursor, self)
 
-        self.sql_insert = SqlInsert(self.cls.sql_table)
+    def save(self, cursor, columns=None):
+        self._class.save_object(cursor, self, columns)
 
-    def load(self, cursor, id):
-        pass # XXX self.__select.execute(cursor, {"id": id})
+    def delete(self, cursor):
+        self._class.delete_object(cursor, self)
 
-    def save(self, cursor):
-        assert self.id is not None
+    def get_title(self):
+        if self._class._object_title:
+            return self._class._object_title % self.__dict__
+
+        for attr in ("name", "Name"):
+            if hasattr(self, attr):
+                return getattr(self, attr)
+
+class RosemaryNotFound(Exception):
+    pass

Modified: mgmt/newdata/rosemary/python/rosemary/sqlmodel.py
===================================================================
--- mgmt/trunk/rosemary/python/rosemary/sqlmodel.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/rosemary/python/rosemary/sqlmodel.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -1,93 +1,106 @@
 from util import *
 
 class SqlModel(object):
-    def __init__(self):
-        self.schemas = list()
-        self.schemas_by_name = dict()
+    def __init__(self, model):
+        self._model = model
 
-        self.relations = list()
-        self.relations_by_name = dict()
+        self._schemas = list()
+        self._schemas_by_name = dict()
 
     def write_create_ddl(self, out):
-        for schema in self.schemas:
+        for schema in self._schemas:
             schema.write_create_ddl(out)
 
-        for schema in self.schemas:
-            for table in schema.tables:
+        for schema in self._schemas:
+            for table in schema._tables:
                 table.write_alter_dll(out)
 
     def write_drop_ddl(self, out):
-        for schema in self.schemas:
+        for schema in self._schemas:
             schema.write_drop_ddl(out)
 
 class SqlSchema(object):
     def __init__(self, model, name):
-        self.model = model
-        self.name = name
+        self._model = model
+        self._name = name
 
-        self.model.schemas.append(self)
-        self.model.schemas_by_name[self.name] = self
+        self._model._schemas.append(self)
+        self._model._schemas_by_name[self._name] = self
 
-        mangled = self.name.replace(".", "_")
+        mangled = self._name.replace(".", "_")
 
-        if not hasattr(self.model, mangled):
-            setattr(self.model, mangled, self)
+        if hasattr(self._model, mangled):
+            raise Exception("Collision")
 
-        self.identifier = "\"%s\"" % self.name
+        setattr(self._model, mangled, self)
 
-        self.tables = list()
-        self.tables_by_name = dict()
+        # XXX _ this one too
+        self.identifier = "\"%s\"" % self._name
 
-        self.indexes = list()
-        self.indexes_by_name = dict()
+        self._tables = list()
+        self._tables_by_name = dict()
 
+        self._indexes = list()
+        self._indexes_by_name = dict()
+
+        self._views = list()
+        self._views_by_name = dict()
+
     def write_create_ddl(self, out):
         out.write("create schema %s\n" % self.identifier)
 
-        for table in self.tables:
+        for table in self._tables:
             table.write_create_ddl(out)
 
-        for index in self.indexes:
+        for index in self._indexes:
             index.write_create_ddl(out)
 
+        for view in self._views:
+            view.write_create_ddl(out)
+
         out.write("    ;\n")
 
     def write_drop_ddl(self, out):
         out.write("drop schema %s cascade;\n" % self.identifier)
 
     def __repr__(self):
-        args = (self.__class__.__name__, self.name)
+        args = (self.__class__.__name__, self._name)
         return "%s(%s)" % args
 
 class SqlTable(object):
     def __init__(self, schema, name):
-        self.schema = schema
-        self.name = name
+        assert isinstance(schema, SqlSchema)
 
-        self.schema.tables.append(self)
-        self.schema.tables_by_name[self.name] = self
+        self._schema = schema
+        self._name = name
 
-        if not hasattr(self.schema, self.name):
-            setattr(self.schema, self.name, self)
+        self._schema._tables.append(self)
+        self._schema._tables_by_name[self._name] = self
 
-        self.identifier = "%s.\"%s\"" % (self.schema.identifier, self.name)
+        if hasattr(self._schema, self._name):
+            raise Exception("Collision")
+
+        setattr(self._schema, self._name, self)
+
+        # XXX _ these as well
+        self.identifier = "%s.\"%s\"" % (self._schema.identifier, self._name)
         self.key_column = None
 
-        self.columns = list()
-        self.columns_by_name = dict()
+        self._columns = list()
+        self._columns_by_name = dict()
 
-        self.constraints = list()
-        self.deferred_constraints = list()
+        self._constraints = list()
+        self._deferred_constraints = list()
 
     def write_create_ddl(self, out):
-        out.write("    create table \"%s\" (" % self.name)
+        out.write("    create table \"%s\" (" % self._name)
 
         exprs = list()
 
-        for col in self.columns:
+        for col in self._columns:
             exprs.append(col.get_ddl())
 
-        for constraint in self.constraints:
+        for constraint in self._constraints:
             exprs.append(constraint.get_ddl())
 
         exprs = ["\n        %s" % x.strip() for x in exprs]
@@ -97,19 +110,19 @@
         out.write("\n        )\n")
 
     def write_alter_dll(self, out):
-        if not self.deferred_constraints:
+        if not self._deferred_constraints:
             return
 
         out.write("alter table %s\n    " % self.identifier)
 
-        constraints = [x.get_ddl() for x in self.deferred_constraints]
+        constraints = [x.get_ddl() for x in self._deferred_constraints]
 
         out.write(",\n    ".join(constraints))
 
         out.write("\n    ;\n")
 
     def __repr__(self):
-        args = (self.__class__.__name__, self.schema.name, self.name)
+        args = (self.__class__.__name__, self._schema._name, self._name)
         return "%s(%s,%s)" % args
 
 class SqlColumn(object):
@@ -118,14 +131,16 @@
         self.name = name
         self.type = type
 
-        self.table.columns.append(self)
-        self.table.columns_by_name[self.name] = self
+        self.table._columns.append(self)
+        self.table._columns_by_name[self.name] = self
 
-        if not hasattr(self.table, self.name):
-            setattr(self.table, self.name, self)
+        if hasattr(self.table, self.name):
+            raise Exception("%s already has %s" % (self.table, self.name))
 
-        self.identifier = "\"%s\".\"%s\"" % (self.table.name, self.name)
+        setattr(self.table, self.name, self)
 
+        self.identifier = "\"%s\".\"%s\"" % (self.table._name, self.name)
+
         self.nullable = False
         self.foreign_key_column = None
 
@@ -141,7 +156,7 @@
         return " ".join(tokens)
 
     def __repr__(self):
-        args = (self.__class__.__name__, self.table.name, self.name)
+        args = (self.__class__.__name__, self.table._name, self.name)
         return "%s(%s,%s)" % args
 
 class SqlTableConstraint(object):
@@ -150,11 +165,11 @@
         self.name = name
         self.columns = columns
 
-        self.table.constraints.append(self)
+        self.table._constraints.append(self)
 
 class SqlPrimaryKeyConstraint(SqlTableConstraint):
     def get_ddl(self):
-        cols = "\"%s\"" % "\", \"".join([x.name for x in self.columns])
+        cols = ", ".join(["\"%s\"" % x.name for x in self.columns])
 
         return "constraint \"%s\" primary key (%s)" % (self.name, cols)
 
@@ -171,8 +186,11 @@
         self.this_column = this_column
         self.that_column = that_column
 
-        self.table.deferred_constraints.append(self)
+        self.table._deferred_constraints.append(self)
 
+        self.on_update = "cascade"
+        self.on_delete = "set null"
+
     def get_ddl(self):
         tokens = list()
 
@@ -180,8 +198,8 @@
         tokens.append("foreign key (\"%s\")" % self.this_column.name)
         tokens.append("references %s" % self.that_column.table.identifier)
         tokens.append("(\"%s\")" % self.that_column.name)
-
-        # XXX tokens.append("on delete set null;\n")
+        tokens.append("on update %s" % self.on_update)
+        tokens.append("on delete %s" % self.on_delete)
         
         return " ".join(tokens)
 
@@ -193,11 +211,27 @@
         self.name = name
         self.columns = columns
 
-        self.schema.indexes.append(self)
-        self.schema.indexes_by_name[self.name] = self
+        self.schema._indexes.append(self)
+        self.schema._indexes_by_name[self.name] = self
 
     def write_create_ddl(self, out):
-        cols = "\"%s\"" % "\", \"".join([x.name for x in self.columns])
+        cols = ", ".join(["\"%s\"" % x.name for x in self.columns])
         args = (self.name, self.columns[0].table.name, cols)
 
         out.write("    create index \"%s\" on \"%s\"(%s)\n" % args)
+
+class SqlView(object):
+    def __init__(self, schema, name, query):
+        self.schema = schema
+        self.name = name
+        self.query = query
+
+        self.schema._views.append(self)
+        self.schema._views_by_name[self.name] = self
+
+        self.identifier = "%s.\"%s\"" % (self.schema.identifier, self.name)
+
+    def write_create_ddl(self, out):
+        query = self.query.emit(("*",))
+
+        out.write("    create view \"%s\" as %s" % (self.name, query))

Modified: mgmt/newdata/rosemary/python/rosemary/sqloperation.py
===================================================================
--- mgmt/trunk/rosemary/python/rosemary/sqloperation.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/rosemary/python/rosemary/sqloperation.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -7,137 +7,100 @@
     def __init__(self, table):
         self.table = table
 
-    def execute(self, cursor, columns, values, options=None):
+    def execute(self, cursor, columns, values=None, options=None):
         text = self.emit(columns, options)
 
-        log.debug("Sql text:\n%s", text)
-        log.debug("Sql values: %s", values)
+        if values is None:
+            values = {}
 
-        cursor.execute(text, values)
+        if self.table._schema._model._model.sql_logging_enabled:
+            self.log_sql(cursor, text, values)
 
+        try:
+            cursor.execute(text, values)
+        except:
+            log.exception("%s failed", self)
+
+            self.log_sql(cursor, text, values)
+
+            raise
+
+    def log_sql(self, cursor, text, values):
+        log.info("Sql text: %s", text)
+        log.info("Sql values:")
+
+        for item in sorted(values.items()):
+            log.info("    %-34s  %r", *item)
+
+        # XXX log.info("Sql row count: %i", cursor.rowcount)
+
+    def __repr__(self):
+        return "%s(%s)" % (self.__class__.__name__, self.table)
+
 class SqlSelectItem(SqlOperation):
     def emit(self, columns, options=None):
         cols = ", ".join([x.identifier for x in columns])
-        args = (cols, self.table.identifier)
+        table = getattr(self.table, "identifier", self.table)
 
-        return "select %s from %s where _id = %%(id)s" % args
+        return "select %s from %s where _id = %%(_id)s" % (cols, table)
 
-class SqlSelectItemByObjectId(SqlOperation):
+class SqlSelectItemByQmfId(SqlOperation):
     def emit(self, columns, options=None):
         cols = ", ".join([x.identifier for x in columns])
-        args = (cols, self.table.identifier)
+        table = getattr(self.table, "identifier", self.table)
 
-        return "select %s from %s where _qmf_object_id = %%(_qmf_object_id)s" \
-            % args
+        exprs = list()
+        exprs.append("_qmf_agent_id = %(_qmf_agent_id)s")
+        exprs.append("_qmf_object_id = %(_qmf_object_id)s")
+        exprs = " and ".join(exprs)
 
+        return "select %s from %s where %s" % (cols, table, exprs)
+
 class SqlInsertItem(SqlOperation):
     def emit(self, columns, options=None):
+        table = getattr(self.table, "identifier", self.table)
         names = [x.name for x in columns]
         cols = ", ".join(["\"%s\"" % x for x in names])
         vals = ", ".join(["%%(%s)s" % x for x in names])
-        args = (self.table.identifier, cols, vals)
 
-        return "insert into %s (%s) values (%s)" % args
+        args = (table, cols, vals)
 
-class SqlUpdateItem(SqlOperation):
-    def emit(self, columns, options=None):
-        exprs = ["\"%s\" = %%(%s)s" % (x.name, x.name) for x in columns]
-        exprs = ", ".join(exprs)
-        args = (self.table.identifier, exprs)
+        return "insert into %s (%s) values (%s) returning _id" % args
 
-        return "update %s set %s where _id = %%(id)s" % args
-
-class SqlDeleteItem(SqlOperation):
+class SqlInsertItemStats(SqlOperation):
     def emit(self, columns, options=None):
-        return "delete from %s where _id = %%(id)s" % self.table.identifier
+        table = getattr(self.table, "identifier", self.table)
+        names = [x.name for x in columns]
 
-class SqlQuery(SqlOperation):
-    def __init__(self, table):
-        super(SqlQuery, self).__init__(table)
+        cols = ["\"%s\"" % x for x in names]
+        vals = ["%%(%s)s" % x for x in names]
 
-        self.order_by = self.OrderBy()
-        self.limit = self.Limit()
+        cols.append("\"_parent_id\"")
+        vals.append("%(_id)s")
 
-        self.joins = list()
+        cols = ", ".join(cols)
+        vals = ", ".join(vals)
 
-    def emit(self, columns, options):
-        tokens = list()
-
-        cols = list()
-
-        for column in columns:
-            if isinstance(column, SqlColumn):
-                cols.append(column.identifier)
-            else:
-                cols.append(str(column))
-
-        tokens.append("select %s" % ", ".join(cols))
-
-        tokens.append("from %s" % self.table.identifier)
-
-        for join in self.joins:
-            tokens.append(join.emit())
-
-        if options:
-            if options.sort_column:
-                tokens.append(self.order_by.emit(options.sort_column,
-                                                 options.sort_ascending))
-
-            tokens.append(self.limit.emit(options.limit, options.offset))
-
-        return "%s\n" % "\n".join(tokens)
-
-    class OrderBy(object):
-        def emit(self, column, ascending):
-            if ascending:
-                direction = "asc"
-            else:
-                direction = "desc"
-
-            return "order by %s %s" % (column.identifier, direction)
-
-    class Limit(object):
-        def emit(self, limit, offset):
-            if limit is None:
-                limit = "all"
-
-            return "limit %s offset %i" % (str(limit), offset)
-
-class SqlQueryOptions(object):
-    def __init__(self):
-        self.sort_column = None
-        self.sort_ascending = True
-        self.limit = None
-        self.offset = 0
+        return "insert into %s (%s) values (%s)" % (table, cols, vals)
         
-class SqlQueryJoin(object):
-    def __init__(self, query, this_column, that_column):
-        assert query
-        assert this_column
-        assert that_column
+class SqlUpdateItem(SqlOperation):
+    def emit(self, columns, options=None):
+        table = getattr(self.table, "identifier", self.table)
+        exprs = ["\"%s\" = %%(%s)s" % (x.name, x.name) for x in columns]
+        exprs = ", ".join(exprs)
 
-        self.query = query
-        self.this_column = this_column
-        self.that_column = that_column
+        return "update %s set %s where _id = %%(_id)s" % (table, exprs)
 
-        assert self not in self.query.joins
+class SqlDeleteItem(SqlOperation):
+    def emit(self, columns, options=None):
+        table = getattr(self.table, "identifier", self.table)
 
-        self.query.joins.append(self)
+        return "delete from %s where _id = %%(_id)s" % table
 
-class SqlInnerJoin(SqlQueryJoin):
-    def emit(self):
-        table = self.that_column.table.identifier
-        this = self.this_column.identifier
-        that = self.that_column.identifier
-        args = (table, this, that)
+class SqlDeleteItemStats(SqlOperation):
+    def emit(self, columns, options=None):
+        table = getattr(self.table, "identifier", self.table)
 
-        return "inner join %s on %s = %s" % args
+        expr = "_qmf_update_time < now() - interval '%(seconds)s seconds'"
 
-class SqlOuterJoin(SqlQueryJoin):
-    def emit(self):
-        table = self.that_column.table.identifier
-        this = self.this_column.identifier
-        that = self.that_column.identifier
-        args = (table, this, that)
-
-        return "left outer join %s on %s = %s" % args
+        return "delete from %s where %s" % (table, expr)

Added: mgmt/newdata/rosemary/python/rosemary/sqlquery.py
===================================================================
--- mgmt/newdata/rosemary/python/rosemary/sqlquery.py	                        (rev 0)
+++ mgmt/newdata/rosemary/python/rosemary/sqlquery.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,139 @@
+from sqlmodel import *
+from sqloperation import *
+from util import *
+
+log = logging.getLogger("rosemary.sqlquery")
+
+class SqlQuery(SqlOperation):
+    def __init__(self, table):
+        super(SqlQuery, self).__init__(table)
+
+        self.order_by = self.OrderBy()
+        self.limit = self.Limit()
+
+        self.joins = list()
+        self.filters = list()
+
+    def emit(self, columns, options=None):
+        tokens = list()
+
+        cols = list()
+
+        for column in columns:
+            cols.append(getattr(column, "identifier", column))
+
+        tokens.append("select %s" % ", ".join(cols))
+
+        table = getattr(self.table, "identifier", self.table)
+
+        tokens.append("from %s" % table)
+
+        for join in self.joins:
+            tokens.append(join.emit())
+
+        filters = list()
+
+        if self.filters:
+            filters.extend([x.emit() for x in self.filters])
+
+        if options and options.filters:
+            filters.extend([x.emit() for x in options.filters])
+
+        if filters:
+            tokens.append("where %s" % " and ".join(filters))
+
+        if options:
+            if options.sort_column:
+                tokens.append(self.order_by.emit(options.sort_column,
+                                                 options.sort_ascending))
+
+            tokens.append(self.limit.emit(options.limit, options.offset))
+
+        return " ".join(tokens)
+
+    class OrderBy(object):
+        def emit(self, column, ascending):
+            if ascending:
+                direction = "asc"
+            else:
+                direction = "desc"
+
+            column = getattr(column, "identifier", column)
+
+            return "order by %s %s" % (column, direction)
+
+    class Limit(object):
+        def emit(self, limit, offset):
+            if limit is None:
+                limit = "all"
+
+            return "limit %s offset %i" % (str(limit), offset)
+
+class SqlQueryOptions(object):
+    def __init__(self):
+        self.sort_column = None
+        self.sort_ascending = True
+        self.limit = None
+        self.offset = 0
+        self.filters = list()
+
+class SqlQueryJoin(object):
+    def __init__(self, query, table, this, that):
+        assert query
+        assert table
+        assert this
+        assert that
+        
+        self.query = query
+        self.table = getattr(table, "identifier", table)
+        self.this = getattr(this, "identifier", this)
+        self.that = getattr(that, "identifier", that)
+
+        assert self not in self.query.joins
+        self.query.joins.append(self)
+
+class SqlInnerJoin(SqlQueryJoin):
+    def emit(self):
+        args = (self.table, self.this, self.that)
+
+        return "inner join %s on %s = %s" % args
+
+class SqlOuterJoin(SqlQueryJoin):
+    def emit(self):
+        args = (self.table, self.this, self.that)
+
+        return "left outer join %s on %s = %s" % args
+
+class SqlQueryFilter(object):
+    def __init__(self, query):
+        if query:
+            assert isinstance(query, SqlQuery), query
+
+        self.query = query
+
+        if self.query:
+            assert self not in self.query.filters
+            self.query.filters.append(self)
+
+class SqlComparisonFilter(SqlQueryFilter):
+    def __init__(self, query, this, that, operator="="):
+        super(SqlComparisonFilter, self).__init__(query)
+
+        assert isinstance(operator, str)
+
+        self.this = getattr(this, "identifier", this)
+        self.that = getattr(that, "identifier", that)
+        self.operator = operator
+
+    def emit(self):
+        return "%s %s %s" % (self.this, self.operator, self.that)
+
+class SqlExistenceFilter(SqlQueryFilter):
+    def __init__(self, query, subquery, operator="exists"):
+        super(SqlExistenceFilter, self).__init__(query)
+
+        self.subquery = subquery
+        self.operator = operator
+
+    def emit(self):
+        return "%s (%s)" % (self.operator, self.subquery)

Modified: mgmt/newdata/rosemary/xml/condor.xml
===================================================================
--- mgmt/trunk/rosemary/xml/condor.xml	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/rosemary/xml/condor.xml	2010-03-22 14:58:08 UTC (rev 3871)
@@ -198,17 +198,19 @@
     <property name="LastFetchWorkCompleted"
 	       type="absTime" unit="nanosecond"
 	       desc="Number of nanoseconds since epoch when the
-		     FetchWork Hook returned"/>
-
+		     FetchWork Hook returned"
+               optional="y"/>
     <property name="LastFetchWorkSpawned"
 	       type="absTime" unit="nanosecond"
 	       desc="Number of nanoseconds since epoch when the
-		     FetchWork Hook was invoked"/>
+		     FetchWork Hook was invoked"
+               optional="y"/>
     <property name="LastPeriodicCheckpoint"
 	       type="absTime" unit="nanosecond"
 	       desc="The number of nanoseconds since epoch when the
 		     job last performed a periodic checkpoint, only
-		     present when a job is executing"/>
+		     present when a job is executing"
+               optional="y"/>
 <!--
     <statistic name="LastHeardFrom"
 	       type="absTime" unit="nanosecond"

Added: mgmt/newdata/rosemary/xml/cumin.xml
===================================================================
--- mgmt/newdata/rosemary/xml/cumin.xml	                        (rev 0)
+++ mgmt/newdata/rosemary/xml/cumin.xml	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,11 @@
+<schema package="com.redhat.cumin">
+  <class name="BrokerGroup">
+    <property name="name" type="sstr"/>
+    <property name="description" type="lstr" optional="y"/>
+  </class>
+
+  <class name="BrokerGroupMapping">
+    <property name="broker" type="objId" references="org.apache.qpid.broker:Broker"/>
+    <property name="group" type="objId" references="BrokerGroup"/>
+  </class>
+</schema>

Modified: mgmt/newdata/rosemary/xml/qpid.xml
===================================================================
--- mgmt/trunk/rosemary/xml/qpid.xml	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/rosemary/xml/qpid.xml	2010-03-22 14:58:08 UTC (rev 3871)
@@ -152,7 +152,7 @@
     <statistic name="consumerCount"       type="hilo32"   unit="consumer"    desc="Current consumers on queue"/>
     <statistic name="bindingCount"        type="hilo32"   unit="binding"     desc="Current bindings"/>
     <statistic name="unackedMessages"     type="hilo32"   unit="message"     desc="Messages consumed but not yet acked"/>
-    <statistic name="messageLatency"      type="mmaTime"  unit="nanosecond"  desc="Broker latency through this queue"/>
+    <statistic name="messageLatency"      type="mmaTime"  unit="nanosecond"  desc="Broker latency through this queue" optional="y"/>
 
     <method name="purge" desc="Discard all or some messages on a queue">
       <arg name="request" dir="I" type="uint32" desc="0 for all messages or n>0 for n messages"/>
@@ -236,6 +236,7 @@
     <property name="remoteProcessName"  type="sstr"   access="RO" optional="y" desc="Name of executable running as remote client"/>
     <property name="remotePid"          type="uint32" access="RO" optional="y" desc="Process ID of remote client"/>
     <property name="remoteParentPid"    type="uint32" access="RO" optional="y" desc="Parent Process ID of remote client"/>
+    <property name="shadow"             type="bool"   access="RO" desc="True for shadow connections"/>
     <statistic name="closing"          type="bool" desc="This client is closing by management request"/>
     <statistic name="framesFromClient" type="count64"/>
     <statistic name="framesToClient"   type="count64"/>

Modified: mgmt/newdata/rosemary/xml/rosemary.xml
===================================================================
--- mgmt/trunk/rosemary/xml/rosemary.xml	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/rosemary/xml/rosemary.xml	2010-03-22 14:58:08 UTC (rev 3871)
@@ -1,13 +1,117 @@
 <model>
   <package name="org.apache.qpid.broker">
+    <class name="Binding">
+      <property name="bindingKey">
+        <title>Binding Key</title>
+      </property>
+
+      <property name="arguments">
+        <title>Arguments</title>
+      </property>
+
+      <property name="origin">
+        <title>Origin</title>
+      </property>
+
+      <statistic name="msgMatched">
+        <title>Messages Matched</title>
+      </statistic>
+    </class>
+
+    <class name="Broker">
+      <object>
+        <title>host:%(port)s</title>
+      </object>
+
+      <property name="port">
+        <title>Port</title>
+      </property>
+    </class>
+
     <class name="Connection">
       <property name="remotePid">
         <title>Process ID</title>
+        <!-- value -->
       </property>
 
       <property name="remoteParentPid">
         <title>Parent PID</title>
       </property>
     </class>
+
+    <class name="Exchange">
+      <property name="name">
+        <title>Name</title>
+      </property>
+
+      <statistic name="producerCount">
+        <title>Producers</title>
+      </statistic>
+
+      <statistic name="bindingCount">
+        <title>Bindings</title>
+      </statistic>
+
+      <statistic name="msgRoutes">
+        <title>Messages Routed</title>
+      </statistic>
+
+      <statistic name="byteRoutes">
+        <title>Bytes Routed</title>
+      </statistic>
+    </class>
+
+    <class name="Queue">
+      <property name="name">
+        <title>Name</title>
+      </property>
+
+      <statistic name="consumerCount">
+        <title>Consumers</title>
+      </statistic>
+
+      <statistic name="bindingCount">
+        <title>Bindings</title>
+      </statistic>
+
+      <statistic name="msgDepth">
+        <title>Messages</title>
+      </statistic>
+
+      <statistic name="byteDepth">
+        <title>Bytes</title>
+      </statistic>
+    </class>
+
+    <class name="System">
+      <property name="nodeName">
+        <title>Host</title>
+      </property>
+    </class>
   </package>
+
+  <package name="org.apache.qpid.cluster">
+    <class name="Cluster">
+      <property name="clusterName">
+        <title>Cluster</title>
+      </property>
+    </class>
+  </package>
+
+  <package name="com.redhat.cumin">
+    <class name="BrokerGroup">
+      <title>Broker Group</title>
+
+      <property name="name">
+        <title>Name</title>
+      </property>
+
+      <property name="description">
+        <title>Description</title>
+      </property>
+    </class>
+  </package>
+
+  <package name="com.redhat.sesame">
+  </package>
 </model>

Modified: mgmt/newdata/wooly/python/wooly/__init__.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/__init__.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/wooly/python/wooly/__init__.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -32,8 +32,8 @@
         else:
             self.path = self.name
 
-    def check_me_merrily(self):
-        log.debug("Checking %s", self)
+    def seal(self):
+        log.debug("Sealing %s", self)
 
     def validate(self, session):
         value = self.get(session)
@@ -144,9 +144,6 @@
         for child in self.children:
             child.init()
 
-        assert not self.sealed
-        self.sealed = True
-
     def init_computed_values(self):
         ancestors = list()
         widget = self.parent
@@ -168,29 +165,71 @@
                 self.frame = widget
                 break
 
-    def check_me_merrily(self):
-        assert self.sealed
-        assert self.path is not None
-        assert isinstance(self.page, Page)
+        if not self.html_class:
+            self.init_html_class()
 
+    def init_html_class(self):
+        tokens = list()
+
+        if self.page.page_html_class:
+            tokens.append(self.page.page_html_class)
+
+        for cls in self.__class__.__mro__:
+            tokens.append(cls.__name__)
+
+            if cls is Widget:
+                break
+
+        if not tokens:
+            tokens.append("_")
+
+        self.html_class = " ".join(tokens)
+
+    def seal(self):
+        assert not self.sealed
+        self.sealed = True
+
+        assert self.path is not None, self
+        assert isinstance(self.page, Page), self.page
+
         for attr in self.attributes:
-            attr.check_me_merrily()
+            attr.seal()
 
         for param in self.attributes:
-            param.check_me_merrily()
+            param.seal()
 
         for child in self.children:
-            child.check_me_merrily()
+            child.seal()
 
     def add_child(self, child):
-        assert not self.sealed
-        assert isinstance(child, Widget)
-        assert child is not self
+        assert not self.sealed, self
+        assert isinstance(child, Widget), "%s %s" % (self, child)
+        assert child is not self, self
+        assert child not in self.children, self
 
+        # XXX assert child.name not in self.children_by_name
+        if child.name in self.children_by_name:
+            log.error("%s is already in %s", child.name, self)
+
         self.children.append(child)
         self.children_by_name[child.name] = child
         child.parent = self
 
+    def remove_child(self, child):
+        assert not self.sealed
+        assert isinstance(child, Widget)
+        assert child is not self
+
+        self.children.remove(child)
+        del self.children_by_name[child.name]
+        child.parent = None
+
+    def replace_child(self, child):
+        existing = self.children_by_name[child.name]
+
+        self.remove_child(existing)
+        self.add_child(child)
+
     def add_attribute(self, attribute):
         assert not self.sealed
         assert isinstance(attribute, Attribute)
@@ -251,14 +290,11 @@
         for child in self.children:
             child.save_parameters(session, params)
 
-    def get_args(self, session):
-        return ()
-
-    def process(self, session):
+    def process(self, session, *args):
         if self.app.devel_enabled:
             profile = self.page.profile.get(session)
 
-            call = ProcessCall(profile, self)
+            call = ProcessCall(profile, self, args)
             call.do(session)
         else:
             # XXX these should move into do_process
@@ -269,21 +305,19 @@
             if self.defer_enabled:
                 self.page.enable_defer(session, self)
 
-            args = self.get_args(session)
             self.do_process(session, *args)
 
     def do_process(self, session, *args):
         for child in self.children:
             child.process(session)
 
-    def render(self, session):
+    def render(self, session, *args):
         if self.app.devel_enabled:
             profile = self.page.profile.get(session)
 
-            call = RenderCall(profile, self)
+            call = RenderCall(profile, self, args)
             string = call.do(session)
         else:
-            args = self.get_args(session)
             string = self.do_render(session, *args)
 
             if string is None:
@@ -305,7 +339,7 @@
         return self.path
 
     def render_class(self, session, *args):
-        return self.html_class or "_"
+        return self.html_class
 
     def render_href(self, session, *args):
         return session.marshal()
@@ -357,6 +391,8 @@
     def __init__(self, app, name):
         super(Page, self).__init__(app, name)
 
+        self.page_html_class = None
+
         self.page_widgets = list()
         self.page_widgets_by_path = dict()
 
@@ -375,6 +411,9 @@
         self.page = self
         self.frame = self
 
+        if not self.html_class:
+            self.init_html_class()
+
     def init_widget(self, widget):
         assert not self.sealed
         assert isinstance(widget, Widget)
@@ -445,7 +484,7 @@
     def do_init(self):
         for page in self.pages:
             page.init()
-            page.check_me_merrily()
+            page.seal()
 
     def add_page(self, page):
         if page.parent:

Added: mgmt/newdata/wooly/python/wooly/datatable.py
===================================================================
--- mgmt/newdata/wooly/python/wooly/datatable.py	                        (rev 0)
+++ mgmt/newdata/wooly/python/wooly/datatable.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,253 @@
+from wooly import *
+from wooly.parameters import *
+from wooly.table import *
+from wooly.widgets import *
+from util import *
+
+strings = StringCatalog(__file__)
+
+class DataAdapter(object):
+    def __init__(self):
+        self.fields = list()
+        self.fields_by_name = dict()
+
+    def init(self):
+        for field in self.fields:
+            field.init()
+
+    def get_count(self):
+        pass
+
+    def get_data(self, values, options):
+        return ()
+
+class DataAdapterField(object):
+    def __init__(self, adapter, name, type):
+        self.adapter = adapter
+        self.name = name
+        self.type = type
+
+        self.index = len(self.adapter.fields)
+        self.adapter.fields.append(self)
+        self.adapter.fields_by_name[self.name] = self
+
+        self.visible = True
+
+    def init(self):
+        pass
+
+    def get_title(self, session):
+        pass
+
+    def get_content(self, session, record):
+        return record[self.index]
+
+class DataAdapterOptions(object):
+    def __init__(self):
+        self.sort_field = None
+        self.sort_ascending = True
+        self.limit = None
+        self.offset = None
+
+        self.attributes = dict()
+
+class DataTable(Table):
+    def __init__(self, app, name, adapter):
+        super(DataTable, self).__init__(app, name)
+
+        self.adapter = adapter
+
+        self.data = Attribute(app, "data")
+        self.add_attribute(self.data)
+
+        self.summary = Attribute(app, "summary")
+        self.add_attribute(self.summary)
+
+        self.count = Attribute(app, "count")
+        self.add_attribute(self.count)
+
+        self.header = DataTableHeader(app, "header")
+        self.replace_child(self.header)
+
+        self.footer = DataTableFooter(app, "footer")
+        self.replace_child(self.footer)
+
+    def init(self):
+        assert self.adapter
+
+        self.adapter.init()
+
+        super(DataTable, self).init()
+
+    def get_data(self, session):
+        values = self.get_data_values(session)
+        options = self.get_data_options(session)
+
+        return self.adapter.get_data(values, options)
+
+    def get_data_values(self, session):
+        return {}
+
+    def get_data_options(self, session):
+        options = DataAdapterOptions()
+
+        name = self.sort.get(session)
+
+        if name:
+            column = self.children_by_name[name]
+
+            if hasattr(column, "field"):
+                options.sort_field = column.field
+
+        options.sort_ascending = self.ascending.get(session)
+        options.limit = self.header.limit.get(session)
+        options.offset = self.header.offset.get(session)
+
+        return options
+
+    def get_count(self, session):
+        values = self.get_data_values(session)
+        return self.adapter.get_count(values)
+
+    def do_process(self, session):
+        super(DataTable, self).do_process(session)
+
+        start = time.time()
+
+        data = self.get_data(session)
+
+        seconds = time.time() - start
+
+        self.data.set(session, data)
+        self.summary.set(session, (len(data), seconds))
+        self.count.set(session, self.get_count(session))
+
+    def render_font_size(self, session):
+        return "%.1fem" % self.header.font.get(session)
+
+    def render_css(self, session):
+        writer = Writer()
+
+        font = self.header.font.get(session)
+        writer.write("table.DataTable { font-size: %.1fem; }" % font)
+
+        for column in self.get_visible_columns(session):
+            writer.write(column.css.render(session))
+
+        return writer.to_string()
+
+    def render_rows(self, session):
+        data = self.data.get(session)
+
+        writer = Writer()
+
+        for record in data:
+            writer.write(self.row.render(session, record))
+
+        return writer.to_string()
+
+class DataTableColumn(TableColumn):
+    def __init__(self, app, name, field):
+        super(DataTableColumn, self).__init__(app, name)
+
+        self.field = field
+
+    def render_text_align(self, session):
+        if self.field.type in (long, int, float, complex):
+            return "right"
+
+        return "left"
+
+    def render_header_content(self, session):
+        return self.field.get_title(session)
+
+    def render_cell_content(self, session, record):
+        return self.field.get_content(session, record)
+
+class DataTableHeader(TableHeader):
+    def __init__(self, app, name):
+        super(DataTableHeader, self).__init__(app, name)
+
+        self.font = FloatParameter(app, "font")
+        self.font.default = 0.9
+        self.add_parameter(self.font)
+
+        self.limit = IntegerParameter(app, "limit")
+        self.limit.default = 25
+        self.add_parameter(self.limit)
+
+        self.offset = IntegerParameter(app, "offset")
+        self.offset.default = 0
+        self.add_parameter(self.offset)
+
+        self.font_selector = DataTableFontSelector(app, "font", self.font)
+        self.add_child(self.font_selector)
+
+        self.limit_selector = DataTableLimitSelector(app, "limit", self.limit)
+        self.add_child(self.limit_selector)
+
+        self.page_selector = DataTablePageSelector(app, "page", self.offset)
+        self.add_child(self.page_selector)
+
+class DataTableFooter(TableFooter):
+    def render_summary(self, session):
+        results, seconds = self.table.summary.get(session)
+        count = self.table.count.get(session)
+
+        args = (results, count, seconds * 1000)
+        return "%i of %i, %.02f millis" % args
+
+class DataTableSelector(TableChild):
+    def __init__(self, app, name, selection):
+        super(DataTableSelector, self).__init__(app, name)
+
+        self.selection = selection
+
+        self.option = self.Option(app, "option")
+        self.add_child(self.option)
+
+    def get_options(self, session):
+        return ()
+
+    def render_options(self, session):
+        options = list()
+
+        for option in self.get_options(session):
+            options.append(self.option.render(session, option))
+
+        return ", ".join(options)
+
+    class Option(Link):
+        def render_class(self, session, option):
+            if self.parent.selection.get(session) == option[0]:
+                return "selected"
+
+        def edit_session(self, session, option):
+            self.parent.selection.set(session, option[0])
+
+        def render_content(self, session, option):
+            return option[1]
+
+class DataTableFontSelector(DataTableSelector):
+    def get_options(self, session):
+        return ((0.8, "S"), (0.9, "M"), (1.0, "L"))
+
+    def render_title(self, session):
+        return "Font"
+
+class DataTablePageSelector(DataTableSelector):
+    def get_options(self, session):
+        count = self.table.count.get(session)
+        limit = self.table.header.limit.get(session)
+
+        return [(x[1], x[0]) for x in enumerate(range(0, count, limit), 1)]
+
+    def render_title(self, session):
+        return "Page"
+
+class DataTableLimitSelector(DataTableSelector):
+    def get_options(self, session):
+        return ((25, 25), (50, 50), (100, 100))
+
+    def render_title(self, session):
+        return "Limit"

Added: mgmt/newdata/wooly/python/wooly/datatable.strings
===================================================================
--- mgmt/newdata/wooly/python/wooly/datatable.strings	                        (rev 0)
+++ mgmt/newdata/wooly/python/wooly/datatable.strings	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,35 @@
+[DataTable.css]
+table.DataTable th.controls {
+    text-align: left;
+}
+
+table.DataTable th.controls > div.right {
+    float: right;
+    text-align: right;
+    margin: 0 0 0 2em;
+}
+
+[DataTableHeader.html]
+<thead>
+  <tr>
+    <th colspan="{colspan}" class="controls">
+      <div class="right">{limit}</div>
+      <div class="right">{font}</div>
+      {page}
+    </th>
+  </tr>
+  <tr>{headers}</tr>
+</thead>
+
+[DataTableFooter.html]
+<tfoot><tr><td colspan="{colspan}">{summary}</td></tr></tfoot>
+
+[DataTableSelector.css]
+ul.DataTableSelector {
+    display: inline;
+    padding: 0;
+}
+
+[DataTableSelector.html]
+<span>{title}</span>
+<span>{options}</span>

Modified: mgmt/newdata/wooly/python/wooly/forms.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/forms.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/wooly/python/wooly/forms.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -66,7 +66,7 @@
             if isinstance(anc, Form):
                 self.form = anc
 
-        assert self.form # Not inside a form
+        assert self.form, "Not inside a form"
 
     def get_items(self, session, *args):
         return self.form.errors.get(session)
@@ -193,11 +193,12 @@
         self.size = 15
 
 class CheckboxInput(FormInput):
-    def __init__(self, app, name):
-        super(CheckboxInput, self).__init__(app, name, None)
+    def __init__(self, app, name, param=None):
+        super(CheckboxInput, self).__init__(app, name, param)
 
-        self.param = VoidBooleanParameter(app, "param")
-        self.add_parameter(self.param)
+        if not self.param:
+            self.param = VoidBooleanParameter(app, "param")
+            self.add_parameter(self.param)
 
     def render_checked_attr(self, session, *args):
         if self.get(session):
@@ -335,8 +336,8 @@
             if isinstance(anc, Form):
                 self.form = anc
 
-    def check_me_merrily(self):
-        super(FormField, self).check_me_merrily()
+    def seal(self):
+        super(FormField, self).seal()
 
         assert self.form # Not inside a form
 

Modified: mgmt/newdata/wooly/python/wooly/parameters.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/parameters.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/wooly/python/wooly/parameters.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -87,6 +87,13 @@
         except:
             return string
 
+class FloatParameter(Parameter):
+    def do_unmarshal(self, string):
+        try:
+            return float(string)
+        except:
+            return string
+
 class BooleanParameter(Parameter):
     def __init__(self, app, name):
         Parameter.__init__(self, app, name)

Modified: mgmt/newdata/wooly/python/wooly/profile.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/profile.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/wooly/python/wooly/profile.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -88,9 +88,10 @@
         visit(root, 0)
 
 class WidgetCall(object):
-    def __init__(self, profile, widget):
+    def __init__(self, profile, widget, args):
         self.profile = profile
         self.widget = widget
+        self.args = args
 
         self.profile
 
@@ -131,7 +132,7 @@
             self.self_time = self.time
 
     def __repr__(self):
-        return "%s(%s)" % (self.__class__.__name__, self.widget)
+        return "%s(%s,%s)" % (self.__class__.__name__, self.widget, self.args)
 
 class ProcessCall(WidgetCall):
     def do(self, session):
@@ -145,8 +146,7 @@
         if self.widget.defer_enabled:
             self.widget.page.enable_defer(session, self.widget)
 
-        args = self.widget.get_args(session)
-        self.widget.do_process(session, *args)
+        self.widget.do_process(session, *self.args)
 
         self.end = time.time()
 
@@ -159,8 +159,7 @@
 
         self.start = time.time()
 
-        args = self.widget.get_args(session)
-        result = self.widget.do_render(session, *args)
+        result = self.widget.do_render(session, *self.args)
 
         if result is None:
             result = ""

Modified: mgmt/newdata/wooly/python/wooly/server.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/server.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/wooly/python/wooly/server.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -27,6 +27,7 @@
         self.server = CherryPyWSGIServer \
             ((self.addr, self.port), self.service_request)
         self.server.environ["wsgi.version"] = (1, 1)
+        self.server._interrupt = True
 
         self.client_sessions_by_id = dict()
         self.client_session_expire_thread = ClientSessionExpireThread(self)
@@ -38,6 +39,8 @@
         self.server.ssl_private_key = path
 
     def do_init(self):
+        return # XXX urgh
+
         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
         try:

Added: mgmt/newdata/wooly/python/wooly/table.py
===================================================================
--- mgmt/newdata/wooly/python/wooly/table.py	                        (rev 0)
+++ mgmt/newdata/wooly/python/wooly/table.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,238 @@
+from forms import *
+from parameters import *
+from util import *
+from wooly import *
+
+strings = StringCatalog(__file__)
+
+class Table(Widget):
+    def __init__(self, app, name):
+        super(Table, self).__init__(app, name)
+
+        self.header = TableHeader(app, "header")
+        self.add_child(self.header)
+
+        self.footer = TableFooter(app, "footer")
+        self.add_child(self.footer)
+
+        self.columns = list()
+
+        self.row = TableRow(app, "row")
+        self.add_child(self.row)
+
+        self.sort = SymbolParameter(app, "sort")
+        self.add_parameter(self.sort)
+
+        self.ascending = BooleanParameter(app, "ascending")
+        self.add_parameter(self.ascending)
+
+    def add_column(self, column):
+        self.add_child(column)
+
+        self.columns.append(column)
+
+    def get_data(self, session):
+        return ()
+
+    def get_visible_columns(self, session):
+        return [x for x in self.columns if x.visible]
+
+    def render_css(self, session):
+        writer = Writer()
+
+        for column in self.get_visible_columns(session):
+            writer.write(column.css.render(session))
+
+        return writer.to_string()
+
+    def render_columns(self, session):
+        writer = Writer()
+
+        for column in self.get_visible_columns(session):
+            writer.write(column.render(session))
+
+        return writer.to_string()
+
+    def render_colspan(self, session):
+        return len(self.get_visible_columns(session))
+            
+    def render_rows(self, session):
+        data = self.get_data(session)
+
+        writer = Writer()
+
+        for record in data:
+            writer.write(self.row.render(session, record))
+
+        return writer.to_string()
+
+class TableChild(Widget):
+    def init(self):
+        super(TableChild, self).init()
+
+        for anc in self.ancestors:
+            if isinstance(anc, Table):
+                self.table = anc
+
+        assert self.table, "Not inside a table"
+
+class TableColumn(TableChild):
+    def __init__(self, app, name):
+        super(TableColumn, self).__init__(app, name)
+
+        self.css = TableColumnCss(app, "css")
+        self.add_child(self.css)
+
+        self.header = TableColumnHeader(app, "header")
+        self.add_child(self.header)
+
+        self.cell = TableColumnCell(app, "cell")
+        self.add_child(self.cell)
+
+        self.visible = True
+        self.width = None
+
+    def render_class(self, session):
+        tokens = list()
+        tokens.append(self.name)
+
+        if self.name == self.table.sort.get(session):
+            tokens.append("selected")
+
+        return " ".join(tokens)
+
+    def render_width(self, session):
+        width = self.width
+
+        if not width:
+            width = "auto"
+
+        return width
+
+    def render_text_align(self, session):
+        return "left"
+
+    def render_header_href(self, session):
+        sort = self.table.sort.get(session)
+        ascending = self.table.ascending.get(session)
+
+        branch = session.branch()
+
+        self.table.sort.set(branch, self.name)
+
+        if sort == self.name:
+            self.table.ascending.set(branch, not ascending)
+
+        return branch.marshal()
+
+    def render_header_content(self, session):
+        pass
+
+    def render_cell_content(self, session, record):
+        pass
+    
+class TableColumnCss(TableChild):
+    def render_class(self, session):
+        return self.parent.name
+
+    def render_text_align(self, session):
+        return self.parent.render_text_align(session)
+
+    def render_width(self, session):
+        return self.parent.render_width(session)
+
+class TableColumnHeader(TableChild):
+    def render_class(self, session):
+        return self.parent.render_class(session)
+
+    def render_href(self, session):
+        return self.parent.render_header_href(session)
+
+    def render_content(self, session):
+        return self.parent.render_header_content(session)
+
+class TableColumnCell(TableChild):
+    def render_class(self, session, record):
+        return self.parent.render_class(session)
+
+    def render_content(self, session, record):
+        return self.parent.render_cell_content(session, record)
+
+class TableHeader(TableChild):
+    def render_colspan(self, session):
+        return self.table.render_colspan(session)
+
+    def render_headers(self, session):
+        writer = Writer()
+
+        for column in self.table.get_visible_columns(session):
+            writer.write(column.header.render(session))
+
+        return writer.to_string()
+
+class TableFooter(TableChild):
+    def render_colspan(self, session):
+        return self.table.render_colspan(session)
+
+class TableRow(TableChild):
+    def render_cells(self, session, record):
+        writer = Writer()
+
+        for column in self.table.get_visible_columns(session):
+            writer.write(column.cell.render(session, record))
+
+        return writer.to_string()
+
+class LinkColumn(TableColumn):
+    def __init__(self, app, name):
+        super(LinkColumn, self).__init__(app, name)
+
+        self.cell = LinkColumnCell(app, "cell")
+        self.replace_child(self.cell)
+
+    def render_cell_href(self, session, record):
+        pass
+
+class LinkColumnCell(TableColumnCell):
+    def render_href(self, session, record):
+        return self.parent.render_cell_href(session, record)
+
+class NewCheckboxColumn(TableColumn):
+    def __init__(self, app, name):
+        super(NewCheckboxColumn, self).__init__(app, name)
+
+        self.selection = selection
+
+        self.header = CheckboxColumnHeader(app, "header")
+        self.replace_child(self.header)
+
+        self.cell = CheckboxColumnCell(app, "cell", selection)
+        self.replace_child(self.cell)
+
+        self.width = "1%"
+
+    def init(self):
+        super(NewCheckboxColumn, self).init()
+
+        assert self.selection, self
+
+    def render_cell_value(self, session, record):
+        pass
+
+class CheckboxColumnHeader(TableColumnHeader):
+    def render_name(self, session):
+        return self.parent.selection.path
+
+class CheckboxColumnCell(TableColumnCell):
+    def __init__(self, app, name, selection):
+        super(CheckboxColumnCell, self).__init__(app, name)
+
+        self.input = CheckboxColumnInput(app, "input", selection)
+        self.add_child(self.input)
+
+    def render_input(self, session, record):
+        return self.input.render(session, record)
+
+class CheckboxColumnInput(CheckboxInput):
+    def render_value(self, session, record):
+        return self.parent.parent.render_cell_value(session, record)

Added: mgmt/newdata/wooly/python/wooly/table.strings
===================================================================
--- mgmt/newdata/wooly/python/wooly/table.strings	                        (rev 0)
+++ mgmt/newdata/wooly/python/wooly/table.strings	2010-03-22 14:58:08 UTC (rev 3871)
@@ -0,0 +1,106 @@
+[Table.css]
+table.Table {
+    table-layout: auto;
+    width: 100%;
+    border-collapse: collapse;
+}
+
+table.Table tr {
+    vertical-align: top;
+}
+
+table.Table td, table.Table th {
+    white-space: nowrap;
+    overflow: hidden;
+}
+
+table.Table th.controls {
+    text-align: left;
+}
+
+table.Table th.controls > div.right {
+    float: right;
+    text-align: right;
+    margin: 0 0 0 2em;
+}
+
+table.Table input {
+    margin: 0;
+    vertical-align: -10%;
+}
+
+[Table.html]
+<table id="{id}" class="{class}">
+  <style type="text/css">
+    {css}
+  </style>
+  {header}
+  {footer}
+  <tbody>{rows}</tbody>
+</table>
+
+[TableHeader.html]
+<thead><tr>{headers}</tr></thead>
+
+[TableColumn.html]
+<col class="{class}" width="{width}"/>
+
+[TableColumnCss.html]
+table.Table td.{class},
+table.Table th.{class} {
+    text-align: {text_align};
+    width: {width};
+}
+
+table.Table th.{class}.selected {
+    background-color: #f0f0f0;
+}
+
+table.Table td.{class}.selected {
+    background-color: #f7f7f7;
+}
+
+[TableColumnHeader.html]
+<th class="{class}"><a href="{href}">{content}</a></th>
+
+[TableColumnCell.html]
+<td class="{class}">{content}</td>
+
+[TableRow.html]
+<tr>{cells}</tr>
+
+[TableSelector.css]
+ul.TableSelector {
+    display: inline;
+    padding: 0;
+}
+
+[TableSelector.html]
+<span>{title}</span>
+<span>{options}</span>
+
+[LinkColumnCell.html]
+<td class="{class}"><a href="{href}">{content}</a></td>
+
+[CheckboxColumnHeader.javascript]
+(function() {
+    wooly.clickTableCheckboxes = function(control, inputName) {
+        var elems = $$("input[name=" + inputName + "]");
+
+        for (var i = 0; i < elems.length; i++) {
+            var elem = elems[i];
+
+            if (elem.checked != control.checked) {
+                elem.click();
+            }
+        }
+    };
+}())
+
+[CheckboxColumnHeader.html]
+<th class="{class}"><input id="{id}" type="checkbox"
+   onclick="wooly.clickTableCheckboxes(this, '{name}')"
+/></th>
+
+[CheckboxColumnCell.html]
+<td class="{class}">{input}</td>

Modified: mgmt/newdata/wooly/python/wooly/tables.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/tables.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/wooly/python/wooly/tables.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -29,7 +29,7 @@
     def get_request_visible_columns(self, session, vlist):
         return [col for col in self.columns if col.visible or col.name in vlist]
 
-    def render_headers(self, session, *args):
+    def render_headers(self, session):
         writer = Writer()
 
         for column in self.get_visible_columns(session):
@@ -38,7 +38,7 @@
 
         return writer.to_string()
 
-    def render_column_count(self, session, *args):
+    def render_column_count(self, session):
         vlist = self.get_visible_columns(session)
         return len(vlist)
 
@@ -46,8 +46,7 @@
         writer = Writer()
 
         for col in self.get_visible_columns(session):
-            col.set_item(session, item)
-            writer.write(col.render(session))
+            writer.write(col.render(session, item))
 
         return writer.to_string()
 
@@ -62,7 +61,7 @@
         if self.scolumn.default is None:
             self.scolumn.default = column.name
 
-    def render_columns(self, session, *args):
+    def render_columns(self, session):
         writer = Writer()
 
         for column in self.get_visible_columns(session):
@@ -91,16 +90,16 @@
     def is_reversed(self, session):
         return self.reversed.get(session)
 
-    def do_get_items(self, session, *args):
+    def do_get_items(self, session):
         """Gets the rows"""
 
         return None
 
-    def render_count(self, session, *args):
-        count = self.get_item_count(session, *args)
+    def render_count(self, session):
+        count = self.get_item_count(session)
         return "%i %s" % (count, count == 1 and "item" or "items")
 
-    def render_none(self, session, *args):
+    def render_none(self, session):
         """For producing a message when the table is empty"""
 
         return None
@@ -116,15 +115,6 @@
         self.align = None
         self.width = None
 
-        self.__item = Attribute(app, "item")
-        self.add_attribute(self.__item)
-
-    def set_item(self, session, item):
-        return self.__item.set(session, item)
-
-    def get_args(self, session):
-        return (self.__item.get(session),)
-
     def get_column_key(self, session):
         return self.name
 
@@ -170,10 +160,10 @@
 
         self.column = column
 
-    def render_attrs(self, session, *args):
+    def render_attrs(self, session):
         return self.column.render_attrs(session)
 
-    def render_href(self, session, *args):
+    def render_href(self, session):
         branch = session.branch()
 
         sel = self.parent.get_selected_column(session)
@@ -186,11 +176,10 @@
 
         return branch.marshal()
 
-    def render_content(self, session, *args):
-        cargs = self.column.get_args(session)
-        return self.column.render_title(session, *cargs)
+    def render_content(self, session):
+        return self.column.render_title(session)
 
-    def render_sorted_dir(self, session, *args):
+    def render_sorted_dir(self, session):
         sel = self.parent.get_selected_column(session)
 
         if sel is self.column:
@@ -211,77 +200,77 @@
         self.__count_sql_tmpl = WidgetTemplate(self, "count_sql")
         self.__find_sql_tmpl = WidgetTemplate(self, "find_sql")
 
-    def render_sql(self, session, *args):
+    def render_sql(self, session):
         writer = Writer()
-        self.__sql_tmpl.render(writer, session, *args)
+        self.__sql_tmpl.render(writer, session)
         return writer.to_string()
 
-    def render_sql_where(self, session, *args):
+    def render_sql_where(self, session):
         constraints = self.get_sql_where_constraints(session)
 
         if constraints:
             return "where %s" % " and ".join(constraints)
 
-    def render_find_sql_where(self, session, *args):
+    def render_find_sql_where(self, session):
         pass
 
-    def render_sql_order_by(self, session, *args):
+    def render_sql_order_by(self, session):
         scol = self.get_selected_column(session)
         if scol:
             return scol.get_order_by_sql(session)
 
-    def render_sql_orderby(self, session, *args):
-        return self.render_sql_order_by(session, *args)
+    def render_sql_orderby(self, session):
+        return self.render_sql_order_by(session)
 
-    def render_sql_limit(self, session, *args):
+    def render_sql_limit(self, session):
         return None
 
-    def render_count_sql(self, session, *args):
+    def render_count_sql(self, session):
         writer = Writer()
-        self.__count_sql_tmpl.render(writer, session, *args)
+        self.__count_sql_tmpl.render(writer, session)
         return writer.to_string()
 
-    def render_find_sql(self, session, *args):
+    def render_find_sql(self, session):
         writer = Writer()
-        self.__find_sql_tmpl.render(writer, session, *args)
+        self.__find_sql_tmpl.render(writer, session)
         return writer.to_string()
 
-    def get_sql_where_constraints(self, session, *args):
+    def get_sql_where_constraints(self, session):
         return list()
 
-    def get_sql_values(self, session, *args):
+    def get_sql_values(self, session):
         return None
 
-    def get_find_sql_values(self, session, *args):
+    def get_find_sql_values(self, session):
         return None
 
     def get_connection(self, session):
         pass
 
-    def get_item_count(self, session, *args):
+    def get_item_count(self, session):
         conn = self.get_connection(session)
 
         if not conn:
             raise Exception("Database error")
             
         cursor = conn.cursor()
-        sql = self.render_count_sql(session, *args)
-        sql_values = self.get_sql_values(session, *args)
+        sql = self.render_count_sql(session)
+        sql_values = self.get_sql_values(session)
 
         cursor.execute(sql, sql_values)
         data = cursor.fetchone()
 
         return data[0]
 
-    def do_get_items(self, session, *args):
+    def do_get_items(self, session):
         conn = self.get_connection(session)
 
         if not conn:
             raise Exception("Database error")
 
         cursor = conn.cursor()
-        sql = self.render_sql(session, *args)
-        sql_values = self.get_sql_values(session, *args)
+        sql = self.render_sql(session)
+        sql_values = self.get_sql_values(session)
 
         #print "SQL TEXT", sql
         #print "SQL VALS", sql_values
@@ -290,8 +279,8 @@
 
         return cursor
 
-    def render_items(self, session, *args):
-        cursor = self.get_items(session, *args)
+    def render_items(self, session):
+        cursor = self.get_items(session)
         cols = [spec[0] for spec in cursor.description]
         data = dict()
 
@@ -305,7 +294,7 @@
 
         return writer.to_string()
 
-    def find_item(self, session, *args):
+    def find_item(self, session):
         """ Find items in the current ItemSet
 
             To use this an SqlTable derived object needs to have a
@@ -316,12 +305,12 @@
         conn = self.get_connection(session)
         if conn:
             cursor = conn.cursor()
-            select = self.render_find_sql(session, *args)
-            where = self.render_sql_where(session, *args)
-            sql_values = self.get_sql_values(session, *args)
+            select = self.render_find_sql(session)
+            where = self.render_sql_where(session)
+            sql_values = self.get_sql_values(session)
 
-            find_where = self.render_find_sql_where(session, *args)
-            find_values = self.get_find_sql_values(session, *args)
+            find_where = self.render_find_sql_where(session)
+            find_values = self.get_find_sql_values(session)
             sql = " and ".join(["%s %s" % (select, where), find_where])
             if sql_values:
                 for sql_val in sql_values:
@@ -355,5 +344,5 @@
             return "order by %s %s" % (key, dir)
 
 class SqlTableFilter(Widget):
-    def get_sql_constraint(self, session, *args):
+    def get_sql_constraint(self, session):
         pass

Modified: mgmt/newdata/wooly/python/wooly/tables.strings
===================================================================
--- mgmt/trunk/wooly/python/wooly/tables.strings	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/wooly/python/wooly/tables.strings	2010-03-22 14:58:08 UTC (rev 3871)
@@ -3,8 +3,7 @@
     table-layout: fixed;
 }
 
-table.ItemTable td
-table.ItemTable th {
+table.ItemTable td, table.ItemTable th {
     white-space: nowrap;
     overflow: hidden;
 }

Modified: mgmt/newdata/wooly/python/wooly/template.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/template.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/wooly/python/wooly/template.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -3,11 +3,11 @@
 class ObjectTemplate(object):
     def __init__(self, obj, text):
         self.__object = obj
-        self.__text = text
+        self.text = text
         self.__fragments = None
 
     def compile(self):
-        return self.resolve(self.parse(self.__text))
+        return self.resolve(self.parse(self.text))
 
     def parse(self, text):
         strings = list()

Modified: mgmt/newdata/wooly/python/wooly/widgets.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/widgets.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/wooly/python/wooly/widgets.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -167,43 +167,40 @@
     def __init__(self, app, name):
         super(ItemSet, self).__init__(app, name)
 
-        self.html_class = ItemSet.__name__
-
         self.items = Attribute(app, "items")
         self.add_attribute(self.items)
 
         self.item_tmpl = WidgetTemplate(self, "item_html")
 
-    def get_item_count(self, session, *args):
-        return len(self.get_items(session, *args))
+    def get_item_count(self, session):
+        return len(self.get_items(session))
 
-    def get_items(self, session, *args):
+    def get_items(self, session):
         items = self.items.get(session)
 
         if items is None:
-            items = self.do_get_items(session, *args)
+            items = self.do_get_items(session)
 
             if items is None:
                 items = ()
 
         return items
 
-    def do_get_items(self, session, *args):
+    def do_get_items(self, session):
         return ()
 
-    def render_items(self, session, *args):
-        items = self.get_items(session, *args)
+    def render_items(self, session):
         writer = Writer()
 
-        for item in items:
+        for item in self.get_items(session):
             self.item_tmpl.render(writer, session, item)
 
         return writer.to_string()
 
     def render_item_content(self, session, item):
-        return None
+        return item
 
-    def render_none(self, session, *args):
+    def render_none(self, session):
         """For producing a message when the set is empty"""
 
         return None
@@ -225,6 +222,13 @@
         else:
             return "_"
 
+    def render_item_href(self, session, item):
+        branch = session.branch()
+
+        self.selection.set(branch, item)
+
+        return branch.marshal()
+
 class RenderingItemSet(Widget):
     def __init__(self, app, name, item_renderer):
         super(RenderingItemSet, self).__init__(app, name)
@@ -234,27 +238,27 @@
         self.items = Attribute(app, "items")
         self.add_attribute(self.items)
 
-    def get_item_count(self, session, *args):
-        return len(self.get_items(session, *args))
+    def get_item_count(self, session):
+        return len(self.get_items(session))
 
-    def get_items(self, session, *args):
+    def get_items(self, session):
         items = self.items.get(session)
 
         if items is None:
-            items = self.do_get_items(session, *args)
+            items = self.do_get_items(session)
+
             if items is None:
                 items = ()
 
         return items
 
-    def do_get_items(self, session, *args):
+    def do_get_items(self, session):
         pass
 
-    def render_items(self, session, *args):
-        items = self.get_items(session, *args)
+    def render_items(self, session):
         writer = Writer()
 
-        for item in items:
+        for item in self.get_items(session):
             self.item_renderer.render(writer, session, item)
 
         return writer.to_string()
@@ -280,17 +284,17 @@
         self.__tmpl.render(writer, session, item)
 
 class ItemTree(ItemSet):
-    def get_items(self, session, *args):
+    def get_items(self, session):
         """Get the root items"""
         pass
 
-    def get_child_items(self, session, *args):
+    def get_child_items(self, session):
         pass
 
-    def render_child_items(self, session, *args):
+    def render_child_items(self, session):
         writer = Writer()
 
-        for child in self.get_child_items(session, *args):
+        for child in self.get_child_items(session):
             self.item_renderer.render(writer, session, child)
 
         return writer.to_string()

Modified: mgmt/newdata/wooly/python/wooly/widgets.strings
===================================================================
--- mgmt/trunk/wooly/python/wooly/widgets.strings	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/wooly/python/wooly/widgets.strings	2010-03-22 14:58:08 UTC (rev 3871)
@@ -78,7 +78,7 @@
 <a href="{href}" class="Toggle {state}">{content}</a>
 
 [ItemSet.css]
-ul.ItemSet li.selected {
+ul.ItemSet li.selected, ul.ItemSet li.selected a {
     color: black;
 }
 
@@ -88,6 +88,9 @@
 [ItemSet.item_html]
 <li class="{item_class}">{item_content}</li>
 
+[SelectionItemSet.item_html]
+<li class="{item_class}"><a href="{item_href}">{item_content}</a></li>
+
 [ItemTree.html]
 <ul class="ItemTree">{items}</ul>
 

Modified: mgmt/newdata/wooly/python/wooly/wsgiserver/__init__.py
===================================================================
--- mgmt/trunk/wooly/python/wooly/wsgiserver/__init__.py	2010-03-22 14:00:07 UTC (rev 3870)
+++ mgmt/newdata/wooly/python/wooly/wsgiserver/__init__.py	2010-03-22 14:58:08 UTC (rev 3871)
@@ -130,6 +130,9 @@
     'WWW-AUTHENTICATE']
 
 
+# XXX JR
+socket.setdefaulttimeout(1.0)
+
 class WSGIPathInfoDispatcher(object):
     """A WSGI dispatcher for dispatch based on the PATH_INFO.
     
@@ -1679,6 +1682,7 @@
     def bind(self, family, type, proto=0):
         """Create (or recreate) the actual socket object."""
         self.socket = socket.socket(family, type, proto)
+
         prevent_socket_inheritance(self.socket)
         self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         if self.nodelay and not isinstance(self.bind_addr, str):
@@ -1699,7 +1703,7 @@
                 pass
         
         self.socket.bind(self.bind_addr)
-    
+
     def tick(self):
         """Accept a new connection and put it on the Queue."""
         try:



More information about the rhmessaging-commits mailing list