[rhmessaging-commits] rhmessaging commits: r3457 - in mgmt/trunk/cumin: metadata and 8 other directories.

rhmessaging-commits at lists.jboss.org rhmessaging-commits at lists.jboss.org
Wed Jun 17 16:20:11 EDT 2009


Author: justi9
Date: 2009-06-17 16:20:11 -0400 (Wed, 17 Jun 2009)
New Revision: 3457

Added:
   mgmt/trunk/cumin/metadata/
   mgmt/trunk/cumin/metadata/model.xml
   mgmt/trunk/cumin/metadata/qmf/
   mgmt/trunk/cumin/metadata/qmf/Makefile
   mgmt/trunk/cumin/metadata/qmf/condor.xml
   mgmt/trunk/cumin/metadata/qmf/qpid-acl.xml
   mgmt/trunk/cumin/metadata/qmf/qpid-cluster.xml
   mgmt/trunk/cumin/metadata/qmf/qpid-store.xml
   mgmt/trunk/cumin/metadata/qmf/qpid.xml
   mgmt/trunk/cumin/metadata/qmf/sesame.xml
   mgmt/trunk/cumin/python/cumin/messaging/model.py
Modified:
   mgmt/trunk/cumin/python/cumin/__init__.py
   mgmt/trunk/cumin/python/cumin/account/main.py
   mgmt/trunk/cumin/python/cumin/account/main.strings
   mgmt/trunk/cumin/python/cumin/account/user.py
   mgmt/trunk/cumin/python/cumin/grid/main.py
   mgmt/trunk/cumin/python/cumin/inventory/main.py
   mgmt/trunk/cumin/python/cumin/messaging/binding.py
   mgmt/trunk/cumin/python/cumin/messaging/binding.strings
   mgmt/trunk/cumin/python/cumin/messaging/broker.py
   mgmt/trunk/cumin/python/cumin/messaging/broker.strings
   mgmt/trunk/cumin/python/cumin/messaging/brokergroup.py
   mgmt/trunk/cumin/python/cumin/messaging/brokerlink.py
   mgmt/trunk/cumin/python/cumin/messaging/brokerlink.strings
   mgmt/trunk/cumin/python/cumin/messaging/client.py
   mgmt/trunk/cumin/python/cumin/messaging/client.strings
   mgmt/trunk/cumin/python/cumin/messaging/exchange.py
   mgmt/trunk/cumin/python/cumin/messaging/exchange.strings
   mgmt/trunk/cumin/python/cumin/messaging/main.py
   mgmt/trunk/cumin/python/cumin/messaging/queue.py
   mgmt/trunk/cumin/python/cumin/messaging/queue.strings
   mgmt/trunk/cumin/python/cumin/model.py
   mgmt/trunk/cumin/python/cumin/modelwidgets.py
   mgmt/trunk/cumin/python/cumin/page.py
   mgmt/trunk/cumin/python/cumin/parameters.py
   mgmt/trunk/cumin/python/cumin/usergrid/main.py
   mgmt/trunk/cumin/python/cumin/usergrid/submission.py
   mgmt/trunk/cumin/python/cumin/util.py
   mgmt/trunk/cumin/python/cumin/widgets.py
   mgmt/trunk/cumin/python/cumin/widgets.strings
   mgmt/trunk/cumin/resources/app.css
Log:
 * Introduce module objects for usergrid, messaging, grid, inventory,
   and account

 * Introduce a top-level form page, for holding and serving forms;
   forms are now top-level things themselves, rather than being
   embedded with object views

 * Add a CuminSubject class to the ui metadata

 * Introduce Task, a replacement for Action; Tasks know how to
   navigate to their own forms, and they encapsulate domain logic

   Attendant things: TaskInvocation, QmfTask, SetTask,
   TaskInvocationSet

   Tasks and Actions coexist for now

 * Convert all the messaging actions to tasks, and taskify the related
   forms

 * Eliminate ModelPage, which is no longer used

 * Put in missing super calls in class init methods

 * Add a VhostParameter, BindingParameter, RouteParameter, and
   LinkParameter, with PeerParameter aliased to the last

 * Introduce a BackgroundInclude widget, for throwing the output of
   some url inside a page area

 * Introduce CuminSelectionTable, to replace a lot of custom table
   code; it is a table that comes with a checkbox column and a set of
   buttons (for set tasks) and links (for add tasks, for instance); it
   takes an item parameter

 * For use in the above class, a TaskButton and TaskLink.  These take
   tasks in their constructors and use that metadata to do navigation

 * Convert messaging module tables to use CuminSelectionTable

 * Add plural(noun) and conjugate(noun, count) methods for
   pluralizing words

 * Modernize the names of things under brokerlink

 * Move BindingSet to a better location

 * Rename NewBrokerSet to BrokerSet

 * Remove various unused status widgets


Added: mgmt/trunk/cumin/metadata/model.xml
===================================================================
--- mgmt/trunk/cumin/metadata/model.xml	                        (rev 0)
+++ mgmt/trunk/cumin/metadata/model.xml	2009-06-17 20:20:11 UTC (rev 3457)
@@ -0,0 +1,27 @@
+<model>
+  <package name="org.apache.qpid.broker">
+    <class name="System">
+      <property name="systemId">
+        <title>System ID</title>
+      </property>
+    </class>
+  </package>
+</model>
+<model>
+  <package name="org.apache.qpid.broker">
+    <class name="System">
+      <property name="systemId">
+        <title>System ID</title>
+      </property>
+    </class>
+  </package>
+</model>
+<model>
+  <package name="org.apache.qpid.broker">
+    <class name="System">
+      <property name="systemId">
+        <title>System ID</title>
+      </property>
+    </class>
+  </package>
+</model>

Added: mgmt/trunk/cumin/metadata/qmf/Makefile
===================================================================
--- mgmt/trunk/cumin/metadata/qmf/Makefile	                        (rev 0)
+++ mgmt/trunk/cumin/metadata/qmf/Makefile	2009-06-17 20:20:11 UTC (rev 3457)
@@ -0,0 +1,78 @@
+.PHONY: schema clean
+
+FILES := qpid.xml qpid-store.xml qpid-acl.xml qpid-cluster.xml condor.xml sesame.xml
+
+schema: ${FILES}
+
+clean:
+	rm -f ${FILES}
+
+qpid.xml:
+	svn export http://svn.apache.org/repos/asf/qpid/trunk/qpid/specs/management-schema.xml qpid.xml
+
+qpid-store.xml:
+	svn export http://anonsvn.jboss.org/repos/rhmessaging/store/trunk/cpp/lib/qmf-schema.xml qpid-store.xml
+
+qpid-acl.xml:
+	svn export http://svn.apache.org/repos/asf/qpid/trunk/qpid/cpp/src/qpid/acl/management-schema.xml qpid-acl.xml
+
+qpid-cluster.xml:
+	svn export http://svn.apache.org/repos/asf/qpid/trunk/qpid/cpp/src/qpid/cluster/management-schema.xml qpid-cluster.xml
+
+condor.xml:
+	wget "http://git.et.redhat.com/?p=mrg-grid.git;a=blob_plain;f=src/management/condor-management-schema.xml;hb=refs/heads/V7.3-QMF-Plugins" -O condor.xml
+
+sesame.xml:
+	svn export http://anonsvn.jboss.org/repos/rhmessaging/mgmt/trunk/sesame/cpp/src/qmfgen/schema.xml sesame.xml
+.PHONY: schema clean
+
+FILES := qpid.xml qpid-store.xml qpid-acl.xml qpid-cluster.xml condor.xml sesame.xml
+
+schema: ${FILES}
+
+clean:
+	rm -f ${FILES}
+
+qpid.xml:
+	svn export http://svn.apache.org/repos/asf/qpid/trunk/qpid/specs/management-schema.xml qpid.xml
+
+qpid-store.xml:
+	svn export http://anonsvn.jboss.org/repos/rhmessaging/store/trunk/cpp/lib/qmf-schema.xml qpid-store.xml
+
+qpid-acl.xml:
+	svn export http://svn.apache.org/repos/asf/qpid/trunk/qpid/cpp/src/qpid/acl/management-schema.xml qpid-acl.xml
+
+qpid-cluster.xml:
+	svn export http://svn.apache.org/repos/asf/qpid/trunk/qpid/cpp/src/qpid/cluster/management-schema.xml qpid-cluster.xml
+
+condor.xml:
+	wget "http://git.et.redhat.com/?p=mrg-grid.git;a=blob_plain;f=src/management/condor-management-schema.xml;hb=refs/heads/V7.3-QMF-Plugins" -O condor.xml
+
+sesame.xml:
+	svn export http://anonsvn.jboss.org/repos/rhmessaging/mgmt/trunk/sesame/cpp/src/qmfgen/schema.xml sesame.xml
+.PHONY: schema clean
+
+FILES := qpid.xml qpid-store.xml qpid-acl.xml qpid-cluster.xml condor.xml sesame.xml
+
+schema: ${FILES}
+
+clean:
+	rm -f ${FILES}
+
+qpid.xml:
+	svn export http://svn.apache.org/repos/asf/qpid/trunk/qpid/specs/management-schema.xml qpid.xml
+
+qpid-store.xml:
+	svn export http://anonsvn.jboss.org/repos/rhmessaging/store/trunk/cpp/lib/qmf-schema.xml qpid-store.xml
+
+qpid-acl.xml:
+	svn export http://svn.apache.org/repos/asf/qpid/trunk/qpid/cpp/src/qpid/acl/management-schema.xml qpid-acl.xml
+
+qpid-cluster.xml:
+	svn export http://svn.apache.org/repos/asf/qpid/trunk/qpid/cpp/src/qpid/cluster/management-schema.xml qpid-cluster.xml
+
+condor.xml:
+	wget "http://git.et.redhat.com/?p=mrg-grid.git;a=blob_plain;f=src/management/condor-management-schema.xml;hb=refs/heads/V7.3-QMF-Plugins" -O condor.xml
+
+sesame.xml:
+	svn export http://anonsvn.jboss.org/repos/rhmessaging/mgmt/trunk/sesame/cpp/src/qmfgen/schema.xml sesame.xml

