فهرست منبع

HPCC-11177 Return TargetCluster info after WsSMC.PauseQueue

1. Create a new WsSMC method: GetStatusServerInfo() which returns
information from /Status/Servers/Server for a specified target cluster
or HPCC server; 2. The same method is called to return TargetCluster
info after WsSMC.PauseQueue and WsSMC.ResumeQueue; 3. Upgrade code
to display HPCC server even if there is no workunit on the server. A
user may pause or resume the server even if no WU is runnning on the
server.

Signed-off-by: Kevin Wang kevin.wang@lexisnexis.com
Kevin Wang 11 سال پیش
والد
کامیت
852154675b
4فایلهای تغییر یافته به همراه408 افزوده شده و 43 حذف شده
  1. 30 7
      esp/eclwatch/ws_XSLT/index.xslt
  2. 32 2
      esp/scm/ws_smc.ecm
  3. 330 32
      esp/services/ws_smc/ws_smcService.cpp
  4. 16 2
      esp/services/ws_smc/ws_smcService.hpp

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

@@ -110,32 +110,35 @@
                                 document.location.href = "/WsSmc/Activity?SortBy=Name";
                         }
 
-                        function commandQueue(action,cluster,clusterType,queue,wuid)
+                        function commandQueue(action,cluster,clusterType,queue,wuid,serverType,ip,port)
                         {
                             document.getElementById("ClusterType").value=clusterType;
                             document.getElementById("Cluster").value=cluster;
                             document.getElementById("QueueName").value=queue;
                             document.getElementById("Wuid").value=wuid || '';
+                            document.getElementById("ServerType").value=serverType;
+                            document.getElementById("NetworkAddress").value=ip;
+                            document.getElementById("Port").value=port;
                             document.forms["queue"].action='/WsSMC/'+action;
                             document.forms["queue"].submit();
                         }
 
                         var oMenu;
 
