Ver código fonte

Merge pull request #1842 from hpcc-systems/candidate-3.6.x

Merge latest 3.6.x fixes back into master

Reviewed-by: Gavin Halliday <ghalliday@hpccsystems.com>
Gavin Halliday 13 anos atrás
pai
commit
9f367f9302

+ 7 - 8
dali/base/dasds.cpp

@@ -5752,6 +5752,7 @@ void CCovenSDSManager::loadStore(const char *storeName, const bool *abort)
         ICopyArrayOf<CServerRemoteTree> convertQueue;
     } treeMaker(&nodeCreator);
 
+    Owned<IPropertyTree> oldEnvironment;
     try
     {
         bool saveNeeded = false;
@@ -5783,7 +5784,6 @@ void CCovenSDSManager::loadStore(const char *storeName, const bool *abort)
         LOG(MCdebugInfo(100), unknownJob, "store loaded");
         const char *environment = config.queryProp("@environment");
 
-        Owned<IPropertyTree> oldEnvironment;
         if (environment && *environment)
         {
             LOG(MCdebugInfo(100), unknownJob, "loading external Environment from: %s", environment);
@@ -5803,13 +5803,6 @@ void CCovenSDSManager::loadStore(const char *storeName, const bool *abort)
             externalEnvironment = true;
         }
 
-        bool forceGroupUpdate = config.getPropBool("@forceGroupUpdate");
-        StringBuffer response;
-        initClusterGroups(forceGroupUpdate, response, oldEnvironment);
-        if (response.length())
-            PROGLOG("DFS group initialization : %s", response.str()); // should this be a syslog?
-
-
         UInt64Array refExts;
         PROGLOG("Scanning store for external references");
         Owned<IPropertyTreeIterator> rootIter = root->getElements("//*");
@@ -6118,6 +6111,12 @@ void CCovenSDSManager::loadStore(const char *storeName, const bool *abort)
     }
     Owned<IRemoteConnection> conn = connect("/", 0, RTM_INTERNAL, INFINITE);
     initializeInternals(conn->queryRoot());
+    conn.clear();
+    bool forceGroupUpdate = config.getPropBool("@forceGroupUpdate");
+    StringBuffer response;
+    initClusterGroups(forceGroupUpdate, response, oldEnvironment);
+    if (response.length())
+        PROGLOG("DFS group initialization : %s", response.str()); // should this be a syslog?
 }
 
 void CCovenSDSManager::saveStore(const char *storeName, bool currentEdition)

+ 18 - 2
ecl/eclcmd/queries/ecl-queries.cpp

