Преглед на файлове

Merge pull request #4794 from wangkx/h9249

HPCC-9249 Return user/IP for paused queue to WsSMC/Activity response

Reviewed-By: Anthony Fishbeck <anthony.fishbeck@lexisnexis.com>
Reviewed-By: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman преди 11 години
родител
ревизия
f458fe1eec
променени са 6 файла, в които са добавени 181 реда и са изтрити 56 реда
  1. 78 3
      common/workunit/wujobq.cpp
  2. 6 1
      common/workunit/wujobq.hpp
  3. 7 6
      esp/eclwatch/ws_XSLT/index.xslt
  4. 3 1
      esp/scm/ws_smc.ecm
  5. 82 42
      esp/services/ws_smc/ws_smcService.cpp
  6. 5 3
      esp/services/ws_smc/ws_smcService.hpp

+ 78 - 3
common/workunit/wujobq.cpp

@@ -1163,7 +1163,7 @@ public:
         return copyItemsImpl(qd,dest);
     }
 
-    void copyItemsAndState(CJobQueueContents& contents, StringBuffer& state)
+    void copyItemsAndState(CJobQueueContents& contents, StringBuffer& state, StringBuffer& stateDetails)
     {
         assertex(qdata);
         Cconnlockblock block(this,false);
@@ -1174,6 +1174,12 @@ public:
         const char *st = qdata->root->queryProp("@state");
         if (st&&*st)
             state.set(st);
+        if (st && (strieq(st, "paused") || strieq(st, "stopped")))
+        {
+            const char *stDetails = qdata->root->queryProp("@stateDetails");
+            if (stDetails&&*stDetails)
+                stateDetails.set(stDetails);
+        }
     }
 
     unsigned takeItems(sQueueData &qd,CJobQueueContents &dest)
@@ -1768,8 +1774,19 @@ public:
     {
         Cconnlockblock block(this,true);
         ForEachQueue(qd) {
-            if (qd->root) 
+            if (qd->root)
+                qd->root->setProp("@state","paused");
+        }
+    }
+    void pause(const char* info)
+    {
+        Cconnlockblock block(this,true);
+        ForEachQueue(qd) {
+            if (qd->root) {
                 qd->root->setProp("@state","paused");
+                if (info && *info)
+                    qd->root->setProp("@stateDetails",info);
+            }
         }
     }
     bool paused()
@@ -1785,12 +1802,41 @@ public:
         }
         return true;
     }
+    bool paused(StringBuffer& info)
+    {
+        // true if all paused
+        Cconnlockblock block(this,false);
+        ForEachQueue(qd) {
+            if (qd->root) {
+                const char *state = qd->root->queryProp("@state");
+                if (state&&(strcmp(state,"paused")!=0))
+                    return false;
+                if (state&&!info.length()) {
+                    const char *stateDetails = qd->root->queryProp("@stateDetails");
+                    if (stateDetails && *stateDetails)
+                        info.set(stateDetails);
+                }
+            }
+        }
+        return true;
+    }
     void stop()
     {
         Cconnlockblock block(this,true);
         ForEachQueue(qd) {
-            if (qd->root) 
+            if (qd->root)
+                qd->root->setProp("@state","stopped");
+        }
+    }
+    void stop(const char* info)
+    {
+        Cconnlockblock block(this,true);
+        ForEachQueue(qd) {
+            if (qd->root) {
                 qd->root->setProp("@state","stopped");
+                if (info && *info)
+                    qd->root->setProp("@stateDetails",info);
+            }
         }
     }
     bool stopped()
@@ -1806,6 +1852,24 @@ public:
         }
         return true;
     }
+    bool stopped(StringBuffer& info)
+    {
+        // true if all stopped
+        Cconnlockblock block(this,false);
+        ForEachQueue(qd) {
+            if (qd->root) {
+                const char *state = qd->root->queryProp("@state");
+                if (state&&(strcmp(state,"stopped")!=0))
+                    return false;
+                if (state&&!info.length()) {
+                    const char *stateDetails = qd->root->queryProp("@stateDetails");
+                    if (stateDetails && *stateDetails)
+                        info.set(stateDetails);
+                }
+            }
+        }
+        return true;
+    }
 
     void resume()
     {
@@ -1815,6 +1879,17 @@ public:
                 qd->root->setProp("@state","active");
         }
     }