Added: mgmt/trunk/cumin/metadata/qmf/condor.xml
===================================================================
--- mgmt/trunk/cumin/metadata/qmf/condor.xml	                        (rev 0)
+++ mgmt/trunk/cumin/metadata/qmf/condor.xml	2009-06-17 20:20:11 UTC (rev 3457)
@@ -0,0 +1,816 @@
+<schema package="mrg.grid">
+
+<!--
+/*
+ * Copyright 2008 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<group name="daemon-stats">
+  <property name="CondorPlatform"
+	    type="sstr"
+	    desc="The Condor platform string for the daemon's platform"/>
+  <property name="CondorVersion"
+	    type="sstr"
+	    desc="The Condor version string for the daemon's version"/>
+  <property name="DaemonStartTime"
+	    type="absTime" unit="nanosecond"
+	    desc="Number of nanoseconds since epoch when the daemon
+		  was started"/>
+
+  <statistic name="MonitorSelfAge" type="uint32"/>
+  <statistic name="MonitorSelfCPUUsage" type="double"/>
+  <statistic name="MonitorSelfImageSize" type="double"/>
+  <statistic name="MonitorSelfRegisteredSocketCount" type="uint32"/>
+  <statistic name="MonitorSelfResidentSetSize" type="uint32"/>
+  <statistic name="MonitorSelfTime" type="absTime"/>
+</group>
+
+<!--
+CpuBusy = ((LoadAvg - CondorLoadAvg) >= 0.500000)
+CpuBusyTime = 0
+CpuIsBusy = FALSE
+
+HasCheckpointing = TRUE
+HasFileTransfer = TRUE
+HasIOProxy = TRUE
+HasJava = TRUE
+HasJICLocalConfig = TRUE
+HasJICLocalStdin = TRUE
+HasJobDeferral = TRUE
+HasMPI = TRUE
+HasPerFileEncryption = TRUE
+HasReconnect = TRUE
+HasRemoteSyscalls = TRUE
+HasTDP = TRUE
+HasVM = FALSE
+
+JavaMFlops = 8.156164
+JavaVendor = "Free Software Foundation, Inc."
+JavaVersion = "1.4.2"
+
+Subnet = "10.16.43"
+
+Set by Collector:
+  UpdateSequenceNumber = 627
+  UpdatesHistory = "0x00000000000000000000000000000000"
+  UpdatesLost = 0
+  UpdatesSequenced = 58
+  UpdatesTotal = 59
+-->
+
+  <class name="Slot">
+
+    <group name="daemon-stats"/>
+
+    <property name="Pool" type="sstr" index="y"/>
+    <property name="System" type="sstr" index="y"/>
+
+    <property name="AccountingGroup"
+	      type="sstr"
+	      optional="y"
+	      desc="AccountingGroup of the running job, fully
+		    qualified with a UidDomain, UidDomain taken from
+		    RemoteUser, only present when a job is
+		    executing"/>
+    <statistic name="Activity"
+	       type="sstr"
+	       desc="One of: Idle, No job activity; Busy, Job is
+		     running; Suspended, Job is suspended; Vacating,
+		     Job is being removed; Killing, Job is being
+		     killed; Benchmarking, Benchmarks being run"/>
+    <property name="Arch"
+	      type="sstr"
+	      desc="Slot's architecture, e.g.: ALPHA, Diginal Alpha;
+		    HPPA1, HP PA-RISC 1.x (7000 series); HPPA2, HP
+		    PA-RISC 2.x (8000 series); IA64, Intel Itanium;
+		    INTEL, Intel x86 (Pentium, Xeon, etc); SGI, SGI
+		    MIPS; SUN4u, Sun UltraSparc; SUN4x, Sun Sparc
+		    (not UltraSparc); PPC, Power Macintosh; PPC64,
+		    64-bit Power Macintosh; X86_64, AMD/Intel 64-bit
+		    x86"/>
+    <property name="CheckpointPlatform"
+	      type="sstr"
+	      desc="Opaque string encoding OS, hardware and kernel
+		    attributes"/>
+    <property name="ClientMachine"
+	      type="sstr"
+	      optional="y"
+	      desc="The hostname of the machine that has claimed the
+		    slot, only present when slot is claimed"/>
+    <statistic name="ClockDay"
+	       type="uint32"
+	       desc="Day of the week: 0 = Sunday, 1 = Monday, ..., 6 =
+		     Saturaday"/>
+    <statistic name="ClockMin"
+	       type="uint32" unit="minute"
+	       desc="Number of elapsed minutes since midnight"/>
+    <property name="ConcurrencyLimits"
+	      type="sstr"
+	      optional="y"
+	      desc="Set of concurrency limits associated with the
+		    current job"/>
+    <statistic name="CondorLoadAvg"
+	       type="double"
+	       desc="Portion of LoadAvg generated by Condor (job or
+		     benchmark)"/>
+    <statistic name="ConsoleIdle"
+	       type="uint32" unit="second"
+	       desc="Seconds since activity on console keyboard or
+		     mouse"/>
+    <property name="Cpus"
+	      type="uint32"
+	      desc="Number of CPUs in slot"/>
+    <!-- XXX: CurrentRank could quite possibly be a statistic -->
+    <property name="CurrentRank"
+	      type="double"
+	      optional="y"
+	      desc="Slots' affinity for running the job it is
+		    currently hosting, calculated as Rank expression
+		    evaluated in context of the running job's ad"/>
+    <property name="Disk"
+	      type="uint32" unit="KiB"
+	      desc="Amount of disk space in KiB available in the slot"/>
+    <statistic name="EnteredCurrentActivity"
+	       type="absTime" unit="nanosecond"
+	       desc="Time at which current Activity was entered,
+		     number of nanoseconds since Unix epoch"/>
+    <statistic name="EnteredCurrentState"
+	       type="absTime" unit="nanosecond"
+	       desc="Time at which current State was entered,
+		     number of seconds since Unix epoch"/>
+    <property name="FileSystemDomain"
+	      type="sstr"
+	      desc="Configured namespace shared by slots with
+		    uniformly mounted shared storage"/>
+    <property name="GlobalJobId"
+	      type="sstr"
+	      optional="y"
+	      desc="The running job's GlobalJobId, only present when
+		    a job is executing"/>
+    <!-- XXX: ImageSize could quite possibly be a statistic -->
+    <property name="ImageSize"
+	      type="uint32" unit="KiB"
+	      optional="y"
+	      desc="Estimate of the memory image size, in KiB, of the
+		    running job, only present when a job is
+		    executing, pulled by STARTD_JOB_EXPRS"/>
+    <property name="IsValidCheckpointPlatform"
+	      type="lstr"
+	      desc="A configurable expression representing if a
+		    checkpointed job can run on the slot, part of the
+		    slot's Requirements along with the Start
+		    expression"/>
+    <!-- XXX: JobId could quite possibly be a statistic, snapshots
+	      would show use over time -->
+    <property name="JobId"
+	      type="sstr"
+	      optional="y"
+	      desc="The running job's identifier,
+		    i.e. ClusterId.ProcId, only present when a job is
+		    executing"/>
+    <property name="JobStart"
+	      type="absTime" unit="nanosecond"
+	      optional="y"
+	      desc="The number of nanosecond since epoch when the job
+		    began executing, only present when a job is
+		    executing"/>
+    <statistic name="KeyboardIdle"
+	       type="uint32" unit="second"
+	       desc="Number of seconds since any activity on any
+		     keyboard or mouse associated with the machine,
+		     including pseudo-terminals"/>
+    <property name="KFlops"
+	      type="uint32"
+	      desc="Relative floating point performance on a Linpack
+		    benchmark"/>
+    <statistic name="LastBenchmark"
+	       type="absTime" unit="nanosecond"
+	       desc="Number of nanoseconds since epoch when the last
+		     benchmark was run"/>
+    <statistic name="LastFetchWorkCompleted"
+	       type="absTime" unit="nanosecond"
+	       desc="Number of nanoseconds since epoch when the
+		     FetchWork Hook returned"/>
+
+    <statistic name="LastFetchWorkSpawned"
+	       type="absTime" unit="nanosecond"
+	       desc="Number of nanoseconds since epoch when the
+		     FetchWork Hook was invoked"/>
+    <statistic 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"/>
+<!--
+    <statistic name="LastHeardFrom"
+	       type="absTime" unit="nanosecond"
+	       desc="Time when the Collector received an update from
+		     the slot, nanoseconds since epoch, inserted by
+		     Collector"/>
+-->
+    <statistic name="LoadAvg"
+	       type="double"
+	       desc="Load average of CPUs hosting the slot"/>
+    <property name="Machine"
+	      type="sstr"
+	      desc="The fully qualified hostname of slot's host
+		    machine"/>
+    <property name="MaxJobRetirementTime"
+	      type="lstr" unit="second"
+	      desc="Expression evaluated in context of job ad
+		    producing the number of seconds a job is allowed
+		    to finish before being killed, relevant when job
+		    is being kicked out of the slot"/>
+    <property name="Memory"
+	      type="uint32" unit="MiB"
+	      desc="Amount of RAM available in the slot, in MiB"/>
+    <property name="Mips"
+	      type="uint32"
+	      desc="Relative integer performance on a Dhrystone
+		    benchmark"/>
+    <property name="MyAddress"
+	      type="sstr"
+	      desc="IP:Port of StartD in charge of the slot"/>
+    <statistic name="MyCurrentTime"
+	       type="absTime" unit="nanosecond"
+	       desc="The number of nanoseconds since epoch that the
+		     slot produced an updated ad"/>
+<!--
+    <property name="MyType"
+	       type="sstr"
+	       desc="Always 'Machine'"\>
+-->
+    <property name="Name"
+	      type="sstr"
+	      index="y"
+	      desc="Name of the slot, either the same as Machine,
+		    slot#@Machine, or a configured value"/>
+    <statistic name="NextFetchWorkDelay"
+	       type="int32" unit="second"
+	       desc="Number of seconds until the next FetchWork
+		     Hook will be invoked, -1 means never"/>
+    <property name="OpSys"
+	      type="sstr"
+	      desc="Slot's operating system, e.g.: HPUX10, HPUX
+		    10.20; HPUX11, HPUX B.11.00; LINUX, Linux
+		    2.[0,2,4,6].x kernels; OSF1, Diginal Unix 4.x;
+		    OSX, Darwin; OSX10_2, Darwin 6.4; SOLARIS25,
+		    Solaris 2.4 or 5.5; SOLARIS251, Solaris 2.5.1 or
+		    5.5.1; SOLARIS26, Solaris 2.6 or 5.6; SOLARIS27,
+		    Solaris 2.7 or 5.7; SOLARIS28, Solaris 2.8 or
+		    5.8; SOLARIS29, Solaris 2.9 or 5.9; WINNT50,
+		    Windows 2000; WINNT51, Windows XP; WINNT52,
+		    Windows Server 2003; WINNT60, Windows Vista"/>
+    <property name="PreemptingConcurrencyLimits"
+	      type="sstr"
+	      optional="y"
+	      desc="Set of concurrency limits associated with the
+		    preempting job"/>
+    <property name="PreemptingOwner"
+	      type="sstr"
+	      optional="y"
+	      desc="The name of the user originally preempting the
+		    current job, i.e. the incoming user, only present
+		    when slot is claimed"/>
+    <!-- XXX: PreemptingUser could quite possibly be a statistic -->
+    <property name="PreemptingUser"
+	      type="sstr"
+	      optional="y"
+	      desc="The name of the user preempting the current job,
+		    different from PreemptingOwner only if the claim
+		    was given to another user who is using it to
+		    preempt, only present when slot is claimed"/>
+    <!-- XXX: PreemptingRank could quite possibly be a statistic -->
+    <property name="PreemptingRank"
+	      type="double"
+	      optional="y"
+	      desc="Slots' affinity for running the incoming,
+		    preempting, job, calculated as Rank expression
+		    evaluated in context of the incoming job's ad,
+		    only present when slot is claimed"/>
+    <!-- XXX: RemoteOwner could quite possibly be a statistic, showing
+	      use over time -->
+    <property name="RemoteOwner"
+	      type="sstr"
+	      optional="y"
+	      desc="The name of the user who originally claimed the
+		    slot, only present when slot is claimed"/>
+    <!-- XXX: RemoteUser could quite possibly be a statistic, showing
+	      use over time -->
+    <property name="RemoteUser"
+	      type="sstr"
+	      optional="y"
+	      desc="The name of the user who is currently using the
+		    slot, different from RemoteOwner only if the
+		    claim was given to another user who is using the
+		    slot, only present when slot is claimed"/>
+    <property name="Requirements"
+	      type="lstr"
+	      desc="Expression evaluated in the context of a job ad
+		    to determine if the slot will run a job"/>
+    <property name="PublicNetworkIpAddr"
+	      type="sstr"
+	      desc="IP:Port used to communicate with the slot"/>
+    <property name="Rank"
+	      type="lstr"
+	      desc="Configured expression representing how the slot
+		    prefers jobs"/>
+    <property name="SlotID"
+	      type="uint32"
+	      desc="The # in the slot's Name, i.e. Name='slot#@Machine'"/>
+    <property name="Start"
+	      type="lstr"
+	      desc="Expression evaluated to determine if a slot is
+		    willing to start running a job"/>
+    <property name="StarterAbilityList"
+	      type="lstr"
+	      desc="StringList, comma separated, set of abilities the
+		    slot has, i.e. HasFileTransfer,HasJava,HasVM,
+		    query with stringListMember('Element',
+		    StarterAbilityList)"/>
+    <statistic name="State"
+	       type="sstr"
+	       desc="One of: Owner, unavailable to Condor; Unclaimed,
+		     available to Condor, but no job match yet;
+		     Matched, job found, but not yet claimed; Claimed,
+		     claimed and job likely running (see Activity);
+		     Preempting, running job is being kicked off the
+		     slot"/>
+<!--
+    <statistic name="TargetType"
+	       type="sstr"
+	       desc="Always 'Job'"/>
+-->
+    <statistic name="TimeToLive"
+	       type="uint32" unit="second"
+	       desc="Number of second until StartD managing the slot
+		     has until it will exit"/>
+    <!-- XXX: TotalClaimRunTime is a statistic -->
+    <property name="TotalClaimRunTime"
+	      type="uint32" unit="second"
+	      optional="y"
+	      desc="Number of seconds the current claim has spent
+		    running jobs, only present when slot is
+		    claimed"/>
+    <!-- XXX: TotalClaimSuspendTime is a statistic -->
+    <property name="TotalClaimSuspendTime"
+	      type="uint32" unit="second"
+	      optional="y"
+	      desc="Number of seconds the current claim has spent
+		    with suspended jobs, only present when slot is
+		    claimed"/>
+    <statistic name="TotalCondorLoadAvg"
+	       type="double"
+	       desc="Portion of TotalLoadAvg generated by Condor (jobs
+		     or benchmarks)"/>
+    <property name="TotalCpus"
+	      type="uint32"
+	      desc="Total number of CPUs on slot's host machine, or
+		    NUM_CPUS configuration option"/>
+    <property name="TotalDisk"
+	      type="uint32" unit="KiB"
+	      desc="Amount of disk space available on the slot's host
+		    machine"/>
+    <!-- XXX: TotalJobRunTime is a statistic -->
+    <property name="TotalJobRunTime"
+	      type="uint32" unit="second"
+	      optional="y"
+	      desc="Number of seconds the current job has spent
+		    running, i.e. Claimed/Busy, only present when
+		    slot is claimed"/>
+    <!-- XXX: TotalJobSuspendTime is a statistic -->
+    <property name="TotalJobSuspendTime"
+	      type="uint32" unit="second"
+	      optional="y"
+	      desc="Number of seconds the current job has spent
+		    suspended, i.e. Claimed/Suspended, only present
+		    when slot is claimed"/>
+    <statistic name="TotalLoadAvg"
+	       type="double"
+	       desc="Total load average of the slot's host machine"/>
+    <property name="TotalMemory"
+	      type="uint32" unit="MiB"
+	      desc="Total RAM available on slot's machine, in MiB"/>
+    <property name="TotalSlots"
+	      type="uint32"
+	      desc="Total number of slots sharing the Machine"/>
+    <statistic name="TotalTimeBackfillBusy"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Backfill and Activity=Busy since the
+		     Startd started"/>
+    <statistic name="TotalTimeBackfillIdle"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Backfill and Activity=Idle since the
+		     Startd started"/>
+    <statistic name="TotalTimeBackfillKilling"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Backfill and Activity=Killing since the
+		     Startd started"/>
+    <statistic name="TotalTimeClaimedBusy"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Claimed and Activity=Busy since the
+		     Startd started"/>
+    <statistic name="TotalTimeClaimedIdle"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Claimed and Activity=Idle since the
+		     Startd started"/>
+    <statistic name="TotalTimeClaimedRetiring"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Claimed and Activity=Retiring since the
+		     Startd started"/>
+    <statistic name="TotalTimeClaimedSuspended"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Claimed and Activity=Suspended since the
+		     Startd started"/>
+    <statistic name="TotalTimeMatchedIdle"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Matched and Activity=Idle since the
+		     Startd started"/>
+    <statistic name="TotalTimeOwnerIdle"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Owner and Activity=Idle since the
+		     Startd started"/>
+    <statistic name="TotalTimePreemptingKilling"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Preempting and Activity=Killing since the
+		     Startd started"/>
+    <statistic name="TotalTimePreemptingVacating"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Preempting and Activity=Vacating since the
+		     Startd started"/>
+    <statistic name="TotalTimeUnclaimedBenchmarking"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Unclaimed and Activity=Benchmarking since
+		     the Startd started"/>
+    <statistic name="TotalTimeUnclaimedIdle"
+	       type="uint32" unit="second"
+	       desc="Accumulated number of seconds the slot has been
+		     in State=Unclaimed and Activity=Idle since the
+		     Startd started"/>
+    <property name="TotalVirtualMemory"
+	      type="uint32" unit="KiB"
+	      desc="Amount of swap space available on slot"/>
+    <property name="UidDomain"
+	      type="sstr"
+	      desc="Configured namespace shared by slots with
+		    uniform uid/gid entries, i.e. same logins and
+		    groups"/>
+    <property name="VirtualMemory"
+	      type="uint32" unit="KiB"
+	      desc="Amount of currently available virtual memory
+		    (swap space) in KiB"/>
+    <property name="WindowsBuildNumber"
+	      type="uint32"
+	      desc="Integer extracted from the platform type,
+		    representing a build number for a Windows
+		    operating system, only present on Windows
+		    slots"/>
+    <property name="WindowsMajorVersion"
+	      type="uint32"
+	      desc="Integer extracted from the platform type,
+		    representing a major version number for a Windows
+		    operating system, only present on Windows
+		    slots, e.g. 5 for OpSys=WINNT50"/>
+    <property name="WindowsMinorVersion"
+	      type="uint32"
+	      desc="Integer extected from the platform type,
+		    representing a minor version numer for a Windows
+		    operating system, only present on Windows
+		    slots, e.g. 2 for OpSys=WINNT52"/>
+
+<!--
+    <property name="AdditionalAttributes" type="map"/>
+-->
+  </class>
+
+<!--
+Exec Host, Order(Rank?), StartTime, TotalTime (Sys, User), Project, AccountingGroup
+-->
+  <class name="Job">
+    <property name="schedulerRef" type="objId" parentRef="y" index="y" references="mrg.grid.Scheduler"/>
+    <property name="submitterRef" type="objId" references="mrg.grid.Submitter"/>
+
+    <property name="AccountingGroup" type="sstr" optional="y" desc=""/>
+    <property name="Args" type="lstr" optional="y" desc=""/>
+    <property name="ClusterId"
+	      type="uint32" index="y"
+	      desc="The id of the cluster the job belongs
+		    to. ClusterIds are unique within a SchedD."/>
+    <property name="Cmd" type="lstr" desc=""/>
+    <property name="ConcurrencyLimits" type="lstr" optional="y" desc=""/>
+    <property name="CustomGroup" type="sstr" optional="y" desc=""/>
+    <property name="CustomId" type="sstr" optional="y" desc=""/>
+    <property name="CustomPriority" type="uint32" optional="y" desc=""/>
+    <property name="GlobalJobId" type="sstr" desc=""/>
+    <property name="In"
+	      type="lstr"
+	      desc="The file where the job's standard input is read
+		    from."/>
+    <property name="Iwd" type="lstr" desc=""/>
+    <property name="JobStatus"
+	      type="uint32"
+	      desc="One of: 0, unexpanded; 1, idle; 2, running; 3,
+		    removed; 4, completed; 5, held; or, 6, submission
+		    error"/>
+    <property name="Note"
+	      type="lstr" optional="y"
+	      desc="An arbitrary note attached to the job."/>
+    <property name="Out"
+	      type="lstr"
+	      desc="The file where the job's standard output is
+		    written."/>
+    <property name="Owner"
+	      type="sstr"
+	      desc="The submitter of the job."/>
+    <property name="User"
+	      type="sstr"
+	      desc="The Owner '@' the configured UidDomain namespace"/>
+    <property name="ProcId"
+	      type="uint32" index="y"
+	      desc="The id of the job within its cluster. ProcIds re
+		    unique within a cluster."/>
+    <property name="QDate"
+	      type="absTime" unit="nanoseconds"
+	      desc="The number of nanoseconds since epoch when the
+		    job was submitted."/>
+
+<!--
+    <property name="Requirements" type="lstr" desc=""/>
+    <property name="Scheduler" type="sstr" desc=""/>
+-->
+
+    <property name="JobUniverse"
+	      type="uint32"
+	      desc=""/>
+
+    <property name="Title" type="sstr" optional="y" desc=""/>
+    <property name="UserLog" type="lstr" optional="y" desc=""/>
+
+    <property name="HoldReason" type="lstr" optional="y" desc=""/>
+
+    <property name="DAGNodeName"
+	      type="sstr" optional="y" desc=""/>
+    <property name="DAGParentNodeNames"
+	      type="lstr" optional="y"
+	      desc="Comma separated list of the job's parent's node
+		    names"/>
+    <property name="DAGManJobId"
+	      type="uint32" optional="y"
+	      desc="The ClusterId of the DAGMan job who spawned the
+		    job"/>
+
+    <property name="Ad" type="map" optional="y" desc=""/>
+
+    <method name="GetAd">
+      <arg name="JobAd" dir="O" type="map"
+	   desc="(name,value,type) tuples; Values are INTEGER, FLOAT,
+		 STRING and EXPR. The EXPR value is not first class,
+		 it is an unquoted, with double quotes, string"/>
+    </method>
+
+    <method name="SetAttribute">
+      <arg name="Name" dir="I" type="sstr"/>
+      <arg name="Value" dir="I" type="lstr"/>
+    </method>
+
+    <method name="Hold">
+      <arg name="Reason" dir="I" type="sstr"/>
+    </method>
+
+    <method name="Release">
+      <arg name="Reason" dir="I" type="sstr"/>
+    </method>
+
+    <method name="Remove">
+      <arg name="Reason" dir="I" type="sstr"/>
+    </method>
+
+    <method name="Fetch">
+      <arg name="File" dir="I" type="sstr"/>
+      <arg name="Start" dir="I" type="int32"/>
+      <arg name="End" dir="I" type="int32"/>
+      <arg name="Data" dir="O" type="lstr"/>
+    </method>
+  </class>
+
+  <class name="Scheduler">
+    <group name="daemon-stats"/>
+
+    <property name="Pool" type="sstr" index="y"/>
+    <property name="System" type="sstr" index="y"/>
+
+    <property name="JobQueueBirthdate" type="absTime"/>
+    <property name="MaxJobsRunning" type="uint32" desc=""/>
+    <property name="Machine" type="sstr" desc=""/>
+    <property name="MyAddress" type="sstr" desc=""/>
+    <statistic name="NumUsers" type="uint32"/>
+    <property name="Name" type="sstr" index="y" desc=""/>
+    <property name="PublicNetworkIpAddr" type="sstr" desc=""/>
+    <statistic name="TotalHeldJobs" type="uint32"/>
+    <statistic name="TotalIdleJobs" type="uint32"/>
+    <statistic name="TotalJobAds" type="uint32"/>
+    <statistic name="TotalRemovedJobs" type="uint32"/>
+    <statistic name="TotalRunningJobs" type="uint32"/>    
+
+    <method name="Submit">
+      <arg name="Ad" dir="I" type="map"/>
+      <arg name="Id" dir="O" type="sstr"/>
+    </method>
+  </class>
+
+  <class name="Submitter">
+    <property name="schedulerRef" type="objId" parentRef="y" index="y" references="mrg.grid.Scheduler"/>
+
+    <statistic name="HeldJobs" type="uint32"/>
+    <statistic name="IdleJobs" type="uint32"/>
+    <property name="JobQueueBirthdate" type="absTime"/>
+    <property name="Machine" type="sstr"/>
+    <property name="Name" type="sstr" index="y"/>
+    <statistic name="RunningJobs" type="uint32"/>
+    <property name="ScheddName" type="sstr"/>
+  </class>
+
+  <class name="Negotiator">
+    <property name="Pool" type="sstr" index="y"/>
+    <property name="System" type="sstr" index="y"/>
+
+    <property name="Name" type="sstr" index="y"/>
+    <property name="Machine" type="sstr"/>
+    <property name="MyAddress" type="sstr" desc=""/>
+
+    <!-- NOTE: MonitorSelf* statistics are currently missing in 7.0.0 -->
+    <group name="daemon-stats"/>
+
+    <method name="GetLimits">
+      <arg name="Limits" dir="O" type="map"/>
+    </method>
+
+    <method name="SetLimit">
+      <arg name="Name" dir="I" type="sstr"/>
+      <arg name="Max" dir="I" type="double"/>
+    </method>
+
+    <method name="GetStats">
+      <arg name="Name" dir="I" type="sstr" desc="User or group name"/>
+      <arg name="Ad" dir="O" type="map"/>
+<!--
+      <arg name="Effective" dir="O" type="double"/>
+      <arg name="Real" dir="O" type="double"/>
+      <arg name="Factor" dir="O" type="double"/>
+      <arg name="Resources" dir="O" type="unit32"/>
+      <arg name="Usage" dir="O" type="double" units="hours"/>
+-->
+    </method>
+
+    <method name="SetPriority">
+      <arg name="Name" dir="I" type="sstr" desc="User or group name"/>
+      <arg name="Priority" dir="I" type="double"/>
+    </method>
+
+    <method name="SetPriorityFactor">
+      <arg name="Name" dir="I" type="sstr" desc="User or group name"/>
+      <arg name="PriorityFactor" dir="I" type="double"/>
+    </method>
+
+    <method name="SetUsage">
+      <arg name="Name" dir="I" type="sstr" desc="User or group name"/>
+      <arg name="Usage" dir="I" type="double"/>
+    </method>
+
+ <!--
+    <method name="GetStaticQuota">
+      <arg name="Name" dir="I" type="sstr" desc="Group name"/>
+      <arg name="Quota" dir="O" type="uint32"/>
+    </method>
+
+    <method name="GetDynamicQuota">
+      <arg name="Name" dir="I" type="sstr" desc="Group name"/>
+      <arg name="Quota" dir="O" type="double"/>
+    </method>
+
+    <method name="SetStaticQuota">
+      <arg name="Name" dir="I" type="sstr" desc="Group name"/>
+      <arg name="Quota" dir="I" type="uint32"/>
+    </method>
+
+    <method name="SetDynamicQuota">
+      <arg name="Name" dir="I" type="sstr" desc="Group name"/>
+      <arg name="Quota" dir="I" type="double"/>
+    </method>
+-->
+
+    <method name="GetRawConfig">
+      <arg name="Name" dir="I" type="sstr" desc="Config param name"/>
+      <arg name="Value" dir="O" type="lstr"/>
+    </method>
+
+    <method name="SetRawConfig">
+      <arg name="Name" dir="I" type="sstr" desc="Config param name"/>
+      <arg name="Value" dir="I" type="lstr"/>
+    </method>
+
+    <method name="Reconfig"/>
+  </class>
+
+  <class name="Collector">
+    <property name="Pool" type="sstr" index="y"/>
+    <property name="System" type="sstr" index="y"/>
+
+    <property name="CondorPlatform" type="sstr"/>
+    <property name="CondorVersion" type="sstr"/>
+    <property name="Name" type="sstr" index="y"/>
+    <property name="PublicNetworkIpAddr" type="sstr"/>
+
+    <statistic name="RunningJobs" type="uint32"/>
+    <statistic name="IdleJobs" type="uint32"/>
+    <statistic name="HostsTotal" type="uint32"/>
+    <statistic name="HostsClaimed" type="uint32"/>
+    <statistic name="HostsUnclaimed" type="uint32"/>
+    <statistic name="HostsOwner" type="uint32"/>
+  </class>
+
+  <class name="Master">
+
+    <group name="daemon-stats"/>
+
+    <property name="Pool" type="sstr" index="y"/>
+    <property name="System" type="sstr" index="y"/>
+
+    <property name="Name" type="sstr" index="y"/>
+    <property name="Machine" type="sstr"/>
+    <property name="PublicNetworkIpAddr" type="sstr"/>
+    <property name="MyAddress" type="sstr"/>
+    <property name="RealUid" type="int32"/>
+
+    <method name="Start">
+      <arg name="Subsystem"
+	   dir="I" type="sstr"
+	   desc="The component/subsystem to start: one of STARTD,
+		 SCHEDD, COLLECTOR, NEGOTIATOR, KBDD or QUILL"/>
+    </method>
+
+    <method name="Stop">
+      <arg name="Subsystem"
+	   dir="I" type="sstr"
+	   desc="The component/subsystem to stop: one of STARTD,
+		 SCHEDD, COLLECTOR, NEGOTIATOR, KBDD or QUILL"/>
+    </method>
+  </class>
+
+  <class name="Grid">
+    <property name="Pool" type="sstr" index="y"/>
+
+    <property name="Name" type="sstr"/>
+    <property name="ScheddName" type="sstr"/>
+    <property name="Owner" type="sstr"/>
+
+    <statistic name="NumJobs" type="uint32"/>
+    <property name="JobLimit"
+	      type="uint32"
+	      desc="Maximum number of jobs that can be in the process
+		    of being submitted at any time."/>
+    <property name="SubmitLimit"
+	      type="uint32"
+	      desc="Limit on the number of jobs that will be submitted
+		    to the grid resource at once."/>
+
+    <statistic name="SubmitsInProgress" type="uint32"/>
+    <statistic name="SubmitsQueued" type="uint32"/>
+    <statistic name="SubmitsAllowed" type="uint32"/>
+    <statistic name="SubmitsWanted" type="uint32"/>
+
+    <property name="GridResourceUnavailableTime"
+	      type="absTime" unit="nanosecond"
+	      optional="y"
+	      desc="If present, the Grid is down for the specified
+		    amount of time."/>
+
+    <statistic name="RunningJobs" type="uint32"/>
+    <statistic name="IdleJobs" type="uint32"/>
+  </class>
+</schema>

Added: mgmt/trunk/cumin/metadata/qmf/qpid-acl.xml
===================================================================
--- mgmt/trunk/cumin/metadata/qmf/qpid-acl.xml	                        (rev 0)
+++ mgmt/trunk/cumin/metadata/qmf/qpid-acl.xml	2009-06-17 20:20:11 UTC (rev 3457)
@@ -0,0 +1,44 @@
+<schema package="org.apache.qpid.acl">
+
+<!--
+ * Copyright (c) 2008 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+  <class name="Acl">
+    <property name="brokerRef"     type="objId"   references="org.apache.qpid.broker:Broker" access="RO" index="y" parentRef="y"/>
+    <property name="policyFile"    type="sstr"    access="RO"    desc="Name of the policy file"/>
+    <property name="enforcingAcl"  type="bool"    access="RO"    desc="Currently Enforcing ACL"/>
+    <property name="transferAcl"   type="bool"    access="RO"    desc="Any transfer ACL rules in force"/>
+    <property name="lastAclLoad"   type="absTime" access="RO"    desc="Timestamp of last successful load of ACL"/>
+    <statistic name="aclDenyCount" type="count64" unit="request" desc="Number of ACL requests denied"/>
+
+    <method name="reloadACLFile" desc="Reload the ACL file"/>
+  </class>
+
+  <eventArguments>
+    <arg name="action"     type="sstr"/>
+    <arg name="arguments"  type="map"/>
+    <arg name="objectName" type="sstr"/>
+    <arg name="objectType" type="sstr"/>
+    <arg name="reason"     type="sstr"/>
+    <arg name="userId"     type="sstr"/>
+  </eventArguments>
+
+  <event name="allow"          sev="inform" args="userId, action, objectType, objectName, arguments"/>
+  <event name="deny"           sev="notice" args="userId, action, objectType, objectName, arguments"/>
+  <event name="fileLoaded"     sev="inform" args="userId"/>
+  <event name="fileLoadFailed" sev="error"  args="userId, reason"/>
+
+</schema>

Added: mgmt/trunk/cumin/metadata/qmf/qpid-cluster.xml
===================================================================
--- mgmt/trunk/cumin/metadata/qmf/qpid-cluster.xml	                        (rev 0)
+++ mgmt/trunk/cumin/metadata/qmf/qpid-cluster.xml	2009-06-17 20:20:11 UTC (rev 3457)
@@ -0,0 +1,61 @@
+<schema package="org.apache.qpid.cluster">
+
+  <!--
+      Licensed to the Apache Software Foundation (ASF) under one
+      or more contributor license agreements.  See the NOTICE file
+      distributed with this work for additional information
+      regarding copyright ownership.  The ASF licenses this file
+      to you under the Apache License, Version 2.0 (the
+      "License"); you may not use this file except in compliance
+      with the License.  You may obtain a copy of the License at
+      
+      http://www.apache.org/licenses/LICENSE-2.0
+      
+      Unless required by applicable law or agreed to in writing,
+      software distributed under the License is distributed on an
+      "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+      KIND, either express or implied.  See the License for the
+      specific language governing permissions and limitations
+      under the License.
+  -->
+
+  <!-- Type information:
+
+Numeric types with "_wm" suffix are watermarked numbers.  These are compound
+values containing a current value, and a low and high water mark for the reporting
+interval.  The low and high water marks are set to the current value at the
+beginning of each interval and track the minimum and maximum values of the statistic
+over the interval respectively.
+
+Access rights for configuration elements:
+
+RO => Read Only
+RC => Read/Create, can be set at create time only, read-only thereafter
+RW => Read/Write
+
+If access rights are omitted for a property, they are assumed to be RO.
+
+  -->
+
+  <class name="Cluster">
+    <property name="brokerRef"        type="objId"  references="Broker" access="RC" index="y" parentRef="y"/>
+    <property name="clusterName"      type="sstr"   access="RC" desc="Name of cluster this server is a member of"/>
+    <property name="clusterID"        type="sstr"   access="RO" desc="Globally unique ID (UUID) for this cluster instance"/>
+    <property name="memberID"         type="sstr"   access="RO" desc="ID of this member of the cluster"/>
+    <property name="publishedURL"     type="sstr"   access="RC" desc="URL this node advertizes itself as"/>
+    <property name="clusterSize"      type="uint16" access="RO" desc="Number of brokers currently in the cluster"/>
+    <property name="status"           type="sstr"   access="RO" desc="Cluster node status (STALLED,ACTIVE,JOINING)"/>
+    <property name="members"          type="lstr"   access="RO" desc="List of member URLs delimited by ';'"/> 
+    <property name="memberIDs"        type="lstr"   access="RO" desc="List of member IDs delimited by ';'"/> 
+
+    <method name="stopClusterNode">
+      <arg name="brokerId" type="sstr" dir="I"/>
+    </method>
+    <method name="stopFullCluster"/>
+
+  </class>
+
+
+
+</schema>
+

Added: mgmt/trunk/cumin/metadata/qmf/qpid-store.xml
===================================================================
--- mgmt/trunk/cumin/metadata/qmf/qpid-store.xml	                        (rev 0)
+++ mgmt/trunk/cumin/metadata/qmf/qpid-store.xml	2009-06-17 20:20:11 UTC (rev 3457)
@@ -0,0 +1,103 @@
+<schema package="com.redhat.rhm.store">
+
+<!--
+ Copyright (c) 2007, 2008 Red Hat, Inc.
+
+ This file is part of the Qpid async store library msgstore.so.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ USA
+
+ The GNU Lesser General Public License is available in the file COPYING.
+ -->
+
+  <class name="Store">
+    <property name="brokerRef"               type="objId"  access="RO" references="qpid.Broker" index="y" parentRef="y"/>
+    <property name="location"                type="sstr"   access="RO"              desc="Logical directory on disk"/>
+    <property name="defaultInitialFileCount" type="uint16" access="RO" unit="file"  desc="Default number of files initially allocated to each journal"/>
+    <property name="defaultDataFileSize"     type="uint32" access="RO" unit="RdPg"  desc="Default size of each journal data file"/>
+    <property name="tplIsInitialized"        type="bool"   access="RO"              desc="Transaction prepared list has been initialized by a transactional prepare"/>
+    <property name="tplDirectory"            type="sstr"   access="RO"              desc="Transaction prepared list directory"/>
+    <property name="tplWritePageSize"        type="uint32" access="RO" unit="byte"  desc="Page size in transaction prepared list write-page-cache"/>
+    <property name="tplWritePages"           type="uint32" access="RO" unit="wpage" desc="Number of pages in transaction prepared list write-page-cache"/>
+    <property name="tplInitialFileCount"     type="uint16" access="RO" unit="file"  desc="Number of files initially allocated to transaction prepared list journal"/>
+    <property name="tplDataFileSize"         type="uint32" access="RO" unit="byte"  desc="Size of each journal data file in transaction prepared list journal"/>
+    <property name="tplCurrentFileCount"     type="uint32" access="RO" unit="file"  desc="Number of files currently allocated to transaction prepared list journal"/>
+    
+    <statistic name="tplTransactionDepth"    type="hilo32"  unit="txn"    desc="Number of currently enqueued prepared transactions"/>
+    <statistic name="tplTxnPrepares"         type="count64" unit="record" desc="Total transaction prepares on transaction prepared list"/>
+    <statistic name="tplTxnCommits"          type="count64" unit="record" desc="Total transaction commits on transaction prepared list"/>
+    <statistic name="tplTxnAborts"           type="count64" unit="record" desc="Total transaction aborts on transaction prepared list"/>
+    <statistic name="tplOutstandingAIOs"     type="hilo32"  unit="aio_op" desc="Number of currently outstanding AIO requests in Async IO system"/>
+  </class>
+
+  <class name="Journal">
+    <property name="queueRef"           type="objId"  access="RO" references="qpid.Queue" isGeneralReference="y"/>
+    <property name="name"               type="sstr"   access="RO" index="y"/>
+    <property name="directory"          type="sstr"   access="RO"              desc="Directory containing journal files"/>
+    <property name="baseFileName"       type="sstr"   access="RO"              desc="Base filename prefix for journal"/>
+    <property name="writePageSize"      type="uint32" access="RO" unit="byte"  desc="Page size in write-page-cache"/>
+    <property name="writePages"         type="uint32" access="RO" unit="wpage" desc="Number of pages in write-page-cache"/>
+    <property name="readPageSize"       type="uint32" access="RO" unit="byte"  desc="Page size in read-page-cache"/>
+    <property name="readPages"          type="uint32" access="RO" unit="rpage" desc="Number of pages in read-page-cache"/>
+    <property name="initialFileCount"   type="uint16" access="RO" unit="file"  desc="Number of files initially allocated to this journal"/>
+    <property name="autoExpand"         type="bool"   access="RO"              desc="Auto-expand enabled"/>
+    <property name="currentFileCount"   type="uint16" access="RO" unit="file"  desc="Number of files currently allocated to this journal"/>
+    <property name="maxFileCount"       type="uint16" access="RO" unit="file"  desc="Max number of files allowed for this journal"/>
+    <property name="dataFileSize"       type="uint32" access="RO" unit="byte"  desc="Size of each journal data file"/>
+    
+    <statistic name="recordDepth"       type="hilo32"  unit="record" desc="Number of currently enqueued records (durable messages)"/>
+    <statistic name="enqueues"          type="count64" unit="record" desc="Total enqueued records on journal"/>
+    <statistic name="dequeues"          type="count64" unit="record" desc="Total dequeued records on journal"/>
+    <statistic name="txn"               type="count32" unit="record" desc="Total open transactions (xids) on journal"/>
+    <statistic name="txnEnqueues"       type="count64" unit="record" desc="Total transactional enqueued records on journal"/>
+    <statistic name="txnDequeues"       type="count64" unit="record" desc="Total transactional dequeued records on journal"/>
+    <statistic name="txnCommits"        type="count64" unit="record" desc="Total transactional commit records on journal"/>
+    <statistic name="txnAborts"         type="count64" unit="record" desc="Total transactional abort records on journal"/>
+    <statistic name="outstandingAIOs"   type="hilo32"  unit="aio_op" desc="Number of currently outstanding AIO requests in Async IO system"/>
+
+<!--
+    The following are not yet "wired up" in JournalImpl.cpp
+-->
+    <statistic name="freeFileCount"       type="hilo32"  unit="file"   desc="Number of files free on this journal. Includes free files trapped in holes."/>
+    <statistic name="availableFileCount"  type="hilo32"  unit="file"   desc="Number of files available to be written.  Excluding holes"/>
+    <statistic name="writeWaitFailures"   type="count64" unit="record" desc="AIO Wait failures on write"/>
+    <statistic name="writeBusyFailures"   type="count64" unit="record" desc="AIO Busy failures on write"/>
+    <statistic name="readRecordCount"     type="count64" unit="record" desc="Records read from the journal"/>
+    <statistic name="readBusyFailures"    type="count64" unit="record" desc="AIO Busy failures on read"/>
+    <statistic name="writePageCacheDepth" type="hilo32"  unit="wpage"  desc="Current depth of write-page-cache"/>
+    <statistic name="readPageCacheDepth"  type="hilo32"  unit="rpage"  desc="Current depth of read-page-cache"/>
+
+    <method name="expand" desc="Increase number of files allocated for this journal">
+      <arg name="by" type="uint32" dir="I" desc="Number of files to increase journal size by"/>
+    </method>
+  </class>
+ 
+  <eventArguments>
+    <arg name="autoExpand" type="bool"   desc="Journal auto-expand enabled"/>
+    <arg name="fileSize"   type="uint32" desc="Journal file size in bytes"/>
+    <arg name="jrnlId"     type="sstr"   desc="Journal Id"/>
+    <arg name="numEnq"     type="uint32" desc="Number of recovered enqueues"/>
+    <arg name="numFiles"   type="uint16" desc="Number of journal files"/>
+    <arg name="numTxn"     type="uint32" desc="Number of recovered transactions"/>
+    <arg name="numTxnDeq"  type="uint32" desc="Number of recovered transactional dequeues"/>
+    <arg name="numTxnEnq"  type="uint32" desc="Number of recovered transactional enqueues"/>
+    <arg name="what"       type="sstr"   desc="Description of event"/>
+  </eventArguments>
+  <event name="enqThresholdExceeded" sev="warn"   args="jrnlId, what"/>
+  <event name="created"              sev="notice" args="jrnlId, fileSize, numFiles"/>
+  <event name="full"                 sev="error"  args="jrnlId, what"/>
+  <event name="recovered"            sev="notice" args="jrnlId, fileSize, numFiles, numEnq, numTxn, numTxnEnq, numTxnDeq"/>
+</schema>

Added: mgmt/trunk/cumin/metadata/qmf/qpid.xml
===================================================================
--- mgmt/trunk/cumin/metadata/qmf/qpid.xml	                        (rev 0)
+++ mgmt/trunk/cumin/metadata/qmf/qpid.xml	2009-06-17 20:20:11 UTC (rev 3457)
@@ -0,0 +1,340 @@
+<schema package="org.apache.qpid.broker">
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+  <!-- Type information:
+
+       Numeric types with "_wm" suffix are watermarked numbers.  These are compound
+       values containing a current value, and a low and high water mark for the reporting
+       interval.  The low and high water marks are set to the current value at the
+       beginning of each interval and track the minimum and maximum values of the statistic
+       over the interval respectively.
+
+       Access rights for configuration elements:
+
+           RO => Read Only
+           RC => Read/Create, can be set at create time only, read-only thereafter
+           RW => Read/Write
+
+           If access rights are omitted for a property, they are assumed to be RO.
+
+  -->
+
+  <!-- Questions:  Does C++ broker round-robin dests on queues? -->
+
+  <!--
+  ===============================================================
+  System
+  ===============================================================
+  -->
+  <class name="System">
+    <property name="systemId" index="y" type="uuid" access="RC"/>
+
+    <property name="osName"   type="sstr" access="RO" desc="Operating System Name"/>
+    <property name="nodeName" type="sstr" access="RO" desc="Node Name"/>
+    <property name="release"  type="sstr" access="RO"/>
+    <property name="version"  type="sstr" access="RO"/>
+    <property name="machine"  type="sstr" access="RO"/>
+
+  </class>
+
+  <!--
+  ===============================================================
+  Broker
+  ===============================================================
+  -->
+  <class name="Broker">
+    <property name="systemRef"        type="objId"  references="System" access="RC" index="y" desc="System ID" parentRef="y"/>
+    <property name="port"             type="uint16" access="RC" index="y" desc="TCP Port for AMQP Service"/>
+    <property name="workerThreads"    type="uint16" access="RO" desc="Thread pool size"/>
+    <property name="maxConns"         type="uint16" access="RO" desc="Maximum allowed connections"/>
+    <property name="connBacklog"      type="uint16" access="RO" desc="Connection backlog limit for listening socket"/>
+    <property name="stagingThreshold" type="uint32" access="RO" desc="Broker stages messages over this size to disk"/>
+    <property name="mgmtPubInterval"  type="uint16" access="RW" unit="second" min="1" desc="Interval for management broadcasts"/>
+    <property name="version"          type="sstr"   access="RO" desc="Running software version"/>
+    <property name="dataDir"          type="sstr"   access="RO" optional="y" desc="Persistent configuration storage location"/>
+    <statistic name="uptime" type="deltaTime"/>
+
+    <method name="echo" desc="Request a response to test the path to the management broker">
+      <arg name="sequence" dir="IO" type="uint32" default="0"/>
+      <arg name="body"     dir="IO" type="lstr"   default=""/>
+    </method>
+
+    <method name="connect" desc="Establish a connection to another broker">
+      <arg name="host"          dir="I" type="sstr"/>
+      <arg name="port"          dir="I" type="uint32"/>
+      <arg name="durable"       dir="I" type="bool"/>
+      <arg name="authMechanism" dir="I" type="sstr"/>
+      <arg name="username"      dir="I" type="sstr"/>
+      <arg name="password"      dir="I" type="sstr"/>
+      <arg name="transport"     dir="I" type="sstr"/>
+    </method>
+
+    <method name="queueMoveMessages" desc="Move messages from one queue to another">
+      <arg name="srcQueue"          dir="I" type="sstr" desc="Source queue"/>
+      <arg name="destQueue"         dir="I" type="sstr" desc="Destination queue"/>
+      <arg name="qty"               dir="I" type="uint32" desc="# of messages to move. 0 means all messages"/>
+    </method>
+
+  </class>
+
+  <!--
+  ===============================================================
+  Management Agent
+  ===============================================================
+  -->
+  <class name="Agent">
+    <property name="connectionRef" type="objId"  references="Connection" access="RO" index="y"/>
+    <property name="label"         type="sstr"   access="RO"           desc="Label for agent"/>
+    <property name="registeredTo"  type="objId"  references="Broker" access="RO" desc="Broker agent is registered to"/>
+    <property name="systemId"      type="uuid"   access="RO"           desc="Identifier of system where agent resides"/>
+    <property name="brokerBank"    type="uint32" access="RO"           desc="Assigned object-id broker bank"/>
+    <property name="agentBank"     type="uint32" access="RO"           desc="Assigned object-id agent bank"/>
+  </class>
+
+  <!--
+  ===============================================================
+  Virtual Host
+  ===============================================================
+  -->
+  <class name="Vhost">
+    <property name="brokerRef"     type="objId" references="Broker" access="RC" index="y" parentRef="y"/>
+    <property name="name"          type="sstr"  access="RC" index="y"/>
+    <property name="federationTag" type="sstr"  access="RO"/>
+  </class>
+
+  <!--
+  ===============================================================
+  Queue
+  ===============================================================
+  -->
+  <class name="Queue">
+    <property name="vhostRef"   type="objId" references="Vhost" access="RC" index="y" parentRef="y"/>
+    <property name="name"       type="sstr"  access="RC" index="y"/>
+
+    <property name="durable"    type="bool"  access="RC"/>
+    <property name="autoDelete" type="bool"  access="RC"/>
+    <property name="exclusive"  type="bool"  access="RC"/>
+    <property name="arguments"  type="map"   access="RO" desc="Arguments supplied in queue.declare"/>
+
+    <statistic name="msgTotalEnqueues"    type="count64"  unit="message"     desc="Total messages enqueued"/>
+    <statistic name="msgTotalDequeues"    type="count64"  unit="message"     desc="Total messages dequeued"/>
+    <statistic name="msgTxnEnqueues"      type="count64"  unit="message"     desc="Transactional messages enqueued"/>
+    <statistic name="msgTxnDequeues"      type="count64"  unit="message"     desc="Transactional messages dequeued"/>
+    <statistic name="msgPersistEnqueues"  type="count64"  unit="message"     desc="Persistent messages enqueued"/>
+    <statistic name="msgPersistDequeues"  type="count64"  unit="message"     desc="Persistent messages dequeued"/>
+    <statistic name="msgDepth"            type="count32"  unit="message"     desc="Current size of queue in messages" assign="msgTotalEnqueues - msgTotalDequeues"/>
+    <statistic name="byteDepth"           type="count32"  unit="octet"       desc="Current size of queue in bytes"    assign="byteTotalEnqueues - byteTotalDequeues"/>
+    <statistic name="byteTotalEnqueues"   type="count64"  unit="octet"       desc="Total messages enqueued"/>
+    <statistic name="byteTotalDequeues"   type="count64"  unit="octet"       desc="Total messages dequeued"/>
+    <statistic name="byteTxnEnqueues"     type="count64"  unit="octet"       desc="Transactional messages enqueued"/>
+    <statistic name="byteTxnDequeues"     type="count64"  unit="octet"       desc="Transactional messages dequeued"/>
+    <statistic name="bytePersistEnqueues" type="count64"  unit="octet"       desc="Persistent messages enqueued"/>
+    <statistic name="bytePersistDequeues" type="count64"  unit="octet"       desc="Persistent messages dequeued"/>
+    <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"/>
+
+    <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"/>
+    </method>
+  </class>
+
+  <!--
+  ===============================================================
+  Exchange
+  ===============================================================
+  -->
+  <class name="Exchange">
+    <property name="vhostRef"   type="objId" references="Vhost" access="RC" index="y" parentRef="y"/>
+    <property name="name"       type="sstr"  access="RC" index="y"/>
+    <property name="type"       type="sstr"  access="RO"/>
+    <property name="durable"    type="bool"  access="RC"/>
+    <property name="arguments"  type="map"   access="RO" desc="Arguments supplied in exchange.declare"/>
+
+    <statistic name="producerCount" type="hilo32"  desc="Current producers on exchange"/>
+    <statistic name="bindingCount"  type="hilo32"  desc="Current bindings"/>
+    <statistic name="msgReceives"   type="count64" desc="Total messages received"/>
+    <statistic name="msgDrops"      type="count64" desc="Total messages dropped (no matching key)"/>
+    <statistic name="msgRoutes"     type="count64" desc="Total routed messages"/>
+    <statistic name="byteReceives"  type="count64" desc="Total bytes received"/>
+    <statistic name="byteDrops"     type="count64" desc="Total bytes dropped (no matching key)"/>
+    <statistic name="byteRoutes"    type="count64" desc="Total routed bytes"/>
+  </class>
+
+  <!--
+  ===============================================================
+  Binding
+  ===============================================================
+  -->
+  <class name="Binding">
+    <property name="exchangeRef" type="objId" references="Exchange" access="RC" index="y" parentRef="y"/>
+    <property name="queueRef"    type="objId" references="Queue"    access="RC" index="y"/>
+    <property name="bindingKey"  type="sstr"  access="RC" index="y"/>
+    <property name="arguments"   type="map"   access="RC"/>
+    <property name="origin"      type="sstr"  access="RO" optional="y"/>
+
+    <statistic name="msgMatched" type="count64"/>
+  </class>
+  
+  <!--
+  ===============================================================
+  Connection
+  ===============================================================
+  -->
+  <class name="Connection">
+    <property name="vhostRef" type="objId"  references="Vhost" access="RC" index="y" parentRef="y"/>
+    <property name="address"  type="sstr"   access="RC" index="y"/>
+    <property name="incoming" type="bool"   access="RC"/>
+    <property name="SystemConnection"   type="bool"   access="RC" desc="Infrastucture/ Inter-system connection (Cluster, Federation, ...)"/>
+    <property name="federationLink"     type="bool"   access="RO" desc="Is this a federation link"/>
+    <property name="authIdentity"       type="sstr"   access="RO" desc="authId of connection if authentication enabled"/>
+    <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"/>
+    <statistic name="closing"          type="bool" desc="This client is closing by management request"/>
+    <statistic name="framesFromClient" type="count64"/>
+    <statistic name="framesToClient"   type="count64"/>
+    <statistic name="bytesFromClient"  type="count64"/>
+    <statistic name="bytesToClient"    type="count64"/>
+
+    <method name="close"/> 
+  </class>
+
+  <!--
+  ===============================================================
+  Link
+  ===============================================================
+  -->
+  <class name="Link">
+
+    This class represents an inter-broker connection.
+
+    <property name="vhostRef"  type="objId"  references="Vhost" access="RC" index="y" parentRef="y"/>
+    <property name="host"      type="sstr"   access="RC" index="y"/>
+    <property name="port"      type="uint16" access="RC" index="y"/>
+    <property name="transport" type="sstr"   access="RC"/>
+    <property name="durable"   type="bool"   access="RC"/>
+
+    <statistic name="state"       type="sstr" desc="Operational state of the link"/>
+    <statistic name="lastError"   type="sstr" desc="Reason link is not operational"/>
+
+    <method name="close"/> 
+
+    <method name="bridge" desc="Bridge messages over the link">
+      <arg name="durable"     dir="I" type="bool"/>
+      <arg name="src"         dir="I" type="sstr"/>
+      <arg name="dest"        dir="I" type="sstr"/>
+      <arg name="key"         dir="I" type="sstr"/>
+      <arg name="tag"         dir="I" type="sstr"/>
+      <arg name="excludes"    dir="I" type="sstr"/>
+      <arg name="srcIsQueue"  dir="I" type="bool"/>
+      <arg name="srcIsLocal"  dir="I" type="bool"/>
+      <arg name="dynamic"     dir="I" type="bool"/>
+      <arg name="sync"        dir="I" type="uint16"/>
+    </method>
+  </class>
+
+
+  <!--
+  ===============================================================
+  Bridge
+  ===============================================================
+  -->
+  <class name="Bridge">
+    <property name="linkRef"     type="objId"  references="Link" access="RC" index="y" parentRef="y"/>
+    <property name="channelId"   type="uint16" access="RC" index="y"/>
+    <property name="durable"     type="bool"   access="RC"/>
+    <property name="src"         type="sstr"   access="RC"/>
+    <property name="dest"        type="sstr"   access="RC"/>
+    <property name="key"         type="sstr"   access="RC"/>
+    <property name="srcIsQueue"  type="bool"   access="RC"/>
+    <property name="srcIsLocal"  type="bool"   access="RC"/>
+    <property name="tag"         type="sstr"   access="RC"/>
+    <property name="excludes"    type="sstr"   access="RC"/>
+    <property name="dynamic"     type="bool"   access="RC"/>
+    <property name="sync"        type="uint16" access="RC"/>
+    <method name="close"/> 
+  </class>
+
+
+  <!--
+  ===============================================================
+  Session
+  ===============================================================
+  -->
+  <class name="Session">
+    <property name="vhostRef"         type="objId"   references="Vhost" access="RC" index="y" parentRef="y"/>
+    <property name="name"             type="sstr"    access="RC" index="y"/>
+    <property name="channelId"        type="uint16"  access="RO"/>
+    <property name="connectionRef"    type="objId"   references="Connection" access="RO"/>
+    <property name="detachedLifespan" type="uint32"  access="RO" unit="second"/>
+    <property name="attached"         type="bool"    access="RO"/>
+    <property name="expireTime"       type="absTime" access="RO" optional="y"/>
+    <property name="maxClientRate"    type="uint32"  access="RO" unit="msgs/sec" optional="y"/>
+
+    <statistic name="framesOutstanding" type="count32"/>
+
+    <statistic name="TxnStarts"    type="count64"  unit="transaction" desc="Total transactions started "/>
+    <statistic name="TxnCommits"   type="count64"  unit="transaction" desc="Total transactions committed"/>
+    <statistic name="TxnRejects"   type="count64"  unit="transaction" desc="Total transactions rejected"/>
+    <statistic name="TxnCount"     type="count32"  unit="transaction" desc="Current pending transactions"/>
+
+    <statistic name="clientCredit" type="count32" unit="message" desc="Client message credit"/>
+
+    <method name="solicitAck"/>
+    <method name="detach"/>
+    <method name="resetLifespan"/>
+    <method name="close"/>
+  </class>
+
+  <eventArguments>
+    <arg name="altEx"   type="sstr"   desc="Name of the alternate exchange"/>
+    <arg name="args"    type="map"    desc="Supplemental arguments or parameters supplied"/>
+    <arg name="autoDel" type="bool"   desc="Created object is automatically deleted when no longer in use"/>
+    <arg name="dest"    type="sstr"   desc="Destination tag for a subscription"/>
+    <arg name="disp"    type="sstr"   desc="Disposition of a declaration: 'created' if object was created, 'existing' if object already existed"/>
+    <arg name="durable" type="bool"   desc="Created object is durable"/>
+    <arg name="exName"  type="sstr"   desc="Name of an exchange"/>
+    <arg name="exType"  type="sstr"   desc="Type of an exchange"/>
+    <arg name="excl"    type="bool"   desc="Created object is exclusive for the use of the owner only"/>
+    <arg name="key"     type="lstr"   desc="Key text used for routing or binding"/>
+    <arg name="qName"   type="sstr"   desc="Name of a queue"/>
+    <arg name="reason"  type="lstr"   desc="Reason for a failure"/>
+    <arg name="rhost"   type="sstr"   desc="Address (i.e. DNS name, IP address, etc.) of a remotely connected host"/>
+    <arg name="user"    type="sstr"   desc="Authentication identity"/>
+  </eventArguments>
+
+  <event name="clientConnect"     sev="inform" args="rhost, user"/>
+  <event name="clientConnectFail" sev="warn"   args="rhost, user, reason"/>
+  <event name="clientDisconnect"  sev="inform" args="rhost, user"/>
+  <event name="brokerLinkUp"      sev="inform" args="rhost"/>
+  <event name="brokerLinkDown"    sev="warn"   args="rhost"/>
+  <event name="queueDeclare"      sev="inform" args="rhost, user, qName, durable, excl, autoDel, args, disp"/>
+  <event name="queueDelete"       sev="inform" args="rhost, user, qName"/>
+  <event name="exchangeDeclare"   sev="inform" args="rhost, user, exName, exType, altEx, durable, autoDel, args, disp"/>
+  <event name="exchangeDelete"    sev="inform" args="rhost, user, exName"/>
+  <event name="bind"              sev="inform" args="rhost, user, exName, qName, key, args"/>
+  <event name="unbind"            sev="inform" args="rhost, user, exName, qName, key"/>
+  <event name="subscribe"         sev="inform" args="rhost, user, qName, dest, excl, args"/>
+  <event name="unsubscribe"       sev="inform" args="rhost, user, dest"/>
+</schema>
+

Added: mgmt/trunk/cumin/metadata/qmf/sesame.xml
===================================================================
--- mgmt/trunk/cumin/metadata/qmf/sesame.xml	                        (rev 0)
+++ mgmt/trunk/cumin/metadata/qmf/sesame.xml	2009-06-17 20:20:11 UTC (rev 3457)
@@ -0,0 +1,31 @@
+<schema package="com.redhat.sesame">
+
+  <class name="Sysimage">
+    <property name="uuid"    index="y" type="uuid" access="RC" desc="UUID of System Image"/>
+
+    <property name="osName"   type="sstr" access="RO" desc="Operating System Name"/>
+    <property name="nodeName" type="sstr" access="RO" desc="Node Name"/>
+    <property name="release"  type="sstr" access="RO"/>
+    <property name="version"  type="sstr" access="RO"/>
+    <property name="machine"  type="sstr" access="RO"/>
+    <property name="distro"   type="sstr" access="RO" optional="y"/>
+
+    <property name="memTotal"  type="uint32" access="RO" unit="kByte"/>
+    <property name="swapTotal" type="uint32" access="RO" unit="kByte"/>
+
+    The following statistics are gathered from /proc/meminfo
+
+    <statistic name="memFree"  type="uint32" unit="kByte"/>
+    <statistic name="swapFree" type="uint32" unit="kByte"/>
+
+    The following statistics are gathered from /proc/loadavg
+
+    <statistic name="loadAverage1Min"  type="float"/>
+    <statistic name="loadAverage5Min"  type="float"/>
+    <statistic name="loadAverage10Min" type="float"/>
+    <statistic name="procTotal"        type="uint32"/>
+    <statistic name="procRunning"      type="uint32"/>
+  </class>
+
+</schema>
+

Modified: mgmt/trunk/cumin/python/cumin/__init__.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/__init__.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/__init__.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -1,4 +1,4 @@
-import sys, os
+import sys, os, logging
 
 from random import randint
 from parsley.config import Config, ConfigParameter
@@ -12,7 +12,7 @@
 from time import sleep
 from threading import Thread, Event
 from urllib import quote
-from model import CuminModel, ModelPage, CallPage
+from model import CuminModel, CallPage
 from demo import DemoData
 from page import MainPage
 from stat import StatChartPage, StatStackedPage, SlotMapPage
@@ -20,12 +20,17 @@
 from account import LoginPage, AccountPage
 from datetime import timedelta
 from qpid.util import URL
+from widgets import CuminFormPage
 
 from wooly import Session
 
+import account
+import messaging
+import grid
+import inventory
 import usergrid
 
-log = getLogger("cumin")
+log = logging.getLogger("cumin")
 
 class Cumin(Application):
     def __init__(self, config):
@@ -47,18 +52,19 @@
         self.add_page(self.main_page)
         self.set_default_page(self.main_page)
 
+        self.form_page = FormPage(self, "form.html")
+        self.add_page(self.form_page)
+
+        # XXX move this to the account module
         self.account_page = AccountPage(self, "account.html")
         self.add_page(self.account_page)
+
         self.add_page(DevelPage(self, "devel.html"))
-        self.add_page(ModelPage(self, "model.xml"))
         self.add_page(CallPage(self, "call.xml"))
         self.add_page(StatChartPage(self, "stats.png"))
         self.add_page(StatStackedPage(self, "stacked.png"))
         self.add_page(SlotMapPage(self, "slots.png"))
 
-        self.user_grid_page = usergrid.MainPage(self, "usergrid.html")
-        self.add_page(self.user_grid_page)
-
         unprotected = set()
 
         unprotected.add(self.main_page.css_page)
@@ -83,10 +89,20 @@
         self.model.check()
 
     def init(self):
-        super(Cumin, self).init()
+        modules = list()
+        modules.append(account.module)
+        modules.append(messaging.module)
+        modules.append(grid.module)
+        modules.append(inventory.module)
+        modules.append(usergrid.module)
 
+        for module in modules:
+            module.init(self)
+
         self.model.init()
 
+        super(Cumin, self).init()
+
     def start(self):
         self.model.start()
         self.user_session_expire_thread.start()
@@ -175,3 +191,7 @@
 
         if self.debug:
             enable_logging("cumin", "debug", sys.stderr)
+
+class FormPage(CuminFormPage):
+    def __init__(self, app, name):
+        super(FormPage, self).__init__(app, name)

Modified: mgmt/trunk/cumin/python/cumin/account/main.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/account/main.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/account/main.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -3,14 +3,25 @@
 from wooly.resources import *
 
 from cumin.widgets import *
+from cumin.modelwidgets import *
 from cumin.util import *
 from cumin.grid.job import JobTab
 from user import UserSession
 
+from model import *
+
 from wooly import Session
 
 strings = StringCatalog(__file__)
 
+class AccountModule(object):
+    def init(self, app):
+        subject = app.model.subject
+
+        self.change_password = ChangePasswordTask(app, subject)
+
+module = AccountModule()
+
 class AccountPage(CuminPage, ModeSet):
     def __init__(self, app, name):
         super(AccountPage, self).__init__(app, name)
@@ -39,9 +50,6 @@
         self.view = AccountView(app, "view")
         self.add_mode(self.view)
 
-        self.change_password = ChangePasswordForm(app, "password")
-        self.add_mode(self.change_password)
-
     def render_title(self, session):
         return "Account"
 
@@ -56,9 +64,8 @@
         self.add_tab(self.MyGridJobs(app, "jobs"))
 
     def render_change_password_href(self, session):
-        branch = session.branch()
-        self.frame.change_password.show(branch)
-        return branch.marshal()
+        subject = session.user_session.subject
+        return self.app.change_password.get_href(session, subject)
 
     class Heading(CuminHeading):
         def render_title(self, session):
@@ -68,14 +75,20 @@
             return "resource?name=action-36.png"
 
     class AccountTab(ActionSet):
+        def __init__(self, app, name):
+            super(AccountView.AccountTab, self).__init__(app, name)
+
+        def init(self):
+            # XXX deferring this, but I don't like it
+            task = module.change_password
+            link = TaskLink(self.app, "change_password", task, None)
+            self.add_child(link)
+
+            super(AccountView.AccountTab, self).init()
+
         def render_title(self, session):
             return "Account Settings"
 
-        def render_change_password_href(self, session):
-            branch = session.branch()
-            self.frame.change_password.show(branch)
-            return branch.marshal()
-
     class MyGridJobs(JobTab):
         def render_title(self, session):
             return "Your Grid Jobs %s" % fmt_count(self.get_item_count(session))
@@ -129,19 +142,22 @@
         def get_default(self, session):
             return Session(self.app.main_page).marshal()
 
-class LoginForm(FieldForm):
+class LoginForm(Form):
     def __init__(self, app, name):
         super(LoginForm, self).__init__(app, name)
 
         self.__login_invalid = Attribute(app, "login_invalid")
         self.add_attribute(self.__login_invalid)
 
+        self.fields = FormFieldSet(app, "fields")
+        self.add_child(self.fields)
+
         self.__name = self.Name(app, "name")
-        self.add_field(self.__name)
+        self.fields.add_field(self.__name)
         self.__name.input.size = 20
 
         self.__password = self.Password(app, "password")
-        self.add_field(self.__password)
+        self.fields.add_field(self.__password)
         self.__password.input.size = 20
 
         self.__submit = self.Submit(app, "submit")
@@ -198,80 +214,3 @@
     class Submit(FormButton):
         def render_content(self, session):
             return "Submit"
-
-class ChangePasswordForm(CuminFieldForm):
-    def __init__(self, app, name):
-        super(ChangePasswordForm, self).__init__(app, name)
-
-        self.__errors = self.Errors(app, "errors")
-        self.add_attribute(self.__errors)
-
-        self.__current = self.Current(app, "current")
-        self.add_field(self.__current)
-        self.__current.input.size = 20
-
-        self.__new0 = self.New0(app, "new0")
-        self.add_field(self.__new0)
-        self.__new0.input.size = 20
-
-        self.__new1 = self.New1(app, "new1")
-        self.add_field(self.__new1)
-        self.__new1.input.size = 20
-
-    def process_cancel(self, session):
-        branch = session.branch()
-        self.page.account.main.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
-    def process_submit(self, session):
-        curr = self.__current.get(session)
-        new0 = self.__new0.get(session)
-        new1 = self.__new1.get(session)
-
-        errors = self.validate(session)
-
-        subject = session.user_session.subject
-        crypted = subject.password
-
-        if crypt_password(curr, crypted) != crypted:
-            msg = "The current password you entered is incorrect"
-            self.__errors.get(session).append(msg)
-            errors.append(msg)
-
-        if new0 != new1:
-            msg = "The new passwords do not match"
-            self.__errors.get(session).append(msg)
-            errors.append(msg)
-
-        if not errors:
-            subject.password = crypt_password(new0)
-            subject.syncUpdate()
-
-            self.process_cancel(session)
-
-    def render_title(self, session):
-        return "Change Password"
-
-    def render_form_error(self, session):
-        errors = self.__errors.get(session)
-
-        if errors:
-            return "<ul class=\"errors\" style=\"margin:0; float:left;\"><li>" + \
-                ("</li><li>".join(errors)) + \
-                "</li></ul>"
-
-    class Current(PasswordField):
-        def render_title(self, session):
-            return "Current Password"
-
-    class New0(PasswordField):
-        def render_title(self, session):
-            return "New Password"
-
-    class New1(PasswordField):
-        def render_title(self, session):
-            return "Repeat New Password"
-
-    class Errors(Attribute):
-        def get_default(self, session):
-            return list()

Modified: mgmt/trunk/cumin/python/cumin/account/main.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/account/main.strings	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/account/main.strings	2009-06-17 20:20:11 UTC (rev 3457)
@@ -99,7 +99,7 @@
 
 [AccountTab.html]
 <ul class="actions">
-  <a class="nav" href="{change_password_href}">Change Password</a>
+  <li>{change_password}</li>
 </ul>
 
 [MyGridJobs.html]

Modified: mgmt/trunk/cumin/python/cumin/account/user.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/account/user.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/account/user.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -48,3 +48,58 @@
                 count += 1
 
         log.info("Expired %i user sessions", count)
+
+class ChangePasswordForm(CuminFieldForm):
+    def __init__(self, app, name, task):
+        super(ChangePasswordForm, self).__init__(app, name)
+
+        self.task = task
+
+        self.__current = self.Current(app, "current")
+        self.add_field(self.__current)
+        self.__current.input.size = 20
+
+        self.__new0 = self.New0(app, "new0")
+        self.add_field(self.__new0)
+        self.__new0.input.size = 20
+
+        self.__new1 = self.New1(app, "new1")
+        self.add_field(self.__new1)
+        self.__new1.input.size = 20
+
+    def process_submit(self, session):
+        curr = self.__current.get(session)
+        new0 = self.__new0.get(session)
+        new1 = self.__new1.get(session)
+
+        subject = session.user_session.subject
+        crypted = subject.password
+
+        if crypt_password(curr, crypted) != crypted:
+            error = FormError("The password is incorrect", self.__current)
+            self.errors.add(session, error)
+
+        if new0 != new1:
+            error = FormError("The new passwords do not match")
+            self.errors.add(session, error)
+
+        self.check(session)
+
+        if not self.errors.get(session):
+            self.task.invoke(session, subject, new0)
+            self.task.exit_with_redirect(session, subject)
+
+    def render_title(self, session):
+        return "Change password"
+
+    class Current(PasswordField):
+        def render_title(self, session):
+            return "Current password"
+
+    class New0(PasswordField):
+        def render_title(self, session):
+            return "New password"
+
+    class New1(PasswordField):
+        def render_title(self, session):
+            return "Repeat new password"

Modified: mgmt/trunk/cumin/python/cumin/grid/main.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/grid/main.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/grid/main.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -8,6 +8,17 @@
 
 strings = StringCatalog(__file__)
 
+class GridModule(object):
+    def __init__(self):
+        self.frame = None
+
+    def init(self, app):
+        self.frame = GridFrame(app, "grid")
+        app.main_page.main.grid = self.frame
+        app.main_page.main.add_tab(self.frame)
+
+module = GridModule()
+
 class GridFrame(CuminFrame):
     def __init__(self, app, name):
         super(GridFrame, self).__init__(app, name)

Modified: mgmt/trunk/cumin/python/cumin/inventory/main.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/inventory/main.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/inventory/main.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -8,6 +8,17 @@
 
 strings = StringCatalog(__file__)
 
+class InventoryModule(object):
+    def __init__(self):
+        self.frame = None
+
+    def init(self, app):
+        self.frame = InventoryFrame(app, "inventory")
+        app.main_page.main.inventory = self.frame
+        app.main_page.main.add_tab(self.frame)
+
+module = InventoryModule()
+
 class InventoryFrame(CuminFrame):
     def __init__(self, app, name):
         super(InventoryFrame, self).__init__(app, name)

Modified: mgmt/trunk/cumin/python/cumin/messaging/binding.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/binding.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/binding.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -5,14 +5,95 @@
 from wooly.parameters import DictParameter
 from wooly.resources import StringCatalog
 from cumin.util import sorted_by
-from cumin.widgets import SubmitSwitch
+from cumin.widgets import *
+from cumin.modelwidgets import *
 from cumin.formats import *
 
-from exchange import ExchangeInfo
+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)
+
+        col = self.QNameColumn(app, "q_id")
+        col.visible = False
+        self.add_column(col)
+
+        col = self.ENameColumn(app, "e_id")
+        col.visible = False
+        self.add_column(col)
+
+        col = self.KeyColumn(app, "key")
+        self.add_column(col)
+
+        col = self.RateColumn(app, "rate")
+        col.alignment = "right"
+        self.add_column(col)
+
+        col = self.MatchedColumn(app, "matched")
+        col.alignment = "right"
+        self.add_column(col)
+
+        self.phase = PhaseSwitch(app, "phase")
+        self.filters.add_child(self.phase)
+
+        task = main.module.binding_set_remove
+        button = TaskButton(app, "remove", task, self.selection)
+        self.buttons.add_child(button)
+
+    class QNameColumn(SqlTableColumn):
+        def render_title(self, session, data):
+            return "Queue"
+
+        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))
+
+    class ENameColumn(SqlTableColumn):
+        def render_title(self, session, data):
+            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)
+
+            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)
@@ -416,3 +497,14 @@
                 form_binding_info[this_exchange]["arguments"] = arguments
 
         return form_binding_info
+
+class BindingSetTaskForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(BindingSetTaskForm, self).__init__(app, name, task)
+
+        item = BindingParameter(app, "item")
+
+        self.object = ListParameter(app, "binding", item)
+        self.add_parameter(self.object)
+
+from exchange import ExchangeInfo

Modified: mgmt/trunk/cumin/python/cumin/messaging/binding.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/binding.strings	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/binding.strings	2009-06-17 20:20:11 UTC (rev 3457)
@@ -1,3 +1,21 @@
+[BindingSet.sql]
+select
+  b.id,
+  b.exchange_id as e_id,
+  b.queue_id as q_id,
+  b.binding_key as key
+from binding as b
+left outer join binding_stats as c on c.id = b.stats_curr_id
+{sql_where}
+{sql_orderby}
+{sql_limit}
+
+[BindingSet.count_sql]
+select count(*)
+from binding as b
+left outer join binding_stats as c on c.id = b.stats_curr_id
+{sql_where}
+
 [ExchangeInput.name_html]
 <td>
   <input type="checkbox" id="{id}.{exchange_id}" name="{name_path}"
@@ -137,23 +155,28 @@
 }
 
 [ExchangeKeysField.html]
-<div class="field">
-  <div class="rfloat">{phase}</div>
-  <div class="title">{title}</div>
-  <div class="rclear">&nbsp;</div>
+<tr>
+  <th>
+    <div class="title">{title}</div>
+    <div class="help">{help}</div>
+  </th>
+  <td>
+    <div class="rfloat">{phase}</div>
+    <div class="rclear">&nbsp;</div>
 
-  <div class="inputs">
-    <table class="mobjects" id="exchange_types">
-      <thead>
-        <tr>
-          <th colspan="2"><label class="exchange_name">Exchange Name</label></th>
-          <th class="exchange_type">Exchange Type</th>
-          <th>Binding Key</th>
-        </tr>
-      </thead>
-      <tbody>
-        {exchanges}
-      </tbody>
-    </table>
-  </div>
-</div>
+    <div class="inputs">
+      <table class="mobjects" id="exchange_types">
+        <thead>
+          <tr>
+            <th colspan="2"><label class="exchange_name">Exchange Name</label></th>
+            <th class="exchange_type">Exchange Type</th>
+            <th>Binding Key</th>
+          </tr>
+        </thead>
+        <tbody>
+          {exchanges}
+        </tbody>
+      </table>
+    </div>
+  </td>
+</tr>

Modified: mgmt/trunk/cumin/python/cumin/messaging/broker.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/broker.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/broker.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -14,11 +14,14 @@
 from brokerlink import *
 from client import *
 
+from brokerlink import LinkSet
+
 strings = StringCatalog(__file__)
 
-class NewBrokerSet(CuminClassTable):
+class BrokerSet(CuminSelectionTable):
     def __init__(self, app, name):
-        super(NewBrokerSet, self).__init__(app, name, app.model.broker)
+        item = BrokerGroupParameter(app, "item")
+        super(BrokerSet, self).__init__(app, name, item)
 
         self.group = BrokerGroupParameter(app, "group")
         self.add_parameter(self.group)
@@ -33,6 +36,10 @@
         col = self.ClusterColumn(app, "cluster")
         self.add_column(col)
 
+        task = main.module.broker_set_engroup
+        button = TaskButton(app, "engroup", task, self.selection)
+        self.buttons.add_child(button)
+
     def render_title(self, session):
         count = self.get_item_count(session)
         return "Brokers %s" % fmt_count(count)
@@ -122,47 +129,17 @@
         self.view = BrokerView(app, "view", self.vhost)
         self.add_mode(self.view)
 
-        self.edit = BrokerEdit(app, "edit")
-        self.add_mode(self.edit)
-
         self.queue = QueueFrame(app, "queue")
         self.add_mode(self.queue)
 
-        self.queues_purge = QueueSetPurge(app, "queuespurge")
-        self.add_mode(self.queues_purge)
-
-        self.queues_remove = QueueSetRemove(app, "queuesremove")
-        self.add_mode(self.queues_remove)
-
-        self.queue_add = QueueAdd(app, "queueadd", self.vhost)
-        self.add_mode(self.queue_add)
-
         self.exchange = ExchangeFrame(app, "exchange")
         self.add_mode(self.exchange)
 
-        self.exchange_add = ExchangeAdd(app, "exchangeadd", self.vhost)
-        self.add_mode(self.exchange_add)
-
-        self.exchanges_remove = ExchangeSetRemove(app, "exchangesremove")
-        self.add_mode(self.exchanges_remove)
-
-        self.bindings_remove = BindingSetRemove(app, "bindingsremove")
-        self.add_mode(self.bindings_remove)
-
-        self.link = PeerFrame(app, "link")
-        self.add_mode(self.link)
-
-        self.link_add = BrokerLinkAdd(app, "linkadd", self.vhost)
-        self.add_mode(self.link_add)
-
-        self.links_close = BrokerSetClose(app, "linksclose")
-        self.add_mode(self.links_close)
-
         self.connection = ConnectionFrame(app, "conn")
         self.add_mode(self.connection)
 
-        self.connections_close = ConnectionSetClose(app, "connsclose")
-        self.add_mode(self.connections_close)
+        self.link = LinkFrame(app, "link")
+        self.add_mode(self.link)
 
     class VhostAttribute(Attribute):
         def get_default(self, session):
@@ -222,7 +199,7 @@
         tabs.add_tab(self.BrokerQueueTab(app, "queues", self.vhost))
         tabs.add_tab(ExchangeSet(app, "exchanges", self.vhost))
         tabs.add_tab(ConnectionSet(app, "conns", self.vhost))
-        tabs.add_tab(PeerSet(app, "peers", self.vhost))
+        tabs.add_tab(LinkSet(app, "links", self.vhost))
         tabs.add_tab(BrokerAccessControl(app, "access", self.vhost))
         tabs.add_tab(BrokerClustering(app, "cluster", self.vhost))
         tabs.add_tab(self.BrokerDetailsTab(app, "details", self.vhost))
@@ -416,7 +393,7 @@
 
         self.group_tmpl = Template(self, "group_html")
 
-        self.brokers = NewBrokerSet(app, "brokers")
+        self.brokers = BrokerSet(app, "brokers")
         self.add_child(self.brokers)
 
     def render_title(self, session, *args):
@@ -694,11 +671,15 @@
         for group in reg.groups:
             self.groups.get(session).append(group)
 
-class BrokerSetEngroup(CuminSetActionForm):
-    def __init__(self, app, name):
-        param = BrokerParameter(app, "item")
+class BrokerSetEngroupForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(BrokerSetEngroupForm, self).__init__(app, name, task)
 
-        super(BrokerSetEngroup, self).__init__ \
-            (app, name, app.model.broker.engroup_set, param)
+        item = BrokerParameter(app, "item")
+        self.object = ListParameter(app, "broker", item)
+        self.add_parameter(self.object)
 
+#    def process_submit(self, session):
+#        pass
+
 from brokergroup import BrokerGroupCheckboxField

Modified: mgmt/trunk/cumin/python/cumin/messaging/broker.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/broker.strings	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/broker.strings	2009-06-17 20:20:11 UTC (rev 3457)
@@ -1,50 +1,4 @@
 [BrokerSet.sql]
-select br.id, br.name, c.cluster_name as cluster
-from broker_registration as br
-left outer join broker as b on b.registration_id = br.id
-left outer join cluster as c on c.broker_id = b.id
-{sql_where}
-{sql_orderby}
-{sql_limit}
-
-[BrokerSet.count_sql]
-select count(*)
-from broker_registration as br
-{sql_where}
-
-[BrokerSet.css]
-div.sactions button.unregister {
-	margin-right: 2em;
-}
-
-[BrokerSet.html]
-<form id="{id}" method="post" action="?">
-  <!-- <select onchange="document.getElementById('{id}.submit').submit()"> -->
-
-  <div class="sactions">
-    <h2>Act on Selection:</h2>
-    {unregister}
-
-    <h2>Add to Group:</h2>
-    {groups} {groupify}
-  </div>
-
-  <table class="mobjects">
-    <thead>
-      <tr>
-        <th class="setnav" colspan="{column_count}">
-          <div class="rfloat">{page}</div>
-          {count}
-        </th>
-      </tr>
-      <tr>{headers}</tr>
-    </thead>
-    <tbody>{items}</tbody>
-  </table>
-  <div>{hidden_inputs}</div>
-</form>
-
-[NewBrokerSet.sql]
 select
   b.id,
   b.qmf_scope_id,
@@ -57,7 +11,7 @@
 {sql_orderby}
 {sql_limit}
 
-[NewBrokerSet.count_sql]
+[BrokerSet.count_sql]
 select count(*)
 from broker as b
 {sql_where}

Modified: mgmt/trunk/cumin/python/cumin/messaging/brokergroup.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/brokergroup.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/brokergroup.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -9,8 +9,10 @@
 from cumin.formats import *
 from cumin.util import *
 
-from broker import NewBrokerSet
+import main
 
+from broker import BrokerSet
+
 strings = StringCatalog(__file__)
 
 class BrokerGroupSet(CuminClassTable):
@@ -22,6 +24,10 @@
         col = CuminClassNameColumn(app, "name", cls)
         self.add_column(col)
 
+        self.add = TaskLink \
+            (self.app, "add", main.module.broker_group_add, None)
+        self.add_child(self.add)
+
 class BrokerGroupInputSet(CheckboxInputSet):
     def __init__(self, app, name):
         super(BrokerGroupInputSet, self).__init__(app, name, None)
@@ -66,128 +72,71 @@
         self.view = BrokerGroupView(app, "view", self.object)
         self.add_child(self.view)
 
-        self.add = BrokerGroupAdd(app, "add")
-        self.add_child(self.add)
-
-        self.edit = BrokerGroupEdit(app, "edit")
-        self.add_child(self.edit)
-
-        self.remove = BrokerGroupRemove(app, "remove")
-        self.add_child(self.remove)
-
-    def render_title(self, session, group):
-        if group:
-            return super(BrokerGroupFrame, self).render_title(session, group)
-        else:
-            return "Broker Group"
-
-    def render_href(self, session, group):
-        if group:
-            return super(BrokerGroupFrame, self).render_href(session, group)
-
-class BrokerGroupStatus(CuminStatus):
-    pass
-
 class BrokerGroupView(CuminView):
     def __init__(self, app, name, group):
         super(BrokerGroupView, self).__init__(app, name)
 
         self.group = group
 
-        status = BrokerGroupStatus(app, "status")
-        self.add_child(status)
-
         self.__tabs = TabbedModeSet(app, "tabs")
         self.add_child(self.__tabs)
 
-        brokers = NewBrokerSet(app, "brokers")
+        brokers = BrokerSet(app, "brokers")
         brokers.group = self.group
         self.__tabs.add_tab(brokers)
 
         self.__tabs.add_tab(CuminDetails(app, "details"))
 
 class BrokerGroupForm(CuminFieldForm):
-    def __init__(self, app, name):
+    def __init__(self, app, name, task):
         super(BrokerGroupForm, self).__init__(app, name)
 
+        self.task = task
+
         self.group_name = UniqueNameField(app, "name", BrokerGroup)
         self.add_field(self.group_name)
 
-    def init(self):
-        super(BrokerGroupForm, self).init()
-
-        self.group_name.set_object_attr(self.frame.object)
-
-class BrokerGroupAdd(BrokerGroupForm):
-    def process_cancel(self, session):
-        branch = session.branch()
-        self.page.main.messaging.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
+class BrokerGroupAddForm(BrokerGroupForm):
     def process_submit(self, session):
-        errors = self.validate(session)
+        self.check(session)
 
-        if errors:
-            pass
-        else:
-            args = {"name": self.group_name.get(session)}
-            method = self.app.model.broker_group.add
-            method.invoke(None, args)
+        if not self.errors.get(session):
+            name = self.group_name.get(session)
 
-            self.process_cancel(session)
+            self.task.invoke(session, None, name)
+            self.task.exit_with_redirect(session, None)
 
     def render_title(self, session):
-        return "Add Group"
+        return self.task.get_title(session)
 
-class BrokerGroupEdit(BrokerGroupForm):
-    def get_args(self, session):
-        return self.frame.get_args(session)
+class BrokerGroupEditForm(BrokerGroupForm):
+    def __init__(self, app, name, task):
+        super(BrokerGroupEditForm, self).__init__(app, name, task)
 
-    def process_cancel(self, session, group):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
+        self.group = BrokerGroupParameter(app, "group")
+        self.add_parameter(self.group)
 
-    def process_submit(self, session, group):
-        errors = self.validate(session)
+    def process_submit(self, session):
+        self.check(session)
 
-        if errors:
-            pass
-        else:
-            args = {"name": self.group_name.get(session)}
-            method = self.app.model.broker_group.edit
-            method.invoke(group, args)
+        if not self.errors.get(session):
+            group = self.group.get(session)
+            name = self.group_name.get(session)
 
-            self.process_cancel(session, group)
+            self.task.invoke(session, group, name)
+            self.task.exit_with_redirect(session, group)
 
-    def process_display(self, session, group):
+    def process_display(self, session):
+        group = self.group.get(session)
         self.group_name.set(session, group.name)
 
-    def render_title(self, session, group):
-        return "Edit Group '%s'" % group.name
+    def render_title(self, session):
+        group = self.group.get(session)
+        return self.task.get_title(session, group)
 
-class BrokerGroupRemove(CuminConfirmForm, Frame):
-    def get_args(self, session):
-        return self.frame.get_args(session)
+class BrokerGroupRemoveForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(BrokerGroupRemoveForm, self).__init__(app, name, task)
 
-    def process_cancel(self, session, group):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
-    def process_submit(self, session, group):
-        method = self.app.model.broker_group.remove
-        method.invoke(group)
-
-        branch = session.branch()
-        self.frame.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
-    def render_title(self, session, group):
-        return "Remove Broker Group '%s'" % group.name
-
-    def render_submit_content(self, session, group):
-        return "Yes, Remove Broker Group '%s'" % group.name
-
-    def render_cancel_content(self, session, group):
-        return "No, Cancel"
+        self.object = BrokerGroupParameter(app, "group")
+        self.add_parameter(self.object)

Modified: mgmt/trunk/cumin/python/cumin/messaging/brokerlink.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/brokerlink.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/brokerlink.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -11,20 +11,17 @@
 from queue import *
 from exchange import *
 
+import main
+
 strings = StringCatalog(__file__)
 
-class PeerSet(CuminTable, Form):
+class LinkSet(CuminSelectionTable):
     def __init__(self, app, name, vhost):
-        super(PeerSet, self).__init__(app, name)
+        item = LinkParameter(app, "item")
+        super(LinkSet, self).__init__(app, name, item)
 
         self.vhost = vhost
 
-        self.ids = FilteredCheckboxIdColumn(app, "id", self, callback=self.disable_closed)
-        self.add_column(self.ids)
-
-        #self.ids = CheckboxIdColumn(app, "id")
-        #self.add_column(self.ids)
-
         col = self.AddressColumn(app, "addr")
         self.add_column(col)
         self.set_default_column(col)
@@ -44,20 +41,19 @@
         #col = self.ToPeerColumn(app, "to_peer")
         #self.add_column(col)
 
-        self.__close = self.Close(app, "close")
-        self.add_child(self.__close)
+        task = main.module.link_add
+        self.links.add_child(TaskLink(app, "link_add", task, vhost))
 
-    def render_add_broker_link_url(self, session):
-        branch = session.branch()
-        self.frame.link_add.show(branch)
-        return branch.marshal()
+        task = main.module.link_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 "Broker Links %s" % fmt_count(count)
 
     def get_sql_where_constraints(self, session):
-        constraints = super(PeerSet, self).get_sql_where_constraints(session)
+        constraints = super(LinkSet, self).get_sql_where_constraints(session)
         constraints.append("v.id = %(vhost_id)r")
         return constraints
 
@@ -73,8 +69,8 @@
             return "Address"
 
         def render_content(self, session, data):
-            peer = Identifiable(data["id"])
-            href = self.frame.link.get_href(session, peer)
+            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))
 
@@ -108,24 +104,13 @@
         def render_value(self, session, value):
             return fmt_rate(value)
 
-    class Close(FormButton):
-        def render_content(self, session):
-            return "Close"
+class RouteSet(CuminSelectionTable):
+    def __init__(self, app, name, link):
+        item = RouteParameter(app, "item")
+        super(RouteSet, self).__init__(app, name, item)
 
-        def process_submit(self, session):
-            ids = self.parent.ids.get(session)
-            self.parent.ids.clear(session)
+        self.link = link
 
-            href = self.frame.links_close.get_href(session, ids)
-            self.page.set_redirect_url(session, href)
-
-class PeerRouteSet(CuminTable, Form):
-    def __init__(self, app, name):
-        super(PeerRouteSet, self).__init__(app, name)
-
-        self.ids = CheckboxIdColumn(app, "id")
-        self.add_column(self.ids)
-
         #col = self.SourceColumn(app, "source")
         #self.add_column(col)
 
@@ -145,27 +130,24 @@
         col = self.ExcludesColumn(app, "excludes")
         self.add_column(col)
 
-        self.__remove = self.Remove(app, "remove")
-        self.add_child(self.__remove)
+        task = main.module.route_add
+        self.links.add_child(TaskLink(app, "add", task, self.link))
 
-    def get_args(self, session):
-        return self.frame.get_args(session)
+        task = main.module.route_set_remove
+        button = TaskButton(app, "remove", task, self.selection)
+        self.buttons.add_child(button)
 
-    def render_title(self, session, link):
-        count = self.get_item_count(session, link)
+    def render_title(self, session):
+        count = self.get_item_count(session)
         return "Link Routes %s" % fmt_count(count)
 
-    def render_sql_where(self, session, link):
+    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):
+    def get_sql_values(self, session):
+        link = self.link.get(session)
         return {"link_id": link.id}
 
-    def render_add_bridge_url(self, session, route):
-        branch = session.branch()
-        self.frame.show_bridge_add(branch)
-        return branch.marshal()
-
     class SourceColumn(SqlTableColumn):
         def render_title(self, session, data):
             return "Source"
@@ -197,116 +179,56 @@
         def render_title(self, session, data):
             return "Excludes"
 
-    class Remove(FormButton):
-        def render_content(self, session):
-            return "Remove"
-
-        def process_submit(self, session):
-            ids = self.parent.ids.get(session)
-            self.parent.ids.clear(session)
-
-            branch = session.branch()
-            frame = self.frame.show_routes_close(branch)
-            frame.ids.set(branch, ids)
-            self.page.set_redirect_url(session, branch.marshal())
-
-class PeerFrame(CuminFrame):
+class LinkFrame(CuminFrame):
     def __init__(self, app, name):
-        super(PeerFrame, self).__init__(app, name)
+        super(LinkFrame, self).__init__(app, name)
 
-        self.object = PeerParameter(app, "id")
+        self.object = LinkParameter(app, "id")
         self.add_parameter(self.object)
 
-        self.view = PeerView(app, "view")
+        self.view = LinkView(app, "view", self.object)
         self.add_mode(self.view)
 
-        self.remove = LinkRemove(app, "remove")
-        self.add_mode(self.remove)
+    def show_object(self, session, link):
+        if hasattr(link, "vhost"):
+            self.page.main.messaging.broker.set_object(session, link.vhost)
 
-        self.__bridge_add = BridgeAdd(app, "bridgeadd")
-        self.add_mode(self.__bridge_add)
+        return super(LinkFrame, self).show_object(session, link)
 
-        self.__routes_close = PeerRouteSetClose(app, "routesclose")
-        self.add_mode(self.__routes_close)
+class LinkRemoveForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(LinkRemoveForm, self).__init__(app, name, task)
 
-    def render_title(self, session, peer):
-        return super(PeerFrame, self).render_title(session, peer)
+        self.object = LinkParameter(app, "link")
+        self.add_parameter(self.object)
 
-    def show_object(self, session, peer):
-        self.page.main.broker.set_object(session, peer.vhost.broker)
-        return super(PeerFrame, self).show_object(session, peer)
+class LinkSetRemoveForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(LinkSetRemoveForm, self).__init__(app, name, task)
 
-    def show_bridge_add(self, session):
-        self.page.set_frame(session, self.__bridge_add)
-        return self.__bridge_add.show(session)
+        item = LinkParameter(app, "item")
 
-    def show_routes_close(self, session):
-        self.page.set_frame(session, self.__routes_close)
-        return self.__routes_close.show(session)
+        self.object = ListParameter(app, "link", item)
+        self.add_parameter(self.object)
 
-class LinkRemove(CuminConfirmForm):
-    def get_args(self, session):
-        return self.frame.get_args(session)
+class LinkView(CuminView):
+    def __init__(self, app, name, link):
+        super(LinkView, self).__init__(app, name)
 
-    def process_cancel(self, session, link):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
+        self.tabs = TabbedModeSet(app, "tabs")
+        self.add_child(self.tabs)
 
-    def process_submit(self, session, link):
-        action = self.app.model.link.close
-        action.invoke(link)
+        #self.tabs.add_tab(LinkStats(app, "stats"))
 
-        branch = session.branch()
-        self.frame.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
+        self.routes = RouteSet(app, "routes", link)
+        self.tabs.add_tab(self.routes)
 
-    def render_title(self, session, link):
-        return "Close Broker Link '%s:%d'" % (link.host, link.port)
+        self.tabs.add_tab(CuminDetails(app, "details"))
 
-    def render_submit_content(self, session, link):
-        return "Yes, Close Broker Link"
-
-    def render_cancel_content(self, session, link):
-        return "No, Cancel"
-
-class PeerStatus(CuminStatus):
-    def render_color(self, session, link):
-        if link.statsCurr:
-            return link.statsCurr.lastError and "red" or "green"
-
-    def render_peer_state(self, session, peer):
-        if peer.statsCurr:
-            return peer.statsCurr.state
-
-    def render_peer_error(self, session, peer):
-        if peer.statsCurr:
-            return peer.statsCurr.lastError
-
-class PeerView(CuminView):
+class LinkStats(TabbedModeSet):
     def __init__(self, app, name):
-        super(PeerView, self).__init__(app, name)
+        super(LinkStats, self).__init__(app, name)
 
-        status = PeerStatus(app, "status")
-        self.add_child(status)
-
-        self.__tabs = TabbedModeSet(app, "tabs")
-        self.add_child(self.__tabs)
-
-        #self.__tabs.add_tab(PeerStats(app, "stats"))
-
-        self.__routes = PeerRouteSet(app, "routes")
-        self.__tabs.add_tab(self.__routes)
-
-        self.__tabs.add_tab(CuminDetails(app, "details"))
-
-    def show_routes(self, session):
-        return self.__routes.show(session)
-
-class PeerStats(TabbedModeSet):
-    def __init__(self, app, name):
-        super(PeerStats, self).__init__(app, name)
-
         self.add_child(StatSet(app, "io", "io"))
         self.add_child(StatSet(app, "general", "general"))
 
@@ -322,7 +244,7 @@
 
     class ReceiveRouteDropRateChart(StatValueChart):
         def __init__(self, app, name):
-            super(PeerStats.ReceiveRouteDropRateChart, self).__init__ \
+            super(LinkStats.ReceiveRouteDropRateChart, self).__init__ \
                 (app, name)
 
             self.stats = ("msgReceives", "msgRoutes", "msgDrops")
@@ -343,7 +265,7 @@
     def do_get_items(self, session, *args):
         exchanges = list()
 
-        link = self.frame.frame.get_object(session)
+        link = self.form.link.get(session)
         vhost = link.vhost
         sortedExchanges = sorted_by(vhost.exchanges)
 
@@ -384,10 +306,15 @@
     def render_title(self, session):
         return "Choose an Exchange"
 
-class BridgeAddForm(CuminFieldForm):
-    def __init__(self, app, name):
-        super(BridgeAddForm, self).__init__(app, name)
+class RouteAddForm(CuminFieldForm):
+    def __init__(self, app, name, task):
+        super(RouteAddForm, self).__init__(app, name)
 
+        self.task = task
+
+        self.link = LinkParameter(app, "link")
+        self.add_parameter(self.link)
+
         self.exchange = ExchangeRadioField(app, "exchange")
         self.add_field(self.exchange)
 
@@ -417,18 +344,14 @@
         self.sync = self.SyncField(app, "sync")
         self.more.add_field(self.sync)
 
-    def get_args(self, session):
-        return self.frame.get_args(session)
+    def process_display(self, session):
+        link = self.link.get(session)
 
-    def render_title(self, session, link):
-        return "Add Route to '%s:%d'" % (link.host, link.port)
-
-    def process_display(self, session, *args):
         if not self.tag.get(session):
-            self.tag.set(session, args[0].qmfBrokerId)
+            self.tag.set(session, link.qmfBrokerId)
 
         if not self.excludes.get(session):
-            self.excludes.set(session, "%s:%s" % (args[0].host, args[0].port))
+            self.excludes.set(session, "%s:%s" % (link.host, link.port))
 
         if not self.sync.get(session):
             self.sync.set(session, self.sync.get_default(session))
@@ -456,21 +379,15 @@
         def render_title_2(self, session):
             return "Not dynamic"
 
-
     class BridgeAddHelpField(FormField):
         pass
 
-class BridgeAdd(BridgeAddForm):
-    def process_cancel(self, session, *args):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
+    def process_submit(self, session):
+        self.check(session)
 
-    def process_submit(self, session, link):
-        errors = self.validate(session)
-        if errors:
-            pass
-        else:
+        if not self.errors.get(session):
+            link = self.link.get(session)
+
             key = self.key.get(session)
             tag = self.tag.get(session)
             excludes = self.excludes.get(session)
@@ -480,26 +397,27 @@
             dynamic = self.dynamic.get(session) == "yes"
             sync = self.sync.get(session)
 
-            args = {"durable": durable,
-                    "exchange": exchange.name,
-                    "key": key,
-                    "tag": tag,
-                    "excludes": excludes,
-                    "dynamic": dynamic,
-                    "sync": sync
-                    }
+            self.task.invoke(session, link, exchange, key, tag,
+                             dynamic, sync, excludes)
+            self.task.exit_with_redirect(session, link)
 
-            action = self.app.model.link.bridge
-            action.invoke(link, args)
+    def render_title(self, session):
+        link = self.link.get(session)
+        return self.task.get_title(session, link)
 
-            self.process_cancel(session, link)
+class RouteSetRemoveForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(RouteSetRemoveForm, self).__init__(app, name, task)
 
-class BrokerLinkAdd(CuminFieldForm):
-    def __init__(self, app, name, vhost):
-        super(BrokerLinkAdd, self).__init__(app, name)
+        item = RouteParameter(app, "item")
 
-        self.vhost = vhost
+        self.object = ListParameter(app, "route", item)
+        self.add_parameter(self.object)
 
+class LinkForm(CuminFieldForm):
+    def __init__(self, app, name):
+        super(LinkForm, self).__init__(app, name)
+
         self.host = self.Host(app, "host")
         self.add_field(self.host)
 
@@ -521,11 +439,6 @@
         self.transport = self.TransportField(app, "transport")
         self.more.add_field(self.transport)
 
-    def render_title(self, session):
-        vhost = self.vhost.get(session)
-        name = self.app.model.broker.get_object_name(vhost.broker)
-        return "Add Broker Link to '%s'" % name
-
     class ShowButton(MoreFieldSet):
         def render_more_text(self, session):
             return "Show Optional Inputs..."
@@ -539,7 +452,7 @@
 
     class PortField(StringField):
         def __init__(self, app, name):
-            super(BrokerLinkAdd.PortField, self).__init__(app, name)
+            super(LinkForm.PortField, self).__init__(app, name)
 
             self.input.size = 5
             self.css_class = "compact first"
@@ -571,7 +484,7 @@
 
     class TransportField(RadioField):
         def __init__(self, app, name):
-            super(BrokerLinkAdd.TransportField, self).__init__(app, name, None)
+            super(LinkForm.TransportField, self).__init__(app, name, None)
 
             self.param = Parameter(app, "param")
             self.param.default = "tcp"
@@ -617,78 +530,35 @@
         def render_title_2(self, session):
             return "No, do not restore if broker restarts"
 
-    def process_cancel(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
+class LinkAddForm(LinkForm):
+    def __init__(self, app, name, task):
+        super(LinkAddForm, self).__init__(app, name)
 
+        self.task = task
+
+        self.vhost = VhostParameter(app, "vhost")
+        self.add_parameter(self.vhost)
+
+    def render_title(self, session):
+        vhost = self.vhost.get(session)
+        return self.task.get_description(session, vhost)
+
     def process_submit(self, session):
-        if self.validate(session):
-            pass
-        else:
+        self.check(session)
+
+        if not self.errors.get(session):
+            vhost = self.vhost.get(session)
+
             host = self.host.get(session)
             port = self.port.get(session) or 5672
-            if port:
-                port = int(port)
             username = self.username.get(session) or "anonymous"
             password = self.password.get(session)
-            durable = self.durable.get(session)
+            durable = self.durable.get(session) == "yes"
             transport = self.transport.get(session)
 
-            args = {
-                "host": host,
-                "port": port,
-                "durable": durable == "yes",
-                "username": username,
-                "password": password,
-                "transport": transport
-                }
+            if port:
+                port = int(port)
 
-            action = self.app.model.broker.add_link
-            action.invoke(self.vhost.get(session).broker, args)
-
-            # navigate back to main queue frame
-            self.process_cancel(session)
-
-class BrokerSetClose(CuminBulkActionForm):
-    def process_return(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
-    def process_item(self, session, id):
-        link = Link.get(id)
-        action = self.app.model.link.close
-        action.invoke(link)
-
-    def render_title(self, session):
-        return "Close Broker Link"
-
-    def render_form_heading(self, session, *args):
-        return "Close Link to"
-
-    def render_item_content(self, session, id):
-        link = Link.get(id)
-        return "Broker: %s:%d" % (link.host, link.port)
-
-class PeerRouteSetClose(CuminBulkActionForm):
-    def process_return(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
-    def process_item(self, session, id):
-        bridge = Bridge.get(id)
-        action = self.app.model.route.remove
-        action.invoke(bridge)
-
-    def render_title(self, session):
-        link = self.parent.get_object(session)
-        return "Remove Route from %s:%i" % (link.host, link.port)
-
-    def render_form_heading(self, session, *args):
-        return "Remove Route"
-
-    def render_item_content(self, session, id):
-        bridge = Bridge.get(id)
-        return "<td>%s</td><td>%s</td>" % (bridge.dest, bridge.key)
+            self.task.invoke(session, vhost, host, port,
+                             durable, username, password, transport)
+            self.task.exit_with_redirect(session, vhost)

Modified: mgmt/trunk/cumin/python/cumin/messaging/brokerlink.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/brokerlink.strings	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/brokerlink.strings	2009-06-17 20:20:11 UTC (rev 3457)
@@ -1,4 +1,4 @@
-[PeerSet.sql]
+[LinkSet.sql]
 select
   l.id,
   l.host,
@@ -14,39 +14,13 @@
 {sql_orderby}
 {sql_limit}
 
-[PeerSet.count_sql]
+[LinkSet.count_sql]
 select count(*)
 from link as l
 join vhost as v on v.id = l.vhost_id
 {sql_where}
 
-[PeerSet.html]
-<form id="{id}" method="post" action="?">
-  <ul class="actions">
-    <li><a class="nav" href="{add_broker_link_url}">Add Broker Link</a></li>
-  </ul>
-
-  <div class="sactions">
-    <h2>Act on Selected Broker Links:</h2>
-    {close}
-  </div>
-
-  <table class="mobjects">
-    <thead>
-      <tr>
-        <th class="setnav" colspan="{column_count}">
-          <div class="rfloat">{page}</div>
-          {count}
-        </th>
-      </tr>
-      <tr>{headers}</tr>
-    </thead>
-    <tbody>{items}</tbody>
-  </table>
-  <div>{hidden_inputs}</div>
-</form>
-
-[PeerRouteSet.sql]
+[RouteSet.sql]
 select
   b.id,
   b.src,
@@ -65,38 +39,12 @@
 {sql_orderby}
 {sql_limit}
 
-[PeerRouteSet.count_sql]
+[RouteSet.count_sql]
 select count(*)
 from bridge as b
 join link as l on l.id = b.link_id
 {sql_where}
 
-[PeerRouteSet.html]
-<form id="{id}" method="post" action="?">
-  <ul class="actions">
-    <li><a class="nav" href="{add_bridge_url}">Add Route</a></li>
-  </ul>
-
-  <div class="sactions">
-    <h2>Act on Selected Routes:</h2>
-    {remove}
-  </div>
-
-  <table class="mobjects">
-    <thead>
-      <tr>
-        <th class="setnav" colspan="{column_count}">
-          <div class="rfloat">{page}</div>
-          {count}
-        </th>
-      </tr>
-      <tr>{headers}</tr>
-    </thead>
-    <tbody>{items}</tbody>
-  </table>
-  <div>{hidden_inputs}</div>
-</form>
-
 [ExchangeRadioField.html]
 <div class="{form_field_class}">
   <div class="rfloat">{phase}</div>
@@ -105,37 +53,6 @@
   <div class="inputs">{inputs}</div><div style="clear:left;"><!-- --></div>
 </div>
 
-[PeerRouteSetClose.html]
-<form id="{id}" class="mform" method="post" action="?">
-  <div class="head">
-    <h1>{title}</h1>
-  </div>
-  <div class="body">
-    <span class="legend">{form_heading}</span>
-          <table class="mobjects">
-            <thead>
-              <tr>
-                <th>Exchange</th>
-                <th>Key</th>
-              </tr>
-            </thead>
-            <tbody>{items}</tbody>
-          </table>
-    {hidden_inputs}
-  </div>
-  <div class="foot">
-    {help}
-    {submit}
-    {cancel}
-  </div>
-</form>
-<script type="text/javascript">
-  $("{id}").elements[0].focus();
-</script>
-
-[PeerRouteSetClose.item_html]
-<tr>{item_content}</tr>
-
 [BridgeAddHelpField.css]
 div#bridge_add_help span.tag1 {
         color:red;
@@ -148,7 +65,6 @@
         color: #444444;
 }
 
-
 [BridgeAddHelpField.html]
 <div id="bridge_add_help">
   <p>The <strong>Tag</strong> and <strong>Excludes</strong> are used to prevent a message from routing back
@@ -160,17 +76,3 @@
     <em>broker2</em> &lt;==&gt; <em>broker1</em> tag: <span class="tag1">tag1</span> excludes: <span class="tag2">tag2</span>
   </p>
 </div>
-
-[PeerStatus.html]
-<div id="{id}" class="CuminStatus {color}">
-  <table>
-    <tr>
-      <th><strong>State</strong></th>
-      <td>{peer_state}</td>
-    </tr>
-    <tr>
-      <th><strong>Last Error</strong></th>
-      <td>{peer_error}</td>
-    </tr>
-  </table>
-</div>

Modified: mgmt/trunk/cumin/python/cumin/messaging/client.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/client.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/client.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -4,24 +4,24 @@
 from datetime import datetime
 from cumin.stat import *
 from cumin.widgets import *
+from cumin.modelwidgets import *
 from cumin.parameters import *
 from cumin.formats import *
 from cumin.util import *
 
+import main
+
 strings = StringCatalog(__file__)
 
-class ConnectionSet(CuminTable, Form):
+class ConnectionSet(CuminSelectionTable):
     def __init__(self, app, name, vhost):
-        super(ConnectionSet, self).__init__(app, name)
+        item = ConnectionParameter(app, "item")
+        super(ConnectionSet, self).__init__(app, name, item)
 
         self.vhost = vhost
 
-        self.ids = CheckboxIdColumn(app, "id")
-        self.add_column(self.ids)
-
         col = self.AddressColumn(app, "addr")
         self.add_column(col)
-
         self.set_default_column(col)
 
         col = self.SystemConnectionColumn(app, "sysconn")
@@ -44,13 +44,14 @@
         self.unit = StateSwitch(app, "unit")
         self.unit.add_state("b", "Bytes")
         self.unit.add_state("f", "Frames")
-        self.add_child(self.unit)
+        self.switches.add_child(self.unit)
 
-        self.__phase = PhaseSwitch(app, "phase")
-        self.add_child(self.__phase)
+        self.phase = PhaseSwitch(app, "phase")
+        self.filters.add_child(self.phase)
 
-        self.__close = self.Close(app, "close")
-        self.add_child(self.__close)
+        task = main.module.connection_set_close
+        button = TaskButton(app, "close", task, self.selection)
+        self.add_child(button)
 
     def get_unit_plural(self, session):
         return self.unit.get(session) == "b" and "Bytes" or "Frames"
@@ -64,7 +65,7 @@
 
         constraints = list()
         constraints.append("l.vhost_id = %(id)r")
-        constraints.append(self.__phase.get_sql_constraint(session, vhost))
+        constraints.append(self.phase.get_sql_constraint(session, vhost))
 
         return "where %s" % " and ".join(constraints)
 
@@ -72,17 +73,6 @@
         vhost = self.vhost.get(session)
         return {"id": vhost.id}
 
-    class Close(FormButton):
-        def render_content(self, session):
-            return "Close"
-
-        def process_submit(self, session):
-            ids = self.parent.ids.get(session)
-            # XXX Make table a frame to elim this
-            self.parent.ids.clear(session)
-            href = self.frame.connections_close.get_href(session, ids)
-            self.page.set_redirect_url(session, href)
-
     class AddressColumn(SqlTableColumn):
         def render_title(self, session, data):
             return "Address"
@@ -145,117 +135,45 @@
         self.object = ConnectionParameter(app, "id")
         self.add_parameter(self.object)
 
-        self.view = ConnectionView(app, "view")
+        self.view = ConnectionView(app, "view", self.object)
         self.add_mode(self.view)
 
-        self.__close = ConnectionClose(app, "close", self.object)
-        self.add_mode(self.__close)
-
-        self.__sessions_detach = SessionSetDetach(app, "sessionsdetach")
-        self.add_mode(self.__sessions_detach)
-
-        self.__sessions_close = SessionSetClose(app, "sessionsclose")
-        self.add_mode(self.__sessions_close)
-
     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)
 
-    def show_close(self, session):
-        return self.__close.show(session)
+class ConnectionCloseForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(ConnectionCloseForm, self).__init__(app, name, task)
 
-    def show_sessions_detach(self, session):
-        return self.__sessions_detach.show(session)
+        self.object = ConnectionParameter(app, "conn")
+        self.add_parameter(self.object)
 
-    def show_sessions_close(self, session):
-        return self.__sessions_close.show(session)
+class ConnectionSetCloseForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(ConnectionSetCloseForm, self).__init__(app, name, task)
 
-# XXX get rid of this
-def doit(error, args):
-    pass
-    #print error, args
-    #print "did it!"
+        item = ConnectionParameter(app, "item")
 
-class ConnectionClose(CuminConfirmForm):
-    def __init__(self, app, name, conn):
-        super(ConnectionClose, self).__init__(app, name)
+        self.object = ListParameter(app, "conn", item)
+        self.add_parameter(self.object)
 
-        self.conn = conn
-
-    def process_cancel(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
-    def process_submit(self, session):
-        conn = self.conn.get(session)
-
-        action = self.app.model.connection.close
-        action.invoke(conn)
-
-        self.process_cancel(session, conn)
-
-    def render_title(self, session):
-        return "Close Connection '%s'" % self.conn.get(session).address
-
-    def render_submit_content(self, session):
-        return "Yes, Close Connection '%s'" % self.conn.get(session).address
-
-    def render_cancel_content(self, session):
-        return "No, Cancel"
-
-class ConnectionSetClose(CuminBulkActionForm):
-    def process_return(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
-    def process_item(self, session, id):
-        conn = ClientConnection.get(id)
-        action = self.app.model.connection.close
-        action.invoke(conn)
-
-    def render_title(self, session):
-        return "Close Connections"
-
-    def render_item_content(self, session, id):
-        return "Close Connection %s" % ClientConnection.get(id).address
-
-class ConnectionStatus(CuminStatus):
-    def render_frames_from(self, session, conn):
-        return self.app.model.connection.framesFromClient.rate_html(conn)
-
-    def render_frames_to(self, session, conn):
-        return self.app.model.connection.framesToClient.rate_html(conn)
-
-    def render_bytes_from(self, session, conn):
-        return self.app.model.connection.bytesFromClient.rate_html(conn)
-
-    def render_bytes_to(self, session, conn):
-        return self.app.model.connection.bytesToClient.rate_html(conn)
-
 class ConnectionView(CuminView):
-    def __init__(self, app, name):
+    def __init__(self, app, name, conn):
         super(ConnectionView, self).__init__(app, name)
 
-        status = ConnectionStatus(app, "status")
-        self.add_child(status)
+        self.tabs = TabbedModeSet(app, "tabs")
+        self.add_child(self.tabs)
 
-        self.__tabs = TabbedModeSet(app, "tabs")
-        self.add_child(self.__tabs)
+        self.tabs.add_tab(ConnectionStats(app, "stats"))
 
-        self.__tabs.add_tab(ConnectionStats(app, "stats"))
+        self.sessions = SessionSet(app, "sessions", conn)
+        self.tabs.add_tab(self.sessions)
 
-        self.__sessions = SessionSet(app, "sessions")
-        self.__tabs.add_tab(self.__sessions)
+        self.tabs.add_tab(CuminDetails(app, "details"))
 
-        self.__tabs.add_tab(CuminDetails(app, "details"))
-
-    def show_sessions(self, session):
-        return self.__sessions.show(session)
-
 class ConnectionStats(Widget):
     def __init__(self, app, name):
         super(ConnectionStats, self).__init__(app, name)
@@ -279,50 +197,24 @@
         def render_title(self, session, conn):
             return "Bytes Sent and Received"
 
-class SessionSetDetach(CuminBulkActionForm, Frame):
-    def process_return(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch).show_sessions(branch)
-        self.page.set_redirect_url(session, branch.marshal())
+class SessionSetTaskForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(SessionSetTaskForm, self).__init__(app, name, task)
 
-    def process_item(self, session, id):
-        session_ = Session.get(id)
-        action = self.app.model.session.detach
-        action.invoke(session_)
+        item = SessionParameter(app, "item")
 
-    def render_title(self, session):
-        return "Detach Sessions"
+        self.object = ListParameter(app, "session", item)
+        self.add_parameter(self.object)
 
-    def render_item_content(self, session, id):
-        return "Detach Session '%s'" % Session.get(id).name
+class SessionSet(CuminSelectionTable):
+    def __init__(self, app, name, conn):
+        item = SessionParameter(app, "item")
+        super(SessionSet, self).__init__(app, name, item)
 
-class SessionSetClose(CuminBulkActionForm, Frame):
-    def process_return(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch).show_sessions(branch)
-        self.page.set_redirect_url(session, branch.marshal())
+        self.conn = conn
 
-    def process_item(self, session, id):
-        session_ = Session.get(id)
-        action = self.app.model.session.close
-        action.invoke(session_)
-
-    def render_title(self, session):
-        return "Close Sessions"
-
-    def render_item_content(self, session, id):
-        return "Close Session '%s'" % Session.get(id).name
-
-class SessionSet(CuminTable, Form):
-    def __init__(self, app, name):
-        super(SessionSet, self).__init__(app, name)
-
-        self.ids = CheckboxIdColumn(app, "id")
-        self.add_column(self.ids)
-
         col = self.NameColumn(app, "name")
         self.add_column(col)
-
         self.set_default_column(col)
 
         col = self.ExpiresColumn(app, "expires")
@@ -334,55 +226,29 @@
         self.__phase = PhaseSwitch(app, "phase")
         self.add_child(self.__phase)
 
-        self.__detach = self.Detach(app, "detach")
-        self.add_child(self.__detach)
+        task = main.module.session_set_detach
+        self.buttons.add_child(TaskButton(app, "detach", task, self.selection))
 
-        self.__close = self.Close(app, "close")
-        self.add_child(self.__close)
+        task = main.module.session_set_close
+        self.buttons.add_child(TaskButton(app, "close", task, self.selection))
 
-    def get_args(self, session):
-        return self.frame.get_args(session)
-
-    def render_title(self, session, conn):
+    def render_title(self, session):
+        conn = self.conn.get(session)
         return "Sessions %s" % fmt_count(conn.sessions.count())
 
-    def render_sql_where(self, session, conn):
+    def render_sql_where(self, session):
+        conn = self.conn.get(session)
+
         elems = list()
         elems.append("s.client_connection_id = %(id)r")
         elems.append(self.__phase.get_sql_constraint(session, conn))
+
         return "where %s" % " and ".join(elems)
 
-    def get_sql_values(self, session, conn):
+    def get_sql_values(self, session):
+        conn = self.conn.get(session)
         return {"id": conn.id}
 
-    class Detach(FormButton):
-        def render_content(self, session):
-            return "Detach"
-
-        def process_submit(self, session):
-            ids = self.parent.ids.get(session)
-            self.parent.ids.clear(session)
-
-            branch = session.branch()
-            frame = self.frame.show_sessions_detach(branch)
-            frame.ids.set(branch, ids)
-            self.page.set_frame(branch, frame)
-            self.page.set_redirect_url(session, branch.marshal())
-
-    class Close(FormButton):
-        def render_content(self, session):
-            return "Close"
-
-        def process_submit(self, session):
-            ids = self.parent.ids.get(session)
-            self.parent.ids.clear(session)
-
-            branch = session.branch()
-            frame = self.frame.show_sessions_close(branch)
-            frame.ids.set(branch, ids)
-            self.page.set_frame(branch, frame)
-            self.page.set_redirect_url(session, branch.marshal())
-
     class NameColumn(SqlTableColumn):
         def render_title(self, session, data):
             return "Name"

Modified: mgmt/trunk/cumin/python/cumin/messaging/client.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/client.strings	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/client.strings	2009-06-17 20:20:11 UTC (rev 3457)
@@ -31,53 +31,6 @@
 left outer join client_connection_stats as c on c.id = l.stats_curr_id
 {sql_where}
 
-[ConnectionSet.html]
-<form id="{id}" method="post" action="?">
-  <div class="rfloat">{phase}</div>
-
-  {unit}
-
-  <div class="sactions">
-    <h2>Act on Selected Connections:</h2>
-    {close}
-  </div>
-
-  <table class="mobjects">
-    <thead>
-      <tr>
-        <th class="setnav" colspan="{column_count}">
-          <div class="rfloat">{page}</div>
-          {count}
-        </th>
-      </tr>
-      <tr>{headers}</tr>
-    </thead>
-    <tbody>{items}</tbody>
-  </table>
-  <div>{hidden_inputs}</div>
-</form>
-
-[ConnectionStatus.html]
-<div id="{id}" class="CuminStatus {color}">
-  <table>
-    <tr>
-      <th></th>
-      <th style="width: 35%;" class="ralign">Frames</th>
-      <th style="width: 35%;" class="ralign">Bytes</th>
-    </tr>
-    <tr>
-      <th>Sent</th>
-      <td class="ralign">{frames_from}</td>
-      <td class="ralign">{bytes_from}</td>
-    </tr>
-    <tr>
-      <th>Received</th>
-      <td class="ralign">{frames_to}</td>
-      <td class="ralign">{bytes_to}</td>
-    </tr>
-  </table>
-</div>
-
 [ConnectionStats.html]
 <table class="twocol">
   <tbody>
@@ -110,29 +63,3 @@
 from session as s
 left outer join session_stats as c on c.id = s.stats_curr_id
 {sql_where}
-
-[SessionSet.html]
-<form id="{id}" method="post" action="?">
-  <div class="rfloat">{phase}</div>
-
-  <ul class="radiotabs"><li>&nbsp;</li></ul>
-
-  <div class="sactions">
-    <h2>Act on Selected Sessions:</h2>
-    {close}
-  </div>
-
-  <table class="mobjects">
-    <thead>
-      <tr>
-        <th class="setnav" colspan="{column_count}">
-          <div class="rfloat">{page}</div>
-          {count}
-        </th>
-      </tr>
-      <tr>{headers}</tr>
-    </thead>
-    <tbody>{items}</tbody>
-  </table>
-  <div>{hidden_inputs}</div>
-</form>

Modified: mgmt/trunk/cumin/python/cumin/messaging/exchange.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/exchange.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/exchange.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -6,11 +6,16 @@
 from wooly.resources import *
 from cumin.stat import *
 from cumin.model import *
+from cumin.modelwidgets import *
 from cumin.widgets import *
 from cumin.parameters import *
 from cumin.formats import *
 from cumin.util import *
 
+from binding import *
+
+import main
+
 strings = StringCatalog(__file__)
 log = logging.getLogger("cumin.messaging.exchange")
 
@@ -34,15 +39,13 @@
     def render_item_checked_attr(self, session, exchange):
         return exchange is self.param.get(session) and "checked=\"checked\""
 
-class ExchangeSet(CuminTable, Form):
+class ExchangeSet(CuminSelectionTable):
     def __init__(self, app, name, vhost):
-        super(ExchangeSet, self).__init__(app, name)
+        item = ExchangeParameter(app, "item")
+        super(ExchangeSet, self).__init__(app, name, item)
 
         self.vhost = vhost
 
-        self.ids = FilteredCheckboxIdColumn(app, "id", self, callback=self.disable_exchange)
-        self.add_column(self.ids)
-
         col = self.NameColumn(app, "name")
         self.add_column(col)
         self.set_default_column(col)
@@ -68,22 +71,21 @@
         self.add_column(col)
 
         self.unit = UnitSwitch(app, "unit")
-        self.add_child(self.unit)
+        self.switches.add_child(self.unit)
 
         self.phase = PhaseSwitch(app, "phase")
-        self.add_child(self.phase)
+        self.filters.add_child(self.phase)
 
-        self.__remove = self.Remove(app, "remove")
-        self.add_child(self.__remove)
+        task = main.module.exchange_add
+        self.links.add_child(TaskLink(app, "exchange_add", task, self.vhost))
 
+        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_add_exchange_url(self, session):
-        branch = session.branch()
-        self.frame.exchange_add.show(branch)
-        return branch.marshal()
-
     def render_title(self, session):
         vhost = self.vhost.get(session)
         return "Exchanges %s" % fmt_count(vhost.exchanges.count())
@@ -101,18 +103,6 @@
         vhost = self.vhost.get(session)
         return {"id": vhost.id}
 
-    class Remove(FormButton):
-        def process_submit(self, session):
-            ids = self.parent.ids.get(session)
-            self.parent.ids.clear(session)
-
-            branch = session.branch()
-            self.frame.exchanges_remove.show(branch).ids.set(branch, ids)
-            self.page.set_redirect_url(session, branch.marshal())
-
-        def render_content(self, session):
-            return "Remove"
-
     class NameColumn(SqlTableColumn):
         def render_title(self, session, data):
             return "Name"
@@ -169,24 +159,6 @@
             unit = self.parent.unit.get(session)
             return unit == "b" and "bdropped" or "mdropped"
 
-class ExchangeSetRemove(CuminBulkActionForm):
-    def process_return(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
-    def process_item(self, session, id):
-        exchange = Exchange.get(id)
-        reg = self.frame.get_object(session)
-        action = self.app.model.exchange.remove
-        action.invoke(exchange)
-
-    def render_title(self, session):
-        return "Remove Exchanges"
-
-    def render_item_content(self, session, id):
-        return "Remove Exchange '%s'" % Exchange.get(id).name
-
 class ExchangeFrame(CuminFrame):
     def __init__(self, app, name):
         super(ExchangeFrame, self).__init__(app, name)
@@ -197,9 +169,6 @@
         self.view = ExchangeView(app, "view")
         self.add_mode(self.view)
 
-        self.remove = ExchangeRemove(app, "remove")
-        self.add_mode(self.remove)
-
     def show_object(self, session, exchange):
         if isinstance(exchange, SQLObject):
             self.page.main.messaging.broker.set_object \
@@ -217,133 +186,70 @@
         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)
 
-class ExchangeRemove(CuminConfirmForm):
-    def get_args(self, session):
-        return self.frame.get_args(session)
+        self.object = ExchangeParameter(app, "exchange")
+        self.add_parameter(self.object)
 
-    def process_cancel(self, session, exchange):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
+class ExchangeSetRemoveForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(ExchangeSetRemoveForm, self).__init__(app, name, task)
 
-    def process_submit(self, session, exchange):
-        reg = self.frame.frame.get_object(session)
-        action = self.app.model.exchange.remove
-        action.invoke(exchange)
+        item = ExchangeParameter(app, "item")
 
-        branch = session.branch()
-        self.frame.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
+        self.object = ListParameter(app, "exchange", item)
+        self.add_parameter(self.object)
 
-    def render_title(self, session, exchange):
-        return "Remove Exchange '%s'" % exchange.name
-
-    def render_submit_content(self, session, exchange):
-        return "Yes, Remove Exchange '%s'" % exchange.name
-
-    def render_cancel_content(self, session, exchange):
-        return "No, Cancel"
-
-class ExchangeStatus(CuminStatus):
-    def render_messages_received(self, session, exchange):
-        return self.app.model.exchange.msgReceives.rate_html(exchange)
-
-    def render_messages_routed(self, session, exchange):
-        return self.app.model.exchange.msgRoutes.rate_html(exchange)
-
-    def render_messages_dropped(self, session, exchange):
-        return self.app.model.exchange.msgDrops.value(exchange)
-
-    def render_bytes_received(self, session, exchange):
-        return self.app.model.exchange.byteReceives.rate_html(exchange)
-
-    def render_bytes_routed(self, session, exchange):
-        return self.app.model.exchange.byteRoutes.rate_html(exchange)
-
-    def render_bytes_dropped(self, session, exchange):
-        return self.app.model.exchange.byteDrops.value(exchange)
-
 class ExchangeView(CuminView):
     def __init__(self, app, name):
         super(ExchangeView, self).__init__(app, name)
 
-        status = ExchangeStatus(app, "status")
-        self.add_child(status)
+        self.tabs = TabbedModeSet(app, "tabs")
+        self.add_child(self.tabs)
 
-        self.__tabs = TabbedModeSet(app, "tabs")
-        self.add_child(self.__tabs)
+        self.tabs.add_tab(ExchangeStats(app, "stats"))
 
-        self.__tabs.add_tab(ExchangeStats(app, "stats"))
+        self.bindings = ExchangeBindingSet(app, "bindings")
+        self.tabs.add_tab(self.bindings)
 
-        self.__bindings = ExchangeBindingSet(app, "bindings")
-        self.__tabs.add_tab(self.__bindings)
+        self.tabs.add_tab(CuminDetails(app, "details"))
 
-        self.__tabs.add_tab(CuminDetails(app, "details"))
-
-    def show_bindings(self, session):
-        return self.__bindings.show(session)
-
-class ExchangeBindingSet(BindingSet, Form):
+class ExchangeBindingSet(BindingSet):
     def __init__(self, app, name):
         super(ExchangeBindingSet, self).__init__(app, name)
 
-        self.__remove = self.Remove(app, "remove")
-        self.add_child(self.__remove)
-
         self.set_default_column_name("q_id")
 
     def get_visible_columns(self, session):
         return self.get_request_visible_columns(session, ["q_id"])
 
-    def render_title(self, session, exchange):
+    def get_sql_values(self, session):
+        exchange = self.frame.object.get(session)
+        return {"id": exchange.id}
+
+    def render_title(self, session):
+        exchange = self.frame.object.get(session)
         return "Queue Bindings %s" % \
             fmt_count(exchange.bindings.count())
 
-    def render_sql_where(self, session, exchange):
+    def render_sql_where(self, session):
+        exchange = self.frame.object.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 Remove(FormButton):
-        def process_submit(self, session):
-            ids = self.parent.ids.get(session)
-            self.parent.ids.clear(session)
+class ExchangeAddForm(CuminFieldForm):
+    def __init__(self, app, name, task):
+        super(ExchangeAddForm, self).__init__(app, name)
 
-            href = self.page.main.messaging.broker.bindings_remove.get_href \
-                (session, ids)
-            self.page.set_redirect_url(session, href)
+        self.task = task
 
-        def render_content(self, session):
-            return "Remove"
+        self.vhost = VhostParameter(app, "vhost")
+        self.add_parameter(self.vhost)
 
-class BindingSetRemove(CuminBulkActionForm):
-    def process_item(self, session, id):
-        binding = Binding.get(id)
-        action = self.app.model.binding.remove
-        action.invoke(binding)
-
-    def render_title(self, session):
-        return "Remove Binding"
-
-    def render_form_heading(self, session, *args):
-        return "Remove Binding between:"
-
-    def render_item_content(self, session, id):
-        binding = Binding.get(id)
-        ename = binding.exchange.name or "Default"
-        qname = binding.queue.name
-        return "Exchange: %s <i>and</i> Queue: %s" % (ename, qname)
-
-class ExchangeForm(CuminFieldForm):
-    def __init__(self, app, name, vhost):
-        super(ExchangeForm, self).__init__(app, name)
-
-        assert vhost
-
-        self.vhost = vhost
-
         self.exchange_name = ExchangeNameField(app, "exchange_name")
         self.add_field(self.exchange_name)
 
@@ -362,6 +268,25 @@
         self.ive = self.IVEField(app, "ive")
         self.more.add_field(self.ive)
 
+    def process_submit(self, session):
+        self.check(session)
+
+        if not self.errors.get(session):
+            vhost = self.vhost.get(session)
+            name = self.exchange_name.get(session)
+            type = self.exchange_type.get(session)
+            durable = self.durable.get(session) == "yes"
+            sequence = self.sequence.get(session) == "yes"
+            ive = self.ive.get(session) == "yes"
+
+            self.task.invoke(session, vhost, name, type,
+                             durable, sequence, ive)
+            self.task.exit_with_redirect(session, vhost)
+
+    def render_title(self, session):
+        vhost = self.vhost.get(session)
+        return self.task.get_title(session, vhost)
+
     class SequenceField(TwoOptionRadioField):
         def render_title(self, session):
             return "Insert Sequence?"
@@ -403,7 +328,8 @@
 
     class ExchangeTypeField(RadioField):
         def __init__(self, app, name):
-            super(ExchangeForm.ExchangeTypeField, self).__init__(app, name, None)
+            super(ExchangeAddForm.ExchangeTypeField, self).__init__ \
+                (app, name, None)
 
             self.param = Parameter(app, "param")
             self.param.default = "direct"
@@ -465,40 +391,6 @@
             def render_title(self, session):
                 return "<em>XML:</em> Route message to queues based on XML content of the message"
 
-class ExchangeAdd(ExchangeForm):
-    def process_cancel(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
-    def process_submit(self, session):
-        errors = self.validate(session)
-
-        if errors:
-            pass
-        else:
-            reg = self.vhost.get(session).broker.registration
-
-            exchange = Exchange()
-            exchange.name = self.exchange_name.get(session)
-            exchange.type = self.exchange_type.get(session)
-            exchange.durable = self.durable.get(session) == "yes"
-
-            args = {}
-            args["reg"] = reg
-            args["sequence"] = self.sequence.get(session) == "yes"
-            args["ive"] = self.ive.get(session) == "yes"
-
-            action = self.app.model.broker.add_exchange
-            action.invoke(exchange, args)
-
-            self.process_cancel(session)
-
-    def render_title(self, session):
-        broker = self.vhost.get(session).broker
-        title = self.app.model.broker.get_object_title(session, broker)
-        return "Add Exchange to %s" % title
-
 class ExchangeStats(Widget):
     def __init__(self, app, name):
         super(ExchangeStats, self).__init__(app, name)
@@ -527,23 +419,6 @@
         def render_title(self, session, exchange):
             return "Messages Received, Routed, and Dropped"
 
-class ExchangeProducerSet(ItemSet):
-    def get_args(self, session):
-        return self.frame.get_args(session)
-
-    def render_title(self, session, exchange):
-        return "Producers %s" % \
-            fmt_count(self.get_item_count(session, exchange))
-
-    def get_item_count(self, session, exchange):
-        return exchange.producers.count()
-
-    def do_get_items(self, session, exchange):
-        return exchange.producers
-
-    def render_item_name(self, session, producer):
-        return producer.name
-
 class ExchangeInfo(object):
     @classmethod
     def is_builtin(cls, exchange):

Modified: mgmt/trunk/cumin/python/cumin/messaging/exchange.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/exchange.strings	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/exchange.strings	2009-06-17 20:20:11 UTC (rev 3457)
@@ -32,108 +32,12 @@
 {sql_orderby}
 {sql_limit}
 
-
 [ExchangeSet.count_sql]
 select count(*)
 from exchange as e
 left outer join exchange_stats as c on c.id = e.stats_curr_id
 {sql_where}
 
-[ExchangeSet.css]
-ul.ExchangeSet li:before {
-  content: url(resource?name=exchange-20.png);
-  vertical-align: -30%;
-  padding: 0 0.25em;
-}
-
-[ExchangeSet.html]
-<form id="{id}" method="post" action="?" update="{id}.table">
-  <ul class="actions">
-    <li><a class="nav" href="{add_exchange_url}">Add New Exchange</a></li>
-  </ul>
-
-  <div class="rfloat">{phase}</div>
-  {unit}
-
-    <div class="sactions">
-      <h2>Act on Selected Exchanges:</h2>
-      {remove}
-    </div>
-
-    <table id="{id}.table" class="mobjects">
-      <thead>
-        <tr>
-          <th class="setnav" colspan="{column_count}">
-            <div class="rfloat">{page}</div>
-            {count}
-          </th>
-        </tr>
-        <tr>{headers}</tr>
-      </thead>
-      <tbody>{items}</tbody>
-    </table>
-
-  <div>{hidden_inputs}</div>
-</form>
-
-[ExchangeSet.item_html]
-<tr>{cells}</tr>
-
-[ExchangeStatus.html]
-<div id="{id}" class="CuminStatus {color}">
-  <table>
-    <tr>
-      <th></th>
-      <th style="width: 35%;" class="ralign">Messages</th>
-      <th style="width: 35%;" class="ralign">Bytes</th>
-    </tr>
-    <tr>
-      <th>Received</th>
-      <td class="ralign">{messages_received}</td>
-      <td class="ralign">{bytes_received}</td>
-    </tr>
-    <tr>
-      <th>Routed</th>
-      <td class="ralign">{messages_routed}</td>
-      <td class="ralign">{bytes_routed}</td>
-    </tr>
-    <tr>
-      <th>Dropped</th>
-      <td class="ralign">{messages_dropped}</td>
-      <td class="ralign">{bytes_dropped}</td>
-    </tr>
-  </table>
-</div>
-
-[ExchangeBindingSet.html]
-<form id="{id}" method="post" action="?">
-
-  <div class="rfloat">{phase}</div>
-  <ul class="radiotabs"><li>&nbsp;</li></ul>
-
-  <div class="sactions">
-    <h2>Act on Selected Bindings:</h2>
-    {remove}
-  </div>
-
-  <table class="mobjects">
-    <thead>
-      <tr>
-        <th class="setnav" colspan="{column_count}">
-          <div class="rfloat">{page}</div>
-          {count}
-        </th>
-      </tr>
-      <tr>{headers}</tr>
-    </thead>
-    <tbody>{items}</tbody>
-  </table>
-  <div>{hidden_inputs}</div>
-</form>
-
-[ExchangeBindingSet.item_html]
-<tr>{cells}</tr>
-
 [ExchangeStats.html]
 <table class="twocol">
   <tbody>
@@ -152,32 +56,3 @@
   </tr>
   </tbody>
 </table>
-
-[ExchangeProducerSet.html]
-<div class="sactions">
-  <h2>Act on Selected Producers:</h2>
-  <button>Start</button>
-  <button>Stop</button>
-  <button>Throttle</button>
-</div>
-
-<table class="mobjects">
-  <tr>
-    <th><input type="checkbox"/></th>
-    <th>Name</th>
-    <th class="ralign" colspan="2">Msgs. Produced</th>
-    <th class="ralign" colspan="2">Bytes Produced</th>
-  </tr>
-
-  {items}
-</table>
-
-[ExchangeProducerSet.item_html]
-<tr>
-  <td><input type="checkbox"/></td>
-  <td>{item_name}</td>
-  <td class="ralign">{item_messages_produced_rate}</td>
-  <td class="ralign">{item_messages_produced}</td>
-  <td class="ralign">{item_bytes_produced_rate}</td>
-  <td class="ralign">{item_bytes_produced}</td>
-</tr>

Modified: mgmt/trunk/cumin/python/cumin/messaging/main.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/main.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/main.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -6,9 +6,70 @@
 
 from broker import *
 from brokergroup import *
+from model import *
 
 strings = StringCatalog(__file__)
 
+class MessagingModule(object):
+    def init(self, app):
+        broker = app.model.broker
+
+        self.broker_set_engroup = BrokerSetEngroupTask(app, broker)
+        self.exchange_add = ExchangeAddTask(app, broker)
+        self.link_add = LinkAddTask(app, broker)
+        self.queue_add = QueueAddTask(app, broker)
+
+        broker_group = app.model.broker_group
+
+        self.broker_group_add = BrokerGroupAddTask(app, broker_group)
+        self.broker_group_edit = BrokerGroupEditTask(app, broker_group)
+        self.broker_group_remove = BrokerGroupRemoveTask(app, broker_group)
+
+        queue = app.model.queue
+
+        self.queue_remove = QueueRemoveTask(app, queue)
+        self.queue_set_remove = QueueSetRemoveTask(app, queue)
+        self.queue_purge = QueuePurgeTask(app, queue)
+        self.queue_set_purge = QueueSetPurgeTask(app, queue)
+        self.binding_add = BindingAddTask(app, queue)
+        self.move_messages = MoveMessagesTask(app, queue)
+
+        exchange = app.model.exchange
+
+        self.exchange_remove = ExchangeRemoveTask(app, exchange)
+        self.exchange_set_remove = ExchangeSetRemoveTask(app, exchange)
+
+        binding = app.model.binding
+
+        self.binding_remove = BindingRemoveTask(app, binding)
+        self.binding_set_remove = BindingSetRemoveTask(app, binding)
+
+        link = app.model.link
+
+        self.link_remove = LinkRemoveTask(app, link)
+        self.link_set_remove = LinkSetRemoveTask(app, link)
+        self.route_add = RouteAddTask(app, link)
+
+        route = app.model.route
+
+        self.route_set_remove = RouteSetRemoveTask(app, route)
+
+        connection = app.model.connection
+
+        self.connection_close = ConnectionCloseTask(app, connection)
+        self.connection_set_close = ConnectionSetCloseTask(app, connection)
+
+        session = app.model.session
+
+        self.session_set_detach = SessionSetDetachTask(app, session)
+        self.session_set_close = SessionSetCloseTask(app, session)
+
+        self.frame = MessagingFrame(app, "messaging")
+        app.main_page.main.messaging = self.frame
+        app.main_page.main.add_tab(self.frame)
+
+module = MessagingModule()
+
 class MessagingFrame(CuminFrame):
     def __init__(self, app, name):
         super(MessagingFrame, self).__init__(app, name)
@@ -22,15 +83,6 @@
         self.broker_group = BrokerGroupFrame(app, "group")
         self.add_mode(self.broker_group)
 
-        self.brokers_group = BrokerSetEngroup(app, "brokersengroup")
-        self.add_mode(self.brokers_group)
-
-        action = self.app.model.broker_group.remove_set
-        item = BrokerGroupParameter(app, "item")
-        self.broker_groups_remove = CuminSetActionForm \
-            (app, "groupsremove", action, item)
-        self.add_mode(self.broker_groups_remove)
-
     def render_title(self, session):
         return "Messaging"
 

Added: mgmt/trunk/cumin/python/cumin/messaging/model.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/model.py	                        (rev 0)
+++ mgmt/trunk/cumin/python/cumin/messaging/model.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -0,0 +1,484 @@
+from wooly import Session
+
+from cumin.util import *
+
+from cumin.model import Task
+from broker import *
+from brokergroup import *
+from brokerlink import *
+from queue import *
+from exchange import *
+
+class ConnectionCloseTask(Task):
+    def __init__(self, app, cls):
+        super(ConnectionCloseTask, self).__init__(app, cls)
+
+        self.form = ConnectionCloseForm(app, "connection_close", self)
+
+    def get_title(self, session):
+        return "Close"
+
+    def do_enter(self, session, conn):
+        self.form.object.set(session, conn)
+
+    def do_invoke(self, completion, session, conn):
+        # XXX generalize this check and use it for other closes
+
+        session_ids = set()
+
+        for broker in self.app.model.mint.model.mintBrokersByQmfBroker:
+            session_ids.add(broker.getSessionId())
+
+        for sess in conn.sessions:
+            if sess.name in session_ids:
+                raise Exception \
+                    ("Cannot close management connection %s" % conn.address)
+
+        conn.close(self.app.model.mint.model, completion)
+
+class ConnectionSetCloseTask(SetTask):
+    def __init__(self, app, cls):
+        super(ConnectionSetCloseTask, self).__init__(app, cls)
+
+        self.form = ConnectionSetCloseForm(app, "connection_set_close", self)
+        self.item_task = ConnectionCloseTask(app, cls)
+
+    def get_title(self, session):
+        return "Close"
+
+    def do_enter(self, session, conns):
+        self.form.object.set(session, conns)
+
+class SessionDetachTask(QmfTask):
+    def get_title(self, session):
+        return "Detach"
+
+    def do_invoke(self, completion, session, sess):
+        sess.detach(self.app.model.mint.model, completion)
+
+class SessionSetDetachTask(SetTask):
+    def __init__(self, app, cls):
+        super(SessionSetDetachTask, self).__init__(app, cls)
+
+        self.form = SessionSetTaskForm(app, "session_set_detach", self)
+        self.item_task = SessionDetachTask(app, cls)
+
+    def do_enter(self, session, sessions):
+        self.form.object.set(session, sessions)
+
+class SessionCloseTask(QmfTask):
+    def get_title(self, session):
+        return "Close"
+
+    def do_invoke(self, completion, session, sess):
+        sess.close(self.app.model.mint.model, completion)
+
+class SessionSetCloseTask(SetTask):
+    def __init__(self, app, cls):
+        super(SessionSetCloseTask, self).__init__(app, cls)
+
+        self.form = SessionSetTaskForm(app, "session_set_close", self)
+        self.item_task = SessionCloseTask(app, cls)
+
+    def do_enter(self, session, sessions):
+        self.form.object.set(session, sessions)
+
+class QueueAddTask(Task):
+    def __init__(self, app, cls):
+        super(QueueAddTask, self).__init__(app, cls)
+
+        self.form = QueueAddForm(app, "queue_add", self)
+
+    def get_title(self, session):
+        return "Add queue"
+
+    def get_description(self, session, vhost):
+        return "Add queue to broker '%s'" % get_vhost_name(vhost)
+
+    def do_enter(self, session, vhost):
+        self.form.vhost.set(session, vhost)
+
+    # XXX need to move logic here
+
+class QueueRemoveTask(Task):
+    def __init__(self, app, cls):
+        super(QueueRemoveTask, self).__init__(app, cls)
+
+        self.form = QueueRemoveForm(app, "queue_remove", self)
+
+    def get_title(self, session):
+        return "Remove"
+
+    def do_enter(self, session, queue):
+        self.form.object.set(session, queue)
+
+    def do_exit(self, session, queue):
+        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()
+
+class QueueSetRemoveTask(SetTask):
+    def __init__(self, app, cls):
+        super(QueueSetRemoveTask, self).__init__(app, cls)
+
+        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)
+
+class QueuePurgeTask(QmfTask):
+    def __init__(self, app, cls):
+        super(QueuePurgeTask, self).__init__(app, cls)
+
+        self.form = QueuePurgeForm(app, "queue_purge", self)
+
+    def get_title(self, session):
+        return "Purge"
+
+    def do_enter(self, session, queue):
+        self.form.queue.set(session, queue)
+
+    def do_invoke(self, completion, session, queue, count):
+        """A count of 0 purges all"""
+
+        assert isinstance(queue, Queue)
+
+        queue.purge(self.app.model.mint.model, completion, count)
+
+class QueueSetPurgeTask(SetTask):
+    def __init__(self, app, cls):
+        super(QueueSetPurgeTask, self).__init__(app, cls)
+
+        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)
+
+class BindingAddTask(Task):
+    def __init__(self, app, cls):
+        super(BindingAddTask, self).__init__(app, cls)
+
+        self.form = BindingAddForm(app, "binding_add", self)
+
+    def get_title(self, session):
+        return "Add binding"
+
+    def get_description(self, session, queue):
+        return "Add binding to queue '%s'" % queue.name
+
+    def do_enter(self, session, queue):
+        self.form.queue.set(session, queue)
+
+    def do_invoke(self, queue, exchange, binding_key, arguments):
+        session = self.app.model.get_session_by_object(queue)
+        session.exchange_bind(queue=queue.name, exchange=exchange.name,
+                              binding_key=binding_key, arguments=arguments)
+        session.sync()
+
+class BindingRemoveTask(Task):
+    def __init__(self, app, cls):
+        super(BindingRemoveTask, self).__init__(app, cls)
+
+        self.form = BindingRemoveForm(app, "binding_remove", self)
+
+    def get_title(self, session):
+        return "Remove"
+
+    def do_enter(self, session, binding):
+        self.form.binding.set(session, binding)
+
+    def do_invoke(self, binding):
+        assert isinstance(binding, Binding)
+
+        session = self.app.model.get_session_by_object(binding)
+        session.exchange_unbind(queue=binding.queue.name,
+                                exchange=binding.exchange.name,
+                                binding_key=binding.bindingKey)
+        session.sync()
+
+class BindingSetRemoveTask(SetTask):
+    def __init__(self, app, cls):
+        super(BindingSetRemoveTask, self).__init__(app, cls)
+
+        self.form = BindingSetTaskForm(app, "binding_set_remove", self)
+        self.item_task = BindingRemoveTask(app, cls)
+
+    def do_enter(self, session, bindings):
+        self.form.object.set(session, bindings)
+
+class MoveMessagesTask(QmfTask):
+    def __init__(self, app, cls):
+        super(MoveMessagesTask, self).__init__(app, cls)
+
+        self.form = MoveMessagesForm(app, "move_messages", self)
+
+    def get_title(self, session):
+        return "Move messages"
+
+    def do_enter(self, session, queue):
+        self.form.queue.set(session, queue)
+
+    def do_invoke(self, completion, session, queue, dest_queue, count):
+        model = self.app.model.mint.model
+        broker = queue.vhost.broker
+        broker.queueMoveMessages \
+            (model, completion, queue.name, dest_queue.name, count)
+
+class ExchangeAddTask(Task):
+    MSG_SEQUENCE = "qpid.msg_sequence"
+    IVE = "qpid.ive"
+
+    def __init__(self, app, cls):
+        super(ExchangeAddTask, self).__init__(app, cls)
+
+        self.form = ExchangeAddForm(app, "exchange_add", self)
+
+    def get_title(self, session):
+        return "Add exchange"
+
+    def do_enter(self, session, vhost):
+        self.form.vhost.set(session, vhost)
+
+    def do_invoke(self, session, vhost, name, type, durable, sequence, ive):
+        assert isinstance(vhost, Vhost)
+
+        args = dict()
+
+        if sequence:
+            args[self.MSG_SEQUENCE] = 1
+
+        if ive:
+            args[self.IVE] = 1
+
+        session = self.app.model.get_session_by_object(vhost)
+        session.exchange_declare \
+            (exchange=name, type=type, durable=durable, arguments=args)
+        session.sync()
+
+class ExchangeRemoveTask(Task):
+    def __init__(self, app, cls):
+        super(ExchangeRemoveTask, self).__init__(app, cls)
+
+        self.form = ExchangeRemoveForm(app, "exchange_remove", self)
+
+    def get_title(self, session):
+        return "Remove"
+
+    def do_enter(self, session, exchange):
+        self.form.object.set(session, exchange)
+
+    def do_exit(self, session, exchange):
+        self.app.main_page.main.messaging.broker.view.show(session)
+
+    def do_invoke(self, session, exchange):
+        assert isinstance(exchange, Exchange)
+
+        session = self.app.model.get_session_by_object(exchange)
+        session.exchange_delete(exchange=exchange.name)
+        session.sync()
+
+class ExchangeSetRemoveTask(SetTask):
+    def __init__(self, app, cls):
+        super(ExchangeSetRemoveTask, self).__init__(app, cls)
+
+        self.form = ExchangeSetRemoveForm(app, "exchange_set_remove", self)
+        self.item_task = ExchangeRemoveTask(app, cls)
+
+    def get_description(self, session, exchanges):
+        count = len(exchanges)
+        return "Remove %i exchange%s" % (count, ess(count))
+
+    def do_enter(self, session, exchanges):
+        self.form.object.set(session, exchanges)
+
+class LinkAddTask(QmfTask):
+    def __init__(self, app, cls):
+        super(LinkAddTask, self).__init__(app, cls)
+
+        self.form = LinkAddForm(app, "link_add", self)
+
+    def get_title(self, session):
+        return "Add broker link"
+
+    def get_description(self, session, vhost):
+        return "Add broker link to vhost '%s'" % get_vhost_name(vhost)
+
+    def do_enter(self, session, vhost):
+        self.form.vhost.set(session, vhost)
+
+    def do_invoke(self, completion, session, vhost,
+                  host, port, durable, username, password, transport):
+        broker = vhost.broker
+
+        if username == "anonymous":
+            mech = "ANONYMOUS"
+        else:
+            mech = "PLAIN"
+
+        broker.connect(self.app.model.mint.model, completion,
+                       host, port, durable, mech, username, password,
+                       transport)
+
+class LinkRemoveTask(Task):
+    def __init__(self, app, cls):
+        super(LinkRemoveTask, self).__init__(app, cls)
+
+        self.form = LinkRemoveForm(app, "link_remove", self)
+
+    def get_title(self, session):
+        return "Remove"
+
+    def do_enter(self, session, link):
+        self.form.object.set(session, link)
+
+    def do_invoke(self, completion, session, link):
+        link.close(self.app.model.mint.model, completion)
+
+class LinkSetRemoveTask(SetTask):
+    def __init__(self, app, cls):
+        super(LinkSetRemoveTask, self).__init__(app, cls)
+
+        self.form = LinkSetRemoveForm(app, "link_set_remove", self)
+        self.item_task = LinkRemoveTask(app, cls)
+
+    def do_enter(self, session, links):
+        self.form.object.set(session, links)
+
+class RouteAddTask(QmfTask):
+    def __init__(self, app, cls):
+        super(RouteAddTask, self).__init__(app, cls)
+
+        self.form = RouteAddForm(app, "route_add", self)
+
+    def get_title(self, session):
+        return "Add route"
+
+    def get_description(self, session, link):
+        return "Add route to link '%s:%i'" % (link.host, link.port)
+
+    def do_enter(self, session, link):
+        self.form.link.set(session, link)
+
+    def do_invoke(self, completion, session, link, exchange, key, tag,
+                  dynamic, sync, excludes):
+        link.bridge(self.app.model.mint.model, completion,
+                    link.durable, exchange.name, exchange.name,
+                    key, tag, excludes, False, False, dynamic, sync)
+
+class RouteRemoveTask(QmfTask):
+    def get_title(self, session):
+        return "Remove"
+
+    def do_invoke(self, completion, session, route):
+        route.close(self.app.model.mint.model, completion)
+
+class RouteSetRemoveTask(SetTask):
+    def __init__(self, app, cls):
+        super(RouteSetRemoveTask, self).__init__(app, cls)
+
+        self.form = RouteSetRemoveForm(app, "route_set_remove", self)
+        self.item_task = RouteRemoveTask(app, cls)
+
+    def do_enter(self, session, routes):
+        self.form.object.set(session, routes)
+
+class BrokerGroupAddTask(Task):
+    def __init__(self, app, cls):
+        super(BrokerGroupAddTask, self).__init__(app, cls)
+
+        self.form = BrokerGroupAddForm(app, "broker_group_add", self)
+
+    def get_title(self, session):
+        return "Add broker group"
+
+    def do_invoke(self, object, name):
+        group = BrokerGroup(name=name)
+        group.syncUpdate()
+        return group
+
+class BrokerGroupEditTask(Task):
+    def __init__(self, app, cls):
+        super(BrokerGroupEditTask, self).__init__(app, cls)
+
+        self.form = BrokerGroupEditForm(app, "broker_group_edit", self)
+
+    def get_title(self, session):
+        return "Edit"
+
+    def do_enter(self, session, group):
+        self.form.group.set(session, group)
+
+    def do_invoke(self, group, name):
+        assert isinstance(group, BrokerGroup)
+
+        group.set(name=name)
+        group.syncUpdate()
+
+class BrokerGroupRemoveTask(Task):
+    def __init__(self, app, cls):
+        super(BrokerGroupRemoveTask, self).__init__(app, cls)
+
+        self.form = BrokerGroupRemoveForm(app, "broker_group_remove", self)
+
+    def get_title(self, session):
+        return "Remove"
+
+    def do_enter(self, session, group):
+        self.form.object.set(session, group)
+
+    def do_exit(self, session, group):
+        self.app.main_page.main.messaging.view.show(session)
+
+    def do_invoke(self, group):
+        assert isinstance(group, BrokerGroup)
+
+        group.destroySelf()
+        group.syncUpdate()
+
+class BrokerGroupSetRemoveTask(SetTask):
+    def __init__(self, app, cls):
+        super(BrokerGroupSetRemoveTask, self).__init__(app, cls)
+
+        self.form = BrokerGroupSetRemoveForm(app, "broker_groups_remove", self)
+        self.item_task = BrokerGroupRemoveTask(app, cls)
+
+    def do_enter(self, session, groups):
+        self.form.groups.set(session, groups)
+
+class BrokerEngroupTask(Task):
+    def get_title(self, session):
+        return "Add to groups"
+
+    def do_invoke(self, session, broker):
+        print "XXX engroup", broker
+
+class BrokerSetEngroupTask(SetTask):
+    def __init__(self, app, cls):
+        super(BrokerSetEngroupTask, self).__init__(app, cls)
+
+        self.form = BrokerSetEngroupForm(app, "brokers_engroup", self)
+        self.item_task = BrokerEngroupTask(app, cls)
+
+    def get_description(self, session, brokers):
+        count = len(brokers)
+        return "Place %i broker%s in broker groups" % (count, ess(count))
+
+    def do_enter(self, session, brokers):
+        self.form.object.set(session, brokers)
+
+    def do_invoke(self, brokers):
+        pass
+
+def get_vhost_name(vhost):
+    broker = vhost.broker
+    name = broker.system.nodeName
+    port = broker.port
+
+    return "%s:%i" % (name, port)

Modified: mgmt/trunk/cumin/python/cumin/messaging/queue.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/queue.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/queue.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -10,6 +10,7 @@
 from sqlobject.sqlbuilder import LEFTJOINOn
 from cumin.stat import *
 from cumin.widgets import *
+from cumin.modelwidgets import *
 from cumin.parameters import *
 from cumin.formats import *
 from cumin.util import *
@@ -17,21 +18,20 @@
 from binding import *
 from exchange import ExchangeInputSet
 
+import main
+
 strings = StringCatalog(__file__)
 log = logging.getLogger("cumin.messaging.queue")
 
-class QueueSet(CuminTable, Form):
+class QueueSet(CuminSelectionTable):
     def __init__(self, app, name, vhost):
-        super(QueueSet, self).__init__(app, name)
+        item = QueueParameter(app, "item")
+        super(QueueSet, self).__init__(app, name, item)
 
         self.vhost = vhost
 
-        self.ids = CheckboxIdColumn(app, "id")
-        self.add_column(self.ids)
-
         col = self.NameColumn(app, "name")
         self.add_column(col)
-
         self.set_default_column(col)
 
         col = self.ConsumersColumn(app, "consumers")
@@ -55,21 +55,21 @@
         self.add_column(col)
 
         self.unit = UnitSwitch(app, "unit")
-        self.add_child(self.unit)
+        self.switches.add_child(self.unit)
 
         self.phase = PhaseSwitch(app, "phase")
-        self.add_child(self.phase)
+        self.filters.add_child(self.phase)
 
-        self.__purge = self.Purge(app, "purge")
-        self.add_child(self.__purge)
+        task = main.module.queue_add
+        self.links.add_child(TaskLink(app, "add", task, vhost))
 
-        self.__remove = self.Remove(app, "remove")
-        self.add_child(self.__remove)
+        task = main.module.queue_set_purge
+        button = TaskButton(app, "purge", task, self.selection)
+        self.buttons.add_child(button)
 
-    def render_add_queue_url(self, session):
-        branch = session.branch()
-        self.frame.queue_add.show(branch)
-        return branch.marshal()
+        task = main.module.queue_set_remove
+        button = TaskButton(app, "remove", task, self.selection)
+        self.buttons.add_child(button)
 
     def render_title(self, session):
         return "Queues %s" % fmt_count(Queue.select().count())
@@ -87,38 +87,13 @@
         vhost = self.vhost.get(session)
         return {"id": vhost.id}
 
-    class Purge(FormButton):
-        def process_submit(self, session):
-            ids = self.parent.ids.get(session)
-            self.parent.ids.clear(session)
-
-            branch = session.branch()
-            self.frame.queues_purge.show(branch).ids.set(branch, ids)
-            self.page.set_redirect_url(session, branch.marshal())
-
-        def render_content(self, session):
-            return "Purge"
-
-    class Remove(FormButton):
-        def process_submit(self, session):
-            ids = self.parent.ids.get(session)
-            self.parent.ids.clear(session)
-
-            branch = session.branch()
-            self.frame.queues_remove.show(branch).ids.set(branch, ids)
-            self.page.set_redirect_url(session, branch.marshal())
-
-        def render_content(self, session):
-            return "Remove"
-
     class NameColumn(ClientTruncateColumn):
         def render_title(self, session, data):
             return "Name"
 
         def render_content(self, session, data):
             queue = Identifiable(data["id"])
-            href = self.page.main.messaging.broker.queue.get_href \
-                (session, queue)
+            href = main.module.frame.broker.queue.get_href(session, queue)
             return fmt_link(href, data["name"], link_title=data["name"])
 
     class ConsumersColumn(SqlTableColumn):
@@ -203,7 +178,8 @@
             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"])
+            return fmt_link \
+                (branch.marshal(), data["name"], link_title=data["name"])
 
     class EnqueuesColumn(TopTableColumn):
         def render_title(self, session, data):
@@ -216,21 +192,9 @@
         self.object = QueueParameter(app, "id")
         self.add_parameter(self.object)
 
-        self.view = QueueView(app, "view")
+        self.view = QueueView(app, "view", self.object)
         self.add_mode(self.view)
 
-        self.purge = QueuePurge(app, "purge")
-        self.add_mode(self.purge)
-
-        self.binding_add = QueueBindingAdd(app, "bindingadd", self.object)
-        self.add_mode(self.binding_add)
-
-        self.remove = QueueRemove(app, "remove")
-        self.add_mode(self.remove)
-
-        self.move_messages = QueueMoveMessages(app, "move_messages")
-        self.add_mode(self.move_messages)
-
     def show_object(self, session, queue):
         #XXX self.page.main.messaging.broker.object.set(session, queue.vhost.broker)
         return super(QueueFrame, self).show_object(session, queue)
@@ -245,112 +209,72 @@
         else:
             return "Queue"
 
-class QueueStatus(CuminStatus):
-    def render_consumers(self, session, queue):
-        return self.app.model.queue.consumers.value(queue)
-
-    def render_messages_enqueued(self, session, queue):
-        return self.app.model.queue.msgTotalEnqueues.rate_html(queue)
-
-    def render_messages_dequeued(self, session, queue):
-        return self.app.model.queue.msgTotalDequeues.rate_html(queue)
-
-    def render_message_depth(self, session, queue):
-        return self.app.model.queue.msgDepth.value(queue)
-
-    def render_message_depth_accel(self, session, queue):
-        return self.app.model.queue.msgDepth.rate_html(queue)
-
-    def render_bytes_enqueued(self, session, queue):
-        return self.app.model.queue.byteTotalEnqueues.rate_html(queue)
-
-    def render_bytes_dequeued(self, session, queue):
-        return self.app.model.queue.byteTotalDequeues.rate_html(queue)
-
-    def render_byte_depth(self, session, queue):
-        return self.app.model.queue.byteDepth.value(queue)
-
-    def render_byte_depth_accel(self, session, queue):
-        return self.app.model.queue.byteDepth.rate_html(queue)
-
 class QueueView(CuminView):
-    def __init__(self, app, name):
+    def __init__(self, app, name, queue):
         super(QueueView, self).__init__(app, name)
 
-        #status = QueueStatus(app, "status")
-        #self.add_child(status)
+        self.tabs = TabbedModeSet(app, "tabs")
+        self.add_child(self.tabs)
 
-        self.__tabs = TabbedModeSet(app, "tabs")
-        self.add_child(self.__tabs)
+        self.tabs.add_tab(QueueStats(app, "stats"))
 
-        self.__tabs.add_tab(QueueStats(app, "stats"))
+        self.bindings = QueueBindingSet(app, "bindings", queue)
+        self.tabs.add_tab(self.bindings)
 
-        self.bindings = QueueBindingSet(app, "bindings")
-        self.__tabs.add_tab(self.bindings)
+        self.tabs.add_tab(CuminDetails(app, "details", queue))
 
-        self.details = CuminDetails(app, "details")
-        self.__tabs.add_tab(self.details)
-
-    def show_bindings(self, session):
-        self.bindings.show(session);
-
-class QueueBindingSet(BindingSet, Form):
-    def __init__(self, app, name):
+class QueueBindingSet(BindingSet):
+    def __init__(self, app, name, queue):
         super(QueueBindingSet, self).__init__(app, name)
 
-        self.__remove = self.Remove(app, "remove")
-        self.add_child(self.__remove)
+        self.queue = queue
 
         self.set_default_column_name("e_id")
 
+        task = main.module.binding_add
+        self.links.add_child(TaskLink(app, "add", task, self.queue))
+
     def get_visible_columns(self, session):
         return self.get_request_visible_columns(session, ["e_id"])
 
-    def render_add_queue_binding_url(self, session, *args):
-        branch = session.branch()
-        self.frame.binding_add.show(branch)
-        return branch.marshal()
+    def get_sql_values(self, session):
+        queue = self.queue.get(session)
+        return {"id": queue.id}
 
-    def render_sql_where(self, session, queue):
+    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):
+    def render_title(self, session):
+        queue = self.queue.get(session)
         return "Exchange Bindings %s" % \
             fmt_count(queue.bindings.count())
 
-    class Remove(FormButton):
-        def process_submit(self, session):
-            ids = self.parent.ids.get(session)
-            self.parent.ids.clear(session)
+class QueueAddForm(CuminFieldForm):
+    def __init__(self, app, name, task):
+        super(QueueAddForm, self).__init__(app, name)
 
-            href = self.frame.frame.bindings_remove.get_href \
-                (session, ids)
-            self.page.set_redirect_url(session, href)
+        self.task = task
 
-        def render_content(self, session):
-            return "Remove"
+        self.vhost = VhostParameter(app, "vhost")
+        self.add_parameter(self.vhost)
 
-class QueueForm(CuminFieldForm):
-    def __init__(self, app, name, vhost):
-        super(QueueForm, self).__init__(app, name)
-
-        assert vhost
-
-        self.vhost = vhost
-
         self.namef = NameField(app, "name")
         self.add_field(self.namef)
 
-        self.more = MoreFieldSet(app, "more")
+        self.more = self.AdvancedOptions(app, "more")
         self.add_field(self.more)
 
         self.durable = self.QueueDurabilityField(app, "durable")
         self.more.add_field(self.durable)
 
-        self.cluster_durable = self.ClusterDurabilityField(app, "cluster_durable")
+        self.cluster_durable = self.ClusterDurabilityField \
+            (app, "cluster_durable")
         self.more.add_field(self.cluster_durable)
 
         self.lvq = self.LVQField(app, "lvq")
@@ -380,20 +304,24 @@
         self.add_field(self.bindings)
 
     def validate(self, session, queue_name):
-        super_error = super(QueueForm, self).validate(session)
+        super_error = super(QueueAddForm, self).validate(session)
         (errors, form_binding_info) = self.bindings.get_binding_errors(session, queue_name)
         return (errors or super_error, form_binding_info)
 
+    class AdvancedOptions(MoreFieldSet):
+        def render_title(self, session):
+            return "Advanced Options"
+
     class QCountField(IntegerField):
         def render_title(self, session):
-            return "<div style=\"padding-left: 1em;\">Max Queue Count</div>"
+            return "Max Queue Count"
 
         def render_field_help(self, session):
             return "(Maximum in-memory queue size as a number of messages. Applies if Policy is set.)"
 
     class QSizeField(IntegerField):
         def render_title(self, session):
-            return "<div style=\"padding-left: 1em;\">Max Queue Size</div>"
+            return "Max Queue Size"
 
         def render_field_help(self, session):
             return "(Maximum in-memory queue size as bytes. Applies if Policy is set.)"
@@ -466,7 +394,7 @@
 
     class PolicyField(RadioField):
         def __init__(self, app, name):
-            super(QueueForm.PolicyField, self).__init__(app, name, None)
+            super(QueueAddForm.PolicyField, self).__init__(app, name, None)
 
             self.param = Parameter(app, "param")
             self.param.default = "none"
@@ -528,15 +456,6 @@
             def render_title(self, session):
                 return "Ring Strict"
 
-class QueueAdd(QueueForm):
-    def __init__(self, app, name, vhost):
-        super(QueueAdd, self).__init__(app, name, vhost)
-
-    def process_cancel(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
     def process_submit(self, session):
         vhost = self.vhost.get(session)
         name = self.namef.get(session)
@@ -546,8 +465,7 @@
         errors, binding_info = self.validate(session, name)
 
         if not errors:
-            action = self.app.model.broker.add_queue
-            invoc = action.begin(vhost)
+            invoc = self.task.start(session, vhost)
 
             try:
                 args = dict()
@@ -570,7 +488,7 @@
                 args["qpid.optimistic_consume"] = \
                     self.optimistic.get(session) == "yes"
 
-                qsession = action.get_session_by_object(vhost)
+                qsession = self.app.model.get_session_by_object(vhost)
 
                 qsession.queue_declare(queue=name,
                                        durable=durable,
@@ -590,116 +508,71 @@
                                            binding_key=binding_key,
                                            arguments=eargs)
 
-                invoc.status = "OK"
+                self.task.end(invoc)
             except Exception, e:
-                invoc.status = "failed"
-                invoc.exception = e
+                self.task.exception(invoc, e)
 
-                log.exception(e)
+            self.process_return(session)
 
-            # navigate back to main queue frame
-            self.process_cancel(session)
-
     def render_title(self, session):
-        broker = self.vhost.get(session).broker
-        title = self.app.model.broker.get_object_title(session, broker)
-        return "Add Queue to %s" % title
+        vhost = self.vhost.get(session)
+        return self.task.get_description(session, vhost)
 
-class QueueRemove(CuminConfirmForm):
-    def get_args(self, session):
-        return self.frame.get_args(session)
+class QueueRemoveForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(QueueRemoveForm, self).__init__(app, name, task)
 
-    def render_title(self, session, queue):
-        return "Remove Queue '%s'" % queue.name
+        self.object = QueueParameter(app, "queue")
+        self.add_parameter(self.object)
 
-    def process_submit(self, session, queue):
-        action = self.app.model.queue.remove
-        action.invoke(queue)
+class QueuePurgeForm(CuminFieldForm):
+    def __init__(self, app, name, task):
+        super(QueuePurgeForm, self).__init__(app, name)
 
-        self.process_cancel(session, queue)
+        self.task = task
 
-    def render_submit_content(self, session, queue):
-        return "Yes, Remove Queue '%s'" % queue.name
+        self.queue = QueueParameter(app, "queue")
+        self.add_parameter(self.queue)
 
-    def render_cancel_content(self, session, queue):
-        return "No, Cancel"
-
-class QueuePurge(CuminFieldForm):
-    def __init__(self, app, name):
-        super(QueuePurge, self).__init__(app, name)
-
         self.purge_request = MultiplicityField(app, "purge_request")
         self.add_field(self.purge_request)
 
-    def get_args(self, session):
-        return self.frame.get_args(session)
-
-    def render_title(self, session, queue):
+    def render_title(self, session):
+        queue = self.queue.get(session)
         return "Purge Messages from  Queue '%s'" % queue.name
 
-    def process_submit(self, session, queue):
+    def process_submit(self, session):
+        queue = self.queue.get(session)
         request_amt = self.purge_request.get(session)
-        args = dict()
 
         if request_amt == "all":
-            args["request"] = 0
+            count = 0
         elif request_amt == "top":
-            args["request"] = 1
+            count = 1
         elif request_amt == "N":
-            args["request"] = self.purge_request.top_n.get_n_value(session)
+            count = self.purge_request.top_n.get_n_value(session)
         else:
             raise Exception("Wrong Value")
 
-        action = self.app.model.queue.purge
-        action.invoke(queue, args)
+        self.task.invoke(session, queue, count)
+        self.task.exit_with_redirect(session, queue)
 
-        self.process_cancel(session, queue)
-
-    def render_submit_content(self, session, queue):
+    def render_submit_content(self, session):
+        queue = self.queue.get(session)
         return "Yes, Purge Messages from Queue '%s'" % queue.name
 
-    def render_cancel_content(self, session, queue):
+    def render_cancel_content(self, session):
         return "No, Cancel"
 
-class QueueSetPurge(CuminBulkActionForm):
-    def process_return(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
+class QueueSetTaskForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(QueueSetTaskForm, self).__init__(app, name, task)
 
-    def process_item(self, session, id):
-        queue = Queue.get(id)
+        item = QueueParameter(app, "item")
 
-        args = dict()
-        args["request"] = 0
+        self.object = ListParameter(app, "queue", item)
+        self.add_parameter(self.object)
 
-        action = self.app.model.queue.purge
-        action.invoke(queue, args)
-
-    def render_title(self, session):
-        return "Purge Queues"
-
-    def render_item_content(self, session, id):
-        return "Purge Queue '%s'" % Queue.get(id).name
-
-
-class QueueSetRemove(CuminBulkActionForm):
-    def process_return(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
-    def process_item(self, session, id):
-        queue = Queue.get(id)
-        action = self.app.model.queue.remove
-        action.invoke(queue)
-
-    def render_title(self, session):
-        return "Remove Queues"
-
-    def render_item_content(self, session, id):
-        return "Remove Queue '%s'" % Queue.get(id).name
-
 class BindSummaryPropertiesField(FormField):
     def __init__(self, app, name):
         super(BindSummaryPropertiesField, self).__init__(app, name)
@@ -725,95 +598,62 @@
         return self.sum_props.render_items(session, *args)
 
     def get_args(self, session):
-        return (self.frame.frame.get_object(session),)
+        return (self.form.queue.get(session),)
 
-class QueueBindingAdd(CuminFieldForm):
-    def __init__(self, app, name, queue):
-        super(QueueBindingAdd, self).__init__(app, name)
+class BindingAddForm(CuminFieldForm):
+    def __init__(self, app, name, task):
+        super(BindingAddForm, self).__init__(app, name)
 
-        self.queue = queue
+        self.task = task
 
-        self.vhost = self.VhostAttribute(self, "vhost")
+        self.queue = QueueParameter(app, "queue")
+        self.add_parameter(self.queue)
+
+        self.vhost = self.VhostAttribute(app, "vhost")
         self.add_attribute(self.vhost)
 
         self.props = BindSummaryPropertiesField(app, "props")
         self.add_field(self.props)
 
-        self.bindings = ExchangeKeysField(app, "bindings", self.vhost,
-                                          title="Bind to Exchange:")
+        self.bindings = self.ExchangeBindings(app, "bindings", self.vhost)
         self.add_field(self.bindings)
 
-        self.errors = self.Errors(self, "errors")
-        self.add_attribute(self.errors)
+    class ExchangeBindings(ExchangeKeysField):
+        def render_title(self, session):
+            return "Exchange Bindings"
 
     class VhostAttribute(Attribute):
-        def get_default(self, session):
+        def get(self, session):
             return self.widget.queue.get(session).vhost
 
-    def render_form_error(self, session):
-        errors = self.errors.get(session)
-        if "no_exchanges" in errors:
-            return "<ul class=\"errors\" style=\"margin:0; float:left;\"><li>%s</li></ul>" % \
-                "</li><li>".join(errors["no_exchanges"])
-
-    def process_cancel(self, session):
-        branch = session.branch()
-        self.frame.view.show(branch)
-        self.page.set_redirect_url(session, branch.marshal())
-
     def process_submit(self, session):
         queue = self.queue.get(session)
-        (errors, form_binding_info) = self.bindings.get_binding_errors(session, queue.name)
+        errors, form_binding_info = self.bindings.get_binding_errors \
+            (session, queue.name)
 
         if not len(form_binding_info):
-            # no exhchanges were selected is not an
-            # error that ExchangeKeysField looks for
-            errors = self.errors.get(session)
-            errs = errors.setdefault("no_exchanges", list())
-            errs.append("At least one exchange must be selected")
-            errors = True
+            # no exchanges were selected is not an error that
+            # ExchangeKeysField looks for
+            error = FormError("At least one exchange must be selected")
+            self.errors.add(session, error)
 
-        if errors:
-            pass
-        else:
-            reg = self.frame.frame.get_object(session)
-            args = {
-                    "exchange_keys": form_binding_info,
-                    "reg": reg}
+        if not self.errors.get(session):
+            print "XXX queue binding add", queue, form_binding_info
 
-            action = self.app.model.queue.bind
-            action.invoke(queue, args)
+            #self.task.invoke(session, queue, args)
+            self.task.exit_with_redirect(session, queue)
 
-            # navigate back to main queue frame
-            self.process_cancel(session)
-
-    def render_title(self, session, *args):
+    def render_title(self, session):
         queue = self.queue.get(session)
-        title = self.app.model.queue.get_object_title(session, queue)
-        return "Add Binding to %s" % title
+        return self.task.get_description(session, queue)
 
-    class Errors(Attribute):
-        def get_default(self, session):
-            return dict()
+class BindingRemoveForm(CuminTaskForm):
+    def __init__(self, app, name, task):
+        super(BindingRemoveForm, self).__init__(app, name, task)
 
-class QueueBindingRemove(CuminConfirmForm):
-    def get_args(self, session):
-        return self.frame.get_args(session)
+        self.object = BindingParameter(app, "binding")
+        self.add_parameter(self.object)
 
-    def process_submit(self, session, binding):
-        log.info("Removing binding %s", binding)
-
-        self.process_cancel(session, binding)
-
-    def render_title(self, session, binding):
-        return "Remove Binding"
-
-    def render_submit_content(self, session, binding):
-        return "Yes, Remove Binding"
-
-    def render_cancel_content(self, session, binding):
-        return "No, Cancel"
-
 class QueueStats(TabbedModeSet):
     def __init__(self, app, name):
         super(QueueStats, self).__init__(app, name)
@@ -963,73 +803,34 @@
         def render_title(self, session, queue):
             return "Transactional Messages Enqueued and Dequeued"
 
-class QueueConsumerSet(PaginatedItemSet):
-    def get_args(self, session):
-        return self.frame.get_args(session)
-
-    def render_title(self, session, queue):
-        count = fmt_count(self.get_item_count(session, queue))
-        return "Consumers %s" % count
-
-    def get_item_count(self, session, queue):
-        return queue.consumers.count()
-
-    def do_get_items(self, session, queue):
-        start, end = self.get_bounds(session)
-        return queue.consumers[start:end]
-
-    def render_item_name(self, session, consumer):
-        return consumer.name
-
-    def render_item_messages_consumed(self, session, consumer):
-        return self.app.model.consumer.msgsConsumed.value(consumer)
-
-    def render_item_messages_consumed_rate(self, session, consumer):
-        return self.app.model.consumer.msgsConsumed.rate_html(consumer)
-
-    def render_item_bytes_consumed(self, session, consumer):
-        return self.app.model.consumer.bytesConsumed.value(consumer)
-
-    def render_item_bytes_consumed_rate(self, session, consumer):
-        return self.app.model.consumer.bytesConsumed.rate_html(consumer)
-
-    def render_item_unacked_messages(self, session, consumer):
-        return self.app.model.consumer.unackedMessages.value(consumer)
-
-
 class QueueSelectField(FormField):
     def __init__(self, app, name, form):
         super(QueueSelectField, self).__init__(app, name)
 
-        param = QueueParameter(app, "param")
-        self.param = param
-        self.add_parameter(param)
+        self.param = QueueParameter(app, "param")
+        self.add_parameter(self.param)
 
-        self.queue_set = self.QueueInputSet(app, "queue_set", param)
+        self.queue_set = self.QueueInputSet(app, "queue_set", self.param)
         self.add_child(self.queue_set)
 
     def get(self, session):
         return self.param.get(session)
 
-    def get_args(self, session):
-        return self.frame.get_args(session)
-
-    def render_title(self, session, queue):
+    def render_title(self, session):
         return "Select the destination queue"
 
-    def render_inputs(self, session, queue):
+    def render_inputs(self, session):
         return self.queue_set.render(session)
 
     class QueueInputSet(RadioInputSet):
-        def get_args(self, session):
-            return self.frame.get_args(session)
-
-        def do_get_items(self, session, queue):
+        def do_get_items(self, session):
+            queue = self.form.queue.get(session)
             queue_list_full = sorted_by(list(queue.vhost.queues))
             delta = timedelta(minutes=10)
             queue_list = []
             for _queue in queue_list_full:
-                if (_queue.qmfUpdateTime > (datetime.now() - delta)) and (_queue.name != queue.name):
+                if (_queue.qmfUpdateTime > (datetime.now() - delta)) \
+                        and (_queue.name != queue.name):
                     queue_list.append(_queue)
             return queue_list
 
@@ -1040,45 +841,40 @@
             return queue.name or "<em>Default</em>"
 
         def render_item_checked_attr(self, session, queue):
-            return queue is self.param.get(session) and "checked=\"checked\"" or None
+            return queue is self.param.get(session) \
+                and "checked=\"checked\"" or None
 
-class QueueMoveMessages(CuminFieldForm):
-    def __init__(self, app, name):
-        super(QueueMoveMessages, self).__init__(app, name)
+class MoveMessagesForm(CuminFieldForm):
+    def __init__(self, app, name, task):
+        super(MoveMessagesForm, self).__init__(app, name)
 
-        self.move_to = QueueSelectField(app, "move_to", self)
-        self.add_field(self.move_to)
+        self.task = task
 
-        self.move_qty = MultiplicityField(app, "move_qty")
-        self.add_field(self.move_qty)
+        self.queue = QueueParameter(app, "queue")
+        self.add_parameter(self.queue)
 
-    def get_args(self, session):
-        return self.frame.get_args(session)
+        self.dest_queue = QueueSelectField(app, "dest", self)
+        self.add_field(self.dest_queue)
 
-    def render_title(self, session, queue):
-        return "Move Messages from  Queue '%s'" % queue.name
+        self.count = MultiplicityField(app, "count")
+        self.add_field(self.count)
 
-    def process_submit(self, session, queue):
-        request_qty = self.move_qty.get(session)
-        args = dict()
-        args["destQueue"] = self.move_to.get(session).name
-        args["srcQueue"] = queue.name
+    def process_submit(self, session):
+        self.check(session)
 
-        if request_qty == "all":
-            args["qty"] = 0
-        elif request_qty == "top":
-            args["qty"] = 1
-        elif request_qty == "N":
-            args["qty"] = self.move_qty.top_n.get_n_value(session)
-        else:
-            raise Exception("Wrong Value")
+        if not self.errors.get(session):
+            queue = self.queue.get(session)
+            dest_queue = self.dest_queue.get(session)
+            scount = self.count.get(session)
 
-        action = self.app.model.queue.move_messages
-        action.invoke(queue, args)
-        self.process_cancel(session, queue)
+            if scount == "all":
+                count = 0
+            elif scount == "top":
+                count = 1
+            elif scount == "N":
+                count = self.move_qty.top_n.get_n_value(session)
+            else:
+                raise Exception("Wrong Value")
 
-    def render_submit_content(self, session, queue):
-        return "Yes, Move Messages from Queue '%s'" % queue.name
-
-    def render_cancel_content(self, session, queue):
-        return "No, Cancel"
+            self.task.invoke(session, queue, dest_queue, count)
+            self.task.exit_with_redirect(session, queue)

Modified: mgmt/trunk/cumin/python/cumin/messaging/queue.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/messaging/queue.strings	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/messaging/queue.strings	2009-06-17 20:20:11 UTC (rev 3457)
@@ -34,38 +34,6 @@
 left outer join queue_stats as c on c.id = q.stats_curr_id
 {sql_where}
 
-[QueueSet.html]
-<form id="{id}" method="post" action="?">
-  <ul class="actions">
-    <li><a class="nav" href="{add_queue_url}">Add New Queue</a></li>
-  </ul>
-  <div class="rfloat">{phase}</div>
-  {unit}
-
-    <div class="sactions">
-      <h2>Act on Selected Queues:</h2>
-      {purge} {remove}
-    </div>
-    <table class="mobjects">
-      <thead>
-        <tr>
-          <th class="setnav">
-            <div class="rfloat">{page}</div>
-            {count}
-          </th>
-        </tr>
-        </thead>
-    </table>
-    <table class="mobjects{extra_class}">
-      <thead>
-        <tr>{headers}</tr>
-      </thead>
-      <tbody>{items}</tbody>
-    </table>
-
-  <div>{hidden_inputs}</div>
-</form>
-
 [TopQueueSet.sql]
 select
   q.id,
@@ -109,37 +77,6 @@
   </table>
 </div>
 
-[QueueBindingSet.html]
-<form id="{id}" method="post" action="?">
-  <div class="rfloat">{phase}</div>
-
-  <ul class="actions">
-    <li><a class="nav" href="{add_queue_binding_url}">Add Binding</a></li>
-  </ul>
-
-  <div class="sactions">
-    <h2>Act on Selected Bindings:</h2>
-    {remove}
-  </div>
-
-  <table class="mobjects">
-    <thead>
-      <tr>
-	<th class="setnav" colspan="{column_count}">
-	  <div class="rfloat">{page}</div>
-	  {count}
-	</th>
-      </tr>
-      <tr>{headers}</tr>
-    </thead>
-    <tbody>{items}</tbody>
-  </table>
-  <div>{hidden_inputs}</div>
-</form>
-
-[ExchangeBindingSet.item_html]
-<tr>{cells}</tr>
-
 [QueueStats.html]
 <ul class="radiotabs tabs">{tabs}</ul>
 <div class="radiotabs mode">{content}</div>
@@ -216,38 +153,6 @@
   </tbody>
 </table>
 
-[QueueConsumerSet.html]
-<div class="sactions">
-  <h2>Act on Selected Consumers:</h2>
-  <button>Start</button>
-  <button>Stop</button>
-  <button>Close</button>
-  <button>Throttle</button>
-</div>
-
-<table class="mobjects">
-  <tr>
-    <th><input type="checkbox"/></th>
-    <th>Name</th>
-    <th class="ralign" colspan="2">Msgs. Consumed</th>
-    <th class="ralign" colspan="2">Bytes Consumed</th>
-    <th class="ralign">Msgs. Unacked</th>
-  </tr>
-
-  {items}
-</table>
-
-[QueueConsumerSet.item_html]
-<tr>
-  <td><input type="checkbox"/></td>
-  <td>{item_name}</td>
-  <td class="ralign">{item_messages_consumed_rate}</td>
-  <td class="ralign">{item_messages_consumed}</td>
-  <td class="ralign">{item_bytes_consumed_rate}</td>
-  <td class="ralign">{item_bytes_consumed}</td>
-  <td class="ralign">{item_unacked_messages}</td>
-</tr>
-
 [BindSummaryPropertiesField.properties_html]
 <div class="properties" style="width:80%">
   <table class="PropertySet">

Modified: mgmt/trunk/cumin/python/cumin/model.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/model.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/model.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -16,6 +16,8 @@
 from parameters import *
 from util import *
 
+import wooly
+
 log = logging.getLogger("cumin.model")
 
 class CuminModel(object):
@@ -37,6 +39,9 @@
 
         self.frame = None
 
+        self.tasks = list()
+        self.task_invocations = list()
+
         # Messaging
 
         CuminBroker(self)
@@ -74,6 +79,10 @@
         CuminSlot(self)
         CuminGrid(self)
 
+        # Other
+
+        CuminSubject(self)
+
     def check(self):
         self.mint.check()
 
@@ -136,6 +145,13 @@
         for coll in Collector.select():
             return Pool(coll)
 
+    def get_session_by_object(self, object):
+        assert object
+
+        broker = self.mint.model.mintBrokersById[object.qmfBrokerId]
+
+        return broker.getAmqpSession()
+
 class CuminProperty(object):
     def __init__(self, cls, name):
         self.model = cls.model
@@ -398,17 +414,17 @@
         if conn:
             cursor = conn.cursor()
 
-            sql = """select max(qmf_update_time) as interval_end, 
+            sql = """select max(qmf_update_time) as interval_end,
             cast(avg(%s) as integer) as value,
             stddev(%s) as dev
             from %s
             where %s
             group by floor(extract(epoch from qmf_update_time) / %i)
             order by interval_end desc;""" % (field_name, field_name,
-                                              table_name, 
-                                              where, 
+                                              table_name,
+                                              where,
                                               interval)
-            
+
             cursor.execute(sql)
             return cursor.fetchall()
 
@@ -536,6 +552,9 @@
         self.stats = list()
         self.actions = list()
 
+        self.tasks = list()
+        self.tasks_by_class = dict()
+
         self.frame = None
 
         self.model.add_class(self)
@@ -556,7 +575,13 @@
         self.actions.append(action)
         setattr(self, action.name, action)
 
+    def add_task(self, task):
+        self.tasks.append(task)
+        self.tasks_by_class[task.__class__] = task
+
     def init(self):
+        log.debug("Initializing class %s", self)
+
         for prop in self.properties:
             prop.init()
 
@@ -569,6 +594,10 @@
         for action in self.actions:
             action.init()
 
+        for task in self.tasks:
+            log.debug("Initializing task %s", task)
+            task.init()
+
     def get_title(self, session):
         return "Object"
 
@@ -616,6 +645,9 @@
 
         writer.write("</%s>" % self.cumin_name)
 
+    def __str__(self):
+        return "%s.%s" % (self.__module__, self.__class__.__name__)
+
 class RemoteClass(CuminClass):
     def __init__(self, model, name, mint_class, mint_stats_class):
         super(RemoteClass, self).__init__(model, name, mint_class)
@@ -683,6 +715,8 @@
         stat.category = "general"
 
     def init(self):
+        super(CuminGrid, self).init()
+
         self.frame = self.model.frame.grid.pool
 
     def get_title(self, session):
@@ -804,6 +838,8 @@
         #action.summary = True
 
     def init(self):
+        super(CuminSystem, self).init()
+
         self.frame = self.model.frame.inventory.system
 
     def get_title(self, session):
@@ -893,18 +929,9 @@
         stat = self.StatusStat(self, "connection")
         stat.category = "status"
 
-        action = self.EngroupSet(self, "engroup_set")
-
-        action = self.AddExchange(self, "add_exchange")
-        action.summary = True
-
-        action = self.AddLink(self, "add_link")
-        action.summary = True
-
-        action = self.AddQueue(self, "add_queue")
-        action.summary = True
-
     def init(self):
+        super(CuminBroker, self).init()
+
         self.frame = self.model.frame.messaging.broker
 
     def get_icon_href(self, session):
@@ -943,94 +970,6 @@
         def rate_text(self, record):
             return ""
 
-    class EngroupSet(CuminSetAction):
-        def show(self, session, brokers):
-            frame = self.model.frame.messaging.brokers_group
-            frame.objects.set(session, brokers)
-            return frame.show(session)
-
-        def get_title(self, session):
-            return "Add to Group"
-
-    class AddExchange(CuminAction):
-        MSG_SEQUENCE = "qpid.msg_sequence"
-        IVE = "qpid.ive"
-
-        def get_title(self, session):
-            return "Add Exchange"
-
-        def get_verb(self, session):
-            return "Add"
-
-        def show(self, session, reg):
-            frame = self.cumin_class.show_object(session, reg)
-            frame.set_object(session, reg)
-            return frame.exchange_add.show(session)
-
-        def do_invoke(self, exchange, args, completion):
-            declArgs = {}
-
-            if args["sequence"]:
-                declArgs[self.MSG_SEQUENCE] = 1
-            if args["ive"]:
-                declArgs[self.IVE] = 1
-
-            session = self.get_session_by_object(exchange)
-            session.exchange_declare(exchange=exchange.name,
-                                     type=exchange.type,
-                                     durable=exchange.durable,
-                                     arguments=declArgs)
-
-            # if the above call fails, an exception is
-            # raised and we won't get here
-
-            completion("OK")
-
-    class AddLink(CuminAction):
-        def get_title(self, session):
-            return "Add Broker Link"
-
-        def get_verb(self, session):
-            return "Add"
-
-        def show(self, session, reg):
-            frame = self.cumin_class.show_object(session, reg)
-            frame.set_object(session, reg)
-            return frame.link_add.show(session)
-
-        def do_invoke(self, broker, args, completion):
-            host = args["host"]
-            port = args["port"]
-            durable = args["durable"]
-            username = args["username"]
-            password = args["password"]
-            transport = args["transport"]
-
-            if username == "anonymous":
-                authMechanism = "ANONYMOUS"
-            else:
-                authMechanism = "PLAIN"
-
-            broker.connect(self.mint.model, completion, host, port, durable,
-                           authMechanism, username, password, transport)
-
-    class AddQueue(CuminAction):
-        def get_title(self, session):
-            return "Add Queue"
-
-        def get_verb(self, session):
-            return "Add"
-
-        def get_description(self, session, vhost):
-            host = vhost.broker.system.nodeName
-            port = vhost.broker.port
-            return "Add Queue to %s:%i" % (host, port)
-
-        def show(self, session, reg):
-            frame = self.cumin_class.show_object(session, reg)
-            frame.set_object(session, reg)
-            return frame.queue_add.show(session)
-
 # XXX "do_" on this doesn't make sense
 def do_bind(session, queue_name, binding_info):
         for exchange in binding_info:
@@ -1214,84 +1153,17 @@
         stat.unit = "byte"
         stat.category = "io.durable"
 
-        action = self.Purge(self, "purge")
-        action.summary = True
-
-        action = self.Remove(self, "remove")
-        action.summary = True
-
-        action = self.Bind(self, "bind")
-        action.summary = True
-
     def init(self):
+        super(CuminQueue, self).init()
+
         self.frame = self.model.frame.messaging.broker.queue
 
-        action = self.MoveMessages(self, "move_messages")
-        action.summary = True
-
     def get_title(self, session):
         return "Queue"
 
     def get_icon_href(self, session):
         return "resource?name=queue-36.png"
 
-    class Purge(CuminAction):
-        def show(self, session, queue):
-            frame = self.model.frame.messaging.broker
-            frame.queue.set_object(session, queue)
-            return frame.queue.purge.show(session)
-
-        def get_title(self, session):
-            return "Purge"
-
-        def do_invoke(self, queue, args, completion):
-            queue.purge(self.mint.model, completion, args["request"])
-
-    class Remove(CuminAction):
-        def show(self, session, queue):
-            frame = self.model.frame.messaging.broker
-            frame.queue.set_object(session, queue)
-            return frame.queue.remove.show(session)
-
-        def get_title(self, session):
-            return "Remove"
-
-        def do_invoke(self, queue, args, completion):
-            session = self.get_session_by_object(queue)
-            session.queue_delete(queue=queue.name)
-
-            completion("OK")
-
-    class Bind(CuminAction):
-        def show(self, session, queue):
-            frame = self.model.frame.messaging.broker
-            frame.queue.set_object(session, queue)
-            return frame.queue.binding_add.show(session)
-
-        def get_title(self, session):
-            return "Bind"
-
-        def do_invoke(self, queue, args, completion):
-            session = self.get_session_by_object(queue)
-            binding_info = args['exchange_keys']
-            do_bind(session, queue.name, binding_info)
-
-            completion("OK")
-
-    class MoveMessages(CuminAction):
-        def show(self, session, queue):
-            frame = self.cumin_class.show_object(session, queue)
-            return frame.move_messages.show(session)
-
-        def get_title(self, session):
-            return "Move Messages"
-
-        def do_invoke(self, queue, args, completion):
-            broker = queue.vhost.broker
-            broker.queueMoveMessages(self.mint.model, completion,
-                                     args["srcQueue"], args["destQueue"],
-                                     args["qty"])
-
 class CuminExchange(RemoteClass):
     def __init__(self, model):
         super(CuminExchange, self).__init__(model, "exchange",
@@ -1353,10 +1225,9 @@
         stat.unit = "message"
         stat.category = "io"
 
-        action = self.Remove(self, "remove")
-        action.summary = True
-
     def init(self):
+        super(CuminExchange, self).init()
+
         self.frame = self.model.frame.messaging.broker.exchange
 
     def get_title(self, session):
@@ -1373,20 +1244,6 @@
         else:
             return "Default Exchange"
 
-    class Remove(CuminAction):
-        def get_title(self, session):
-            return "Remove"
-
-        def show(self, session, exchange):
-            frame = self.cumin_class.show_object(session, exchange)
-            return frame.remove.show(session)
-
-        def do_invoke(self, exchange, args, completion):
-            session = self.get_session_by_object(exchange)
-            session.exchange_delete(exchange=exchange.name)
-
-            completion("OK")
-
 class CuminBinding(RemoteClass):
     def __init__(self, model):
         super(CuminBinding, self).__init__(model, "binding",
@@ -1399,39 +1256,16 @@
         stat.title = "Msgs. Matched"
         stat.unit = "message"
 
-        action = self.Remove(self, "remove")
-        action.summary = True
-
     def get_title(self, session):
         return "Binding"
 
     def get_object_name(self, binding):
         return "between %s and %s" % (binding.exchange.name, binding.queue.name)
 
-    class Remove(CuminAction):
-        def get_title(self, session):
-            return "Remove"
-
-        def show(self, session, binding):
-            raise Exception("XXX")
-
-            frame = self.cumin_class.show_object(session, binding)
-            frame = frame.exchange.show_object(session, binding)
-            return frame.remove.show(session)
-
-        def do_invoke(self, binding, args, completion):
-            session = self.get_session_by_object(binding)
-            session.exchange_unbind(queue=binding.queue.name,
-                                    exchange=binding.exchange.name,
-                                    binding_key=binding.bindingKey)
-
-            completion("OK")
-
-
 class CuminRoute(RemoteClass):
     def __init__(self, model):
-        super(CuminRoute, self).__init__(model, "route",
-                                           Bridge, BridgeStats)
+        super(CuminRoute, self).__init__ \
+            (model, "route", Bridge, BridgeStats)
 
         prop = CuminProperty(self, "key")
         prop.title = "Route Key"
@@ -1448,27 +1282,12 @@
         prop = CuminProperty(self, "dest")
         prop.title = "Exchange"
 
-        action = self.Remove(self, "remove")
-        #action.summary = True
-
     def get_title(self, session):
         return "Route"
 
     def get_object_name(self, route):
         return route.src
 
-    class Remove(CuminAction):
-        def get_title(self, session):
-            return "Remove"
-
-        def show(self, session, peer):
-            frame = self.cumin_class.show_object(session, peer)
-            frame = frame.show_peer(session, peer)
-            return frame.remove.show(session)
-
-        def do_invoke(self, bridge, args, completion):
-            bridge.close(self.mint.model, completion)
-
 class CuminConnection(RemoteClass):
     def __init__(self, model):
         super(CuminConnection, self).__init__(model, "connection",
@@ -1511,10 +1330,9 @@
         stat.unit = "frame"
         stat.category = "io"
 
-        action = self.Close(self, "close")
-        action.summary = True
-
     def init(self):
+        super(CuminConnection, self).init()
+
         self.frame = self.model.frame.messaging.broker.connection
 
     def get_title(self, session):
@@ -1526,28 +1344,6 @@
     def get_object_name(self, conn):
         return conn.address
 
-    class Close(CuminAction):
-        def show(self, session, conn):
-            frame = self.cumin_class.show_object(session, conn)
-            return frame.show_close(session)
-
-        def get_title(self, session):
-            return "Close"
-
-        def do_invoke(self, conn, args, completion):
-            session_ids = set()
-
-            for broker in self.mint.model.mintBrokersByQmfBroker:
-                session_ids.add(broker.getSessionId())
-
-            for sess in conn.sessions:
-                if sess.name in session_ids:
-                    raise Exception \
-                        ("Cannot close management connection %s" % \
-                             conn.address)
-
-            conn.close(self.mint.model, completion)
-
 class CuminSession(RemoteClass):
     def __init__(self, model):
         super(CuminSession, self).__init__(model, "session",
@@ -1575,8 +1371,6 @@
         stat.title = "Attached"
         stat.category = "general"
 
-        self.Close(self, "close")
-        self.Detach(self, "detach")
         self.ResetLifespan(self, "resetLifespan")
         self.SolicitAck(self, "solicitAck")
 
@@ -1656,13 +1450,9 @@
         stat.unit = "byte"
         stat.category = "io"
 
-        action = self.Close(self, "close")
-        action.summary = True
-
-        action = self.Bridge(self, "bridge")
-        action.summary = True
-
     def init(self):
+        super(CuminLink, self).init()
+
         self.frame = self.model.frame.messaging.broker.link
 
     def get_title(self, session):
@@ -1671,43 +1461,6 @@
     def get_object_name(self, link):
         return "%s:%d" % (link.host, link.port)
 
-    class Bridge(CuminAction):
-        def show(self, session, link):
-            frame = self.cumin_class.show_object(session, link)
-            frame.set_object(session, link)
-            return frame.show_bridge_add(session)
-
-        def get_title(self, session):
-            return "Add Route"
-
-        def get_verb(self, session):
-            return "Add Route"
-
-        def do_invoke(self, link, args, completion):
-            durable = args["durable"]
-            src = args["exchange"]
-            dest = args["exchange"] # see qpid-route
-            key = args["key"]
-            tag = args["tag"]
-            dynamic = args["dynamic"]
-            excludes = args["excludes"]
-            sync = args["sync"]
-
-            link.bridge(self.mint.model, completion,
-                        durable, src, dest, key,
-                        tag, excludes, False, False, dynamic, sync)
-
-    class Close(CuminAction):
-        def show(self, session, link):
-            frame = self.cumin_class.show_object(session, link)
-            return frame.remove.show(session)
-
-        def get_title(self, session):
-            return "Close"
-
-        def do_invoke(self, link, args, completion):
-            link.close(self.mint.model, completion)
-
 class CuminBrokerStoreModule(RemoteClass):
     def __init__(self, model):
         super(CuminBrokerStoreModule, self).__init__ \
@@ -1852,6 +1605,8 @@
         action = self.RemoveSet(self, "remove_set")
 
     def init(self):
+        super(CuminManagementServer, self).init()
+
         self.frame = self.model.frame.messaging.broker # XXX
 
     def get_title(self, session):
@@ -1900,22 +1655,9 @@
         super(CuminBrokerGroup, self).__init__ \
             (model, "broker_group", BrokerGroup)
 
-        action = self.Add(self, "add")
-        action.title = "Add"
-        action.navigable = False
-
-        action = self.Edit(self, "edit")
-        action.title = "Edit"
-        action.summary = True
-
-        action = self.Remove(self, "remove")
-        action.title = "Remove"
-        action.summary = True
-
-        action = self.RemoveSet(self, "remove_set")
-        action.title = "Remove"
-
     def init(self):
+        super(CuminBrokerGroup, self).init()
+
         self.frame = self.model.frame.messaging.broker_group
 
     def get_title(self, session):
@@ -1924,64 +1666,6 @@
     def get_icon_href(self, session):
         return "resource?name=group-36.png"
 
-    class Add(CuminAction):
-        def show(self, session, object):
-            frame = self.cumin_class.show_object(session, object)
-            return frame.add.show(session)
-
-        def do_invoke(self, object, args, completion):
-            assert object is None
-
-            try:
-                object = self.cumin_class.mint_class(**args)
-
-                completion("OK")
-
-                return object
-            except Exception, e:
-                log.exception("Action failed")
-                completion(e.message or "failed")
-
-    class Edit(CuminAction):
-        def show(self, session, object):
-            frame = self.cumin_class.show_object(session, object)
-            return frame.edit.show(session)
-
-        def do_invoke(self, object, args, completion):
-            try:
-                object.set(**args)
-                object.syncUpdate()
-                completion("OK")
-            except Exception, e:
-                completion(e.message or "failed")
-
-    class Remove(CuminAction):
-        def show(self, session, object):
-            frame = self.model.frame.messaging.broker_group
-            frame.object.set(session, object)
-            return frame.remove.show(session)
-
-        def do_invoke(self, object, args, completion):
-            try:
-                object.destroySelf();
-                object.syncUpdate()
-                completion("OK")
-            except Exception, e:
-                completion(e.message or "failed")
-
-    class RemoveSet(CuminSetAction):
-        def show(self, session, groups):
-            frame = self.model.frame.messaging.broker_groups_remove
-            frame.objects.set(session, groups)
-            return frame.show(session)
-
-        def do_invoke(self, groups, args, completion):
-            for group in groups:
-                group.destroySelf()
-                group.syncUpdate()
-
-            completion("OK")
-
 class Pool(object):
     def __init__(self, collector):
         assert collector
@@ -2019,6 +1703,8 @@
         self.status.navigable = False
 
     def init(self):
+        super(CuminPool, self).init()
+
         self.frame = self.model.frame.grid.pool
 
     def get_title(self, session):
@@ -2117,6 +1803,8 @@
         action.title = "Set Limit"
 
     def init(self):
+        super(CuminLimit, self).init()
+
         self.frame = self.model.frame.grid.pool.limit
 
     def get_title(self, session):
@@ -2179,6 +1867,8 @@
         action.summary = True
 
     def init(self):
+        super(CuminJobGroup, self).init()
+
         self.frame = self.model.frame.grid.pool.job_group
 
     def get_object_title(self, session, group):
@@ -2401,6 +2091,8 @@
         action.navigable = False
 
     def init(self):
+        super(CuminJob, self).init()
+
         self.frame = self.model.frame.grid.pool.job
 
     def get_title(self, session):
@@ -2627,6 +2319,8 @@
         action.navigable = False
 
     def init(self):
+        super(CuminScheduler, self).init()
+
         self.frame = self.model.frame.grid.pool.scheduler
 
     def get_title(self, session):
@@ -2671,6 +2365,8 @@
         stat.title = "Held Jobs"
 
     def init(self):
+        super(CuminSubmitter, self).init()
+
         self.frame = self.model.frame.grid.pool.submitter
 
     def get_title(self, session):
@@ -2727,6 +2423,8 @@
         stat.title = "Total Slots"
 
     def init(self):
+        super(CuminCollector, self).init()
+
         self.frame = self.model.frame.grid.pool.collector
 
     def get_title(self, session):
@@ -2805,6 +2503,8 @@
         action.navigable = False
 
     def init(self):
+        super(CuminNegotiator, self).init()
+
         self.frame = self.model.frame.grid.pool.negotiator
 
     def get_title(self, session):
@@ -2854,37 +2554,13 @@
                 self.lim["timeout"] = True
             return self.lim
 
-class ModelPage(Page):
-    def __init__(self, app, name):
-        super(ModelPage, self).__init__(app, name)
+class CuminSubject(CuminClass):
+    def __init__(self, model):
+        super(CuminSubject, self).__init__(model, "subject", Subject)
 
-        self.__class = CuminClassParameter(app, "class")
-        self.add_parameter(self.__class)
+    def get_title(self, session):
+        return "Subject"
 
-        param = IntegerParameter(app, "param")
-        self.add_parameter(param)
-
-        self.__ids = ListParameter(app, "id", param)
-        self.add_parameter(self.__ids)
-
-    def get_content_type(self, session):
-        return Page.xml_content_type
-
-    def do_render(self, session):
-        writer = Writer()
-        writer.write(Page.xml_1_0_declaration)
-
-        cls = self.__class.get(session)
-        objects = list()
-
-        if cls:
-            for id in self.__ids.get(session):
-                objects.append(cls.mint_class.get(id))
-
-        self.app.model.write_xml(session, writer, objects)
-
-        return writer.to_string()
-
 class CallPage(Page):
     def __init__(self, app, name):
         super(CallPage, self).__init__(app, name)
@@ -2927,3 +2603,185 @@
         writer.write("<data>")
         writer.write(data)
         writer.write("</data>")
+
+class Task(object):
+    def __init__(self, app, cls):
+        self.app = app
+        self.cls = cls
+
+        self.form = None
+
+        self.aggregate = False
+
+        if self.cls:
+            if self.__class__ not in self.cls.tasks_by_class:
+                self.cls.add_task(self)
+
+    # make this take app? XXX
+    def init(self):
+        if self.form:
+            self.app.form_page.modes.add_mode(self.form)
+        else:
+            log.debug("Task %s has no form associated with it", self)
+
+    def get_title(self, session):
+        raise Exception("Not implemented")
+
+    def is_enabled(self, session, object):
+        return True
+
+    def get_description(self, session, object):
+        verb = self.get_title(session)
+
+        if object is None:
+            text = verb
+        else:
+            cls = self.cls.get_title(session)
+            obj = self.cls.get_object_name(object)
+            text = "%s %s '%s'" % (verb, cls, obj)
+
+        return text
+
+    def get_href(self, session, object):
+        return self.enter(session, object).marshal()
+
+    def enter(self, session, object):
+        nsession = wooly.Session(self.app.form_page)
+
+        self.form.return_url.set(nsession, session.marshal())
+        self.form.show(nsession)
+
+        self.do_enter(nsession, object)
+
+        return nsession
+
+    def do_enter(self, session, object):
+        pass
+
+    def exit_with_redirect(self, session, object):
+        osession = self.exit(session, object)
+        self.form.page.set_redirect_url(session, osession.marshal())
+
+    def exit(self, session, object):
+        url = self.form.return_url.get(session)
+        osession = wooly.Session.unmarshal(self.app, url)
+
+        self.do_exit(osession, object)
+
+        return osession
+
+    def do_exit(self, session, object):
+        pass
+
+    def invoke(self, session, object, *args, **kwargs):
+        invoc = self.start(session, object)
+
+        try:
+            result = self.do_invoke(session, object, *args, **kwargs)
+
+            self.end(invoc)
+        except Exception, e:
+            self.exception(invoc, e)
+
+    def do_invoke(self, *args, **kwargs):
+        raise Exception("Not implemented")
+
+    def start(self, session, object):
+        now = datetime.now()
+        subject = session.user_session.subject
+
+        invoc = TaskInvocation(self, subject, object)
+        invoc.status = invoc.PENDING
+        invoc.start_time = now
+        invoc.last_change_time = now
+
+        self.app.model.task_invocations.append(invoc)
+
+        return invoc
+
+    def end(self, invoc):
+        now = datetime.now()
+
+        invoc.status = invoc.OK
+        invoc.end_time = now
+        invoc.last_change_time = now
+
+    def exception(self, invoc, e):
+        now = datetime.now()
+
+        invoc.status = invoc.FAILED
+        invoc.end_time = now
+        invoc.last_change_time = now
+        invoc.exception = e
+
+        log.exception(e)
+
+    def __str__(self):
+        return "%s.%s" % (self.__module__, self.__class__.__name__)
+
+class SetTask(Task):
+    def __init__(self, app, cls):
+        super(SetTask, self).__init__(app, cls)
+
+        self.item_task = None
+        self.aggregate = True
+
+    def init(self):
+        super(SetTask, self).init()
+
+        if not self.item_task:
+            raise Exception("Task %s has no item task" % self)
+
+    def get_title(self, session):
+        return self.item_task.get_title(session)
+
+    def get_description(self, session, objects):
+        verb = self.item_task.get_title(session)
+        cls = self.cls.get_title(session)
+        count = len(objects)
+        return "%s %i %s" % (verb, count, conjugate(cls, count))
+
+    def invoke(self, session, objects, *args, **kwargs):
+        for object in objects:
+            self.item_task.invoke(session, object, *args, **kwargs)
+
+class QmfTask(Task):
+    def invoke(self, session, object, *args, **kwargs):
+        invoc = self.start(session, object)
+
+        def completion(status_code, output_args):
+            invoc.last_change_time = datetime.now()
+
+            if status_code == 0:
+                invoc.status = invoc.OK
+            else:
+                invoc.status = invoc.FAILED
+
+            invoc.status_code = status_code
+            invoc.output_args = output_args
+
+        try:
+            self.do_invoke(completion, session, object, *args, **kwargs)
+        except Exception, e:
+            self.exception(invoc, e)
+
+    def do_invoke(self, completion, session, object, *args, **kwargs):
+        raise Exception("Not implemented")
+
+class TaskInvocation(object):
+    PENDING = "pending"
+    FAILED = "failed"
+    OK = "ok"
+
+    def __init__(self, task, subject, object):
+        self.task = task
+        self.subject = subject
+        self.object = object
+        self.start_time = None
+        self.end_time = None
+        self.last_change_time = None
+        self.status = None
+        self.exception = None
+
+        self.status_code = None
+        self.output_args = None

Modified: mgmt/trunk/cumin/python/cumin/modelwidgets.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/modelwidgets.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/modelwidgets.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -1,8 +1,8 @@
 from wooly import *
 from wooly.forms import *
 
+from widgets import *
 from model import *
-from widgets import *
 from util import *
 
 from wooly.widgets import Link
@@ -17,16 +17,14 @@
         self.object = object
 
     def process_submit(self, session, *args):
-        branch = session.branch()
         object = None
 
         if self.object:
             object = self.object.get(session)
 
-        self.action.show(branch, object)
+        url = self.action.get_href(session, object)
+        self.page.set_redirect_url(session, url)
 
-        self.page.set_redirect_url(session, branch.marshal())
-
     def render_content(self, session, *args):
         return self.action.get_title(session)
 
@@ -179,3 +177,36 @@
         href = self.cumin_class.get_object_href_by_id(session, id)
 
         return fmt_link(href, name)
+
+class TaskLink(Link):
+    def __init__(self, app, name, task, param):
+        super(TaskLink, self).__init__(app, name)
+
+        self.task = task
+        self.param = param
+
+    def get(self, session):
+        if self.param:
+            return self.param.get(session)
+
+    def render_href(self, session):
+        return self.task.get_href(session, self.get(session))
+
+    def render_content(self, session):
+        return self.task.get_title(session)
+
+class TaskButton(FormButton):
+    def __init__(self, app, name, task, set_param):
+        super(TaskButton, self).__init__(app, name)
+
+        self.task = task
+        self.set_param = set_param
+
+    def process_submit(self, session):
+        items = self.set_param.get(session)
+
+        href = self.task.get_href(session, items)
+        self.page.set_redirect_url(session, href)
+
+    def render_content(self, session):
+        return self.task.get_title(session)

Modified: mgmt/trunk/cumin/python/cumin/page.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/page.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/page.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -8,18 +8,11 @@
 from widgets import *
 from util import *
 
-from messaging.queue import TopQueueSet
-from grid.job import TopJobSet
-from inventory.system import TopSystemSet
+# XXX For the top-N sets; these should go away soon
+import messaging
+import grid
+import inventory
 
-from messaging import MessagingFrame, MessagingView
-from grid import GridFrame, GridView
-from inventory import InventoryFrame, InventoryView
-
-# We have a wooly.widgets.Link and a mint.Link; we want the former
-from wooly.widgets import Link
-from wooly import Session
-
 strings = StringCatalog(__file__)
 
 class MainPage(CuminPage, ModeSet):
@@ -30,9 +23,6 @@
         self.add_mode(self.main)
         self.set_default_frame(self.main)
 
-        # for form in model actions:
-        #    seld.add_mode(form)
-
     def render_title(self, session):
         return "MRG Management"
 
@@ -43,15 +33,6 @@
         self.home = HomeFrame(app, "home")
         self.add_tab(self.home)
 
-        self.messaging = MessagingFrame(app, "messaging")
-        self.add_tab(self.messaging)
-
-        self.grid = GridFrame(app, "grid")
-        self.add_tab(self.grid)
-
-        self.inventory = InventoryFrame(app, "inventory")
-        self.add_tab(self.inventory)
-
 class HomeFrame(CuminFrame):
     def __init__(self, app, name):
         super(HomeFrame, self).__init__(app, name)
@@ -79,9 +60,7 @@
         self.add_tab(ManagementServerSet(app, "servers"))
 
     def render_change_password_href(self, session):
-        branch = session.branch()
-        self.frame.change_password.show(branch)
-        return branch.marshal()
+        return self.app.change_password.get_href(session, None)
 
     class Heading(CuminHeading):
         def render_title(self, session):
@@ -97,13 +76,13 @@
             notice = self.ManagementServerNotice(app, "notice")
             self.add_child(notice)
 
-            queues = TopQueueSet(app, "queues")
+            queues = messaging.queue.TopQueueSet(app, "queues")
             self.add_child(queues)
 
-            jobs = TopJobSet(app, "jobs")
+            jobs = grid.job.TopJobSet(app, "jobs")
             self.add_child(jobs)
 
-            systems = TopSystemSet(app, "systems")
+            systems = inventory.system.TopSystemSet(app, "systems")
             self.add_child(systems)
 
         def render_title(self, session):

Modified: mgmt/trunk/cumin/python/cumin/parameters.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/parameters.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/parameters.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -1,6 +1,6 @@
 from wooly import *
 from mint import *
-import model 
+import model
 
 class CuminObjectParameter(Parameter):
     def __init__(self, app, name, cumin_class):
@@ -19,6 +19,13 @@
     def do_marshal(self, cls):
         return cls.cumin_name
 
+class BindingParameter(Parameter):
+    def do_unmarshal(self, string):
+        return Binding.get(int(string))
+
+    def do_marshal(self, binding):
+        return str(binding.id)
+
 class BrokerGroupParameter(Parameter):
     def do_unmarshal(self, string):
         if string == "__none__":
@@ -97,6 +104,16 @@
     def get_id(self):
         return self.id
 
+class LinkParameter(Parameter):
+    def do_unmarshal(self, string):
+        return Link.get(int(string))
+
+    def do_marshal(self, link):
+        return str(link.id)
+
+class PeerParameter(LinkParameter):
+    pass
+
 class LimitParameter(Parameter):
     def do_unmarshal(self, string):
         return Limit(string)
@@ -104,13 +121,10 @@
     def do_marshal(self, limit):
         return limit.id
 
-class PeerParameter(Parameter):
-    def do_unmarshal(self, string):
-        return Link.get(int(string))
+# XXX marked for death
+class PeerParameter(LinkParameter):
+    pass
 
-    def do_marshal(self, peer):
-        return str(peer.id)
-
 class PoolParameter(Parameter):
     def do_unmarshal(self, string):
         for coll in Collector.selectBy(Pool=string):
@@ -128,6 +142,13 @@
     def do_marshal(self, queue):
         return str(queue.id)
 
+class RouteParameter(Parameter):
+    def do_unmarshal(self, string):
+        return Bridge.get(int(string))
+
+    def do_marshal(self, route):
+        return str(route.id)
+
 class SchedulerParameter(Parameter):
     def do_unmarshal(self, string):
         return Scheduler.get(int(string))
@@ -145,7 +166,7 @@
 class SlotParameter(Parameter):
     def do_unmarshal(self, string):
         return Slot.get(int(string))
-        
+
     def do_marshal(self, slot):
         return str(slot.id)
 
@@ -156,6 +177,13 @@
     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))

Modified: mgmt/trunk/cumin/python/cumin/usergrid/main.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/usergrid/main.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/usergrid/main.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -1,14 +1,25 @@
 from wooly import *
+from wooly.widgets import *
 
 from cumin.widgets import *
 from cumin.util import *
 
 from submission import *
 
-from wooly.widgets import *
+from wooly.widgets import Link
 
 strings = StringCatalog(__file__)
 
+class UserGridModule(object):
+    def init(self, app):
+        app.user_grid_page = MainPage(app, "usergrid.html")
+        app.add_page(app.user_grid_page)
+
+        app.form_page.submission_add = SubmissionForm(app, "submissionadd")
+        app.form_page.modes.add_mode(app.form_page.submission_add)
+
+module = UserGridModule()
+
 class MainPage(CuminPage, ModeSet):
     def __init__(self, app, name):
         super(MainPage, self).__init__(app, name)
@@ -57,8 +68,12 @@
         def render_content(self, session):
             return "Create New Submission"
 
-        def edit_session(self, session):
-            self.page.submission_add.show(session)
+        def render_href(self, session):
+            form = self.app.form_page.submission_add
+            sess = Session(self.app.form_page)
+            form.show(sess)
+            form.return_url.set(session, session.marshal())
+            return sess.marshal()
 
 class TemplateFrame(Widget):
     def render_title(self, session):

Modified: mgmt/trunk/cumin/python/cumin/usergrid/submission.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/usergrid/submission.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/usergrid/submission.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -7,7 +7,7 @@
 #strings = StringCatalog(__file__)
 log = logging.getLogger("cumin.usergrid.submission")
 
-class SubmissionForm(CuminFieldForm):
+class SubmissionForm(FieldSubmitForm):
     def __init__(self, app, name):
         super(SubmissionForm, self).__init__(app, name)
 
@@ -98,3 +98,10 @@
         class UseCloud(CheckboxFieldOption):
             def render_title(self, session):
                 return "Use cloud"
+
+class SubmissionAdd(SubmissionForm):
+    def __init__(self, app, name):
+        super(SubmissionAdd, self).__init__(app, name)
+
+    def process_submit(self, session):
+        pass

Modified: mgmt/trunk/cumin/python/cumin/util.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/util.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/util.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -16,6 +16,16 @@
 def ess(num, ending="s"):
     return num != 1 and ending or ""
 
+def plural(noun):
+    # XXX handle exceptions here
+    return "%ss" % noun
+
+def conjugate(noun, count):
+    if count != 1:
+        noun = plural(noun)
+
+    return noun
+
 def nvl(expr1, expr2):
     if expr1 == None:
         return expr2
@@ -44,7 +54,7 @@
 
 def is_active(obj):
     delTime = obj._get_qmfDeleteTime()
-    if not delTime:
+    if not delTime and obj.statsCurr:
         updateTime = obj.statsCurr.qmfUpdateTime
         delta = timedelta(minutes=10)
         # mirroring logic in widgets/PhaseSwitch.get_sql_constraint

Modified: mgmt/trunk/cumin/python/cumin/widgets.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/widgets.py	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/widgets.py	2009-06-17 20:20:11 UTC (rev 3457)
@@ -55,6 +55,9 @@
 
         self.__frame_tmpl = Template(self, "frame_html")
 
+        self.tasks = TaskInvocationSet(app, "tasks")
+        self.add_child(self.tasks)
+
         self.actions = ActionInvocationSet(app, "actions")
         self.add_child(self.actions)
 
@@ -62,6 +65,7 @@
         self.add_child(self.heartbeat)
 
     def do_process(self, session, *args):
+        self.tasks.process(session)
         self.actions.process(session)
         self.heartbeat.process(session)
 
@@ -149,7 +153,7 @@
 
         cls = self.app.model.get_class_by_object(obj)
         if cls:
-            return fmt_shorten(cls.get_object_title(session, obj), 16, 4)
+            return cls.get_object_title(session, obj)
 
 class CuminView(Widget):
     def __init__(self, app, name):
@@ -188,89 +192,93 @@
         args = frame.get_args(session)
         return frame.render_title(session, *args)
 
-class FormHelp(Widget):
+class BackgroundInclude(Widget):
     def __init__(self, app, name):
-        super(FormHelp, self).__init__(app, name)
+        super(BackgroundInclude, self).__init__(app, name)
 
-class CuminForm(Form):
-    def __init__(self, app, name):
-        super(CuminForm, self).__init__(app, name)
+        self.data = None
+        self.type = None
 
-        self.__cancel = self.Cancel(app, "cancel")
-        self.__cancel.set_tab_index(201)
-        self.add_child(self.__cancel)
+    def render_data(self, session):
+        return self.data
 
-        self.__submit = self.Submit(app, "submit")
-        self.__submit.set_tab_index(200)
-        self.add_child(self.__submit)
+    def render_type(self, session):
+        return self.type
 
-        self.__help = self.Help(app, "help")
-        self.add_child(self.__help)
+class CuminFormPage(HtmlPage):
+    def __init__(self, app, name):
+        super(CuminFormPage, self).__init__(app, name)
 
-    def submit(self, session):
-        self.__submit.set(session, True)
+        background = self.Background(app, "background")
+        background.type = "text/html"
+        self.add_child(background)
 
-    def cancel(self, session):
-        self.__cancel.set(session, True)
+        self.modes = ModeSet(app, "modes")
+        self.add_child(self.modes)
 
-    def get_modal(self, session):
-        return True
+        # XXX look into this
+        # for form in model tasks:
+        #    self.add_mode(form)
 
-    def do_process(self, session, *args):
-        self.page.set_modal(session, self.get_modal(session))
+    class Background(BackgroundInclude):
+        def render_data(self, session):
+            form = self.parent.modes.mode.get(session)
+            url = form.return_url.get(session)
+            return url
 
-        if self.__cancel.get(session):
-            self.__cancel.set(session, False)
+class CuminTaskForm(SubmitForm, Frame):
+    def __init__(self, app, name, task):
+        super(CuminTaskForm, self).__init__(app, name)
 
-            self.process_cancel(session, *args)
-        elif self.__submit.get(session):
-            self.__submit.set(session, False)
+        self.task = task
+        self.object = None
 
-            self.process_submit(session, *args)
-        else:
-            self.process_display(session, *args)
+    def init(self):
+        super(CuminTaskForm, self).init()
 
-    def process_cancel(self, session, *args):
-        self.page.set_redirect_url(session, self.get_origin(session))
+        assert isinstance(self.object, Parameter)
 
-    def process_submit(self, session, *args):
-        pass
+    def process_submit(self, session):
+        object = self.object.get(session)
 
-    def process_display(self, session, *args):
-        pass
+        self.task.invoke(session, object)
+        self.task.exit_with_redirect(session, object)
 
-    def render_cancel_content(self, session, *args):
-        return "Cancel"
+    def render_submit_content(self, session):
+        return self.task.get_title(session)
 
-    def render_submit_content(self, session, *args):
-        return "Submit"
+    def render_title(self, session):
+        return "Confirm"
 
-    def render_form_error(self, session, *args):
-        pass
+    def render_content(self, session):
+        object = self.object.get(session)
+        return "%s?" % self.task.get_description(session, object)
 
-    class Cancel(FormButton):
-        def render_class(self, session, *args):
-            return "cancel"
+class FormHelp(Widget):
+    def __init__(self, app, name):
+        super(FormHelp, self).__init__(app, name)
 
-        def render_content(self, session, *args):
-            cargs = self.parent.get_args(session)
-            return self.parent.render_cancel_content(session, *cargs)
+class CuminForm(SubmitForm):
+    def __init__(self, app, name):
+        super(CuminForm, self).__init__(app, name)
 
-    class Submit(FormButton):
-        def render_class(self, session, *args):
-            return "submit"
+        self.help = self.Help(app, "help")
+        self.add_child(self.help)
 
-        def render_content(self, session, *args):
-            cargs = self.parent.get_args(session)
-            return self.parent.render_submit_content(session, *cargs)
+    def get_modal(self, session):
+        return True
 
+    def render_form_error(self, session, *args):
+        pass
+
     class Help(FormHelp):
         def render_help_href(self, session, *args):
             return "resource?name=help.html#%s" % self.path
 
-class CuminFieldForm(CuminForm, FieldForm, Frame):
-    def render_body(self, session, *args):
-        return self.render_fields(session, *args)
+class CuminFieldForm(FieldSubmitForm, Frame):
+    def validate(self, session):
+        self.check(session)
+        return self.errors.get(session)
 
 class CuminConfirmForm(CuminForm):
     def __init__(self, app, name):
@@ -514,16 +522,32 @@
                  x.get_enabled(session, object))
                 for x in cls.actions if x.navigable and not x.aggregate]
 
+class CuminTasks(ActionSet):
+    def __init__(self, app, name, object):
+        super(CuminTasks, self).__init__(app, name)
+
+        self.object = object
+
+    def do_get_items(self, session):
+        if self.object:
+            object = self.object.get(session)
+            cls = self.app.model.get_class_by_object(object)
+
+            return [(x.get_href(session, object),
+                     x.get_title(session, object),
+                     True)
+                    for x in cls.tasks if not x.aggregate]
+
 class CuminDetails(Widget):
-    def __init__(self, app, name):
+    # XXX make the object arg mandatory
+
+    def __init__(self, app, name, object=None):
         super(CuminDetails, self).__init__(app, name)
 
-        props = CuminProperties(app, "properties")
-        self.add_child(props)
+        self.add_child(CuminProperties(app, "properties"))
+        self.add_child(CuminActions(app, "actions"))
+        self.add_child(CuminTasks(app, "tasks", object))
 
-        actions = CuminActions(app, "actions")
-        self.add_child(actions)
-
     def render_title(self, session):
         return "Details"
 
@@ -544,6 +568,9 @@
         actions = self.SummaryActions(app, "actions")
         self.add_child(actions)
 
+        tasks = self.SummaryTasks(app, "tasks")
+        self.add_child(tasks)
+
     def get_args(self, session):
         return self.frame.get_args(session)
 
@@ -571,6 +598,16 @@
                 return [(x.get_href(session, object), x.get_title(session), x.get_enabled(session, object))
                     for x in cls.actions if x.summary and x.navigable]
 
+    class SummaryTasks(CuminActions):
+        def do_get_items(self, session, object):
+            cls = self.app.model.get_class_by_object(object)
+            if cls:
+                return [(x.get_href(session, object),
+                         x.get_title(session),
+                         x.is_enabled(session, object))
+                        for x in cls.tasks
+                        if not x.aggregate and x.form]
+
 class StateSwitch(ItemSet):
     def __init__(self, app, name):
         super(StateSwitch, self).__init__(app, name)
@@ -747,7 +784,7 @@
 class TruncatableTable(SqlTable):
     def render_extra_class(self, session, *args):
         """ if any columns are truncate, table needs a fixed layout """
-        return True in [True for x in self.columns 
+        return True in [True for x in self.columns
                         if isinstance(x, ClientTruncateColumn)] and " truncate" or ""
 
 class TopTable(TruncatableTable):
@@ -832,6 +869,24 @@
 
         return constraints
 
+class CuminSelectionTable(CuminTableWithControls, Form):
+    def __init__(self, app, name, item_param):
+        super(CuminSelectionTable, self).__init__(app, name)
+
+        self.selection = ListParameter(app, "item", item_param)
+        self.add_parameter(self.selection)
+
+        self.checkboxes = CheckboxColumn(app, "id", self.selection)
+        self.add_column(self.checkboxes)
+
+        self.buttons = WidgetSet(app, "buttons")
+        self.buttons.html_class = "buttons"
+        self.add_child(self.buttons)
+
+        self.links = WidgetSet(app, "links")
+        self.links.html_class = "actions" # XXX fix this
+        self.add_child(self.links)
+
 class NullSortColumn(SqlTableColumn):
     def get_order_by_sql(self, session):
         key = self.get_column_key(session)
@@ -893,90 +948,6 @@
     def render_item_content(self, session, group):
         return group.name
 
-class BindingSet(CuminTable):
-    def __init__(self, app, name):
-        super(BindingSet, self).__init__(app, name)
-
-        self.ids = CheckboxIdColumn(app, "id")
-        self.add_column(self.ids)
-
-        col = self.QNameColumn(app, "q_id")
-        col.visible = False
-        self.add_column(col)
-
-        col = self.ENameColumn(app, "e_id")
-        col.visible = False
-        self.add_column(col)
-
-        col = self.KeyColumn(app, "key")
-        self.add_column(col)
-
-        col = self.RateColumn(app, "rate")
-        col.alignment = "right"
-        self.add_column(col)
-
-        col = self.MatchedColumn(app, "matched")
-        col.alignment = "right"
-        self.add_column(col)
-
-        self.phase = PhaseSwitch(app, "phase")
-        self.add_child(self.phase)
-
-    def get_args(self, session):
-        return self.frame.get_args(session)
-
-    def get_sql_values(self, session, obj):
-        return {"id": obj.id}
-
-    class QNameColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            return "Queue"
-
-        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))
-
-    class ENameColumn(SqlTableColumn):
-        def render_title(self, session, data):
-            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)
-
-            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 CheckboxInputColumn(FormInput, ItemTableColumn):
     def __init__(self, app, name, item_param):
         super(CheckboxInputColumn, self).__init__(app, name, None)
@@ -996,6 +967,25 @@
 
         return html % (name, id, attr, click)
 
+class CheckboxColumn(FormInput, SqlTableColumn):
+    def __init__(self, app, name, param):
+        super(CheckboxColumn, self).__init__(app, name, param)
+
+        self.header_class = CheckboxColumnHeader
+
+    def do_render(self, session, data, disabled=False):
+        name = self.param.path
+        id = data[self.get_column_key(session)]
+        attr = id in self.param.get(session) and "checked=\"checked\"" or ""
+        disa = disabled and "disabled=\"disabled\"" or ""
+        click = self.parent.update_enabled and \
+            "onclick=\"cumin.clickTableCheckbox(this, '%s')\"" % name or ""
+
+        html = """<td><input type="checkbox" name="%s" value="%s" %s/></td>"""
+        attrs = " ".join((attr, disa, click))
+
+        return html % (name, str(id), attrs)
+
 class CheckboxIdColumn(FormInput, SqlTableColumn):
     def __init__(self, app, name):
         super(CheckboxIdColumn, self).__init__(app, name, None)
@@ -1034,13 +1024,17 @@
         self.param = ListParameter(app, "id", item)
         self.add_parameter(self.param)
 
-class CheckboxIdColumnHeader(ItemTableColumnHeader):
+class CheckboxColumnHeader(ItemTableColumnHeader):
     def render_form_id(self, session, *args):
         return self.column.form.path
 
     def render_elem_name(self, session, *args):
         return self.column.param.path
 
+ # XXX remove this
+class CheckboxIdColumnHeader(CheckboxColumnHeader):
+    pass
+
 class FilteredCheckboxIdColumn(CheckboxIdColumn):
     def __init__(self, app, name, form, callback=None):
         super(FilteredCheckboxIdColumn, self).__init__(app, name)
@@ -1053,6 +1047,18 @@
         return super(FilteredCheckboxIdColumn, self).do_render(session, data,
             disabled=disabled)
 
+class FilteredCheckboxColumn(CheckboxColumn):
+    def __init__(self, app, name, param, callback):
+        super(FilteredCheckboxColumn, self).__init__(app, name, param)
+
+        # call back that returns True if the checkbox is to be disabled
+        self.__callback = callback
+
+    def do_render(self, session, data):
+        disabled = self.__callback and self.__callback(session, data) or False
+        return super(FilteredCheckboxColumn, self).do_render \
+            (session, data, disabled=disabled)
+
 class NameField(StringField):
     def __init__(self, app, name):
         super(NameField, self).__init__(app, name)
@@ -1067,6 +1073,23 @@
     def set_required(self, required):
         self.required = required
 
+    def check(self, session):
+        name = self.get(session)
+
+        if name == "" and self.required:
+            self.form.errors.add(session, MissingValueError())
+        else:
+            for char in self.illegal_chars:
+                if char in name:
+                    msg = "The name contains illegal characters"
+
+                    if self.legal_chars_desc:
+                        msg = msg + "; " + self.legal_chars_desc
+
+                    self.form.errors.add(session, FormError(msg))
+
+                    break
+
     def do_validate(self, session, errors):
         name = self.get(session)
 
@@ -1116,6 +1139,25 @@
     def set_object_attr(self, attr):
         self.__object = attr
 
+    def check(self, session):
+        super(UniqueNameField, self).check(session)
+
+        name = self.get(session)
+
+        if name:
+            args = {self.__field: name, }
+            results = self.__class.selectBy(**args)
+
+            if self.__object:
+                object = self.__object.get(session)
+
+                if object:
+                    results = results.filter(self.__class.q.id != object.id)
+
+                if results.count() > 0:
+                    self.form.errors.add(session, DuplicateValueError())
+
+    # XXX get rid of this
     def do_validate(self, session, errors):
         super(UniqueNameField, self).do_validate(session, errors)
 
@@ -1269,7 +1311,7 @@
     def elem_id(self, session):
         return self.render_id(session)
 
-class MoreFieldSet(FieldForm, FormField):
+class MoreFieldSet(FormFieldSet, FormField):
     """ Displays a button that opens and closes a set of fields
 
         Used in a FieldForm as a FormField. Instead of calling
@@ -1316,3 +1358,32 @@
 class Wait(Widget):
     pass
 
+class TaskInvocationSet(ItemSet):
+    def __init__(self, app, name):
+        super(TaskInvocationSet, self).__init__(app, name)
+
+        self.html_class = TaskInvocationSet.__name__
+        self.update_enabled = True
+
+    def do_get_items(self, session):
+        now = secs(datetime.now())
+
+        invocs = sorted_by(self.app.model.task_invocations, "last_change_time")
+        invocs = [x for x in invocs if now - secs(x.last_change_time) < 10]
+
+        return invocs
+
+    def do_render(self, session):
+        items = self.get_items(session)
+
+        if items:
+            return super(TaskInvocationSet, self).do_render(session)
+
+    def render_item_content(self, session, item):
+        description = item.task.get_description(session, item.object)
+        status = item.status
+        exc = str(item.exception)
+        code = str(item.status_code)
+        outs = str(item.output_args)
+
+        return " | ".join((description, status, exc, code, outs))

Modified: mgmt/trunk/cumin/python/cumin/widgets.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/widgets.strings	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/python/cumin/widgets.strings	2009-06-17 20:20:11 UTC (rev 3457)
@@ -1,24 +1,7 @@
-[BindingSet.sql]
-select
-  b.id,
-  b.exchange_id as e_id,
-  b.queue_id as q_id,
-  b.binding_key as key
-from binding as b
-left outer join binding_stats as c on c.id = b.stats_curr_id
-{sql_where}
-{sql_orderby}
-{sql_limit}
-
-[BindingSet.count_sql]
-select count(*)
-from binding as b
-left outer join binding_stats as c on c.id = b.stats_curr_id
-{sql_where}
-
 [CuminMainView.css]
 #head {
     padding: 0;
+    margin: 0;
     min-height: 2.75em;
     background: #f9f9ff url("resource?name=shade.png") repeat-x scroll bottom center;
 }
@@ -30,6 +13,7 @@
 
 #tabs {
     padding: 0 0 0 2em;
+    margin: 0;
     position: relative;
     top: 1.175em;
 }
@@ -79,6 +63,7 @@
   z-index: 1;
   min-height: 20em;
 }
+
 #messages {
     -moz-border-radius: 0.35em;
     border: 1px solid #886;
@@ -174,10 +159,14 @@
 </div>
 <div id="body">
 
-<div id="messages" style="display: {action_display};"><p title="close" onclick="cumin.hideActions()">x</p>{actions}</div>
+  {tasks}
+
+  <div id="messages" style="display: {action_display};"><p title="close" onclick="cumin.hideActions()">x</p>{actions}</div>
+
   {heartbeat}
 
-{content}</div>
+  {content}
+</div>
 
 <div id="foot"/>
 
@@ -387,6 +376,8 @@
     <td>
       <h2>Actions</h2>
       {actions}
+      <h2>Tasks</h2>
+      {tasks}
     </td>
   </tr>
   </tbody>
@@ -410,7 +401,7 @@
 
 [CuminSummary.css]
 div.CuminSummary {
-    width: 36em;
+    width: 50em;
     margin: 0 0 2em 0;
     min-height: 6em;
 }
@@ -434,7 +425,7 @@
 
 div.CuminSummary div.actions {
     font-size: 0.9em;
-    width: 16em;
+    width: 20em;
     float: right;
 }
 
@@ -453,6 +444,7 @@
 
   <div class="actions">
     {actions}
+    {tasks}
   </div>
 
   <div class="properties">
@@ -596,11 +588,29 @@
 <th {class_attr}>{content}</th>
 
 [CuminTable.css]
-table.mobjects th.setnav {
-  font-size: 0.9em;
+div.CuminTable th.setnav {
+    font-size: 0.9em;
 }
 
-table.mobjects td {
+div.CuminTable ul.switches,
+div.CuminTable ul.filters,
+div.CuminTable ul.buttons {
+    list-style: none;
+    display: inline;
+    padding: 0;
+    margin: 0;
+}
+
+div.CuminTable ul.filters {
+    float: right;
+}
+
+div.CuminTable ul.buttons li {
+    margin: 0 0.4em 0 0;
+    display: inline;
+}
+
+table.CuminTable td {
   text-overflow:ellipsis;
 }
 
@@ -615,13 +625,13 @@
 }
 
 [CuminTable.javascript]
-    wooly.addPageUpdateListener( cumin.restoreTableCheckboxes );
+wooly.addPageUpdateListener(cumin.restoreTableCheckboxes);
 
 [CuminTable.html]
-<table id="{id}" class="mobjects{extra_class}">
+<table id="{id}" class="CuminTable mobjects {extra_class}">
   <thead>
     <tr>
-      <th class="setnav">
+      <th class="setnav" colspan="{column_count}">
         <div class="rfloat">{page}</div>
         {count}
       </th>
@@ -632,11 +642,11 @@
 </table>
 
 [CuminTableWithControls.html]
-<div class="CuminTable">
+<div id="{id}" class="CuminTable {extra_class}">
+  {filters}
+
   {switches}
 
-  {filters}
-
   <table class="mobjects">
     <thead>
       <tr>
@@ -654,6 +664,40 @@
   </table>
 </div>
 
+[CuminSelectionTable.html]
+<div id="{id}" class="CuminTable {extra_class}">
+  {links}
+
+  {filters}
+
+  {switches}
+
+  <form method="post" action="?">
+    <div class="sactions">
+      <h2>Act on selection:</h2>
+      {buttons}
+    </div>
+
+    <table class="mobjects">
+      <thead>
+        <tr>
+          <th class="setnav" colspan="{column_count}">
+            <div class="rfloat">{page}</div>
+
+            {count}
+          </th>
+        </tr>
+
+        <tr>{headers}</tr>
+      </thead>
+
+      <tbody>{items}</tbody>
+    </table>
+
+    <div>{hidden_inputs}</div>
+  </form>
+</div>
+
 [TableHeader.css]
 th.selected a {
     color: black;
@@ -667,12 +711,15 @@
 [TableHeader.item_html]
 <th class="{item_class}"><a href="{item_href}">{item_content}</a></th>
 
-[CheckboxIdColumnHeader.css]
+[CheckboxColumn.html]
+<td><input type="checkbox" name="{name}"
+
+[CheckboxColumnHeader.css]
 th.chk_box {
     width: 2em;
 }
 
-[CheckboxIdColumnHeader.javascript]
+[CheckboxColumnHeader.javascript]
 function checkAll(control_id, form_id, elem_name) {
     control = document.getElementById(control_id)
     form = document.getElementById(form_id);
@@ -687,7 +734,7 @@
     }
 }
 
-[CheckboxIdColumnHeader.html]
+[CheckboxColumnHeader.html]
 <th class="chk_box"><input id="{id}" type="checkbox" name="all"
     onclick="cumin.clickTableCheckbox(this, 'all'); checkAll('{id}', '{form_id}', '{elem_name}')" value="all"/></th>
 
@@ -698,6 +745,36 @@
    <input type="text" name="{arg_name}" value="{arg_value}" /> messages
 </div>
 
+[BackgroundInclude.css]
+object.BackgroundInclude {
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+    position: absolute;
+    top: 0;
+    z-index: -1;
+}
+
+[BackgroundInclude.html]
+<object class="BackgroundInclude" data="{data}" type="{type}"/>
+
+[CuminFormPage.css]
+body {
+    background-color: #000;
+    z-index: 0;
+}
+
+body > object.BackgroundInclude {
+    opacity: 0.2;
+}
+
+body > form {
+    z-index: 1;
+    width: 48em;
+    margin: 6em auto;
+    background-color: #fff;
+}
+
 [FormHelp.javascript]
 function help_window(href) {
    var left = screen.availWidth / 2;
@@ -743,49 +820,57 @@
 
 
 [MoreFieldSet.html]
-<div class="more_field_set">
-  <ul class="actions">
-    <li>
-      <a class="nav" href="#" onclick="toggle_more_fieldset(); return false;"><span id="button_{id}">{state_text}</span></a>
-    </li>
-  </ul>{open}
-  <div id="{id}" class="more_inputs" style="display:{open_display};">
-    {inputs}
-  </div>
-</div><!-- end of "more_field_set" -->
-<script type="text/javascript">
-    function toggle_more_fieldset() {
-        var oDiv = document.getElementById("{id}");
-        var oButton = document.getElementById("button_{id}");
-        var oState = document.forms[0].elements["{open_path}"];
+<tr>
+  <th>
+    <div class="title">{title}</div>
+    <div class="help">{help}</div>
+  </th>
+  <td>
+    <div class="more_field_set">
+      <ul class="actions">
+        <li>
+          <a class="nav" href="#" onclick="toggle_more_fieldset(); return false;"><span id="button_{id}">{state_text}</span></a>
+        </li>
+      </ul>{open}
+      <div id="{id}" class="more_inputs" style="display:{open_display};">
+        {inputs}
+      </div>
+    </div><!-- end of "more_field_set" -->
+    <script type="text/javascript">
+        function toggle_more_fieldset() {
+            var oDiv = document.getElementById("{id}");
+            var oButton = document.getElementById("button_{id}");
+            var oState = document.forms[0].elements["{open_path}"];
 
-        if (oDiv) {
-            if (oDiv.style.display == "none") {
-                oDiv.style.display = "block";
+            if (oDiv) {
+                if (oDiv.style.display == "none") {
+                    oDiv.style.display = "block";
 
-                if (oButton) {
-                    oButton.innerHTML = "{less_text}";
-                }
+                    if (oButton) {
+                        oButton.innerHTML = "{less_text}";
+                    }
 
-                if (oState) {
-                    oState.value = "t";
-                }
-            } else {
-                oDiv.style.display = "none";
+                    if (oState) {
+                        oState.value = "t";
+                    }
+                } else {
+                    oDiv.style.display = "none";
 
-                if (oButton) {
-                    oButton.innerHTML = "{more_text}";
-                }
+                    if (oButton) {
+                        oButton.innerHTML = "{more_text}";
+                    }
 
-                if (oState) {
-                    oState.value = "f";
+                    if (oState) {
+                        oState.value = "f";
+                    }
+
+                    document.forms[0].submit();
                 }
-
-                document.forms[0].submit();
             }
         }
-    }
-</script>
+    </script>
+  </td>
+</tr>
 
 [StartStopStatus.css]
 div.status_time {
@@ -831,3 +916,12 @@
 [Wait.html]
   <div class="loading" style="visibility:visible;"><span>Loading...</span></div>
 
+[TaskInvocationSet.css]
+ul.TaskInvocationSet {
+    background-color: #fe0;
+    padding: 1em 2em;
+    -moz-border-radius: 0.5em;
+    -webkit-border-radius: 0.5em;
+    margin: 0.5em auto 1em auto;
+    width: 80%;
+}

Modified: mgmt/trunk/cumin/resources/app.css
===================================================================
--- mgmt/trunk/cumin/resources/app.css	2009-06-17 19:44:31 UTC (rev 3456)
+++ mgmt/trunk/cumin/resources/app.css	2009-06-17 20:20:11 UTC (rev 3457)
@@ -6,16 +6,6 @@
     background-color: #f7f7f7;
 }
 
-ul {
-    list-style: none;
-    padding: 0;
-    margin: 0;
-}
-
-ul > ul {
-    padding: 1em;
-}
-
 h2 {
     margin: 0;
 }
@@ -52,9 +42,11 @@
 ul.actions li {
     display: inline;
 }
+
 ul.actions.disabled a {
     color: #666666;
 }
+
 ul.actions.disabled li a.nav:before {
     color: #666666;
 }
@@ -62,6 +54,7 @@
 ul.actions.disabled a:hover {
     background-color: #f7f7f7;
 }
+
 a.nav:before {
     content: "\00BB \0020";
     font-weight: bold;
@@ -114,6 +107,7 @@
     color: #bbb;
     border: 1px solid #ddd;
 }
+
 button.disabled:hover {
     background-color: #f7f7f7;
 }
@@ -355,6 +349,12 @@
     content: "";
 }
 
+ul.slist {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+}
+
 ul.slist a:before {
     content: url(resource?name=radio-button.png);
     vertical-align: -10%;




More information about the rhmessaging-commits mailing list