-                        function queuePopup(cluster,clusterType,queue,paused,stopped,q_rowid)
+                        function queuePopup(cluster,clusterType,queue,serverType,ip,port,paused,stopped,q_rowid)
                         {
                             function clearQueue()
                             {
                                 if(confirm('Do you want to clear the queue for cluster: '+cluster+'?'))
-                                    commandQueue("ClearQueue",cluster,clusterType,queue);
+                                    commandQueue("ClearQueue",cluster,clusterType,queue,'',serverType,ip,port);
                             }
                             function pauseQueue()
                             {
-                                commandQueue("PauseQueue",cluster,clusterType,queue);
+                                commandQueue("PauseQueue",cluster,clusterType,queue,'',serverType,ip,port);
                             }
                             function resumeQueue()
                             {
-                                commandQueue("ResumeQueue",cluster,clusterType,queue);
+                                commandQueue("ResumeQueue",cluster,clusterType,queue,'',serverType,ip,port);
                             }
                             function showUsage()
                             {
@@ -566,6 +569,9 @@
                     <input type="hidden" name="Cluster" id="Cluster" value=""/>
                     <input type="hidden" name="QueueName" id="QueueName" value=""/>
                     <input type="hidden" name="Wuid" id="Wuid" value=""/>
+                    <input type="hidden" name="ServerType" id="ServerType" value=""/>
+                    <input type="hidden" name="NetworkAddress" id="NetworkAddress" value=""/>
+                    <input type="hidden" name="Port" id="Port" value=""/>
                     <xsl:for-each select="ThorClusterList/TargetCluster">
                         <xsl:call-template name="show-queue">
                             <xsl:with-param name="workunits" select="//Running/ActiveWorkunit[(Server='ThorMaster' and TargetClusterName=current()/ClusterName) or (ClusterType='Thor' and ClusterQueueName=current()/QueueName)]"/>
@@ -577,6 +583,7 @@
                             <xsl:with-param name="statusDetails" select="StatusDetails"/>
                             <xsl:with-param name="warning" select="Warning"/>
                             <xsl:with-param name="thorlcr" select="ThorLCR"/>
+                            <xsl:with-param name="serverType" select="'ThorMaster'"/>
                         </xsl:call-template>
                     </xsl:for-each>
 
@@ -590,6 +597,7 @@
                             <xsl:with-param name="clusterStatus" select="ClusterStatus"/>
                             <xsl:with-param name="statusDetails" select="StatusDetails"/>
                             <xsl:with-param name="warning" select="Warning"/>
+                            <xsl:with-param name="serverType" select="'RoxieServer'"/>
                         </xsl:call-template>
                     </xsl:for-each>
 
@@ -603,6 +611,7 @@
                             <xsl:with-param name="clusterStatus" select="ClusterStatus"/>
                             <xsl:with-param name="statusDetails" select="StatusDetails"/>
                             <xsl:with-param name="warning" select="Warning"/>
+                            <xsl:with-param name="serverType" select="'HThorServer'"/>
                         </xsl:call-template>
                     </xsl:for-each>
 
@@ -614,6 +623,9 @@
                             <xsl:with-param name="queue" select="QueueName"/>
                             <xsl:with-param name="queueStatus" select="QueueStatus"/>
                             <xsl:with-param name="statusDetails" select="StatusDetails"/>
+                            <xsl:with-param name="ip" select="NetworkAddress"/>
+                            <xsl:with-param name="port" select="Port"/>
+                            <xsl:with-param name="serverType" select="ServerType"/>
                         </xsl:call-template>
                     </xsl:for-each>
 
@@ -625,6 +637,9 @@
                             <xsl:with-param name="queue" select="QueueName"/>
                             <xsl:with-param name="queueStatus" select="QueueStatus"/>
                             <xsl:with-param name="statusDetails" select="StatusDetails"/>
+                            <xsl:with-param name="ip" select="NetworkAddress"/>
+                            <xsl:with-param name="port" select="Port"/>
+                            <xsl:with-param name="serverType" select="ServerType"/>
                         </xsl:call-template>
                     </xsl:for-each>
 
@@ -636,6 +651,9 @@
                             <xsl:with-param name="queue" select="QueueName"/>
                             <xsl:with-param name="queueStatus" select="QueueStatus"/>
                             <xsl:with-param name="statusDetails" select="StatusDetails"/>
+                            <xsl:with-param name="ip" select="NetworkAddress"/>
+                            <xsl:with-param name="port" select="Port"/>
+                            <xsl:with-param name="serverType" select="ServerType"/>
                         </xsl:call-template>
                     </xsl:for-each>
 
@@ -647,6 +665,7 @@
                             <xsl:with-param name="queue" select="QueueName"/>
                             <xsl:with-param name="queueStatus" select="QueueStatus"/>
                             <xsl:with-param name="statusDetails" select="StatusDetails"/>
+                            <xsl:with-param name="serverType" select="ServerType"/>
                         </xsl:call-template>
                     </xsl:for-each>
                     <xsl:apply-templates select="DFUJobs"/>
@@ -710,6 +729,9 @@
         <xsl:param name="statusDetails" select="''"/>
         <xsl:param name="warning" select="''"/>
         <xsl:param name="thorlcr" select="'0'"/>
+        <xsl:param name="serverType" select="''"/>
+        <xsl:param name="ip" select="''"/>
+        <xsl:param name="port" select="'0'"/>
         <xsl:variable name="showTitle">
             <xsl:choose>
                 <xsl:when test="$clusterType = 'THOR'">1</xsl:when>
@@ -761,8 +783,8 @@
                 <td valign="top">
                     <xsl:if test="$accessRight = 'Access_Full'">
                         <xsl:variable name="popup">return queuePopup('<xsl:value-of select="$cluster"/>','<xsl:value-of select="$clusterType"/>',
-                            '<xsl:value-of select="$queue"/>',<xsl:value-of select="$queueStatus='paused'"/>,
-                            <xsl:value-of select="$queueStatus='stopped'"/>, '<xsl:value-of select="$q_rowid"/>');
+                            '<xsl:value-of select="$queue"/>','<xsl:value-of select="$serverType"/>','<xsl:value-of select="$ip"/>','<xsl:value-of select="$port"/>',
+                            <xsl:value-of select="$queueStatus='paused'"/>,<xsl:value-of select="$queueStatus='stopped'"/>, '<xsl:value-of select="$q_rowid"/>');
                         </xsl:variable>
                         <a id="{$q_rowid}" class="configurecontextmenu" title="Option" onclick="{$popup}">&#160;</a>
                     </xsl:if>
@@ -811,6 +833,7 @@
                             <xsl:when test="$clusterType = 'ROXIE'">RoxieCluster - <xsl:value-of select="$cluster"/></xsl:when>
                             <xsl:when test="$clusterType = 'THOR'">ThorCluster - <xsl:value-of select="$cluster"/></xsl:when>
                             <xsl:when test="$clusterType = 'HTHOR'">HThorCluster - <xsl:value-of select="$cluster"/></xsl:when>
+                            <xsl:when test="$clusterType = 'ECLCCserver' or $clusterType = 'ECLserver'"><xsl:value-of select="$clusterType"/> - <xsl:value-of select="$cluster"/></xsl:when>
                             <xsl:otherwise>
                                 <xsl:value-of select="$clusterType"/> - <xsl:value-of select="$queue"/>
                             </xsl:otherwise>

+ 32 - 2
esp/scm/ws_smc.ecm

@@ -107,6 +107,15 @@ ESPStruct ServerJobQueue
     string ServerType;
     string QueueStatus;
     [min_ver("1.17")] string StatusDetails;
+    [min_ver("1.19")] string NetworkAddress;
+    [min_ver("1.19")] int Port;
+};
+
+ESPstruct [nil_remove] StatusServerInfo
+{
+    ESPstruct TargetCluster TargetClusterInfo;
+    ESPstruct ServerJobQueue ServerInfo;
+    ESParray<ESPstruct ActiveWorkunit> Workunits;
 };
 
 ESPrequest [nil_remove] ActivityRequest
@@ -181,15 +190,18 @@ ESPresponse [] SMCPermissionsResponse
 
 ESPrequest SMCQueueRequest
 {
-    int ClusterType;
     string Cluster;
     string QueueName;
     string Comment;
+    [min_ver("1.19")] string ServerType;
+    [min_ver("1.19")] string NetworkAddress;
+    [min_ver("1.19")] int Port;
 };
 
 
 ESPresponse [exceptions_inline] SMCQueueResponse
 {
+    [min_ver("1.19")] ESPstruct StatusServerInfo StatusServerInfo;
 };
 
 ESPrequest SMCJobRequest
@@ -328,7 +340,24 @@ RoxieControlCmdResponse
     ESParray<ESPstruct RoxieControlEndpointInfo, Endpoint> Endpoints;
 };
 