@@ -146,17 +146,33 @@ public:
     {
         const char *queryid = query.getId();
         bool isActive = queryMap.isActive(queryid);
+        bool suspendedOnCluster = false;
+        ForEachItemIn(idx, query.getClusters())
+        {
+            IConstClusterQueryState &state = query.getClusters().item(idx);
+            if (state.getSuspended())
+            {
+                suspendedOnCluster = true;
+                break;
+            }
+        }
+
         if (flags)
         {
             if (isActive && !(flags & QUERYLIST_SHOW_ACTIVE))
                 return;
             if (query.getSuspended() && !(flags & QUERYLIST_SHOW_SUSPENDED))
                 return;
+            if (suspendedOnCluster && !(flags & QUERYLIST_SHOW_CLUSTER_SUSPENDED))
+                return;
             if (!isActive && !query.getSuspended() &&  !(flags & QUERYLIST_SHOW_UNFLAGGED))
                 return;
         }
-        VStringBuffer line("  %c%c  ", query.getSuspended() ? 'S' : ' ', isActive ? 'A' : ' ');
-        line.append(queryid);
+        StringBuffer line(" ");
+        line.append(suspendedOnCluster ? 'X' : ' ');
+        line.append(query.getSuspended() ? 'S' : ' ');
+        line.append(isActive ? 'A' : ' ');
+        line.append(' ').append(queryid);
         if (isActive)
         {
             Owned<IPropertyTreeIterator> activeNames = queryMap.getActiveNames(queryid);

+ 1 - 1
ecl/hql/hqlerror.cpp

@@ -286,7 +286,7 @@ void checkEclVersionCompatible(Shared<IErrorReceiver> & errors, const char * ecl
                 msg.appendf("Mismatch in minor version number (%s v %s)", eclVersion, LANGUAGE_VERSION);
                 errors->reportWarning(HQLERR_VersionMismatch, msg.str(), NULL, 0, 0, 0);
             }
-            else if (subminor != LANGUAGE_VERSION_MINOR)
+            else if (subminor != LANGUAGE_VERSION_SUB)
             {
                 //This adds the warning if any other warnings occur.
                 StringBuffer msg;

+ 64 - 18
esp/eclwatch/ws_XSLT/index.xslt

@@ -20,6 +20,8 @@
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output method="html"/>
     <xsl:variable name="chaturl0" select="ActivityResponse/ChatURL"/>
+    <xsl:variable name="sortby" select="ActivityResponse/SortBy"/>
+    <xsl:variable name="descending" select="ActivityResponse/Descending"/>
     <xsl:template match="ActivityResponse">
         <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
             <head>
@@ -38,14 +40,40 @@
         <script type="text/javascript" src="/esp/files/yui/build/menu/menu-min.js"></script>
         ]]></xsl:text>
         <title>EclWatch</title>
-                <script type="text/javascript">
-                    var chatUrl='<xsl:value-of select="$chaturl0"/>';
-                </script>
-                <script language="JavaScript1.2" id="menuhandlers">
-                  //var chatUrl="chaturl";
-
-                  <xsl:text disable-output-escaping="yes"><![CDATA[
+        <script type="text/javascript">
+            var chatUrl='<xsl:value-of select="$chaturl0"/>';
+            var sortBy='<xsl:value-of select="$sortby"/>';
+            var descending='<xsl:value-of select="$descending"/>';
+        </script>
+        <script language="JavaScript1.2" id="menuhandlers">
+            <xsl:text disable-output-escaping="yes"><![CDATA[
+                function onLoad()
+                {
+                    var selection = document.getElementById("sortClusters");
+                    if (selection != NaN)
+                    {
+                        if (sortBy == 'Name' && descending == 1)
+                            selection.options[1].selected="selected";
+                        else if (sortBy == 'Size' && descending == 0)
+                            selection.options[2].selected="selected";
+                        else if (sortBy == 'Size' && descending == 1)
+                            selection.options[3].selected="selected";
+                        else
+                            selection.options[0].selected="selected";
+                    }
+                }
 
+                function sortClustersChanged(sortClusterBy)
+                {
+                    if (sortClusterBy == 2)
+                        document.location.href = "/WsSmc/Activity?SortBy=Name&Descending=1";
+                    else if (sortClusterBy == 3)
+                        document.location.href = "/WsSmc/Activity?SortBy=Size";
+                    else if (sortClusterBy == 4)
+                        document.location.href = "/WsSmc/Activity?SortBy=Size&Descending=1";
+                    else
+                        document.location.href = "/WsSmc/Activity?SortBy=Name";
+                }
 
                 function commandQueue(action,isThor,cluster,queue,wuid)
                 {
@@ -465,7 +493,7 @@
                ]]></xsl:text>
                </script> 
             </head>
-            <body class="yui-skin-sam" onload="nof5()">
+            <body class="yui-skin-sam" onload="nof5();onLoad()">
                 <form>
                     <table>
                         <xsl:if test="ShowChatURL = 1">
@@ -492,16 +520,34 @@
                         </xsl:if>
                     </table>
                 </form>
-                <xsl:choose>
-                    <xsl:when test="UserPermission = 0">
-                        <A href="javascript:void(0)" onclick="SetBanner();">
-                            <h3>Existing Activity on Servers:</h3>
-                        </A>
-                    </xsl:when>
-                    <xsl:otherwise>
-                        <h3>Existing Activity on Servers:</h3>
-                    </xsl:otherwise>
-                </xsl:choose>
+                <table width="100%">
+                    <tbody>
+                        <tr>
+                            <td align="left">
+                                <xsl:choose>
+                                    <xsl:when test="UserPermission = 0">
+                                        <A href="javascript:void(0)" onclick="SetBanner();">
+                                            <h3>Existing Activity on Servers:</h3>
+                                        </A>
+                                    </xsl:when>
+                                    <xsl:otherwise>
+                                        <h3>
+                                            Existing Activity on Servers:<xsl:value-of select="SortBy"/>
+                                        </h3>
+                                    </xsl:otherwise>
+                                </xsl:choose>
+                            </td>
+                            <td align="right">
+                                <select id="sortClusters" name="sortClusters" onchange="sortClustersChanged(options[selectedIndex].value);">
+                                    <option value="1">Sort clusters by name ascending</option>
+                                    <option value="2">Sort clusters by name descending</option>
+                                    <option value="3">Sort clusters by size ascending</option>
+                                    <option value="4">Sort clusters by size descending</option>
+                                </select>
+                            </td>
+                        </tr>
+                    </tbody>
+                </table>
                 <form id="queue" action="/WsSMC" method="post">
                     <input type="hidden" name="ClusterType" id="ClusterType" value=""/>
           <input type="hidden" name="Cluster" id="Cluster" value=""/>

+ 8 - 2
esp/scm/ws_smc.ecm

@@ -53,6 +53,7 @@ ESPStruct ThorCluster
     [min_ver("1.05")] int JobsInQueue;
     [min_ver("1.07")] int QueueStatus2;
     [min_ver("1.09")] string ThorLCR;
+    [min_ver("1.11")] int ClusterSize;
 };
 
 ESPStruct RoxieCluster
@@ -64,6 +65,7 @@ ESPStruct RoxieCluster
     int JobsRunning;
     int JobsInQueue;
     int QueueStatus2;
+    [min_ver("1.11")] int ClusterSize;
 };
 
 ESPStruct HoleCluster
@@ -86,10 +88,12 @@ ESPrequest [nil_remove] ActivityRequest
     string BannerContent;
     string BannerColor;
     string BannerSize;
-    [min_ver("1.08")] string BannerScroll;
+    string BannerScroll;
     int BannerAction(0);
     bool EnableChatURL;
     bool FromSubmitBtn(false);
+    string SortBy;
+    bool Descending(false);
 };
 
 ESPresponse [exceptions_inline] ActivityResponse
@@ -108,6 +112,8 @@ ESPresponse [exceptions_inline] ActivityResponse
     [min_ver("1.06")] string ChatURL;
     [min_ver("1.06")] int ShowBanner(0);
     [min_ver("1.06")] int ShowChatURL(0);
+    [min_ver("1.11")] string SortBy;
+    [min_ver("1.11")] bool Descending(false);
 };
 
 ESPrequest SMCIndexRequest
