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

rhmessaging-commits at lists.jboss.org rhmessaging-commits at lists.jboss.org
Wed Jun 30 16:28:47 EDT 2010


Author: eallen
Date: 2010-06-30 16:28:45 -0400 (Wed, 30 Jun 2010)
New Revision: 4059

Added:
   mgmt/newdata/cumin/model/qmf.xml
   mgmt/newdata/cumin/python/cumin/qmfadapter.py
Modified:
   mgmt/newdata/cumin/model/rosemary.xml
   mgmt/newdata/cumin/python/cumin/grid/job.py
   mgmt/newdata/cumin/python/cumin/grid/job.strings
   mgmt/newdata/cumin/python/cumin/grid/submission.py
   mgmt/newdata/cumin/python/cumin/inventory/system.py
   mgmt/newdata/cumin/python/cumin/main.py
   mgmt/newdata/cumin/python/cumin/model.py
   mgmt/newdata/cumin/python/cumin/objectframe.py
   mgmt/newdata/cumin/python/cumin/objectselector.py
   mgmt/newdata/cumin/python/cumin/objecttask.py
   mgmt/newdata/cumin/python/cumin/parameters.py
Log:
Moving jobs under submissions and using new schema methods

Added: mgmt/newdata/cumin/model/qmf.xml
===================================================================
--- mgmt/newdata/cumin/model/qmf.xml	                        (rev 0)
+++ mgmt/newdata/cumin/model/qmf.xml	2010-06-30 20:28:45 UTC (rev 4059)
@@ -0,0 +1,13 @@
+<schema package="qmf.response" type="transient">
+  <class name="GetJobSummaries">
+    <property name="ProcId" type="sstr"/>
+    <property name="Args" type="sstr"/>
+    <property name="QDate" type="absTime"/>
+    <property name="Cmd" type="sstr"/>
+    <property name="ClusterId" type="sstr"/>
+    <property name="JobStatus" type="sstr"/>
+    <property name="EnteredCurrentStatus" type="absTime"/>
+    <property name="GlobalJobId" type="sstr"/>
+  </class>
+
+</schema>

Modified: mgmt/newdata/cumin/model/rosemary.xml
===================================================================
--- mgmt/newdata/cumin/model/rosemary.xml	2010-06-30 19:10:29 UTC (rev 4058)
+++ mgmt/newdata/cumin/model/rosemary.xml	2010-06-30 20:28:45 UTC (rev 4059)
@@ -1,4 +1,21 @@
 <model>
+  <package name="qmf.response">
+    <class name="GetJobSummaries">
+      <property name="GlobalJobId">
+        <title>Job Id</title>
+      </property>
+
+      <property name="Cmd">
+        <title>Command</title>
+      </property>
+
+      <property name="JobStatus">
+        <title>Job Status</title>
+      </property>
+
+    </class>
+  </package>
+
   <package name="org.apache.qpid.broker">
     <class name="Binding">
       <property name="bindingKey">

Modified: mgmt/newdata/cumin/python/cumin/grid/job.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/grid/job.py	2010-06-30 19:10:29 UTC (rev 4058)
+++ mgmt/newdata/cumin/python/cumin/grid/job.py	2010-06-30 20:28:45 UTC (rev 4059)
@@ -7,446 +7,208 @@
 from wooly.tables import *
 from time import time
 from cumin.widgets import *
+from cumin.model import FetchJobAd, FetchJobOutput
+from cumin.objecttask import *
 from cumin.parameters import *
 from cumin.stat import *
 from cumin.formats import *
 from cumin.util import *
-from cumin.model import FetchJobAd, FetchJobOutput
+from cumin.qmfadapter import *
+
 import main
 
 strings = StringCatalog(__file__)
 log = logging.getLogger("cumin.job")
 
-class JobSet(CuminQMFSelectionTable):
+class JobFrame(ObjectFrame):
     def __init__(self, app, name, submission):
-        item = JobParameter(app, "item")
-        super(JobSet, self).__init__(app, name, item)
+        cls = app.model.com_redhat_grid.Submission
 
-        self.submission = submission
+        super(JobFrame, self).__init__(app, name, cls)
 
-        self.defer_enabled = True
-        self.update_enabled = True
+        self.view = JobView(app, "view", self.object)
+        self.replace_child(self.view)
 
-        col = self.CustomIdColumn(app, "name")
-        self.add_column(col)
-        self.set_default_column(col)
+        # view or edit
+        self.ads = JobAdModes(app, "ads")
+        self.view.add_tab(self.ads)
 
-        col = self.CommandColumn(app, "Cmd")
-        self.add_column(col)
+        self.view.add_tab(JobOutput(app, "output"))
 
-        col = self.StatusColumn(app, "JobStatus")
-        self.add_column(col)
+        self.job_id = Parameter(app, "job_id")
+        self.add_parameter(self.job_id)
 
-        col = self.ArgsColumn(app, "Args")
-        col.align = "right"
-        self.add_column(col)
+        JobHold(app, self)
+        JobRelease(app, self)
+        JobRemove(app, self)
 
-        self.phase = JobStatusSwitch(app, "phase")
-        self.filters.add_child(self.phase)
+        # task not visible on page. used for edit submit
+        self.set_ad_task = JobSetAttribute(app, self)
 
-        multi_param = self.SubmissionJob(app, "subjob", self.selection, submission)
+    def get_title(self, session):
+        job_id = self.job_id.get(session)
+        return "Job %s" % job_id
 
-        task = main.module.job_set_hold
-        button = TaskButton(app, "hold", task, multi_param)
-        self.buttons.add_child(button)
+    def show_ads_edit(self, session):
+        self.ads.editor.show(session)
 
-        task = main.module.job_set_release
-        button = TaskButton(app, "release", task, multi_param)
-        self.buttons.add_child(button)
+    def show_ads_view(self, session):
+        self.ads.viewer.show(session)
 
-        task = main.module.job_set_remove
-        button = TaskButton(app, "remove", task, multi_param)
-        self.buttons.add_child(button)
+    def get_submission(self, session, id):
+        return self.get_object(session, id)
 
-    def do_get_items(self, session, *args):
-        submission = self.submission.get(session)
+    def get_job_server(self, session, id):
+        submission = self.get_submission(session, id)
 
-        jobs = self.app.model.get_submission_jobs(submission)
+        cls = self.app.model.com_redhat_grid.JobServer
+        return cls.get_object(session.cursor, _id=submission._jobserverRef_id)
 
-        if jobs is None:
-            return ()
+    def get_scheduler(self, session, id):
+        job_server = self.get_job_server(session, id)
 
-        return jobs
+        pool = job_server.Pool
+        machine = job_server.Machine
 
-    def filter_item(self, session, item):
-        state = self.phase.get(session)
+        cls = self.app.model.com_redhat_grid.Scheduler
+        return cls.get_object(session.cursor, Pool=pool, Machine=machine)
 
-        if state == "All":
-            return True
+    def get_href(self, session, id, job_id):
+        branch = session.branch()
 
-        item_state = item["JobStatus"]["VALUE"]
-        item_state = JobStatusInfo.get_status_string(int(item_state))
+        self.id.set(branch, id)
+        self.job_id.set(branch, job_id)
+        self.view.show(branch)
 
-        return state == item_state
+        return branch.marshal()
 
-    def render_deferred_content(self, session, *args):
-        return "Loading..."
+class JobView(ObjectView):
+    def add_details_tab(self):
+        pass
 
-    def get_phase_title(self, session):
-        state = self.phase.get(session)
-        return self.phase.get_title(state)
+    def render_title(self, session):
+        return self.frame.get_title(session)
 
-    class ArgsColumn(ItemTableColumn):
-        def render_title(self, session):
-            return "Arguments"
+class JobSummariesAdapter(ObjectQmfAdapter):
+    def get_data(self, values, options):
+        submission = values["obj"]
+        results = self.app.model.get_submission_job_summaries(submission)
+        rows = self.process_results(results)
+        return rows 
 