+    void resume(const char* info)
+    {
+        Cconnlockblock block(this,true);
+        ForEachQueue(qd) {
+            if (qd->root) {
+                qd->root->setProp("@state","active");
+                if (info && *info)
+                    qd->root->setProp("@stateDetails",info);
+            }
+        }
+    }
 
     IConversation *initiateConversation(IJobQueueItem *item)
     {

+ 6 - 1
common/workunit/wujobq.hpp

@@ -91,7 +91,7 @@ interface IJobQueue: extends IInterface
     virtual unsigned findRank(const char *wuid)=0;
     virtual unsigned copyItems(CJobQueueContents &dest)=0;  // takes a snapshot copy of the entire queue (returns number copied)
     virtual bool getLastDequeuedInfo(StringAttr &wuid, CDateTime &enqueuedt, int &priority)=0;
-    virtual void copyItemsAndState(CJobQueueContents& contents, StringBuffer& state)=0;
+    virtual void copyItemsAndState(CJobQueueContents& contents, StringBuffer& state, StringBuffer& stateDetails)=0;
 
 
 //manipulation
@@ -112,10 +112,15 @@ interface IJobQueue: extends IInterface
 
 // control:
     virtual void pause()=0;     // marks queue as paused - and subsequent dequeues block until resumed
+    virtual void pause(const char *info)=0;     // marks queue as paused - and subsequent dequeues block until resumed
     virtual bool paused()=0;    // true if paused
+    virtual bool paused(StringBuffer& info)=0;    // true if paused
     virtual void stop()=0;      // sets stopped flags - all current and subsequent dequeues return NULL
+    virtual void stop(const char *info)=0;      // sets stopped flags - all current and subsequent dequeues return NULL
     virtual bool stopped()=0;   // true if stopped
+    virtual bool stopped(StringBuffer& info)=0;   // true if stopped
     virtual void resume()=0;    // removes paused or stopped flag
+    virtual void resume(const char *info)=0;    // removes paused or stopped flag
 
 // conversations:
     virtual IConversation *initiateConversation(IJobQueueItem *item)=0; // does enqueue - take ownership of item

+ 7 - 6
esp/eclwatch/ws_XSLT/index.xslt

@@ -613,6 +613,7 @@
                             <xsl:with-param name="clusterType" select="ServerType"/>
                             <xsl:with-param name="queue" select="QueueName"/>
                             <xsl:with-param name="queueStatus" select="QueueStatus"/>
+                            <xsl:with-param name="statusDetails" select="StatusDetails"/>
                         </xsl:call-template>
                     </xsl:for-each>
 
@@ -623,6 +624,7 @@
                             <xsl:with-param name="clusterType" select="ServerType"/>
                             <xsl:with-param name="queue" select="QueueName"/>
                             <xsl:with-param name="queueStatus" select="QueueStatus"/>
+                            <xsl:with-param name="statusDetails" select="StatusDetails"/>
                         </xsl:call-template>
                     </xsl:for-each>
 
@@ -633,6 +635,7 @@
                             <xsl:with-param name="clusterType" select="ServerType"/>
                             <xsl:with-param name="queue" select="QueueName"/>
                             <xsl:with-param name="queueStatus" select="QueueStatus"/>
+                            <xsl:with-param name="statusDetails" select="StatusDetails"/>
                         </xsl:call-template>
                     </xsl:for-each>
 
@@ -643,6 +646,7 @@
                             <xsl:with-param name="clusterType" select="ServerType"/>
                             <xsl:with-param name="queue" select="QueueName"/>
                             <xsl:with-param name="queueStatus" select="QueueStatus"/>
+                            <xsl:with-param name="statusDetails" select="StatusDetails"/>
                         </xsl:call-template>
                     </xsl:for-each>
                     <xsl:apply-templates select="DFUJobs"/>
@@ -768,15 +772,12 @@
                                 <xsl:choose>
                                     <xsl:when test="$queueStatus='paused'">
                                         <xsl:attribute name="class">thorrunningpausedqueuejobs</xsl:attribute>
-                                        <xsl:attribute name="title">Queue paused</xsl:attribute>
                                     </xsl:when>
                                     <xsl:when test="$queueStatus='stopped'">
                                         <xsl:attribute name="class">thorrunningpausedqueuejobs</xsl:attribute>
-                                        <xsl:attribute name="title">Queue stopped</xsl:attribute>
                                     </xsl:when>
                                     <xsl:otherwise>
                                         <xsl:attribute name="class">thorrunning</xsl:attribute>
-                                        <xsl:attribute name="title">Queue running</xsl:attribute>
                                     </xsl:otherwise>
                                 </xsl:choose>
                             </xsl:when>
@@ -798,11 +799,11 @@
                                         <xsl:attribute name="class">thorrunning</xsl:attribute>
                                     </xsl:otherwise>
                                 </xsl:choose>
-                                <xsl:attribute name="title">
-                                    <xsl:value-of select="$statusDetails"/>
-                                </xsl:attribute>
                             </xsl:otherwise>
                         </xsl:choose>
+                        <xsl:attribute name="title">
+                            <xsl:value-of select="$statusDetails"/>
+                        </xsl:attribute>
                         <xsl:if test="$clusterType = 'THOR'">
                             <xsl:attribute name="href">javascript:go('/WsTopology/TpClusterInfo?Name=<xsl:value-of select="$cluster"/>')</xsl:attribute>
                         </xsl:if>

+ 3 - 1
esp/scm/ws_smc.ecm

@@ -106,6 +106,7 @@ ESPStruct ServerJobQueue
     string ServerName;
     string ServerType;
     string QueueStatus;
+    [min_ver("1.17")] string StatusDetails;
 };
 
 ESPrequest [nil_remove] ActivityRequest