-ESPservice [noforms, version("1.18"), default_client_version("1.18"), exceptions_inline("./smc_xslt/exceptions.xslt"), use_method_name] WsSMC
+ESPrequest GetStatusServerInfoRequest
+{
+    string ServerName;
+    string ServerType;
+    string NetworkAddress;
+    int Port;
+};
+
+ESPresponse
+[
+    exceptions_inline, nil_remove
+]
+GetStatusServerInfoResponse
+{
+    ESPstruct StatusServerInfo StatusServerInfo;
+};
+
+ESPservice [noforms, version("1.19"), default_client_version("1.19"), 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);
@@ -353,6 +382,7 @@ ESPservice [noforms, version("1.18"), default_client_version("1.18"), exceptions
     ESPmethod [resp_xsl_default("/esp/xslt/hpccresourcelist.xslt")] BrowseResources(BrowseResourcesRequest, BrowseResourcesResponse);
 
     ESPmethod RoxieControlCmd(RoxieControlCmdRequest, RoxieControlCmdResponse);
+    ESPmethod GetStatusServerInfo(GetStatusServerInfoRequest, GetStatusServerInfoResponse);
 };
 
 SCMexportdef(WSSMC);

+ 330 - 32
esp/services/ws_smc/ws_smcService.cpp

@@ -32,6 +32,15 @@
 #include "exception_util.hpp"
 
 #include "roxiecontrol.hpp"
+#include "workunit.hpp"
+
+#define STATUS_SERVER_THOR "ThorMaster"
+#define STATUS_SERVER_HTHOR "HThorServer"
+#define STATUS_SERVER_ROXIE "RoxieServer"
+#define STATUS_SERVER_DFUSERVER "DFUserver"
+#define STATUS_SERVER_ECLSERVER "ECLserver"
+#define STATUS_SERVER_ECLCCSERVER "ECLCCserver"
+#define STATUS_SERVER_ECLAGENT "ECLagent"
 
 static const char* FEATURE_URL = "SmcAccess";
 const char* THORQUEUE_FEATURE = "ThorQueueAccess";
@@ -732,7 +741,7 @@ void CWsSMCEx::getServersAndWUs(IEspContext &context, IEspActivityRequest &req,
                 aws.append(*wu.getLink());
             }
 
-            addServerJobQueue(version, serverJobQueues, queueName, serverName, "ECLCCserver", queueState.str(), queueStateDetails.str());
+            addServerJobQueue(version, serverJobQueues, queueName, serverName, "ECLCCserver", NULL, 0, queueState.str(), queueStateDetails.str());
         }
     }
 
@@ -809,7 +818,7 @@ void CWsSMCEx::getServersAndWUs(IEspContext &context, IEspActivityRequest &req,
                             e->Release();
                         }
                     }
-                    addServerJobQueue(version, serverJobQueues, queueName, serverName, "DFUserver");
+                    addServerJobQueue(version, serverJobQueues, queueName, serverName, "DFUserver", NULL, 0);
                 }
             }
         } while (services->next());
@@ -1089,12 +1098,12 @@ void CWsSMCEx::getWUsNotOnTargetCluster(IEspContext &context, IPropertyTree* ser
         IPropertyTree& serverNode = it->query();
         const char* serverName = serverNode.queryProp("@name");
         const char* instance = serverNode.queryProp("@node");
-        if (!serverName || !*serverName || !instance || !*instance)
+        const char* queueName = serverNode.queryProp("@queue");
+        unsigned port = serverNode.getPropInt("@mpport", 0);
+        if (!serverName || !*serverName || !instance || !*instance || strieq(serverName, "DFUserver"))//DFUServer already handled separately
             continue;
 
-        bool hasWU = false;
-        StringBuffer queueName;
-        queueName.appendf("%s_on_%s", serverName, instance);
+        VStringBuffer instanceName("%s_on_%s:%d", serverName, instance, port);
         Owned<IPropertyTreeIterator> wuids(serverNode.getElements("WorkUnit"));
         ForEach(*wuids)
         {
@@ -1106,14 +1115,13 @@ void CWsSMCEx::getWUsNotOnTargetCluster(IEspContext &context, IPropertyTree* ser
                 continue;
 
             Owned<IEspActiveWorkunit> wu;
-            createActiveWorkUnit(wu, context, wuid, NULL, 0, serverName, queueName.str(), instance, NULL);
+            createActiveWorkUnit(wu, context, wuid, NULL, 0, serverName, queueName, instance, NULL);
             aws.append(*wu.getLink());
-            hasWU = true;
         }
-        if (hasWU && !uniqueServers.getValue(queueName))
+        if (!uniqueServers.getValue(instanceName))
         {
-            uniqueServers.setValue(queueName, true);
-            addServerJobQueue(version, serverJobQueues, queueName.str(), serverName, serverName);
+            uniqueServers.setValue(instanceName, true);
+            addServerJobQueue(version, serverJobQueues, queueName, instanceName, serverName, instance, port);
         }
     }
 
@@ -1177,7 +1185,7 @@ void CWsSMCEx::getDFUServersAndWUs(IEspContext &context, IPropertyTree* envRoot,
         {
             const char *queueName = queues.item(q);
             readDFUWUs(context, queueName, serverName, aws);
-            addServerJobQueue(version, serverJobQueues, queueName, serverName, "DFUserver");
+            addServerJobQueue(version, serverJobQueues, queueName, serverName, "DFUserver", NULL, 0);
         }
     }
 }