-    class CustomIdColumn(ItemTableColumn):
-        def render_title(self, session):
-            return "ID"
+    def get_count(self, values):
+        submission = values["obj"]
+        results = self.app.model.get_submission_job_summaries(submission)
+        return results and len(results) or 0
 
-        def render_content(self, session, data):
-            id = data[self.name]
+class JobSelector(ObjectSelector):
+    def __init__(self, app, name, submission):
+        cls = app.model.qmf_response.GetJobSummaries
+        adapter = JobSummariesAdapter(app, cls)
+        super(JobSelector, self).__init__(app, name, cls, adapter)
 
-            if id:
-                job = Identifiable(data["id"])
-                scheduler = self.parent.submission.get(session).scheduler
-                href = self.page.main.grid.pool.job.get_href(session, job, scheduler)
-                job_id, hash, num = id.rpartition('#')
-                return fmt_link(href, job_id, link_title=id)
+        self.submission = submission
+        frame = "main.grid.pool.submission.job"
+        self.job_id_col = self.JobIdColumn(app, "job", cls.GlobalJobId, cls._id, frame)
+        self.add_column(self.job_id_col)
 
-    class StatusColumn(ItemTableColumn):
-        def render_title(self, session):
-            return "Status"
+        status_column = self.Status(app, "status", cls.JobStatus)
+        self.add_column(status_column)
 
-        def render_content(self, session, data):
-            stat = data[self.name]
-            return JobStatusInfo.get_status_string(int(stat))
+        self.add_attribute_column(cls.Cmd)
 
-    class CommandColumn(ItemTableColumn):
-        def render_title(self, session):
-            return "Command"
+        # CluserId.ProdId is the string needed to get the job ad from the jobserver
+        self.proc_id_column = ObjectAttributeColumn(app, cls.ProcId.name, cls.ProcId)
+        self.proc_id_column.visible = False
+        self.add_column(self.proc_id_column)
 
-        def render_content(self, session, data):
-            return fmt_shorten(escape_entity(data[self.name]), 0, 16)
+        self.cluster_id_column = ObjectAttributeColumn(app, cls.ClusterId.name, cls.ClusterId)
+        self.cluster_id_column.visible = False
+        self.add_column(self.cluster_id_column)
 
-    class SubmissionJob(object):
-        def __init__(self, app, name, job, submission):
-            self.job = job
-            self.submission = submission
-
-        def get(self, session):
-            return (self.job.get(session), self.submission.get(session))
-
-class JobTab(JobSet):
-    def __init__(self, app, name, pool):
-        super(JobTab, self).__init__(app, name, pool)
-
-        self.job_search = self.JobSearch(app, "job_search")
-        self.add_child(self.job_search)
-
-    def render_title(self, session, *args):
+    def render_title(self, session):
         return "Jobs"
 
-    def find_job(self, session):
-        search_term = self.job_search.get(session)
+    def get_data_values(self, session):
+        values = super(JobSelector, self).get_data_values(session)
 
-        if search_term:
-            object = self.frame.get_object(session)
-            rows = self.find_item(session, object)
+        submission = self.submission.get(session)
 
-            if rows:
-                try:
-                    first = rows[0]
-                    job = Identifiable(first["id"])
-                    href = self.page.main.grid.pool.job.get_href(session, job)
-                    self.page.redirect.set(session, href)
-                except:
-                    self.job_search.set_not_found(session, search_term)
-            else:
-                self.job_search.set_not_found(session, search_term)
+        if submission:
+            values['obj'] = submission
+            values['args'] = ()
+        return values
 
-            self.job_search.set(session, None)
+    class JobIdColumn(ObjectLinkColumn):
+        def render_cell_href(self, session, record):
+            index = self.parent.cluster_id_column.field.index
+            cluster_id = record[index]
+            index = self.parent.proc_id_column.field.index
+            proc_id = record[index]
+            job_id = "%d.%d" % (cluster_id, proc_id)
+            frame = self.page.page_widgets_by_path[self.frame_path]
 
-    def render_find_sql_where(self, session, *args):
-        return "j.custom_id = %(custom_id)s"
+            submission = self.parent.submission.get(session)
+            return frame.get_href(session, submission._id, job_id)
 
-    def get_find_sql_values(self, session, pool):
-        return {"custom_id": self.job_search.get(session)}
-        #return {"custom_id": self.job_search.get(session), "pool": pool.id}
+    class Status(ObjectAttributeColumn):
+        def render_cell_content(self, session, record):
+            status = self.field.get_content(session, record)
+            return JobStatusInfo.get_status_string(status)
 
-    def get_full_item_count(self, session, *args):
-        # request item count with phase=="a"
-        return self.get_item_count(session, 'a')
-
-    def get_visible_columns(self, session):
-        return self.get_request_visible_columns(session, ["custom_group", "scheduler", "submitter"])
-
-    def render_count(self, session, *args):
-        count = self.get_item_count(session, *args)
-        phase = self.get_phase_title(session)
-        if phase == "All":
-            phase = ""
-        return "%i %s %s" % (count, phase, count == 1 and "Job" or "Jobs")
-
-    class JobSearch(StringInput):
-        """ displays the input box and button used to search for job id """
-
-        def __init__(self, app, name):
-            super(JobTab.JobSearch, self).__init__(app, name)
-
-            self.__go = self.JobSearchButton(app, "go")
-            self.add_child(self.__go)
-
-            self.not_found = Attribute(app, "error")
-            self.add_attribute(self.not_found)
-
-        def set_not_found(self, session, value):
-            self.not_found.set(session, value)
-
-        def render_search_prompt(self, session):
-            not_found = self.not_found.get(session)
-            return not_found and "%s Not Found" % escape_entity(not_found) or self.render_search_default_prompt(session)
-
-        def render_search_default_prompt(self, session):
-            return "Enter Job ID"
-
-        def render_search_class(self, session):
-            return self.not_found.get(session) and "search_error" or " "
-
-        def find_job(self, session):
-            self.parent.find_job(session)
-
-        class JobSearchButton(FormButton):
-            def process_submit(self, session):
-                self.parent.find_job(session)
-
-            def render_content(self, session):
-                return "Go"
-
-class JobGroupFrame(CuminFrame):
-    def __init__(self, app, name, pool):
-        super(JobGroupFrame, self).__init__(app, name)
-
-        self.object = JobGroupParameter(app, "id")
-        self.add_parameter(self.object)
-
-        self.view = JobGroupView(app, "view", pool)
-        self.add_mode(self.view)
-
-        # XXX link instead
-        #self.system = SystemFrame(app, "system")
-        #self.add_mode(self.system)
-
-class JobGroupView(CuminView):
-    def __init__(self, app, name, pool):
-        super(JobGroupView, self).__init__(app, name, pool)
-
-        self.tabs = TabbedModeSet(app, "tabs")
-        self.add_child(self.tabs)
-
-        self.tabs.add_tab(JobGroupStats(app, "stats"))
-        self.tabs.add_tab(JobGroupJobSet(app, "jobs", pool))
-        #self.tabs.add_tab(JobGroupSystemSet(app, "systems"))
-        #self.tabs.add_tab(CuminDetails(app, "details"))
-
-class JobGroupStats(Widget):
+class JobAdModes(ModeSet):
     def __init__(self, app, name):
-        super(JobGroupStats, self).__init__(app, name)
+        super(JobAdModes, self).__init__(app, name)
 
-        #stats = JobGroupStatSet(app, "general", "general")
-        #self.add_child(stats)
+        self.viewer = JobAdsViewer(app, "viewer")
+        self.add_mode(self.viewer)
 
-    def render_title(self, session):
-        return "Statistics"
+        self.editor = JobAdsEditor(app, "editor")
+        self.add_mode(self.editor)
 