@@ -183,6 +184,7 @@ ESPrequest SMCQueueRequest
     int ClusterType;
     string Cluster;
     string QueueName;
+    string Comment;
 };
 
 
@@ -319,7 +321,7 @@ RoxieControlCmdResponse
     ESParray<ESPstruct RoxieControlEndpointInfo, Endpoint> Endpoints;
 };
 
-ESPservice [noforms, version("1.16"), default_client_version("1.16"), exceptions_inline("./smc_xslt/exceptions.xslt"), use_method_name] WsSMC
+ESPservice [noforms, version("1.17"), default_client_version("1.17"), exceptions_inline("./smc_xslt/exceptions.xslt"), use_method_name] WsSMC
 {
     ESPmethod Index(SMCIndexRequest, SMCIndexResponse);
     ESPmethod [resp_xsl_default("/esp/xslt/index.xslt")] Activity(ActivityRequest, ActivityResponse);

+ 82 - 42
esp/services/ws_smc/ws_smcService.cpp

@@ -582,10 +582,10 @@ void CWsSMCEx::getServersAndWUs(IEspContext &context, IEspActivityRequest &req,
             const char *queueName = cluster.getThorQueue(str).str();
             returnCluster->setQueueName(queueName);
 
-            StringBuffer queueState;
+            StringBuffer queueState, queueStateDetails;
             CJobQueueContents contents;
             Owned<IJobQueue> queue = createJobQueue(queueName);
-            queue->copyItemsAndState(contents, queueState);
+            queue->copyItemsAndState(contents, queueState, queueStateDetails);
             addQueuedWorkUnits(queueName, contents, aws, context, "ThorMaster", NULL);
 
             BulletType bulletType = bulletGreen;
@@ -593,12 +593,12 @@ void CWsSMCEx::getServersAndWUs(IEspContext &context, IEspActivityRequest &req,
             int numRunningJobsInQueue = (NotFound != serverID) ? runningJobsInQueue[serverID] : -1;
             getQueueState(numRunningJobsInQueue, queueState, bulletType);
 
-            StringBuffer agentQueueState;
+            StringBuffer agentQueueState, agentQueueStateDetails;
             CJobQueueContents agentContents;
             SCMStringBuffer str1;
             const char *agentQueueName = cluster.getAgentQueue(str1).str();
             Owned<IJobQueue> agentQueue = createJobQueue(agentQueueName);
-            agentQueue->copyItemsAndState(agentContents, agentQueueState);
+            agentQueue->copyItemsAndState(agentContents, agentQueueState, agentQueueStateDetails);
             //Use the same 'queueName' because the job belongs to the same cluster
             addQueuedWorkUnits(queueName, agentContents, aws, context, "ThorMaster", NULL);
             if (bulletType == bulletGreen)
@@ -628,10 +628,10 @@ void CWsSMCEx::getServersAndWUs(IEspContext &context, IEspActivityRequest &req,
                 returnCluster->setQueueName(cluster.getAgentQueue(str).str());
                 str.clear();
                 const char *queueName = cluster.getAgentQueue(str).str();
-                StringBuffer queueState;
+                StringBuffer queueState, queueStateDetails;
                 CJobQueueContents contents;
                 Owned<IJobQueue> queue = createJobQueue(queueName);
-                queue->copyItemsAndState(contents, queueState);
+                queue->copyItemsAndState(contents, queueState, queueStateDetails);
                 addQueuedWorkUnits(queueName, contents, aws, context, "RoxieServer", NULL);
 
                 BulletType bulletType = bulletGreen;
@@ -655,10 +655,10 @@ void CWsSMCEx::getServersAndWUs(IEspContext &context, IEspActivityRequest &req,
             returnCluster->setQueueName(cluster.getAgentQueue(str).str());
             str.clear();
             const char *queueName = cluster.getAgentQueue(str).str();
-            StringBuffer queueState;
+            StringBuffer queueState, queueStateDetails;
             CJobQueueContents contents;
             Owned<IJobQueue> queue = createJobQueue(queueName);
-            queue->copyItemsAndState(contents, queueState);
+            queue->copyItemsAndState(contents, queueState, queueStateDetails);
             addQueuedWorkUnits(queueName, contents, aws, context, "HThorServer", NULL);
 
             BulletType bulletType = bulletGreen;
@@ -706,11 +706,11 @@ void CWsSMCEx::getServersAndWUs(IEspContext &context, IEspActivityRequest &req,
             targetClusters->str(targetCluster);
 
             StringBuffer queueName;
-            StringBuffer queueState;
+            StringBuffer queueState, queueStateDetails;
             CJobQueueContents contents;
             getClusterEclCCServerQueueName(queueName, targetCluster.str());
             Owned<IJobQueue> queue = createJobQueue(queueName);
-            queue->copyItemsAndState(contents, queueState);
+            queue->copyItemsAndState(contents, queueState, queueStateDetails);
             unsigned count=0;
             Owned<IJobQueueIterator> iter = contents.getIterator();
             ForEach(*iter)
@@ -726,7 +726,7 @@ void CWsSMCEx::getServersAndWUs(IEspContext &context, IEspActivityRequest &req,
                 aws.append(*wu.getLink());
             }
 
-            addServerJobQueue(serverJobQueues, queueName, queueState.str(), serverName, "ECLCCserver");
+            addServerJobQueue(version, serverJobQueues, queueName, serverName, "ECLCCserver", queueState.str(), queueStateDetails.str());
         }
     }
 
@@ -803,7 +803,7 @@ void CWsSMCEx::getServersAndWUs(IEspContext &context, IEspActivityRequest &req,
                             e->Release();
                         }
                     }
-                    addServerJobQueue(serverJobQueues, queueName, serverName, "DFUserver");
+                    addServerJobQueue(version, serverJobQueues, queueName, serverName, "DFUserver");
                 }
             }
         } while (services->next());