@@ -1346,9 +1354,12 @@ void CWsSMCEx::readTargetClusterInfo(IEspContext& context, IConstWUClusterInfo&
     targetCluster->statusServerName.set(statusServerName.str());
     targetCluster->queueName.set(jobQueue->queueName.str());
 
-    jobQueue->foundQueueInStatusServer = findQueueInStatusServer(context, serverStatusRoot, statusServerName.str(), targetCluster->queueName.get());
-    if (!jobQueue->foundQueueInStatusServer)
-        targetCluster->clusterStatusDetails.appendf("Cluster %s not attached; ", clusterName.str());
+    if (serverStatusRoot)
+    {
+        jobQueue->foundQueueInStatusServer = findQueueInStatusServer(context, serverStatusRoot, statusServerName.str(), targetCluster->queueName.get());
+        if (!jobQueue->foundQueueInStatusServer)
+            targetCluster->clusterStatusDetails.appendf("Cluster %s not attached; ", clusterName.str());
+    }
 
     return;
 }
@@ -1486,22 +1497,24 @@ void CWsSMCEx::readRunningWUsOnStatusServer(IEspContext& context, IPropertyTree*
 void CWsSMCEx::readWUsAndStateFromJobQueue(IEspContext& context, CIArrayOf<CWsSMCTargetCluster>& targetClusters, BoolHash& uniqueWUIDs, IArrayOf<IEspActiveWorkunit>& aws)
 {
     ForEachItemIn(i, targetClusters)
+        readWUsAndStateFromJobQueue(context, targetClusters.item(i), uniqueWUIDs, aws);
+}
+
+void CWsSMCEx::readWUsAndStateFromJobQueue(IEspContext& context, CWsSMCTargetCluster& targetCluster, BoolHash& uniqueWUIDs, IArrayOf<IEspActiveWorkunit>& aws)
+{
+    if (targetCluster.clusterType == ThorLCRCluster)
     {
-        CWsSMCTargetCluster& targetCluster = targetClusters.item(i);
-        if (targetCluster.clusterType == ThorLCRCluster)
-        {
-            readWUsAndStateFromJobQueue(context, targetCluster, targetCluster.clusterQueue, NULL, uniqueWUIDs, aws);
-            targetCluster.queueStatus.set(targetCluster.clusterQueue.queueState);
-        }
-        if (targetCluster.agentQueue.queueName.length())
-        {
-            readWUsAndStateFromJobQueue(context, targetCluster, targetCluster.agentQueue, targetCluster.agentQueue.queueName.str(), uniqueWUIDs, aws);
-            if (targetCluster.clusterType != ThorLCRCluster)
-                targetCluster.queueStatus.set(targetCluster.agentQueue.queueState);
-        }
-        if (targetCluster.serverQueue.queueName.length())
-            readWUsAndStateFromJobQueue(context, targetCluster, targetCluster.serverQueue, targetCluster.serverQueue.queueName.str(), uniqueWUIDs, aws);
+        readWUsAndStateFromJobQueue(context, targetCluster, targetCluster.clusterQueue, NULL, uniqueWUIDs, aws);
+        targetCluster.queueStatus.set(targetCluster.clusterQueue.queueState);
     }
+    if (targetCluster.agentQueue.queueName.length())
+    {
+        readWUsAndStateFromJobQueue(context, targetCluster, targetCluster.agentQueue, targetCluster.agentQueue.queueName.str(), uniqueWUIDs, aws);
+        if (targetCluster.clusterType != ThorLCRCluster)
+            targetCluster.queueStatus.set(targetCluster.agentQueue.queueState);
+    }
+    if (targetCluster.serverQueue.queueName.length())
+        readWUsAndStateFromJobQueue(context, targetCluster, targetCluster.serverQueue, targetCluster.serverQueue.queueName.str(), uniqueWUIDs, aws);
 }
 
 CWsSMCTargetCluster* CWsSMCEx::findTargetCluster(const char* clusterName, CIArrayOf<CWsSMCTargetCluster>& targetClusters)
@@ -1593,7 +1606,8 @@ void CWsSMCEx::setESPTargetClusters(IEspContext& context, CIArrayOf<CWsSMCTarget
     }
 }
 