@@ -250,7 +256,7 @@ BrowseResourcesResponse
 };
 
 //ESPservice [noforms, version("1.04"), default_client_version("1.04"), exceptions_inline("./smc_xslt/exceptions.xslt")] WsSMC
-ESPservice [noforms, version("1.10"), default_client_version("1.10"), exceptions_inline("./smc_xslt/exceptions.xslt"), use_method_name] WsSMC
+ESPservice [noforms, version("1.11"), default_client_version("1.11"), exceptions_inline("./smc_xslt/exceptions.xslt"), use_method_name] WsSMC
 {
     ESPuses ESPStruct Capability;
     ESPuses ESPStruct Permission;

+ 88 - 2
esp/services/ws_smc/ws_smcService.cpp

@@ -515,7 +515,10 @@ bool CWsSMCEx::onActivity(IEspContext &context, IEspActivityRequest &req, IEspAc
                 if (version > 1.06)
                     returnCluster->setQueueStatus2(color_type);
                 returnCluster->setDoCommand(doCommand);
-                ThorClusters.append(*returnCluster);
+                if (version > 1.10)
+                    returnCluster->setClusterSize(cluster.getSize());
+
+                addToThorClusterList(ThorClusters, returnCluster, req.getSortBy(), req.getDescending());
             }
             if (version > 1.06) // JCSMORE->WANGKX , is this necessary?
             {
@@ -533,13 +536,20 @@ bool CWsSMCEx::onActivity(IEspContext &context, IEspActivityRequest &req, IEspAc
                     addQueuedWorkUnits(queueName, queue, aws, context, "RoxieServer", NULL);
                     const char *queueState = getQueueState(queue, -1, NULL);
                     returnCluster->setQueueStatus(queueState);
+                    if (version > 1.10)
+                        returnCluster->setClusterSize(cluster.getSize());
 
-                    RoxieClusters.append(*returnCluster);
+                    addToRoxieClusterList(RoxieClusters, returnCluster, req.getSortBy(), req.getDescending());
                 }
             }
         }
         resp.setThorClusters(ThorClusters);
         resp.setRoxieClusters(RoxieClusters);
+        if (version > 1.10)
+        {
+            resp.setSortBy(req.getSortBy());
+            resp.setDescending(req.getDescending());
+        }
 
         IArrayOf<IConstTpEclServer> eclccservers;
         CTpWrapper dummy;
@@ -700,6 +710,82 @@ bool CWsSMCEx::onActivity(IEspContext &context, IEspActivityRequest &req, IEspAc
     return true;
 }
 
+void CWsSMCEx::addToThorClusterList(IArrayOf<IEspThorCluster>& clusters, IEspThorCluster* cluster, const char* sortBy, bool descending)
+{
+    if (clusters.length() < 1)
+    {
+        clusters.append(*cluster);
+        return;
+    }
+
+    const char* clusterName = cluster->getClusterName();
+    unsigned clusterSize = cluster->getClusterSize();
+    bool clusterAdded = false;
+    ForEachItemIn(i, clusters)
+    {
+        int strCmp = 0;
+        IEspThorCluster& cluster1 = clusters.item(i);
+        if (!sortBy || !*sortBy || strieq(sortBy, "name"))
+        {
+            strCmp = strcmp(cluster1.getClusterName(), clusterName);
+        }
+        else
+        {//size
+            //strCmp = cluster1.getClusterSize() - clusterSize;
+            int si = cluster1.getClusterSize();
+            strCmp = si - clusterSize;
+        }
+
+        if ((descending && (strCmp < 0)) || (!descending && (strCmp > 0)))
+        {
+            clusters.add(*cluster, i);
+            clusterAdded =  true;
+            break;
+        }
+    }
+
+    if (!clusterAdded)
+        clusters.append(*cluster);
+    return;
+}
+
+void CWsSMCEx::addToRoxieClusterList(IArrayOf<IEspRoxieCluster>& clusters, IEspRoxieCluster* cluster, const char* sortBy, bool descending)
+{
+    if (clusters.length() < 1)
+    {
+        clusters.append(*cluster);
+        return;
+    }
+
+    const char* clusterName = cluster->getClusterName();
+    unsigned clusterSize = cluster->getClusterSize();
+    bool clusterAdded = false;
+    ForEachItemIn(i, clusters)
+    {
+        int strCmp = 0;
+        IEspRoxieCluster& cluster1 = clusters.item(i);
+        if (!sortBy || !*sortBy || strieq(sortBy, "name"))
+        {
+            strCmp = strcmp(cluster1.getClusterName(), clusterName);
+        }
+        else
+        {//size
+            strCmp = cluster1.getClusterSize() - clusterSize;
+        }
+
+        if ((descending && (strCmp < 0)) || (!descending && (strCmp > 0)))
+        {
+            clusters.add(*cluster, i);
+            clusterAdded = true;
+            break;
+        }
+    }
+
+    if (!clusterAdded)
+        clusters.append(*cluster);
+    return;
+}
+
 
 void CWsSMCEx::addCapabilities(IPropertyTree* pFeatureNode, const char* access, 
                                          IArrayOf<IEspCapability>& capabilities)

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