@@ -872,7 +872,7 @@ void CWsSMCEx::readWUsAndStateFromJobQueue(IEspContext& context, CWsSMCTargetClu
 {
     CJobQueueContents contents;
     Owned<IJobQueue> queue = createJobQueue(jobQueue.queueName.str());
-    queue->copyItemsAndState(contents, jobQueue.queueState);
+    queue->copyItemsAndState(contents, jobQueue.queueState, jobQueue.queueStateDetails);
     Owned<IJobQueueIterator> iter = contents.getIterator();
     jobQueue.countQueuedJobs=0;
     ForEach(*iter)
@@ -1081,7 +1081,11 @@ void CWsSMCEx::setClusterQueueStatus(CWsSMCTargetCluster& targetCluster)
     if (jobQueue.queueState.length())
     {
         const char* queueState = jobQueue.queueState.str();
-        targetCluster.clusterStatusDetails.appendf("queue %s; ", queueState);
+        const char* queueStateDetails = jobQueue.queueStateDetails.str();
+        if (queueStateDetails && *queueStateDetails)
+            targetCluster.clusterStatusDetails.appendf("queue %s; %s;", queueState, queueStateDetails);
+        else
+            targetCluster.clusterStatusDetails.appendf("queue %s; ", queueState);
         if (strieq(queueState,"stopped") || strieq(queueState,"paused"))
             queuePausedOrStopped = true;
     }
@@ -1128,6 +1132,7 @@ void CWsSMCEx::getTargetClusterAndWUs(IEspContext& context, CConstWUClusterInfoA
      IPropertyTree* serverStatusRoot, IPropertyTreeIterator* itrStatusECLagent, IEspTargetCluster* returnCluster,
      IArrayOf<IEspActiveWorkunit>& aws)
 {
+    double version = context.getClientVersion();
     CWsSMCTargetCluster targetCluster;
     cluster.getServerQueue(targetCluster.serverQueue.queueName);
     targetCluster.clusterType = cluster.getPlatform();
@@ -1169,8 +1174,10 @@ void CWsSMCEx::getTargetClusterAndWUs(IEspContext& context, CConstWUClusterInfoA
     else if (targetCluster.clusterType == RoxieCluster)
     {
         targetCluster.statusServerName.set("RoxieServer");
-        targetCluster.clusterQueue.foundQueueInStatusServer = foundQueueInStatusServer(context, serverStatusRoot, targetCluster.statusServerName.str(), targetCluster.clusterName.str(), ".roxie");
-        if (!targetCluster.clusterQueue.foundQueueInStatusServer)
+        cluster.getAgentQueue(targetCluster.agentQueue.queueName);
+        returnCluster->setQueueName(targetCluster.agentQueue.queueName.str());
+        targetCluster.agentQueue.foundQueueInStatusServer = foundQueueInStatusServer(context, serverStatusRoot, targetCluster.statusServerName.str(), targetCluster.clusterName.str(), ".roxie");
+        if (!targetCluster.agentQueue.foundQueueInStatusServer)
             targetCluster.clusterStatusDetails.appendf("RoxieServer %s not attached; ", targetCluster.clusterName.str());
     }
     else
@@ -1216,6 +1223,7 @@ void CWsSMCEx::getTargetClusterAndWUs(IEspContext& context, CConstWUClusterInfoA
 void CWsSMCEx::getWUsNotOnTargetCluster(IEspContext &context, IPropertyTree* serverStatusRoot, IArrayOf<IEspServerJobQueue>& serverJobQueues,
      IArrayOf<IEspActiveWorkunit>& aws)
 {
+    double version = context.getClientVersion();
     BoolHash uniqueServers;
     Owned<IPropertyTreeIterator> it(serverStatusRoot->getElements("Server"));
     ForEach(*it)
@@ -1247,7 +1255,7 @@ void CWsSMCEx::getWUsNotOnTargetCluster(IEspContext &context, IPropertyTree* ser
         if (hasWU && !uniqueServers.getValue(queueName))
         {
             uniqueServers.setValue(queueName, true);
-            addServerJobQueue(serverJobQueues, queueName.str(), serverName, serverName);
+            addServerJobQueue(version, serverJobQueues, queueName.str(), serverName, serverName);
         }
     }
 
@@ -1293,6 +1301,8 @@ void CWsSMCEx::getDFUServersAndWUs(IEspContext &context, IPropertyTree* envRoot,
     if (!envRoot)
         return;
 
+    double version = context.getClientVersion();
+
     VStringBuffer path("Software/%s", eqDfu);
     Owned<IPropertyTreeIterator> services = envRoot->getElements(path);
     ForEach(*services)
@@ -1309,7 +1319,7 @@ void CWsSMCEx::getDFUServersAndWUs(IEspContext &context, IPropertyTree* envRoot,
         {
             const char *queueName = queues.item(q);
             readDFUWUs(context, queueName, serverName, aws);
-            addServerJobQueue(serverJobQueues, queueName, serverName, "DFUserver");
+            addServerJobQueue(version, serverJobQueues, queueName, serverName, "DFUserver");
         }
     }
 }
@@ -1451,45 +1461,54 @@ bool CWsSMCEx::onActivity(IEspContext &context, IEspActivityRequest &req, IEspAc
     return true;
 }
 
-void CWsSMCEx::addServerJobQueue(IArrayOf<IEspServerJobQueue>& jobQueues, const char* queueName, const char* serverName, const char* serverType)
+void CWsSMCEx::addServerJobQueue(double version, IArrayOf<IEspServerJobQueue>& jobQueues, const char* queueName, const char* serverName, const char* serverType)
 {
     if (!queueName || !*queueName || !serverName || !*serverName || !serverType || !*serverType)
         return;
 
-    Owned<IEspServerJobQueue> jobQueue = createServerJobQueue("", "");
-    jobQueue->setQueueName(queueName);
-    jobQueue->setServerName(serverName);
-    jobQueue->setServerType(serverType);
-
+    StringBuffer queueState;
+    StringBuffer queueStateDetails;
     Owned<IJobQueue> queue = createJobQueue(queueName);
-    if (queue->stopped())
-        jobQueue->setQueueStatus("stopped");
-    else if (queue->paused())
-        jobQueue->setQueueStatus("paused");
+    if (queue->stopped(queueStateDetails))
+        queueState.set("stopped");
+    else if (queue->paused(queueStateDetails))
+        queueState.set("paused");
     else
-        jobQueue->setQueueStatus("running");
-
-    jobQueues.append(*jobQueue.getClear());
+        queueState.set("running");
+    addServerJobQueue(version, jobQueues, queueName, serverName, serverType, queueState.str(), queueStateDetails.str());
 }
 
-void CWsSMCEx::addServerJobQueue(IArrayOf<IEspServerJobQueue>& jobQueues, const char* queueName, const char* queueState, const char* serverName, const char* serverType)
+void CWsSMCEx::addServerJobQueue(double version, IArrayOf<IEspServerJobQueue>& jobQueues, const char* queueName, const char* serverName, const char* serverType, const char* queueState, const char* queueStateDetails)
 {
     if (!queueName || !*queueName || !serverName || !*serverName || !serverType || !*serverType)
         return;
 
+    if (!queueState || !*queueState)
+        queueState = "running";
+
     Owned<IEspServerJobQueue> jobQueue = createServerJobQueue("", "");
     jobQueue->setQueueName(queueName);
     jobQueue->setServerName(serverName);
     jobQueue->setServerType(serverType);
-
-    if (queueState && (strieq(queueState,"stopped") || strieq(queueState,"paused")))
-        jobQueue->setQueueStatus(queueState);
-    else
-        jobQueue->setQueueStatus("running");
+    setServerJobQueueStatus(version, jobQueue, queueState, queueStateDetails);
 
     jobQueues.append(*jobQueue.getClear());
 }
 
+void CWsSMCEx::setServerJobQueueStatus(double version, IEspServerJobQueue* jobQueue, const char* status, const char* details)
+{
+    if (!status || !*status)
+        return;
+    StringBuffer queueState;
+    if (details && *details)
+        queueState.appendf("queue %s; %s;", status, details);
+    else
+        queueState.appendf("queue %s;", status);
+    jobQueue->setQueueStatus(status);
+    if (version >= 1.17)
+        jobQueue->setStatusDetails(queueState.str());
+}
+
 void CWsSMCEx::addToThorClusterList(IArrayOf<IEspThorCluster>& clusters, IEspThorCluster* cluster, const char* sortBy, bool descending)
 {
     if (clusters.length() < 1)
@@ -1767,8 +1786,9 @@ bool CWsSMCEx::onStopQueue(IEspContext &context, IEspSMCQueueRequest &req, IEspS
     {
         checkAccess(context,THORQUEUE_FEATURE,SecAccess_Full);
 
+        StringBuffer info;
         Owned<IJobQueue> queue = createJobQueue(req.getQueueName());
-        queue->stop();
+        queue->stop(createQueueActionInfo(context, "stopped", req, info));
         AccessSuccess(context, "Stopped queue %s",req.getCluster());
         resp.setRedirectUrl("/WsSMC/");
     }
@@ -1785,8 +1805,9 @@ bool CWsSMCEx::onResumeQueue(IEspContext &context, IEspSMCQueueRequest &req, IEs
     {
         checkAccess(context,THORQUEUE_FEATURE,SecAccess_Full);
 
+        StringBuffer info;
         Owned<IJobQueue> queue = createJobQueue(req.getQueueName());
-        queue->resume();
+        queue->resume(createQueueActionInfo(context, "resumed", req, info));
         AccessSuccess(context, "Resumed queue %s",req.getCluster());
         resp.setRedirectUrl("/WsSMC/");
     }
@@ -1797,14 +1818,32 @@ bool CWsSMCEx::onResumeQueue(IEspContext &context, IEspSMCQueueRequest &req, IEs
     return true;
 }
 
+const char* CWsSMCEx::createQueueActionInfo(IEspContext &context, const char* state, IEspSMCQueueRequest &req, StringBuffer& info)
+{
+    StringBuffer peer, currentTime;
+    context.getPeer(peer);
+    const char* userId = context.queryUserId();
+    if (!userId || !*userId)
+        userId = "Unknown user";
+    CDateTime now;
+    now.setNow();
+    now.getString(currentTime);
+    info.appendf("%s by <%s> at <%s> from <%s>", state, userId, currentTime.str(), peer.str());
+    const char* comment = req.getComment();
+    if (comment && *comment)
+        info.append(": ' ").append(comment).append("'");
+    return info.str();
+}
+
 bool CWsSMCEx::onPauseQueue(IEspContext &context, IEspSMCQueueRequest &req, IEspSMCQueueResponse &resp)
 {
     try
     {
         checkAccess(context,THORQUEUE_FEATURE,SecAccess_Full);
 
+        StringBuffer info;
         Owned<IJobQueue> queue = createJobQueue(req.getQueueName());
-        queue->pause();
+        queue->pause(createQueueActionInfo(context, "paused", req, info));
         AccessSuccess(context, "Paused queue %s",req.getCluster());
         resp.setRedirectUrl("/WsSMC/");
     }
@@ -1886,10 +1925,11 @@ bool CWsSMCEx::onGetThorQueueAvailability(IEspContext &context, IEspGetThorQueue
             returnCluster->setClusterName(targetName);
             returnCluster->setQueueName(queueName);
 
+            StringBuffer info;
             Owned<IJobQueue> queue = createJobQueue(queueName);
-            if(queue->stopped())
+            if(queue->stopped(info))
                 returnCluster->setQueueStatus("stopped");
-            else if (queue->paused())
+            else if (queue->paused(info))
                 returnCluster->setQueueStatus("paused");
             else
                 returnCluster->setQueueStatus("running");

+ 5 - 3
esp/services/ws_smc/ws_smcService.hpp

@@ -45,7 +45,7 @@ class CWsSMCQueue
 {
 public:
     SCMStringBuffer queueName;
-    StringBuffer queueState;
+    StringBuffer queueState, queueStateDetails;
     bool foundQueueInStatusServer;
     unsigned countRunningJobs;
     unsigned countQueuedJobs;
@@ -119,8 +119,8 @@ private:
                                  IArrayOf<IEspCapability>& capabilities);
     void addToThorClusterList(IArrayOf<IEspThorCluster>& clusters, IEspThorCluster* cluster, const char* sortBy, bool descending);
     void addToRoxieClusterList(IArrayOf<IEspRoxieCluster>& clusters, IEspRoxieCluster* cluster, const char* sortBy, bool descending);
-    void addServerJobQueue(IArrayOf<IEspServerJobQueue>& jobQueues, const char* queueName, const char* serverName, const char* serverType);
-    void addServerJobQueue(IArrayOf<IEspServerJobQueue>& jobQueues, const char* queueName, const char* queueState, const char* serverName, const char* serverType);
+    void addServerJobQueue(double version, IArrayOf<IEspServerJobQueue>& jobQueues, const char* queueName, const char* serverName, const char* serverType);
+    void addServerJobQueue(double version, IArrayOf<IEspServerJobQueue>& jobQueues, const char* queueName, const char* serverName, const char* serverType, const char* queueState, const char* queueStateDetails);
     void getQueueState(int runningJobsInQueue, StringBuffer& queueState, BulletType& colorType);
     void readClusterTypeAndQueueName(CConstWUClusterInfoArray& clusters, const char* clusterName, StringBuffer& clusterType, SCMStringBuffer& clusterQueue);
     void addRunningWUs(IEspContext &context, IPropertyTree& node, CConstWUClusterInfoArray& clusters,
@@ -149,6 +149,8 @@ private:
     void getWUsNotOnTargetCluster(IEspContext &context, IPropertyTree* serverStatusRoot, IArrayOf<IEspServerJobQueue>& serverJobQueues, IArrayOf<IEspActiveWorkunit>& aws);
     void getDFUServersAndWUs(IEspContext &context, IPropertyTree* envRoot, IArrayOf<IEspServerJobQueue>& serverJobQueues, IArrayOf<IEspActiveWorkunit>& aws);
     void getDFURecoveryJobs(IEspContext &context, IArrayOf<IEspDFUJob>& jobs);
+    const char* createQueueActionInfo(IEspContext &context, const char* action, IEspSMCQueueRequest &req, StringBuffer& info);
+    void setServerJobQueueStatus(double version, IEspServerJobQueue* jobQueue, const char* status, const char* details);
 };