-"""
-class JobGroupStatSet(StatSet):
-    def __init__(self, app, name, category):
-        super(JobGroupStatSet, self).__init__(app, name, category)
-
-        self.jobs = Attribute(app, "jobs")
-        self.add_attribute(self.jobs)
-
-    def process(self, session):
-        group = self.frame.get_args(session)[0]
-        if group:
-            where_group = "custom_group = '%s'" % group.get_id()
-            value = Job.select(where_group).count()
-            self.jobs.set(session, value)
-        super(JobGroupStatSet, self).process(session)
-
-    def render_rate_text(self, session, args):
-        return "Percentage"
-
-    def render_item_name(self, session, args):
-        stat, object = args
-        return stat.name
-
-    def render_item_value(self, session, args):
-        stat, group = args
-        if stat.name == "Jobs":
-            return self.jobs.get(session)
-        else:
-            state = stat.name
-            value = self.get_value(group, state)
-        return value
-
-    def get_value(self, group, state):
-        where_group = "custom_group = '%s' \
-            and job_status = %i" % (group.get_id(),
-                                    JobStatusInfo.get_status_int(state))
-        return Job.select(where_group).count()
-
-    def render_item_rate(self, session, args):
-        stat, group = args
-        jobs = self.jobs.get(session)
-        state = stat.name
-        if stat.name == "Jobs":
-            value = jobs
-        else:
-            value = self.get_value(group, state)
-        percent = (value*1.0) / (jobs*1.0) * 100.0
-        return jobs and "%2.1f" % percent or "-"
-"""
-
-class JobGroupJobSet(JobTab):
-    def __init__(self, app, name, pool):
-        super(JobGroupJobSet, self).__init__(app, name, pool)
-
-    def get_visible_columns(self, session):
-        return self.get_request_visible_columns(session, ["scheduler", "submitter"])
-
     def render_title(self, session):
-        group = self.frame.object.get(session)
-        where_group = "custom_group = '%s'" % group.get_id()
-        return "Jobs %s" % fmt_count(Job.select(where_group).count())
+        return "Attributes"
 
-    def render_sql_where(self, session):
-        group = self.frame.object.get(session)
-        phase_sql = self.get_phase_sql(session)
-        group_sql = "j.custom_group = '%s'" % group.get_id()
-        return "where %s" % " and ".join([phase_sql, group_sql])
-
-    def render_count(self, session):
-        group = self.frame.object.get(session)
-        str = super(JobGroupJobSet, self).render_count(session)
-        return "%s in Job Group '%s'" % (str, group.get_id())
-
-# class JobGroupSystemSet(SystemSet):
-#     def render_sql_where(self, session):
-#         subquery = """
-#             select 1
-#             from slot as l
-#             join job as j on j.custom_id = l.job_id
-#             where j.custom_group = %(id)s and l.system = s.node_name
-#         """
-
-#         return "where exists (%s)" % subquery
-
-#     def get_sql_values(self, session):
-#         group = self.frame.object.get(session)
-#         return {"id": group.id}
-
-class JobFrame(CuminFrame):
-    def __init__(self, app, name):
-        super(JobFrame, self).__init__(app, name)
-
-        self.object = JobParameter(app, "id")
-        self.add_parameter(self.object)
-
-        self.scheduler = SchedulerParameter(app, "scheduler")
-        self.add_parameter(self.scheduler)
-
-        self.view = JobView(app, "view", self.object)
-        self.add_mode(self.view)
-
-        self.__edit_ads = JobAdsEditor(app, "editads", self.object)
-        self.add_mode(self.__edit_ads)
-
-    def show_ads_edit(self, session):
-        self.page.set_frame(session, self.__edit_ads)
-        return self.__edit_ads.show(session)
-
-    def show_job_group(self, session, jobgroup):
-        self.__job.set_object(session, jobgroup)
-        self.page.set_frame(session, self.__job)
-        self.__job.set_switch(session, "group")
-        return self.__job.show(session)
-
-    def get_href(self, session, job, scheduler):
-        branch = session.branch()
-        self.show_object(branch, job)
-        self.scheduler.set(session, scheduler)
-        return branch.marshal()
-
-class JobView(CuminView):
-    def __init__(self, app, name, job):
-        super(JobView, self).__init__(app, name, job)
-
-        self.tabs = TabbedModeSet(app, "tabs")
-        self.add_child(self.tabs)
-
-        self.tabs.add_tab(JobAdsViewer(app, "jobads", job))
-        self.tabs.add_tab(JobOutput(app, "output", job))
-        #self.tabs.add_tab(JobSystemSet(app, "systems", job))
-        #self.tabs.add_tab(CuminDetails(app, "details", job))
-
-# class JobSystemSet(SystemSet):
-#     def __init__(self, app, name, job):
-#         super(JobSystemSet, self).__init__(app, name)
-
-#         self.job = job
-
-#     def render_sql_where(self, session):
-#         return """
-#             where exists
-#               (select 1
-#                from slot
-#                where system = s.node_name and job_id = %(id)s)
-#         """
-
-#     def get_sql_values(self, session):
-#         job = self.job.get(session)
-#         return {"id": "%i.%i" % (job.ClusterId, job.ProcId)}
-
 class JobAdsSet(PropertySet):
     types = {0: "expression",
              1: "integer",
              2: "float",
              3: "string"}
-
-    def __init__(self, app, name, job):
+    def __init__(self, app, name):
         super(JobAdsSet, self).__init__(app, name)
 
-        self.job = job
+        self.items = Attribute(app, "cached_items")
+        self.add_attribute(self.items)
 
     def do_get_items(self, session):
-        job = self.job.get(session)
-        cls = self.app.model.get_class_by_object(job)
+        ad_list = self.items.get(session)
 
-        # XXX
-        job_ads = self.get_raw_ads(session, job, None)
+        if not ad_list:
+            ad_list = list()
+            id = self.frame.id.get(session)
+            job_server = self.frame.get_job_server(session, id)
+            job_id = self.frame.job_id.get(session)
 
-        return [self.gen_item(x, job_ads[x]["VALUE"], cls,
-                              dtype=self.types[job_ads[x]["TYPE"]])
-                for x in job_ads]
+            action = QmfCall(self.app, {'JobAd': {}})
+            ads = action.execute(job_server, "GetJobAd", job_id).data['JobAd']
+            cls = self.app.model.job_meta_data
+            ad_list = [self.gen_item(x, ads[x], cls, dtype=self.get_type(ads[x])) for x in ads]
 
-    def get_raw_ads(self, session, job, scheduler):
-        if not scheduler:
-            schedulers = Scheduler.select()
-            for sched in schedulers:
-                if sched.statsCurr:
-                    if sched.statsCurr.TotalRunningJobs:
-                        scheduler = sched
-                        break
+            self.items.set(session, ad_list)
 
-        if scheduler:
-            action = FetchJobAd(self.app)
-            return action.execute(scheduler, job.id).data['JobAd']
+        return ad_list
+
+    def get_type(self, value):
+        if isinstance(value, (int, long)):
+            type = "integer"
+        elif isinstance(value, float):
+            type = "float"
+        elif isinstance(value, dict):
+            type = "dict"
         else:
-            return () # XXX
+            type = "string"
+        return type
 