@@ -65,6 +65,8 @@ public:
 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);
 };
 
 

+ 40 - 16
esp/services/ws_workunits/ws_workunitsQuerySets.cpp

@@ -435,13 +435,26 @@ bool CWsWorkunitsEx::onWUQuerysets(IEspContext &context, IEspWUQuerysetsRequest
     return true;
 }
 
-void gatherQuerySetQueryDetails(IPropertyTree *query, IEspQuerySetQuery *queryInfo)
+void gatherQuerySetQueryDetails(IPropertyTree *query, IEspQuerySetQuery *queryInfo, const char *cluster, IPropertyTree *queriesOnCluster)
 {
     queryInfo->setId(query->queryProp("@id"));
     queryInfo->setName(query->queryProp("@name"));
     queryInfo->setDll(query->queryProp("@dll"));
     queryInfo->setWuid(query->queryProp("@wuid"));
     queryInfo->setSuspended(query->getPropBool("@suspended", false));
+    if (queriesOnCluster)
+    {
+        VStringBuffer xpath("Endpoint/Queries/Query[@id='%s']/@suspended", query->queryProp("@id"));
+        if (queriesOnCluster->getPropBool(xpath.str()))
+        {
+            IArrayOf<IEspClusterQueryState> clusters;
+            Owned<IEspClusterQueryState> clusterState = createClusterQueryState();
+            clusterState->setCluster(cluster);
+            clusterState->setSuspended(true);
+            clusters.append(*clusterState.getClear());
+            queryInfo->setClusters(clusters);
+        }
+    }
 }
 
 void gatherQuerySetAliasDetails(IPropertyTree *alias, IEspQuerySetAlias *aliasInfo)
@@ -450,14 +463,14 @@ void gatherQuerySetAliasDetails(IPropertyTree *alias, IEspQuerySetAlias *aliasIn
     aliasInfo->setId(alias->queryProp("@id"));
 }
 
-void retrieveAllQuerysetDetails(IPropertyTree *registry, IArrayOf<IEspQuerySetQuery> &queries, IArrayOf<IEspQuerySetAlias> &aliases)
+void retrieveAllQuerysetDetails(IPropertyTree *registry, IArrayOf<IEspQuerySetQuery> &queries, IArrayOf<IEspQuerySetAlias> &aliases, const char *cluster=NULL, IPropertyTree *queriesOnCluster=NULL)
 {
     Owned<IPropertyTreeIterator> regQueries = registry->getElements("Query");
     ForEach(*regQueries)
     {
         IPropertyTree &query = regQueries->query();
         Owned<IEspQuerySetQuery> q = createQuerySetQuery();
-        gatherQuerySetQueryDetails(&query, q);
+        gatherQuerySetQueryDetails(&query, q, cluster, queriesOnCluster);
         queries.append(*q.getClear());
     }
 
@@ -471,7 +484,7 @@ void retrieveAllQuerysetDetails(IPropertyTree *registry, IArrayOf<IEspQuerySetQu
     }
 }
 
-void retrieveQuerysetDetailsFromAlias(IPropertyTree *registry, const char *name, IArrayOf<IEspQuerySetQuery> &queries, IArrayOf<IEspQuerySetAlias> &aliases)
+void retrieveQuerysetDetailsFromAlias(IPropertyTree *registry, const char *name, IArrayOf<IEspQuerySetQuery> &queries, IArrayOf<IEspQuerySetAlias> &aliases, const char *cluster, IPropertyTree *queriesOnCluster)
 {
     StringBuffer xpath;
     xpath.append("Alias[@name='").append(name).append("']");
@@ -495,11 +508,11 @@ void retrieveQuerysetDetailsFromAlias(IPropertyTree *registry, const char *name,
     }
 
     Owned<IEspQuerySetQuery> q = createQuerySetQuery();
-    gatherQuerySetQueryDetails(query, q);
+    gatherQuerySetQueryDetails(query, q, cluster, queriesOnCluster);
     queries.append(*q.getClear());
 }
 