-void CWsSMCEx::addServerJobQueue(double version, 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, const char* networkAddress, unsigned port)
 {
     if (!queueName || !*queueName || !serverName || !*serverName || !serverType || !*serverType)
         return;
@@ -1607,10 +1621,11 @@ void CWsSMCEx::addServerJobQueue(double version, IArrayOf<IEspServerJobQueue>& j
         queueState.set("paused");
     else
         queueState.set("running");
-    addServerJobQueue(version, jobQueues, queueName, serverName, serverType, queueState.str(), queueStateDetails.str());
+    addServerJobQueue(version, jobQueues, queueName, serverName, serverType, networkAddress, port, queueState.str(), queueStateDetails.str());
 }
 
-void CWsSMCEx::addServerJobQueue(double version, IArrayOf<IEspServerJobQueue>& jobQueues, const char* queueName, const char* serverName, const char* serverType, const char* queueState, const char* queueStateDetails)
+void CWsSMCEx::addServerJobQueue(double version, IArrayOf<IEspServerJobQueue>& jobQueues, const char* queueName, const char* serverName,
+    const char* serverType, const char* networkAddress, unsigned port, const char* queueState, const char* queueStateDetails)
 {
     if (!queueName || !*queueName || !serverName || !*serverName || !serverType || !*serverType)
         return;
@@ -1622,6 +1637,11 @@ void CWsSMCEx::addServerJobQueue(double version, IArrayOf<IEspServerJobQueue>& j
     jobQueue->setQueueName(queueName);
     jobQueue->setServerName(serverName);
     jobQueue->setServerType(serverType);
+    if ((version >= 1.19) && networkAddress && *networkAddress)
+    {
+        jobQueue->setNetworkAddress(networkAddress);
+        jobQueue->setPort(port);
+    }
     setServerJobQueueStatus(version, jobQueue, queueState, queueStateDetails);
 
     jobQueues.append(*jobQueue.getClear());
@@ -1922,6 +1942,10 @@ bool CWsSMCEx::onStopQueue(IEspContext &context, IEspSMCQueueRequest &req, IEspS
         Owned<IJobQueue> queue = createJobQueue(req.getQueueName());
         queue->stop(createQueueActionInfo(context, "stopped", req, info));
         AccessSuccess(context, "Stopped queue %s",req.getCluster());
+        double version = context.getClientVersion();
+        if (version >= 1.19)
+            getStatusServerInfo(context, req.getServerType(), req.getCluster(), req.getNetworkAddress(), req.getPort(), resp.updateStatusServerInfo());
+
         resp.setRedirectUrl("/WsSMC/");
     }
     catch(IException* e)
@@ -1941,6 +1965,10 @@ bool CWsSMCEx::onResumeQueue(IEspContext &context, IEspSMCQueueRequest &req, IEs
         Owned<IJobQueue> queue = createJobQueue(req.getQueueName());
         queue->resume(createQueueActionInfo(context, "resumed", req, info));
         AccessSuccess(context, "Resumed queue %s",req.getCluster());
+        double version = context.getClientVersion();
+        if (version >= 1.19)
+            getStatusServerInfo(context, req.getServerType(), req.getCluster(), req.getNetworkAddress(), req.getPort(), resp.updateStatusServerInfo());
+
         resp.setRedirectUrl("/WsSMC/");
     }
     catch(IException* e)
@@ -1977,6 +2005,10 @@ bool CWsSMCEx::onPauseQueue(IEspContext &context, IEspSMCQueueRequest &req, IEsp
         Owned<IJobQueue> queue = createJobQueue(req.getQueueName());
         queue->pause(createQueueActionInfo(context, "paused", req, info));
         AccessSuccess(context, "Paused queue %s",req.getCluster());
+        double version = context.getClientVersion();
+        if (version >= 1.19)
+            getStatusServerInfo(context, req.getServerType(), req.getCluster(), req.getNetworkAddress(), req.getPort(), resp.updateStatusServerInfo());
+
         resp.setRedirectUrl("/WsSMC/");
     }
     catch(IException* e)
@@ -1999,6 +2031,10 @@ bool CWsSMCEx::onClearQueue(IEspContext &context, IEspSMCQueueRequest &req, IEsp
             queue->clear();
         }
         AccessSuccess(context, "Cleared queue %s",req.getCluster());
+        double version = context.getClientVersion();
+        if (version >= 1.19)
+            getStatusServerInfo(context, req.getServerType(), req.getCluster(), req.getNetworkAddress(), req.getPort(), resp.updateStatusServerInfo());
+
         resp.setRedirectUrl("/WsSMC/");
     }
     catch(IException* e)
@@ -2480,3 +2516,265 @@ bool CWsSMCEx::onRoxieControlCmd(IEspContext &context, IEspRoxieControlCmdReques
     resp.setEndpoints(respEndpoints);
     return true;
 }
+
+bool CWsSMCEx::onGetStatusServerInfo(IEspContext &context, IEspGetStatusServerInfoRequest &req, IEspGetStatusServerInfoResponse &resp)
+{
+    getStatusServerInfo(context, req.getServerType(), req.getServerName(), req.getNetworkAddress(), req.getPort(), resp.updateStatusServerInfo());
+    return true;
+}
+
+void CWsSMCEx::getStatusServerInfo(IEspContext &context, const char *serverType, const char *server, const char *networkAddress, unsigned port,
+    IEspStatusServerInfo& statusServerInfo)
+{
+    if (!serverType || !*serverType)
+        throw MakeStringException(ECLWATCH_MISSING_PARAMS, "Server type not specified.");
+
+    if (strieq(serverType,STATUS_SERVER_THOR) || strieq(serverType,STATUS_SERVER_HTHOR) || strieq(serverType,STATUS_SERVER_ROXIE))
+    {
+        if (!server || !*server)
+            throw MakeStringException(ECLWATCH_MISSING_PARAMS, "cluster not specified.");
+        getStatusServerInfo(context, server, statusServerInfo);
+    }
+    else if (!strieq(serverType,STATUS_SERVER_DFUSERVER))
+    {
+        if (!networkAddress || !*networkAddress)
+            throw MakeStringException(ECLWATCH_MISSING_PARAMS, "Server network address not specified.");
+        getStatusServerInfo(context, serverType, networkAddress, port, statusServerInfo);
+    }
+    else
+    {
+        if (!server || !*server)
+            throw MakeStringException(ECLWATCH_MISSING_PARAMS, "Server not specified.");
+        getDFUServerInfo(context, server, statusServerInfo);
+    }
+    return;
+}
+
+void CWsSMCEx::getStatusServerInfo(IEspContext &context, const char* clusteName, IEspStatusServerInfo& statusServerInfo)
+{
+    double version = context.getClientVersion();
+
+    Owned<IConstWUClusterInfo> info = getTargetClusterInfo(clusteName);
+    if (!info)
+        throw MakeStringException(ECLWATCH_CANNOT_GET_ENV_INFO,"Failed to get target cluster information.");
+    CWsSMCTargetCluster targetCluster;
+    readTargetClusterInfo(context, *info, NULL, &targetCluster);
+
+    bool foundQueueInStatusServer = false;
+    Owned<IPropertyTree> statusServerTree = getStatusServerTree(info);
+    if (statusServerTree)
+    {
+        foundQueueInStatusServer = true;
+
+        BoolHash uniqueWUIDs;
+        IArrayOf<IEspActiveWorkunit> aws;
+        StringBuffer networkAddress;
+        statusServerTree->getProp("@node", networkAddress);
+        unsigned port = statusServerTree->getPropInt("@mpport");
+        readRunningWUsOnCluster(context, clusteName, networkAddress.str(), port, targetCluster, statusServerTree, uniqueWUIDs, aws);
+        readWUsAndStateFromJobQueue(context, targetCluster, uniqueWUIDs, aws);
+        statusServerInfo.setWorkunits(aws);
+    }
+
+    IEspTargetCluster& clusterInfo =  statusServerInfo.updateTargetClusterInfo();
+    clusterInfo.setClusterName(targetCluster.clusterName.get());
+    clusterInfo.setClusterSize(targetCluster.clusterSize);
+    clusterInfo.setClusterType(targetCluster.clusterType);
+    clusterInfo.setQueueName(targetCluster.queueName.get());
+    clusterInfo.setQueueStatus(targetCluster.queueStatus.get());
+    if (targetCluster.clusterType != ThorLCRCluster)
+        targetCluster.agentQueue.foundQueueInStatusServer = foundQueueInStatusServer;
+    else
+        targetCluster.clusterQueue.foundQueueInStatusServer = foundQueueInStatusServer;
+    setClusterStatus(context, targetCluster, &clusterInfo);
+}
+
+void CWsSMCEx::getStatusServerInfo(IEspContext &context, const char* type, const char *networkAddress, unsigned port, IEspStatusServerInfo& statusServerInfo)
+{
+    double version = context.getClientVersion();
+
+    Owned<IPropertyTree> statusServerTree = getStatusServerTree(networkAddress, port);
+    if (!statusServerTree)
+        throw MakeStringException(ECLWATCH_CANNOT_GET_STATUS_INFO, "Server not attached");
+
+    IEspServerJobQueue& serverInfo =  statusServerInfo.updateServerInfo();
+    serverInfo.setNetworkAddress(networkAddress);
+    serverInfo.setPort(port);
+
+    StringBuffer queueName, instance;
+    statusServerTree->getProp("@queue", queueName);
+    setServerJobQueue(version, type, NULL, queueName.str(), serverInfo);
+    instance.appendf("%s on %s:%d", type, networkAddress, port);
+
+    IArrayOf<IEspActiveWorkunit> aws;
+    Owned<IPropertyTreeIterator> wuids(statusServerTree->getElements("WorkUnit"));
+    ForEach(*wuids)
+    {
+        const char* wuid=wuids->query().queryProp(NULL);
+        if (!wuid || !*wuid)
+            continue;
+
+        Owned<IEspActiveWorkunit> wu;
+        createActiveWorkUnit(wu, context, wuid, NULL, 0, type, queueName.str(), instance, NULL);
+        aws.append(*wu.getLink());
+    }
+    statusServerInfo.setWorkunits(aws);
+}
+
+void CWsSMCEx::getDFUServerInfo(IEspContext &context, const char* serverName, IEspStatusServerInfo& statusServerInfo)
+{
+    double version = context.getClientVersion();
+
+    VStringBuffer xpath("/Environment/Software/%s[@name=\"%s\"]", eqDfu, serverName);
+    Owned<IRemoteConnection> connEnv = querySDS().connect(xpath.str(), myProcessSession(), RTM_LOCK_READ, SDS_LOCK_TIMEOUT);
+    if (!connEnv)
+        throw MakeStringException(ECLWATCH_CANNOT_GET_ENV_INFO,"Failed to get environment information.");
+    IPropertyTree* serviceTree = connEnv->queryRoot();
+    if (!serviceTree)
+        throw MakeStringException(ECLWATCH_CANNOT_GET_ENV_INFO,"Failed to get environment information.");
+    const char *queueName = serviceTree->queryProp("@queue");
+    if (!queueName || !*queueName)
+        throw MakeStringException(ECLWATCH_CANNOT_GET_ENV_INFO, "Server queue not found.");
+
+    setServerJobQueue(version, "DFUserver", serverName, queueName, statusServerInfo.updateServerInfo());
+
+    IArrayOf<IEspActiveWorkunit> aws;
+    readDFUWUs(context, queueName, serverName, aws);
+    statusServerInfo.setWorkunits(aws);
+}
+
+IPropertyTree* CWsSMCEx::getStatusServerTree(IConstWUClusterInfo* info)
+{
+    SCMStringBuffer str;
+    StringBuffer xpath;
+    if (info->getPlatform() != HThorCluster)
+    {
+        if (info->getPlatform() == ThorLCRCluster)
+            xpath.setf("/Status/Servers/Server[@name=\"%s\"][@cluster=\"%s\"]", getStatusServerTypeName(WsSMCSSTThorLCRCluster), info->getThorProcesses().item(0));
+        else
+            xpath.setf("/Status/Servers/Server[@name=\"%s\"][@cluster=\"%s\"]", getStatusServerTypeName(WsSMCSSTRoxieCluster), info->getRoxieProcess(str).str());
+        Owned<IRemoteConnection> connStatusServer = querySDS().connect(xpath.str(),myProcessSession(),RTM_LOCK_READ,SDS_LOCK_TIMEOUT);
+        if (!connStatusServer)
+            return NULL;
+
+        Owned<IPropertyTree> retServerTree = connStatusServer->queryRoot()->getBranch(NULL);
+        return retServerTree.getClear();
+    }
+    else
+    {
+        Owned<IRemoteConnection> connStatusServer = querySDS().connect("/Status/Servers",myProcessSession(),RTM_LOCK_READ,SDS_LOCK_TIMEOUT);
+        if (!connStatusServer)
+            throw MakeStringException(ECLWATCH_CANNOT_GET_STATUS_INFO, "Status servers not found");
+
+        info->getAgentQueue(str);
+        xpath.setf("Server[@name=\"%s\"]", getStatusServerTypeName(WsSMCSSTHThorCluster));
+        Owned<IPropertyTreeIterator> it(connStatusServer->queryRoot()->getElements(xpath));
+        ForEach(*it)
+        {
+            IPropertyTree &serverTree = it->query();
+            const char *queueNames = serverTree.queryProp("@queue");
+            if (!queueNames || !*queueNames)
+                continue;
+
+            StringArray qlist;
+            qlist.appendListUniq(queueNames, ",");
+            ForEachItemIn(q, qlist)
+            {
+                if (!strieq(qlist.item(q), str.str()))
+                    continue;
+
+                Owned<IPropertyTree> retServerTree = serverTree.getBranch(NULL);
+                return retServerTree.getClear();
+            }
+        }
+    }
+    return NULL;
+}
+
+IPropertyTree* CWsSMCEx::getStatusServerTree(const char *networkAddress, unsigned port)
+{
+    if (!networkAddress || !*networkAddress)
+        throw MakeStringException(ECLWATCH_INVALID_INPUT, "Network Address not specified");
+
+    VStringBuffer xpath("/Status/Servers/Server[@node=\"%s\"][@mpport=\"%d\"]", networkAddress, port);
+    Owned<IRemoteConnection> connStatusServer = querySDS().connect(xpath.str(),myProcessSession(),RTM_LOCK_READ,SDS_LOCK_TIMEOUT);
+    if (!connStatusServer)
+        return NULL;
+
+    Owned<IPropertyTree> retServerTree = connStatusServer->queryRoot()->getBranch(NULL);
+    return retServerTree.getClear();
+}
+
+void CWsSMCEx::readRunningWUsOnCluster(IEspContext& context, const char* serverName, const char* node, unsigned port,
+        CWsSMCTargetCluster& targetCluster, IPropertyTree* statusServerNode, BoolHash& uniqueWUIDs, IArrayOf<IEspActiveWorkunit>& aws)
+{
+    const char *cluster = statusServerNode->queryProp("Cluster");
+    StringBuffer queueName;
+    if (cluster) // backward compat check.
+        getClusterThorQueueName(queueName, cluster);
+    else
+        queueName.append(targetCluster.queueName.get());
+
+    CWsSMCQueue* jobQueue;
+    if (targetCluster.clusterType == ThorLCRCluster)
+        jobQueue = &targetCluster.clusterQueue;
+    else
+        jobQueue = &targetCluster.agentQueue;
+
+    StringBuffer instance;
+    if ((targetCluster.clusterType == ThorLCRCluster) || (targetCluster.clusterType == RoxieCluster))
+        statusServerNode->getProp("@cluster", instance);
+    else
+        instance.appendf("%s on %s:%d", serverName, node, port);
+
+    const char* targetClusterName = targetCluster.clusterName.get();
+    Owned<IPropertyTreeIterator> wuids(statusServerNode->getElements("WorkUnit"));
+    ForEach(*wuids)
+    {
+        const char* wuid=wuids->query().queryProp(NULL);
+        if (!wuid || !*wuid)
+            continue;
+
+        Owned<IEspActiveWorkunit> wu;
+        createActiveWorkUnit(wu, context, wuid, !strieq(targetClusterName, instance.str()) ? instance.str() : NULL, 0, serverName, queueName, instance.str(), targetClusterName);
+        if (wu->getStateID() == WUStateRunning) //'aborting' may be another possible status
+        {
+            StringBuffer durationStr, subgraphStr;
+            int sgDuration = statusServerNode->getPropInt("@sg_duration", -1);
+            int subgraph = statusServerNode->getPropInt("@subgraph", -1);
+            const char* graph = statusServerNode->queryProp("@graph");
+            durationStr.appendf("%d min", sgDuration);
+            subgraphStr.appendf("%d", subgraph);
+            if (subgraph > -1 && sgDuration > -1)
+            {
+                wu->setGraphName(graph);
+                wu->setDuration(durationStr.str());
+                wu->setGID(subgraphStr.str());
+            }
+
+            if (statusServerNode->getPropInt("@memoryBlocked ", 0) != 0)
+                wu->setMemoryBlocked(1);
+        }
+
+        aws.append(*wu.getLink());
+        jobQueue->countRunningJobs++;
+    }
+}
+
+void CWsSMCEx::setServerJobQueue(double version, const char* serverType, const char* serverName, const char* queueName, IEspServerJobQueue& serverInfo)
+{
+    StringBuffer queueState, queueStateDetails;
+    Owned<IJobQueue> queue = createJobQueue(queueName);
+    if (queue->stopped(queueStateDetails))
+        queueState.set("stopped");
+    else if (queue->paused(queueStateDetails))
+        queueState.set("paused");
+    else
+        queueState.set("running");
+
+    serverInfo.setQueueName(queueName);
+    serverInfo.setServerType(serverType);
+    if (serverName && *serverName)
+        serverInfo.setServerName(serverName);
+    setServerJobQueueStatus(version, &serverInfo, queueState, queueStateDetails);
+}

+ 16 - 2
esp/services/ws_smc/ws_smcService.hpp

@@ -128,13 +128,16 @@ public:
 
     virtual bool onBrowseResources(IEspContext &context, IEspBrowseResourcesRequest & req, IEspBrowseResourcesResponse & resp);
     virtual bool onRoxieControlCmd(IEspContext &context, IEspRoxieControlCmdRequest &req, IEspRoxieControlCmdResponse &resp);
+    virtual bool onGetStatusServerInfo(IEspContext &context, IEspGetStatusServerInfoRequest &req, IEspGetStatusServerInfoResponse &resp);
 private:
     void addCapabilities( IPropertyTree* pFeatureNode, const char* access, 
                                  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(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 addServerJobQueue(double version, IArrayOf<IEspServerJobQueue>& jobQueues, const char* queueName, const char* serverName,
+        const char* serverType, const char* networkAddress, unsigned port);
+    void addServerJobQueue(double version, IArrayOf<IEspServerJobQueue>& jobQueues, const char* queueName, const char* serverName,
+        const char* serverType, const char* networkAddress, unsigned port, 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,
@@ -179,12 +182,23 @@ private:
     void readRunningWUsOnStatusServer(IEspContext& context, IPropertyTree* serverStatusRoot, WsSMCStatusServerType statusServerType,
             CIArrayOf<CWsSMCTargetCluster>& targetClusters, CIArrayOf<CWsSMCTargetCluster>& targetClusters1, CIArrayOf<CWsSMCTargetCluster>& targetClusters2,
             BoolHash& uniqueWUIDs, IArrayOf<IEspActiveWorkunit>& aws);
+    void readWUsAndStateFromJobQueue(IEspContext& context, CWsSMCTargetCluster& targetCluster, BoolHash& uniqueWUIDs, IArrayOf<IEspActiveWorkunit>& aws);
     void readWUsAndStateFromJobQueue(IEspContext& context, CIArrayOf<CWsSMCTargetCluster>& targetClusters, BoolHash& uniqueWUIDs, IArrayOf<IEspActiveWorkunit>& aws);
     void setESPTargetClusters(IEspContext& context, CIArrayOf<CWsSMCTargetCluster>& targetClusters, IArrayOf<IEspTargetCluster>& respTargetClusters);
     void updateActivityResponse(IEspContext &context,  IEspActivityRequest &req, IEspActivityResponse& resp,
             CIArrayOf<CWsSMCTargetCluster>& thorTargetClusters, CIArrayOf<CWsSMCTargetCluster>& roxieTargetClusters, CIArrayOf<CWsSMCTargetCluster>& hthorTargetClusters,
             IArrayOf<IEspActiveWorkunit>& aws, IArrayOf<IEspServerJobQueue>& serverJobQueues, IArrayOf<IEspDFUJob>& DFURecoveryJobs);
     const char *getStatusServerTypeName(WsSMCStatusServerType type);
+    void getStatusServerInfo(IEspContext &context, const char *serverType, const char *server, const char *networkAddress, unsigned port,
+        IEspStatusServerInfo& statusServerInfo);
+    void getStatusServerInfo(IEspContext &context, const char* name, IEspStatusServerInfo& statusServerInfo);
+    void getStatusServerInfo(IEspContext &context, const char* type, const char *node, unsigned port, IEspStatusServerInfo& statusServerInfo);
+    void getDFUServerInfo(IEspContext &context, const char* name, IEspStatusServerInfo& statusServerInfo);
+    void setServerJobQueue(double version, const char* type, const char* name, const char* queueName, IEspServerJobQueue& serverInfo);
+    IPropertyTree* getStatusServerTree(IConstWUClusterInfo* info);
+    IPropertyTree* getStatusServerTree(const char *networkAddress, unsigned port);
+    void readRunningWUsOnCluster(IEspContext& context, const char* serverName, const char* node, unsigned port,
+        CWsSMCTargetCluster& targetCluster, IPropertyTree* statusServerNode, BoolHash& uniqueWUIDs, IArrayOf<IEspActiveWorkunit>& aws);
 };