-    def gen_items(self, session, job, scheduler):
-        job_ads = self.get_raw_ads(session, job, scheduler)
-
-        cls = self.app.model.job
-        return [self.gen_item(x, job_ads[x]["VALUE"], cls, dtype=self.types[job_ads[x]["TYPE"]]) for x in job_ads]
-        # list of dictionaries
-        # each disctionary has:
-        # name:, value:, type: [, error:] [, property:] [,path:]
-        #return [self.gen_item(x, job_ads[x], cls) for x in job_ads]
-#
-
     def gen_item(self, name, value, cls, path=None, dtype=None,
                  error=None, orig=None):
         """ Generate a dict with name, value, type, error, path,
@@ -497,8 +259,11 @@
             property = item["property"]
             if property.renderer:
                 value = property.renderer(session, value)
-        ret = escape_entity(str(value))
-        return self.insert_breaks(ret)
+        if item["type"] == "dict":
+            return value
+        else:
+            ret = escape_entity(str(value))
+            return self.insert_breaks(ret)
 
     def insert_breaks(self, value):
         subwords = list()
@@ -517,26 +282,22 @@
             return property.description
 
 class JobAdsGroups(Widget):
-    def __init__(self, app, name, job):
+    def __init__(self, app, name):
         super(JobAdsGroups, self).__init__(app, name)
 
-        self.job = job
-
         self.group_tmpl = WidgetTemplate(self, "group_html")
 
     def render_groups(self, session):
-        job = self.job.get(session)
-        groups = self.app.model.get_ad_groups()
         writer = Writer()
-        for group in groups:
-            self.group_tmpl.render(writer, session, (job, group))
+        for group in self.app.model.get_ad_groups():
+            self.group_tmpl.render(writer, session, group)
         return writer.to_string()
 
-    def render_group_name(self, session, args):
-        return args[1]
+    def render_group_name(self, session, group):
+        return group
 
-    def render_properties(self, session, *args):
-        items = self.parent.do_get_items(session, *args)
+    def render_properties(self, session, group):
+        items = self.parent.get_group_items(session, group)
         writer = Writer()
 
         for item in items:
@@ -545,12 +306,12 @@
         return writer.to_string()
 
 class JobAdsViewer(JobAdsSet):
-    def __init__(self, app, name, job):
-        super(JobAdsViewer, self).__init__(app, name, job)
+    def __init__(self, app, name):
+        super(JobAdsViewer, self).__init__(app, name)
 
         self.item_renderer = JobPropertyRenderer(self, "property_html")
 
-        self.groups = JobAdsGroups(app, "groups", job);
+        self.groups = JobAdsGroups(app, "groups");
         self.add_child(self.groups)
 
         self.wait = Wait(app, "wait")
@@ -566,12 +327,11 @@
         self.frame.show_ads_edit(branch)
         return branch.marshal()
 
-    def do_get_items(self, session, args):
-        group = args[1]
+    def get_group_items(self, session, group):
         group_items = list()
 
-        all_items = super(JobAdsViewer, self).do_get_items(session)
-        for item in all_items:
+        items = self.do_get_items(session)
+        for item in items:
             if "property" in item:
                 property = item["property"]
                 item_group = property.group
@@ -583,8 +343,8 @@
         return group_items
 
 class JobAdsEditor(JobAdsViewer, CuminForm):
-    def __init__(self, app, name, job):
-        super(JobAdsEditor, self).__init__(app, name, job)        
+    def __init__(self, app, name):
+        super(JobAdsEditor, self).__init__(app, name)
 
         # the parameter that will hold all the field values
         self.ads = DictParameter(app, "params")
@@ -592,39 +352,25 @@
 
         self.item_renderer = EditablePropertyRenderer(self, "property_html")
 
-    def get_args(self, session):
-        return tuple()
-
-    def do_get_items(self, session, args):
-        job = args[0]
-        group = args[1]
-        cls = self.app.model.job
+    def do_get_items(self, session):
         ads = self.ads.get(session)
 
         if len(ads):
+            # we just returned from a submit
+            cls = self.app.model.job_meta_data
             return [self.gen_item(x, ads[x]["value"], cls, path=self.ads.path,
                                   dtype=ads[x]["type"], error=ads[x],
-                                  orig=ads[x]["orig"]) for x in ads
-                    if self.is_group(x, cls, group)]
-        else:
-            scheduler = self.frame.scheduler.get(session)
-            items = super(JobAdsEditor, self).do_get_items(session, args)
-            for item in items:
-                item["path"] = self.ads.path
-            return items
+                                  orig=ads[x]["orig"]) for x in ads]
 
-    def is_group(self, name, cls, group):
-        if name in cls.ad_properties_by_name:
-            property = cls.ad_properties_by_name[name]
-            item_group = property.group
-        else:
-            item_group = "Other"
-        return item_group == group
+        items = super(JobAdsEditor, self).do_get_items(session)
+        for item in items:
+            item["path"] = self.ads.path
+        return items
 
     def process_cancel(self, session):
         branch = session.branch()
         self.ads.set(branch, None) # otherwise url is too long
-        self.frame.view.show(branch)
+        self.frame.show_ads_view(branch)
         self.page.redirect.set(session, branch.marshal())
 
     def process_submit(self, session):
@@ -651,10 +397,9 @@
                 except:
                     ads[field]["error"] = "Floating point value expected"
                     errors = True
-            elif ftype == "string":
-                fval = "\"%s\"" % fval
             else:
                 fval = unicode(fval)
+
             if "orig" in ads[field]:
                 orig = ads[field]["orig"]
                 if ftype == "integer":
@@ -662,14 +407,20 @@
                 elif ftype == "float":
                     orig = float(orig)
                 if fval != orig:
+                    if ftype == "string":
+                        quoted = "\"%s\"" % fval
+                        if quoted == orig:
+                            continue
                     just_ads[unicode(field)] = fval
 
         if not errors:
-            scheduler = self.frame.scheduler.get(session)
-            job = self.frame.get_object(session)
-            task = main.module.job_setattribute
+            id = self.frame.id.get(session)
+            scheduler = self.frame.get_scheduler(session, id)
+            job_id = self.frame.job_id.get(session)
+
+            task = self.frame.set_ad_task
             for field in just_ads:
-                task.invoke(session, job.id, field, just_ads[field], scheduler)
+                task.invoke(session, scheduler, job_id, field, str(just_ads[field]))
             self.process_cancel(session)
 
 class OutputFile(Widget):
@@ -683,12 +434,15 @@
         self.defer_enabled = True
 
     def render_content(self, session):
-        scheduler = self.frame.scheduler.get(session)
-        job = self.frame.get_object(session)
+        id = self.frame.id.get(session)
+        job_server = self.frame.get_job_server(session, id)
+        job_id = self.frame.job_id.get(session)
         file, start, end = self.get_file_args(session)
         if file:
-            action = FetchJobOutput(self.app)
-            result = action.execute(scheduler, job.id, file, start, end)
+            action = QmfCall(self.app, {'Data': ""})
+            result = action.execute(job_server, "FetchJobData", job_id, file, start, end)
+            if result.error:
+                return result.status
             return escape_entity(result.data['Data'])
         return self.err_msg
 
@@ -707,12 +461,10 @@
         file = self.which_file.get_current_file_name(session)
         return file and "loading..." or self.err_msg
 
-class JobOutput(Form):
-    def __init__(self, app, name, job):
+class JobOutput(JobAdsSet, Form):
+    def __init__(self, app, name):
         super(JobOutput, self).__init__(app, name)
 
-        self.job = job
-
         self.which_file = self.FileSwitch(app, "file")
         self.add_child(self.which_file)
 
@@ -725,8 +477,6 @@
         self.output = OutputFile(app, "job_output", self.which_file, self.first_last)
         self.add_child(self.output)
 
-        self.ads = JobAdsSet(app, "ads", self.job)
-
     def render_title(self, session):
         return "Output"
 
@@ -739,14 +489,13 @@
         return fmt_datetime(now, sec=True)
 
     def do_process(self, session):
-        job = self.job.get(session)
-
         out_file = None
         user_file = None
         err_file = None
 
-        scheduler = self.frame.scheduler.get(session)
-        ads = self.ads.do_get_items(session)
+        id = self.frame.id.get(session)
+        scheduler = self.frame.get_scheduler(session, id)
+        ads = self.do_get_items(session)
         for ad in ads:
             if ad['name'] == "Out":
                 out_file = ad['value']
@@ -855,172 +604,92 @@
             self.add_state("t", "Tail", "Display end of file")
             self.add_state("h", "Head", "Display beginning of file")
 
-class JobGroupSet(CuminTable):
-    def __init__(self, app, name, pool):
-        super(JobGroupSet, self).__init__(app, name)
+class JobAction(ObjectTask):
+    def __init__(self, app, frame, verb):
+        super(JobAction, self).__init__(app, frame)
 
-        self.pool = pool
+        self.form = JobActionForm(app, self.name, self, verb)
 
-        col = self.GroupColumn(app, "job_group")
-        self.add_column(col)
+    def do_enter(self, session, osession):
+        job_id = self.frame.job_id.get(osession)
+        self.form.job_id.set(session, job_id)
 
-        col = self.JobsCountColumn(app, "jobs")
-        col.align = "right"
-        self.add_column(col)
-
-    class GroupColumn(SqlTableColumn):
-        def render_title(self, session):
-            return "Job Group"
-
-        def render_content(self, session, data):
-            name = data[self.name]
-
-            if name:
-                group = Identifiable(name)
-                href = self.page.main.grid.pool.job_group.get_href \
-                    (session, group)
-                return fmt_link(href, fmt_shorten(name))
-
-    class JobsCountColumn(SqlTableColumn):
-        def render_title(self, session):
-            return "Jobs"
-
-class JobsAndGroupsTab(TabbedModeSet):
-    def __init__(self, app, name, pool):
-        super(JobsAndGroupsTab, self).__init__(app, name)
-
-        self.jobs_tab = JobTab(app, "jobtab", pool)
-        self.add_tab(self.jobs_tab)
-        self.add_tab(JobGroupTab(app, "jobgrouptab", pool))
-
-    def render_title(self, session, *args):
-        return "Jobs %s" % fmt_count(self.jobs_tab.get_full_item_count(session, *args))
-
-    def render_phase(self, session, *args):
-        return self.mode.get(session) == self.jobs_tab and self.jobs_tab.phase.render(session) or ""
-
-class JobGroupTab(JobGroupSet, Form):
-    def __init__(self, app, name, pool):
-        super(JobGroupTab, self).__init__(app, name, pool)
-
-        self.set_default_column_name("job_group")
-
-    def render_title(self, session, *args):
-        return "Job Groups"
-
-class JobReasonForm(FieldSubmitForm):
+class JobActionForm(ObjectTaskForm):
     def __init__(self, app, name, task, verb):
-        super(JobReasonForm, self).__init__(app, name)
+        super(JobActionForm, self).__init__(app, name, task)
 
-        self.task = task
-        self.verb = verb
-        self.object = None
-
         self.reason = self.ReasonField(app, "reason")
         self.add_field(self.reason)
 
-        self.scheduler = SchedulerParameter(app, "scheduler")
-        self.add_parameter(self.scheduler)
+        self.job_id = Parameter(app, "job_id")
+        self.add_parameter(self.job_id)
 
-    def init(self):
-        assert self.object
-        super(JobReasonForm, self).init()
+        self.verb = verb
 
-    def render_submit_content(self, session):
-        return self.verb
-
-    def render_cancel_content(self, session):
-        return "Cancel"
-
-    def process_submit(self, session):
-        job = self.object.get(session)
-        reason = self.get_reason(session, self.verb)
-        scheduler = self.scheduler.get(session)
-
-        if not reason:
-            error = FormError("Reason is required")
-            self.errors.add(session, error)
-
-        if not self.errors.get(session):
-            self.task.invoke(session, job, reason, scheduler)
-            self.task.exit_with_redirect(session, job)
-
     def get_reason(self, session, verb):
         """ returns <verb> by username[: <user input reason>] """
         reason = self.reason.get(session)
         if reason:
             reason = [reason]
-            verb_by = "%s by %s" % (verb, session.user_session.subject.name)
+            user = session.client_session.attributes["login_session"].user
+
+            verb_by = "%s by %s" % (verb, user.name)
             reason.insert(0, verb_by)
             return ": ".join(reason)
 
+    def process_submit(self, session):
+        self.validate(session)
+
+        if not self.errors.get(session):
+            id = self.id.get(session)
+            scheduler = self.task.frame.get_scheduler(session, id)
+            reason = self.get_reason(session, self.verb)
+            job_id = self.job_id.get(session)
+
+            self.task.invoke(session, scheduler, job_id, reason)
+            self.task.exit_with_redirect(session)
+
     class ReasonField(StringField):
         def render_title(self, session):
             return "Reason"
 
-class JobTaskForm(JobReasonForm):
-    def __init__(self, app, name, task, verb):
-        super(JobTaskForm, self).__init__(app, name, task, verb)
+class JobHold(JobAction):
+    def __init__(self, app, frame):
+        super(JobHold, self).__init__(app, frame, "held")
 
-        self.object = JobParameter(app, "job")
-        self.add_parameter(self.object)
+    def get_title(self, session):
+        return "Hold Job"
 
-    def render_title(self, session):
-        job = self.object.get(session)
-        return "%s Job '%s'" % (self.verb, str(job.id))
+    def do_invoke(self, invoc, scheduler, job_id, reason):
+        self.qmf_call(invoc, scheduler, "HoldJob", job_id, reason)
 
-class JobSetTaskForm(JobReasonForm):
-    def __init__(self, app, name, task, verb):
-        super(JobSetTaskForm, self).__init__(app, name, task, verb)
+class JobRelease(JobAction):
+    def __init__(self, app, frame):
+        super(JobRelease, self).__init__(app, frame, "held")
 
-        item = JobParameter(app, "item")
+    def get_title(self, session):
+        return "Release Job"
 
-        self.object = ListParameter(app, "job", item)
-        self.add_parameter(self.object)
+    def do_invoke(self, invoc, scheduler, job_id, reason):
+        self.qmf_call(invoc, scheduler, "ReleaseJob", job_id, reason)
 
-    def render_title(self, session):
-        jobs = self.object.get(session)
-        return "%s %i Job%s" % (self.verb, len(jobs), len(jobs) > 1 and "s" or "")
+class JobRemove(JobAction):
+    def __init__(self, app, frame):
+        super(JobRemove, self).__init__(app, frame, "removed")
 
-class JobStatusSwitch(StateSwitch):
-    def __init__(self, app, name):
-        super(JobStatusSwitch, self).__init__(app, name)
+    def get_title(self, session):
+        return "Remove Job"
 
-        self.add_state("All", "All")
-        self.add_state("Running", "Running")
-        self.add_state("Idle", "Idle")
-        self.add_state("Held", "Held")
-        self.add_state("Completed", "Completed")
-        self.add_state("Removed", "Removed")
+    def do_invoke(self, invoc, scheduler, job_id, reason):
+        self.qmf_call(invoc, scheduler, "RemoveJob", job_id, reason)
 
-    def get_sql_constraint(self, session, phase=None):
-        if not phase:
-            phase = self.get(session)
+class JobSetAttribute(ObjectTask):
+    def get_title(self, session):
+        pass
 
-        alive = "((j.qmf_update_time is null or " + \
-            "j.qmf_update_time <= now() - interval '10 minutes')" + \
-            " and j.qmf_delete_time is null and j.job_status != %i)" % JobStatusInfo.get_status_int("Removed")
+    def get_description(self, session):
+        return "Edit Ad"
 
-        if phase == "a":
-            sql = alive
-        elif phase == "r":
-            sql = "(j.job_status = %i" % JobStatusInfo.get_status_int("Running") + \
-                    " and %s)" % alive
-        elif phase == "i":
-            sql = "(j.job_status = %i" % JobStatusInfo.get_status_int("Idle") + \
-                    " and %s)" % alive
-        elif phase == "h":
-            sql = "(j.job_status = %i" % JobStatusInfo.get_status_int("Held") + \
-                    " and %s)" % alive
-        elif phase == "c":
-            comotose = "(c.qmf_update_time is null or " + \
-                "c.qmf_update_time <= now() - interval '10 minutes')"
-            sql = "(j.job_status = %i" % JobStatusInfo.get_status_int("Completed") + \
-                    " and %s)" % comotose
-        else:
-            not_completed = "(j.qmf_delete_time is not null and j.job_status <> %i )" % JobStatusInfo.get_status_int("Completed")
-            is_removed = "j.job_status = %i" % JobStatusInfo.get_status_int("Removed")
-            sql = " or ".join((not_completed, is_removed))
+    def do_invoke(self, invoc, scheduler, job_id, name, value):
+        self.qmf_call(invoc, scheduler, "SetJobAttribute", job_id, name, value)
 
-        return sql
-

Modified: mgmt/newdata/cumin/python/cumin/grid/job.strings
===================================================================
--- mgmt/newdata/cumin/python/cumin/grid/job.strings	2010-06-30 19:10:29 UTC (rev 4058)
+++ mgmt/newdata/cumin/python/cumin/grid/job.strings	2010-06-30 20:28:45 UTC (rev 4059)
@@ -1,89 +1,3 @@
-[JobTab.css]
-input.search_input {
-    color: #555;
-    border: 1px solid #333;
-    font-size: 0.9em;
-    font-weight: normal;
-    padding-left: 0.25em;
-}
-
-input.search_error {
-    color: #CC0000 !important;
-}
-
-div.searchbox {
-    padding-top: 2px;
-}
-
-[JobTab.javascript]
-function JobSearchFocus() {
-    var val = this.value;
-
-    if (val == job_search_prompt) {
-        this.value = "";
-        this.style.color = "#000";
-        this.className = "search_input"; // remove search_error class
-    }
-}
-
-function JobSearchBlur() {
-    var val = this.value;
-
-    if (val == "") {
-        this.style.color = "#555";
-        job_search_prompt = job_search_default_prompt;
-        this.value = job_search_prompt;
-    }
-}
-
-window.addEvent('domready', function () {
-    var oInput = document.getElementById("job_search");
-
-    if (oInput) {
-        oInput.onfocus = JobSearchFocus;
-        oInput.onblur = JobSearchBlur;
-    }
-});
-
-
-[JobSearch.html]
-<div class="rfloat searchbox">
-  <h2><label for="job_search">Go To Job:</label></h2>
-  <input class="search_input {search_class}" type="text" name="{name}" id="job_search" value="{search_prompt}" />
-  {go}
-</div>
-<script type="text/javascript">
-  var job_search_default_prompt = "{search_default_prompt}"
-  var job_search_prompt = "{search_prompt}"
-</script>
-
-[JobGroupSet.sql]
-select
-  j.custom_group as id,
-  j.custom_group as job_group,
-  count(*) as jobs
-from job as j
-{sql_where}
-group by j.custom_group
-{sql_orderby}
-{sql_limit}
-
-[JobGroupSet.count_sql]
-select count(1) from (select count(1) from job as j group by j.custom_group) as s;
-{sql_where}
-
-[JobGroupStats.html]
-  <h2>General</h2>
-  <div style="width:50%;">
-  {general}
-  </div>
-
-
-[JobsAndGroupsTab.html]
-<div style="float: right;">{phase}</div>
-<ul class="radiotabs tabs">{tabs}</ul>
-<div class="radiotabs mode">{content}</div>
-
 [JobAdsViewer.html]
 <div id="{id}">
 <ul class="actions">
@@ -145,14 +59,15 @@
         display: inline;
 }
 
+[JobAdsEditor.css]
+table.Editable {
+    width: 100%;
+    border-collapse: collapse;
+}
+
 [JobAdsEditor.html]
 <form id="{id}" class="mform editform" method="post" action="?">
-  <div class="inline_help">
-    <h2>Legend</h2>
-    <span class="edit_number">Numeric input expected</span>
-    <span class="edit_string">String input expected</span>
-  </div>
-  {help} {submit} {cancel}
+  <div style="float:left;">{submit} {cancel}</div><div style="clear:both;"></div>
   <table class="CuminDetails Editable">
     <tbody>
       <tr>
@@ -162,7 +77,7 @@
       </tr>
     </tbody>
   </table>
-  {help} {submit} {cancel}
+  <div style="float:left;">{submit} {cancel}</div><div style="clear:both;"></div>
   <div>{hidden_inputs}</div>
 </form>
 

Modified: mgmt/newdata/cumin/python/cumin/grid/submission.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/grid/submission.py	2010-06-30 19:10:29 UTC (rev 4058)
+++ mgmt/newdata/cumin/python/cumin/grid/submission.py	2010-06-30 20:28:45 UTC (rev 4059)
@@ -6,7 +6,7 @@
 from cumin.objecttask import *
 from cumin.widgets import *
 from cumin.util import *
-from job import JobSet
+from job import JobSelector, JobFrame
 
 strings = StringCatalog(__file__)
 log = logging.getLogger("cumin.grid.submission")
@@ -17,6 +17,12 @@
 
         super(SubmissionFrame, self).__init__(app, name, cls)
 
+        self.job = JobFrame(app, "job", self.object)
+        self.add_mode(self.job)
+
+        jobs = JobSelector(app, "jobs", self.object)
+        self.view.add_tab(jobs)
+
 class SubmissionSelector(ObjectSelector):
     def __init__(self, app, name):
         cls = app.model.com_redhat_grid.Submission
@@ -31,10 +37,6 @@
         self.add_attribute_column(cls.Running)
         self.add_attribute_column(cls.Completed)
 
-class SubmissionJobSet(JobSet):
-    def render_title(self, session):
-        return "Jobs"
-
 class SubmissionAdd(ObjectTask):
     EXPR_TYPE, INTEGER_TYPE, FLOAT_TYPE, STRING_TYPE = 0, 1, 2, 3
     UNIVERSE = {"VANILLA": 5,

Modified: mgmt/newdata/cumin/python/cumin/inventory/system.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/inventory/system.py	2010-06-30 19:10:29 UTC (rev 4058)
+++ mgmt/newdata/cumin/python/cumin/inventory/system.py	2010-06-30 20:28:45 UTC (rev 4059)
@@ -136,22 +136,6 @@
         self.tabs.add_tab(SystemServices(app, "services", system))
         self.tabs.add_tab(CuminDetails(app, "details", system))
 
-from cumin.grid.job import JobTab
-
-class SystemJobSet(JobTab):
-    def render_title(self, session):
-        return "Grid Jobs %s" % fmt_count(self.get_item_count(session))
-
-    def render_sql_where(self, session):
-        elems = list()
-        elems.append("s.system = %(nodeName)s")
-        elems.append(self.get_phase_sql(session))
-        return "where %s" % " and ".join(elems)
-
-    def get_sql_values(self, session):
-        system = self.frame.get_object(session)
-        return {"nodeName": system.nodeName}
-
 class SystemServices(ItemSet):
     def __init__(self, app, name, system):
         super(SystemServices, self).__init__(app, name)

Modified: mgmt/newdata/cumin/python/cumin/main.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/main.py	2010-06-30 19:10:29 UTC (rev 4058)
+++ mgmt/newdata/cumin/python/cumin/main.py	2010-06-30 20:28:45 UTC (rev 4059)
@@ -19,6 +19,7 @@
 from session import *
 from sqladapter import *
 from user import *
+from util import *
 from widgets import *
 
 from wooly import Session

Modified: mgmt/newdata/cumin/python/cumin/model.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/model.py	2010-06-30 19:10:29 UTC (rev 4058)
+++ mgmt/newdata/cumin/python/cumin/model.py	2010-06-30 20:28:45 UTC (rev 4059)
@@ -33,6 +33,8 @@
 
         self.lock = Lock()
 
+        self.job_meta_data = JobMetaData("job")
+
     def check(self):
         log.info("Checking %s", self)
 
@@ -144,12 +146,11 @@
 
         return value
 
-class AdProperty(CuminProperty):
+class AdProperty(object):
     groups = ["Main", "Command Info", "Job Status Info", "Condor Info", "Dates", "Other"]
 
     def __init__(self, cls, name):
         # don't call super since we don't want to call add_property
-        self.model = cls.model
         self.cumin_class = cls
 
         self.name = name
@@ -167,10 +168,37 @@
 
         self.cumin_class.add_ad_property(self)
 
+    def get_title(self, session):
+        if self.title:
+            return self.title
+        else:
+            return self.name
+
+    def value(self, session, object):
+        value = getattr(object, self.name, None)
+
+        if isinstance(value, datetime):
+            value = fmt_datetime(value)
+
+        if isinstance(value, dict):
+            value = fmt_dict(value, self.prefix)
+
+        return value
+
     @classmethod
     def get_ad_groups(cls):
         return cls.groups
 
+class DictAdProperty(AdProperty):
+    def __init__(self, cls, name):
+        super(DictAdProperty, self).__init__(cls, name)
+
+        self.renderer = self.render_dict
+        self.group = "Other"
+
+    def render_dict(self, session, value):
+        return fmt_dict(value, self.prefix)
+
 class DateAdProperty(AdProperty):
     def __init__(self, cls, name):
         super(DateAdProperty, self).__init__(cls, name)
@@ -547,6 +575,21 @@
         except KeyError:
             return value
 
+class MetaData(object):
+    def __init__(self, name):
+
+        self.name = name
+        self.ad_properties = list()
+        self.ad_properties_by_name = dict()
+
+    def init(self):
+        for ad_prop in self.ad_properties:
+            ad_prop.init()
+
+    def add_ad_property(self, prop):
+        self.ad_properties.append(prop)
+        self.ad_properties_by_name[prop.name] = prop
+
 class CuminClass(object):
     def __init__(self, model, name, mint_class):
         self.model = model
@@ -1340,52 +1383,26 @@
         title = self.get_title(session)
         return "%s '%s'" % (title, limit)
 
-class CuminJobGroup(CuminClass):
-    def __init__(self, model):
-        super(CuminJobGroup, self).__init__ \
-            (model, "job_group", JobGroup)
+class JobMetaData(MetaData):
+    def __init__(self, name):
+        super(JobMetaData, self).__init__(name)
 
-        stat = CuminStat(self, "Running")
-        stat.title = "Running Jobs"
-
-        stat = CuminStat(self, "Completed")
-        stat.title = "Completed Jobs"
-
-        stat = CuminStat(self, "Idle")
-        stat.title = "Idle Jobs"
-
-        stat = CuminStat(self, "Held")
-        stat.title = "Held Jobs"
-
-        stat = CuminStat(self, "Jobs")
-        stat.title = "Total Jobs"
-
-    def init(self):
-        super(CuminJobGroup, self).init()
-
-        # XXX
-        #self.frame = self.model.frame.grid.pool.job_group
-
-    def get_object_title(self, session, group):
-        title = self.get_title(session)
-        name = group.get_id()
-        return "%s '%s'" % (title, name)
-
-    def get_title(self, session):
-        return "Job Group"
-
-    def get_icon_href(self, session):
-        return "resource?name=group-36.png"
-
-class CuminJob(CuminClass):
-    def __init__(self, model):
-        super(CuminJob, self).__init__(model, "job", None)
-
         ### Main Group
         prop = AdProperty(self, "Owner")
+        prop.group = "Main"
         prop.description = "The submitter of the job"
         prop.writable = False
 
+        prop = AdProperty(self, "GlobalJobId")
+        prop.group = "Main"
+        prop.description = "Unique job id"
+        prop.writable = False
+
+        prop = AdProperty(self, "Submission")
+        prop.group = "Main"
+        prop.description = "Submission name"
+        prop.writable = False
+
         ### Condor Info Group
         prop = AdProperty(self, "CondorVersion")
         prop.group = "Condor Info"
@@ -1395,6 +1412,10 @@
         prop.group = "Condor Info"
         prop.writable = False
 
+        prop = DictAdProperty(self, "!!descriptors")
+        prop.group = "Condor Info"
+        prop.writable = False
+
         ### Command Info Group
         prop = AdProperty(self, "Args")
         prop.description = "Arguments passed to job Cmd"
@@ -1403,20 +1424,27 @@
         prop = AdProperty(self, "Cmd")
         prop.description = "Command that will run the job"
         prop.group = "Command Info"
-        prop.title = "Command"
 
         prop = AdProperty(self, "In")
         prop.description = "The file where the job's standard input is read"
         prop.group = "Command Info"
 
+        prop = AdProperty(self, "Out")
+        prop.description = "The file where the job's standard output is written"
+        prop.example = "'/dev/null' or '~/logs/'"
+        prop.group = "Command Info"
+
+        prop = AdProperty(self, "Err")
+        prop.description = "The file where the job's errors are written"
+        prop.group = "Command Info"
+
         prop = AdProperty(self, "Iwd")
         prop.description = "Command Input Working Directory"
         prop.group = "Command Info"
         prop.title = "Working Directory"
 
-        prop = AdProperty(self, "Out")
-        prop.description = "The file where the job's standard output is written"
-        prop.example = "'/dev/null' or '~/logs/'"
+        prop = AdProperty(self, "UserLog")
+        prop.description = "Log file"
         prop.group = "Command Info"
 
         ######## Job Status Info
@@ -1432,19 +1460,26 @@
         prop.title = "Hold Reason Code"
         prop.writable = False
 
-        prop = AdProperty(self, "ExitStatus")
+        prop = self.JobStatusProperty(self, "ExitStatus")
         prop.description = "Status when job completes"
         prop.group = "Job Status Info"
+        prop.renderer = prop.render_status
         prop.title = "Exit Status"
         prop.writable = False
 
+        prop = self.JobStatusProperty(self, "LastJobStatus")
+        prop.description = ""
+        prop.group = "Job Status Info"
+        prop.renderer = prop.render_status
+        prop.writable = False
+
+        ######## Other
         prop = AdProperty(self, "ProcId")
         prop.description = "The id of the job within its cluster. Proc Ids are unique within a cluster."
-        prop.group = "Job Status Info"
+        prop.group = "Other"
         prop.title = "Proc Id"
         prop.writable = False
 
-        ######## Other
         prop = AdProperty(self, "BufferBlockSize")
         prop.example = "32768"
         prop.group = "Other"
@@ -1455,6 +1490,7 @@
         prop.title = "Cluster ID"
         prop.writable = False
 
+        ###### Dates
         prop = DateAdProperty(self, "QDate")
         prop.description = "When the job was submitted"
         prop.group = "Dates"
@@ -1500,83 +1536,10 @@
         prop.group = "Dates"
         prop.writable = False
 
-        ######## Properties
-        prop = CuminProperty(self, "CustomGroup")
-        prop.title = "Job Group"
-        prop.escape = False
+        prop = DateAdProperty(self, "ScheddBday")
+        prop.group = "Dates"
+        prop.writable = False
 
-        prop = CuminProperty(self, "scheduler")
-        prop.title = "Scheduler"
-        prop.escape = False
-
-        prop = CuminProperty(self, "submitter")
-        prop.title = "Submitter"
-        prop.escape = False
-
-        prop = CuminProperty(self, "AccountingGroup")
-        prop.title = "Accounting Group"
-
-        prop = CuminProperty(self, "Args")
-        prop.title = "Args"
-
-        prop = CuminProperty(self, "ClusterId")
-        prop.title = "Cluster ID"
-
-        prop = CuminProperty(self, "Cmd")
-        prop.title = "Command"
-
-        prop = CuminProperty(self, "ConcurrencyLimits")
-        prop.title = "Limits"
-
-        prop = CuminProperty(self, "CustomId")
-        prop.title = "Custom ID"
-
-        prop = CuminProperty(self, "CustomPriority")
-        prop.title = "Custom Priority"
-
-        prop = CuminProperty(self, "GlobalJobId")
-        prop.title = "Global Job ID"
-
-        prop = CuminProperty(self, "JobStatus")
-        prop.title = "Job Status"
-
-        prop = CuminProperty(self, "Note")
-        prop.title = "Note"
-
-        prop = CuminProperty(self, "ProcId")
-        prop.title = "Proc ID"
-
-        prop = CuminProperty(self, "QDate")
-        prop.title = "QDate"
-
-        prop = CuminProperty(self, "Requirements")
-        prop.title = "Requirements"
-
-        prop = CuminProperty(self, "Title")
-        prop.title = "Title"
-
-        prop = CuminProperty(self, "UserLog")
-        prop.title = "User Log"
-
-        prop = CuminProperty(self, "HoldReason")
-        prop.title = "Hold Reason"
-
-    def init(self):
-        super(CuminJob, self).init()
-
-        #self.frame = self.model.frame.grid.pool.job
-
-    def get_title(self, session):
-        return "Job"
-
-        if isinstance(job, basestring):
-            return job
-        else:
-            return job.CustomId
-
-    def get_icon_href(self, session):
-        return "resource?name=job-36.png"
-
     class JobStatusProperty(AdProperty):
         def render_status(self, session, status):
             return JobStatusInfo.get_status_string(status)
@@ -1625,10 +1588,10 @@
     def done(self):
         return self.got_data or self.error
 
-    def execute(self, negotiator, method_name, *args):
+    def execute(self, obj, method_name, *args):
         session = self.app.session
         try:
-            session.call_method(self.get_completion(), negotiator, method_name, args)
+            session.call_method(self.get_completion(), obj, method_name, args)
         except Exception, e:
             self.error = e
 
@@ -1934,13 +1897,6 @@
         for negotiator in Negotiator.selectBy(Pool=self.id):
             return negotiator
 
-class JobGroup(object):
-    def __init__(self, id):
-        self.id = id
-
-    def get_id(self):
-        return self.id
-
 class ObjectStore(object):
     def __init__(self, model):
         self.model = model

Modified: mgmt/newdata/cumin/python/cumin/objectframe.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/objectframe.py	2010-06-30 19:10:29 UTC (rev 4058)
+++ mgmt/newdata/cumin/python/cumin/objectframe.py	2010-06-30 20:28:45 UTC (rev 4059)
@@ -15,7 +15,7 @@
         super(ObjectFrame, self).__init__(app, name)
 
         self.cls = cls
-        
+
         self.id = IntegerParameter(app, "id")
         self.add_parameter(self.id)
 
@@ -179,10 +179,14 @@
         self.add_child(self.body)
 
     def init(self):
-        self.add_tab(ObjectDetails(self.app, "details", self.object))
+        self.add_details_tab()
 
         super(ObjectView, self).init()
 
+    def add_details_tab(self):
+        """ allow derived views to skip adding details tab """
+        self.add_tab(ObjectDetails(self.app, "details", self.object))
+
     def add_tab(self, widget):
         self.body.add_tab(widget)
 

Modified: mgmt/newdata/cumin/python/cumin/objectselector.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/objectselector.py	2010-06-30 19:10:29 UTC (rev 4058)
+++ mgmt/newdata/cumin/python/cumin/objectselector.py	2010-06-30 20:28:45 UTC (rev 4059)
@@ -6,6 +6,7 @@
 from objectframe import *
 from objecttask import *
 from sqladapter import *
+from qmfadapter import *
 from util import *
 
 strings = StringCatalog(__file__)
@@ -17,7 +18,7 @@
         if not adapter:
             adapter = ObjectSqlAdapter(app, cls)
 
-        assert isinstance(adapter, ObjectSqlAdapter), adapter
+        assert isinstance(adapter, DataAdapter), adapter
 
         super(ObjectTable, self).__init__(app, name, adapter)
 
@@ -33,7 +34,7 @@
 
         assert self.cls, self
         assert self.adapter, self
-        assert self.adapter.id_field, self
+        #assert self.adapter.id_field, self
 
     def add_attribute_column(self, attr):
         assert isinstance(attr, RosemaryAttribute), attr
@@ -120,7 +121,10 @@
         try:
             self.field = self.table.adapter.fields_by_attr[self.attr]
         except KeyError:
-            self.field = ObjectSqlField(self.table.adapter, self.attr)
+            if isinstance(self.table.adapter, ObjectQmfAdapter):
+                self.field = ObjectQmfField(self.table.adapter, self.attr)
+            else:
+                self.field = ObjectSqlField(self.table.adapter, self.attr)
 
 class ObjectCheckboxColumn(ObjectAttributeColumn):
     def __init__(self, app, name, attr, selection):
@@ -167,7 +171,10 @@
         try:
             self.id_field = self.table.adapter.fields_by_attr[self.id_attr]
         except KeyError:
-            self.id_field = ObjectSqlField(self.table.adapter, self.id_attr)
+            if isinstance(self.table.adapter, ObjectQmfAdapter):
+                self.id_field = ObjectQmfField(self.table.adapter, self.id_attr)
+            else:
+                self.id_field = ObjectSqlField(self.table.adapter, self.id_attr)
 
     def render_cell_href(self, session, record):
         id = record[self.id_field.index]

Modified: mgmt/newdata/cumin/python/cumin/objecttask.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/objecttask.py	2010-06-30 19:10:29 UTC (rev 4058)
+++ mgmt/newdata/cumin/python/cumin/objecttask.py	2010-06-30 20:28:45 UTC (rev 4059)
@@ -130,11 +130,11 @@
         self.form.return_url.set(nsession, session.marshal())
         self.form.show(nsession)
 
-        self.do_enter(nsession)
+        self.do_enter(nsession, session)
 
         return nsession
 
-    def do_enter(self, session):
+    def do_enter(self, session, osession):
         pass
 
 class TaskInvocation(object):
@@ -254,11 +254,11 @@
         self.form.return_url.set(nsession, session.marshal())
         self.form.show(nsession)
 
-        self.do_enter(nsession)
+        self.do_enter(nsession, session)
 
         return nsession
 
-    def do_enter(self, session):
+    def do_enter(self, session, osession):
         pass
 
     def invoke(self, session, selection, *args):

Modified: mgmt/newdata/cumin/python/cumin/parameters.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/parameters.py	2010-06-30 19:10:29 UTC (rev 4058)
+++ mgmt/newdata/cumin/python/cumin/parameters.py	2010-06-30 20:28:45 UTC (rev 4059)
@@ -112,13 +112,6 @@
     def do_marshal(self, job):
         return str(job.id)
 
-class JobGroupParameter(Parameter):
-    def do_unmarshal(self, string):
-        return JobGroup(string)
-
-    def do_marshal(self, job_group):
-        return str(job_group.id)
-
 class LinkParameter(Parameter):
     def do_unmarshal(self, string):
         return Link.get(int(string))

Added: mgmt/newdata/cumin/python/cumin/qmfadapter.py
===================================================================
--- mgmt/newdata/cumin/python/cumin/qmfadapter.py	                        (rev 0)
+++ mgmt/newdata/cumin/python/cumin/qmfadapter.py	2010-06-30 20:28:45 UTC (rev 4059)
@@ -0,0 +1,89 @@
+from rosemary.sqlquery import *
+from wooly.datatable import *
+
+from util import *
+
+class QmfAdapter(DataAdapter):
+    def __init__(self, app, method):
+        super(QmfAdapter, self).__init__()
+
+        self.app = app
+        self.method = method
+        self.default = dict()
+        self.columns = list()
+
+    def get_count(self, values):
+        obj = values['obj']
+        args = values['args']
+
+        action = QmfCall(self.app)
+        results = action.execute(obj, self.method, args)
+
+        return results and len(results) or 0
+
+    def get_data(self, values, options):
+        obj = values['obj']
+        args = values['args']
+
+        action = QmfCall(self.app)
+        results = action.execute(obj, self.method, args)
+
+        if results.error:
+            results.data = self.default
+
+        rows = self.process_results(results)
+        return rows
+
+    def process_results(self, results):
+        """ take the dict response from the qmf call and return a list of lists """
+
+        records = list()
+
+        if results:
+            for key in results:
+                row = self.process_record(results[key])
+                records.append(row)
+
+        return records
+
+    def process_record(self, record):
+        field_data = list()
+        for column in self.columns:
+            try:
+                val = record[column.name]
+            except KeyError:
+                val = 0
+            field_data.append(val)
+        return field_data
+
+class ObjectQmfAdapter(QmfAdapter):
+    def __init__(self, app, cls):
+        super(ObjectQmfAdapter, self).__init__(app, cls._name)
+
+        self.cls = cls
+
+        self.fields_by_attr = dict()
+
+class QmfField(DataAdapterField):
+    def __init__(self, adapter, column):
+        python_type = column.type.python_type
+
+        super(QmfField, self).__init__(adapter, column.name, python_type)
+
+        self.column = column
+
+        self.adapter.columns.append(column)
+
+
+class ObjectQmfField(QmfField):
+    def __init__(self, adapter, attr):
+        assert isinstance(adapter, ObjectQmfAdapter), adapter
+
+        super(ObjectQmfField, self).__init__(adapter, attr.sql_column)
+
+        self.attr = attr
+
+        self.adapter.fields_by_attr[self.attr] = self
+
+    def get_title(self, session):
+        return self.attr.title or self.attr.name



More information about the rhmessaging-commits mailing list