-void retrieveQuerysetDetailsFromQuery(IPropertyTree *registry, const char *value, const char *type, IArrayOf<IEspQuerySetQuery> &queries, IArrayOf<IEspQuerySetAlias> &aliases)
+void retrieveQuerysetDetailsFromQuery(IPropertyTree *registry, const char *value, const char *type, IArrayOf<IEspQuerySetQuery> &queries, IArrayOf<IEspQuerySetAlias> &aliases, const char *cluster=NULL, IPropertyTree *queriesOnCluster=NULL)
 {
     if (!strieq(type, "Id") && !strieq(type, "Name"))
         throw MakeStringException(ECLWATCH_INVALID_INPUT, "Unrecognized queryset filter type %s", type);
@@ -515,7 +528,7 @@ void retrieveQuerysetDetailsFromQuery(IPropertyTree *registry, const char *value
     }
 
     Owned<IEspQuerySetQuery> q = createQuerySetQuery();
-    gatherQuerySetQueryDetails(query, q);
+    gatherQuerySetQueryDetails(query, q, cluster, queriesOnCluster);
     xpath.clear().append("Alias[@id='").append(q->getId()).append("']");
     queries.append(*q.getClear());
 
@@ -529,25 +542,25 @@ void retrieveQuerysetDetailsFromQuery(IPropertyTree *registry, const char *value
     }
 }
 
-void retrieveQuerysetDetails(IPropertyTree *registry, const char *type, const char *value, IArrayOf<IEspQuerySetQuery> &queries, IArrayOf<IEspQuerySetAlias> &aliases)
+void retrieveQuerysetDetails(IPropertyTree *registry, const char *type, const char *value, IArrayOf<IEspQuerySetQuery> &queries, IArrayOf<IEspQuerySetAlias> &aliases, const char *cluster=NULL, IPropertyTree *queriesOnCluster=NULL)
 {
     if (strieq(type, "All"))
-        return retrieveAllQuerysetDetails(registry, queries, aliases);
+        return retrieveAllQuerysetDetails(registry, queries, aliases, cluster, queriesOnCluster);
     if (!value || !*value)
         return;
     if (strieq(type, "Alias"))
-        return retrieveQuerysetDetailsFromAlias(registry, value, queries, aliases);
-    return retrieveQuerysetDetailsFromQuery(registry, value, type, queries, aliases);
+        return retrieveQuerysetDetailsFromAlias(registry, value, queries, aliases, cluster, queriesOnCluster);
+    return retrieveQuerysetDetailsFromQuery(registry, value, type, queries, aliases, cluster, queriesOnCluster);
 }
 
-void retrieveQuerysetDetails(IArrayOf<IEspWUQuerySetDetail> &details, IPropertyTree *registry, const char *type, const char *value)
+void retrieveQuerysetDetails(IArrayOf<IEspWUQuerySetDetail> &details, IPropertyTree *registry, const char *type, const char *value, const char *cluster=NULL, IPropertyTree *queriesOnCluster=NULL)
 {
     if (!registry)
         return;
 
     IArrayOf<IEspQuerySetQuery> queries;
     IArrayOf<IEspQuerySetAlias> aliases;
-    retrieveQuerysetDetails(registry, type, value, queries, aliases);
+    retrieveQuerysetDetails(registry, type, value, queries, aliases, cluster, queriesOnCluster);
 
     Owned<IEspWUQuerySetDetail> detail = createWUQuerySetDetail();
     detail->setQuerySetName(registry->queryProp("@id"));
@@ -556,14 +569,14 @@ void retrieveQuerysetDetails(IArrayOf<IEspWUQuerySetDetail> &details, IPropertyT
     details.append(*detail.getClear());
 }
 
-void retrieveQuerysetDetails(IArrayOf<IEspWUQuerySetDetail> &details, const char *queryset, const char *type, const char *value)
+void retrieveQuerysetDetails(IArrayOf<IEspWUQuerySetDetail> &details, const char *queryset, const char *type, const char *value, const char *cluster=NULL, IPropertyTree *queriesOnCluster=NULL)
 {
     if (!queryset || !*queryset)
         return;
     Owned<IPropertyTree> registry = getQueryRegistry(queryset, true);
     if (!registry)
         return;
-    retrieveQuerysetDetails(details, registry, type, value);
+    retrieveQuerysetDetails(details, registry, type, value, cluster, queriesOnCluster);
 }
 
 void retrieveQuerysetDetailsByCluster(IArrayOf<IEspWUQuerySetDetail> &details, const char *cluster, const char *queryset, const char *type, const char *value)
@@ -572,6 +585,17 @@ void retrieveQuerysetDetailsByCluster(IArrayOf<IEspWUQuerySetDetail> &details, c
     if (!info)
         throw MakeStringException(ECLWATCH_CANNOT_RESOLVE_CLUSTER_NAME, "Cluster %s not found", cluster);
 
+    Owned<IPropertyTree> queriesOnCluster;
+    if (info->getPlatform()==RoxieCluster)
+    {
+        const SocketEndpointArray &eps = info->getRoxieServers();
+        if (eps.length())
+        {
+            Owned<ISocket> sock = ISocket::connect_timeout(eps.item(0), 5);
+            queriesOnCluster.setown(sendRoxieControlQuery(sock, "<control:queries/>", 5));
+        }
+    }
+
     SCMStringBuffer clusterQueryset;
     info->getQuerySetName(clusterQueryset);
     if (!clusterQueryset.length())
@@ -579,7 +603,7 @@ void retrieveQuerysetDetailsByCluster(IArrayOf<IEspWUQuerySetDetail> &details, c
     if (notEmpty(queryset) && !strieq(clusterQueryset.str(), queryset))
         throw MakeStringException(ECLWATCH_QUERYSET_NOT_ON_CLUSTER, "Cluster %s not configured to load QuerySet %s", cluster, queryset);
 
-    retrieveQuerysetDetails(details, clusterQueryset.str(), type, value);
+    retrieveQuerysetDetails(details, clusterQueryset.str(), type, value, cluster, queriesOnCluster);
 }
 
 void retrieveAllQuerysetDetails(IArrayOf<IEspWUQuerySetDetail> &details, const char *type, const char *value)

+ 167 - 74
esp/services/ws_workunits/ws_workunitsService.cpp

@@ -89,7 +89,27 @@ public:
     }
 };
 
-void submitWsWorkunit(IEspContext& context, IConstWorkUnit* cw, const char* cluster, const char* snapshot, int maxruntime, bool compile, bool resetWorkflow)
+void setWsWuXmlParameters(IWorkUnit *wu, const char *xml, bool setJobname=false)
+{
+    if (!xml || !*xml)
+        return;
+    Owned<IPropertyTree> tree = createPTreeFromXMLString(xml, ipt_none, (XmlReaderOptions)(xr_ignoreWhiteSpace | xr_ignoreNameSpaces));
+    IPropertyTree *root = tree.get();
+    if (strieq(root->queryName(), "Envelope"))
+        root = root->queryPropTree("Body/*[1]");
+    if (!root)
+        return;
+    if (setJobname)
+    {
+        SCMStringBuffer name;
+        wu->getJobName(name);
+        if (!name.length())
+            wu->setJobName(root->queryName());
+    }
+    wu->setXmlParams(LINK(root));
+}
+
+void submitWsWorkunit(IEspContext& context, IConstWorkUnit* cw, const char* cluster, const char* snapshot, int maxruntime, bool compile, bool resetWorkflow, const char *paramXml=NULL)
 {
     ensureWsWorkunitAccess(context, *cw, SecAccess_Write);
     switch(cw->getState())
@@ -129,6 +149,8 @@ void submitWsWorkunit(IEspContext& context, IConstWorkUnit* cw, const char* clus
             wu->schedule();
     }
 
+    setWsWuXmlParameters(wu, paramXml, (wu->getAction()==WUActionExecuteExisting));
+
     wu->commit();
     wu.clear();
 
@@ -142,13 +164,125 @@ void submitWsWorkunit(IEspContext& context, IConstWorkUnit* cw, const char* clus
     AuditSystemAccess(context.queryUserId(), true, "Submitted %s", wuid.str());
 }
 
-void submitWsWorkunit(IEspContext& context, const char *wuid, const char* cluster, const char* snapshot, int maxruntime, bool compile, bool resetWorkflow)
+void submitWsWorkunit(IEspContext& context, const char *wuid, const char* cluster, const char* snapshot, int maxruntime, bool compile, bool resetWorkflow, const char *paramXml=NULL)
 {
     Owned<IWorkUnitFactory> factory = getWorkUnitFactory(context.querySecManager(), context.queryUser());
     Owned<IConstWorkUnit> cw = factory->openWorkUnit(wuid, false);
-    return submitWsWorkunit(context, cw, cluster, snapshot, maxruntime, compile, resetWorkflow);
+    return submitWsWorkunit(context, cw, cluster, snapshot, maxruntime, compile, resetWorkflow, paramXml);
+}
+
+
+void copyWsWorkunit(IEspContext &context, IWorkUnit &wu, const char *srcWuid)
+{
+    Owned<IWorkUnitFactory> factory = getWorkUnitFactory(context.querySecManager(), context.queryUser());
+    Owned<IConstWorkUnit> src(factory->openWorkUnit(srcWuid, false));
+
+    SCMStringBuffer wuid;
+    wu.getWuid(wuid);
+
+    queryExtendedWU(&wu)->copyWorkUnit(src);
+
+    SCMStringBuffer token;
+    wu.setSecurityToken(createToken(wuid.str(), context.queryUserId(), context.queryPassword(), token).str());
+    wu.commit();
+}
+
+void runWsWorkunit(IEspContext &context, StringBuffer &wuid, const char *srcWuid, const char *cluster, const char *paramXml=NULL)
+{
+    StringBufferAdaptor isvWuid(wuid);
+
+    NewWsWorkunit wu(context);
+    wu->getWuid(isvWuid);
+    copyWsWorkunit(context, *wu, srcWuid);
+    wu.clear();
+
+    submitWsWorkunit(context, wuid.str(), cluster, NULL, 0, false, true, paramXml);
+}
+
+void runWsWorkunit(IEspContext &context, IConstWorkUnit *cw, const char *srcWuid, const char *cluster, const char *paramXml=NULL)
+{
+    WorkunitUpdate wu(&cw->lock());
+    copyWsWorkunit(context, *wu, srcWuid);
+    wu.clear();
+
+    submitWsWorkunit(context, cw, cluster, NULL, 0, false, true, paramXml);
+}
+
+IException *noteException(IWorkUnit *wu, IException *e, WUExceptionSeverity level=ExceptionSeverityError)
+{
+    if (wu)
+    {
+        Owned<IWUException> we = wu->createException();
+        StringBuffer s;
+        we->setExceptionMessage(e->errorMessage(s).str());
+        we->setExceptionSource("WsWorkunits");
+        we->setSeverity(level);
+        if (level==ExceptionSeverityError)
+            wu->setState(WUStateFailed);
+    }
+    return e;
+}
+
+StringBuffer &resolveQueryWuid(StringBuffer &wuid, const char *queryset, const char *query, bool notSuspended=true, IWorkUnit *wu=NULL)
+{
+    Owned<IPropertyTree> qs = getQueryRegistry(queryset, true);
+    if (!qs)
+        throw noteException(wu, MakeStringException(ECLWATCH_QUERYSET_NOT_FOUND, "QuerySet '%s' not found", queryset));
+    Owned<IPropertyTree> q = resolveQueryAlias(qs, query);
+    if (!q)
+        throw noteException(wu, MakeStringException(ECLWATCH_QUERYID_NOT_FOUND, "Query '%s/%s' not found", queryset, query));
+    if (notSuspended && q->getPropBool("@suspended"))
+        throw noteException(wu, MakeStringException(ECLWATCH_QUERY_SUSPENDED, "Query '%s/%s' is suspended", queryset, query));
+    return wuid.append(q->queryProp("@wuid"));
+}
+
+void runWsWuQuery(IEspContext &context, IConstWorkUnit *cw, const char *queryset, const char *query, const char *cluster, const char *paramXml=NULL)
+{
+    StringBuffer srcWuid;
+
+    WorkunitUpdate wu(&cw->lock());
+    resolveQueryWuid(srcWuid, queryset, query, true, wu);
+    copyWsWorkunit(context, *wu, srcWuid);
+    wu.clear();
+
+    submitWsWorkunit(context, cw, cluster, NULL, 0, false, true, paramXml);
+}
+
+void runWsWuQuery(IEspContext &context, StringBuffer &wuid, const char *queryset, const char *query, const char *cluster, const char *paramXml=NULL)
+{
+    StringBuffer srcWuid;
+    StringBufferAdaptor isvWuid(wuid);
+
+    NewWsWorkunit wu(context);
+    wu->getWuid(isvWuid);
+    resolveQueryWuid(srcWuid, queryset, query, true, wu);
+    copyWsWorkunit(context, *wu, srcWuid);
+    wu.clear();
+
+    submitWsWorkunit(context, wuid.str(), cluster, NULL, 0, false, true, paramXml);
 }
 
+class ExecuteExistingQueryInfo
+{
+public:
+    ExecuteExistingQueryInfo(IConstWorkUnit *cw)
+    {
+        SCMStringBuffer isv;
+        cw->getJobName(isv);
+        const char *name = isv.str();
+        const char *div = strchr(name, '.');
+        if (div)
+        {
+            queryset.set(name, div-name);
+            query.set(div+1);
+        }
+    }
+
+public:
+    StringAttr queryset;
+    StringAttr query;
+};
+
 typedef enum _WuActionType
 {
     ActionDelete=0,
@@ -529,9 +663,8 @@ bool CWsWorkunitsEx::onWUUpdate(IEspContext &context, IEspWUUpdateRequest &req,
                     wu->setClusterName(s.str());
             }
         }
-        const char *xmlParams = req.getXmlParams();
-        if (notEmpty(xmlParams))
-            wu->setXmlParams(xmlParams);
+
+        setWsWuXmlParameters(wu, req.getXmlParams(), (req.getAction()==WUActionExecuteExisting));
 
         if (notEmpty(req.getQueryText()))
         {
@@ -909,7 +1042,21 @@ bool CWsWorkunitsEx::onWUSubmit(IEspContext &context, IEspWUSubmitRequest &req,
         if (isEmpty(req.getCluster()))
             throw MakeStringException(ECLWATCH_INVALID_INPUT,"No Cluster defined.");
 
-        submitWsWorkunit(context, req.getWuid(), req.getCluster(), req.getSnapshot(), req.getMaxRunTime(), true, false);
+        Owned<IWorkUnitFactory> factory = getWorkUnitFactory(context.querySecManager(), context.queryUser());
+        Owned<IConstWorkUnit> cw = factory->openWorkUnit(req.getWuid(), false);
+        if (cw->getAction()==WUActionExecuteExisting)
+        {
+            ExecuteExistingQueryInfo info(cw);
+            if (info.queryset.isEmpty() || info.query.isEmpty())
+            {
+                WorkunitUpdate wu(&cw->lock());
+                throw noteException(wu, MakeStringException(ECLWATCH_INVALID_INPUT,"Queryset and/or query not specified"));
+            }
+
+            runWsWuQuery(context, cw, info.queryset.sget(), info.query.sget(), req.getCluster(), NULL);
+        }
+        else
+            submitWsWorkunit(context, cw, req.getCluster(), req.getSnapshot(), req.getMaxRunTime(), true, false);
 
         if (req.getBlockTillFinishTimer() != 0)
             waitForWorkUnitToComplete(req.getWuid(), req.getBlockTillFinishTimer());
@@ -927,84 +1074,30 @@ bool CWsWorkunitsEx::onWURun(IEspContext &context, IEspWURunRequest &req, IEspWU
 {
     try
     {
-        SCMStringBuffer wuid;
-        wuid.set(req.getWuid());
+        const char *runWuid = req.getWuid();
+        StringBuffer wuid;
 
-        bool cloneWorkunit=req.getCloneWorkunit();
-        if (!wuid.length() && notEmpty(req.getQuerySet()) && notEmpty(req.getQuery()))
+        if (runWuid && *runWuid)
         {
-            cloneWorkunit=true;
-            Owned<IPropertyTree> qstree = getQueryRegistry(req.getQuerySet(), true);
-            if (qstree)
+            if (req.getCloneWorkunit())
+                runWsWorkunit(context, wuid, runWuid, req.getCluster(), req.getInput());
+            else
             {
-                IPropertyTree *query = NULL;
-                VStringBuffer xpath("Alias[@name=\"%s\"]", req.getQuery());
-                IPropertyTree *alias = qstree->queryPropTree(xpath.str());
-                if (alias)
-                {
-                    const char *quid = alias->queryProp("@id");
-                    if (!quid)
-                        throw MakeStringException(-1, "Alias %s/%s has no Query defined", req.getQuerySet(), req.getQuery());
-                    xpath.clear().appendf("Query[@id='%s']", quid);
-                    query = qstree->queryPropTree(xpath.str());
-                    if (!query)
-                        throw MakeStringException(-1, "Alias %s/%s refers to a non existing query %s", req.getQuerySet(), req.getQuery(), quid);
-                }
-                else
-                {
-                    xpath.clear().appendf("Query[@id=\"%s\"]", req.getQuery());
-                    query = qstree->queryPropTree(xpath.str());
-                }
-                if (query)
-                {
-                    if (query->getPropBool("@suspended"))
-                        throw MakeStringException(-1, "Query %s/%s is currently suspended", req.getQuerySet(), req.getQuery());
-
-                    wuid.set(query->queryProp("@wuid"));
-                }
-                else
-                    throw MakeStringException(-1, "Query %s/%s not found", req.getQuerySet(), req.getQuery());
+                submitWsWorkunit(context, runWuid, req.getCluster(), NULL, 0, false, true, req.getInput());
+                wuid.set(runWuid);
             }
-            else
-                throw MakeStringException(-1, "QuerySet %s not found", req.getQuerySet());
         }
-
-        if (!wuid.length())
+        else if (notEmpty(req.getQuerySet()) && notEmpty(req.getQuery()))
+            runWsWuQuery(context, wuid, req.getQuerySet(), req.getQuery(), req.getCluster(), req.getInput());
+        else
             throw MakeStringException(ECLWATCH_MISSING_PARAMS,"Workunit or Query required");
 
-        ensureWsWorkunitAccess(context, wuid.str(), SecAccess_Write);
-
-        Owned<IWorkUnitFactory> factory = getWorkUnitFactory(context.querySecManager(), context.queryUser());
-        if(cloneWorkunit)
-        {
-            Owned<IConstWorkUnit> src(factory->openWorkUnit(wuid.str(), false));
-            NewWsWorkunit wu(factory, context);
-            wu->getWuid(wuid);
-            queryExtendedWU(wu)->copyWorkUnit(src);
-
-            SCMStringBuffer token;
-            wu->setSecurityToken(createToken(wuid.str(), context.queryUserId(), context.queryPassword(), token).str());
-        }
-
-        Owned<IConstWorkUnit> cw(factory->openWorkUnit(wuid.str(), false));
-        if (!cw)
-            throw MakeStringException(ECLWATCH_CANNOT_UPDATE_WORKUNIT,"Cannot open workunit %s.", wuid.str());
-
-        if (notEmpty(req.getInput()))
-        {
-            Owned<IWuWebView> web = createWuWebView(*cw, NULL, getCFD(), true);
-            web->addInputsFromXml(req.getInput());
-        }
-
-        submitWsWorkunit(context, cw, req.getCluster(), NULL, 0, false, true);
-        cw.clear();
-
         int timeToWait = req.getWait();
         if (timeToWait != 0)
             waitForWorkUnitToComplete(wuid.str(), timeToWait);
 
-
-        cw.set(factory->openWorkUnit(wuid.str(), false));
+        Owned<IWorkUnitFactory> factory = getWorkUnitFactory(context.querySecManager(), context.queryUser());
+        Owned<IConstWorkUnit> cw = factory->openWorkUnit(wuid.str(), false);
         if (!cw)
             throw MakeStringException(ECLWATCH_CANNOT_UPDATE_WORKUNIT,"Cannot open workunit %s.", wuid.str());
 

+ 1 - 0
esp/smc/SMCLib/exception_util.hpp

@@ -119,6 +119,7 @@
 #define ECLWATCH_ALIAS_NOT_FOUND            ECLWATCH_ERROR_START+98
 #define ECLWATCH_QUERYSET_NOT_ON_CLUSTER    ECLWATCH_ERROR_START+99
 #define ECLWATCH_CONTROL_QUERY_FAILED       ECLWATCH_ERROR_START+100
+#define ECLWATCH_QUERY_SUSPENDED            ECLWATCH_ERROR_START+101
 
 inline void FORWARDEXCEPTION(IEspContext &context, IException *e, unsigned codeNew)
 {