浏览代码

HPCC-12542 Add ECLWatch UI to call resetquerystats

On the Query Details page, a 'Reset' button is added to
send WsWorkunits an HTTP request of WUQuerysetQueryAction
with Action='ResetQueryStats'. After the request is
received, the WsWorkunits resets query stats using
control:resetquerystats.

Signed-off-by: wangkx <kevin.wang@lexisnexis.com>
wangkx 10 年之前
父节点
当前提交
3337a3eb12

+ 22 - 3
esp/eclwatch/ws_XSLT/WUQueryDetails.xslt

@@ -59,6 +59,11 @@
                         actionWorkunits('Delete');
                       }
 
+                      function resetQuery() {
+                          if (confirm("Reset This Query?"))
+                              actionWorkunits('ResetQueryStats');
+                      }
+
                       function toggleQuery() {
                         actionWorkunits('ToggleSuspend');
                       }
@@ -77,14 +82,27 @@
                           return soapXML;
                       }
 
+                      function showResetQueryStatsResponse(doc) {
+                          var response = doc.getElementsByTagName('WUQuerySetQueryActionResponse');
+                          if (response == undefined || response.length < 1)
+                              alert('<No result returned>');
+
+                          var results = response[0].getElementsByTagName('Result');
+                          if (results == undefined || results.length < 1)
+                              alert('<No result returned>');
+                          alert(results[0].childNodes[0].nodeValue);
+                      }
+
                       function actionWorkunits(Action) {
                           var connectionCallback = {
                               success: function(o) {
                                   var xmlDoc = o.responseXML;
-                                  if (Action == 'Delete') {
-                                    document.location.replace( "/WsWorkunits/WUQuerysetDetails?QuerySetName=" + querySet);
+                                  if (Action == 'ResetQueryStats') {
+                                      showResetQueryStatsResponse(xmlDoc.documentElement);
+                                  } else if (Action == 'Delete') {
+                                      document.location.replace( "/WsWorkunits/WUQuerysetDetails?QuerySetName=" + querySet);
                                   } else {
-                                    document.location.replace(document.location.href);
+                                      document.location.replace(document.location.href);
                                   }
                               },
                               failure: function(o) {
@@ -294,6 +312,7 @@
                       </xsl:if>
                     </table>
                 </form>
+                <input id="resetBtn" type="button" value="Reset" onclick="resetQuery();"> </input>
                 <input id="deleteBtn" type="button" value="Delete" onclick="deleteQuery();"> </input>
             </body>
         </html>

+ 2 - 1
esp/scm/ws_workunits.ecm

@@ -1403,7 +1403,8 @@ ESPenum QuerySetQueryActionTypes : string
     ToggleSuspend("ToggleSuspend"),
     Activate("Activate"),
     Delete("Delete"),
-    RemoveAllAliases("RemoveAllAliases")
+    RemoveAllAliases("RemoveAllAliases"),
+    ResetQueryStats("ResetQueryStats")
 };
 
 ESPStruct QuerySetQueryClientState

+ 69 - 0
esp/services/ws_workunits/ws_workunitsQuerySets.cpp

@@ -1837,6 +1837,8 @@ bool CWsWorkunitsEx::onWUQuerysetQueryAction(IEspContext &context, IEspWUQuerySe
 
     Owned<IProperties> queryIds = createProperties();
     expandQueryActionTargetList(queryIds, queryset, req.getQueries(), req.getAction());
+    if (req.getAction() == CQuerySetQueryActionTypes_ResetQueryStats)
+        return resetQueryStats(context, req.getQuerySetName(), queryIds, resp);
 
     IArrayOf<IEspQuerySetQueryActionResult> results;
     Owned<IPropertyIterator> it = queryIds->getIterator();
@@ -2426,3 +2428,70 @@ bool CWsWorkunitsEx::onWUQueryGetGraph(IEspContext& context, IEspWUQueryGetGraph
     }
     return true;
 }
+
+bool CWsWorkunitsEx::resetQueryStats(IEspContext& context, const char* target, IProperties* queryIds, IEspWUQuerySetQueryActionResponse& resp)
+{
+    IArrayOf<IEspQuerySetQueryActionResult> results;
+    Owned<IEspQuerySetQueryActionResult> result = createQuerySetQueryActionResult();
+    try
+    {
+        StringBuffer control;
+        Owned<IPropertyIterator> it = queryIds->getIterator();
+        ForEach(*it)
+        {
+            const char *querySetId = it->getPropKey();
+            if (querySetId && *querySetId)
+                control.appendf("<Query id='%s'/>", querySetId);
+        }
+        if (!control.length())
+            throw MakeStringException(ECLWATCH_MISSING_PARAMS, "CWsWorkunitsEx::resetQueryStats: Query ID not specified");
+
+        control.insert(0, "<control:resetquerystats>");
+        control.append("</control:resetquerystats>");
+
+        if (!sendControlQuery(context, target, control.str(), ROXIECONNECTIONTIMEOUT))
+            throw MakeStringException(ECLWATCH_INTERNAL_ERROR, "CWsWorkunitsEx::resetQueryStats: Failed to send roxie control query");
+
+        result->setMessage("Query stats reset succeeded");
+        result->setSuccess(true);;
+    }
+    catch(IMultiException *me)
+    {
+        StringBuffer msg;
+        result->setMessage(me->errorMessage(msg).str());
+        result->setCode(me->errorCode());
+        result->setSuccess(false);
+        me->Release();
+    }
+    catch(IException *e)
+    {
+        StringBuffer msg;
+        result->setMessage(e->errorMessage(msg).str());
+        result->setCode(e->errorCode());
+        result->setSuccess(false);
+        e->Release();
+    }
+    results.append(*result.getClear());
+    resp.setResults(results);
+    return true;
+}
+
+IPropertyTree* CWsWorkunitsEx::sendControlQuery(IEspContext& context, const char* target, const char* query, unsigned timeout)
+{
+    if (!target || !*target)
+        throw MakeStringException(ECLWATCH_MISSING_PARAMS, "CWsWorkunitsEx::sendControlQuery: target not specified");
+
+    if (!query || !*query)
+        throw MakeStringException(ECLWATCH_MISSING_PARAMS, "CWsWorkunitsEx::sendControlQuery: Control query not specified");
+
+    Owned<IConstWUClusterInfo> info = getTargetClusterInfo(target);
+    if (!info || (info->getPlatform()!=RoxieCluster)) //Only support roxie for now
+        throw MakeStringException(ECLWATCH_INVALID_CLUSTER_NAME, "CWsWorkunitsEx::sendControlQuery: Invalid target name %s", target);
+
+    const SocketEndpointArray &eps = info->getRoxieServers();
+    if (eps.empty())
+        throw MakeStringException(ECLWATCH_INVALID_CLUSTER_NAME, "CWsWorkunitsEx::sendControlQuery: Server not found for %s", target);
+
+    Owned<ISocket> sock = ISocket::connect_timeout(eps.item(0), timeout);
+    return sendRoxieControlQuery(sock, query, timeout);
+}

+ 2 - 0
esp/services/ws_workunits/ws_workunitsService.hpp

@@ -262,6 +262,8 @@ private:
     void createZAPECLQueryArchiveFiles(Owned<IConstWorkUnit>& cwu, const char* pathNameStr);
     void createZAPFile(const char* fileName, size32_t len, const void* data);
     void cleanZAPFolder(IFile* zipDir, bool removeFolder);
+    IPropertyTree* sendControlQuery(IEspContext &context, const char* target, const char* query, unsigned timeout);
+    bool resetQueryStats(IEspContext &context, const char* target, IProperties* queryIds, IEspWUQuerySetQueryActionResponse& resp);
 
     unsigned awusCacheMinutes;
     StringBuffer queryDirectory;

+ 22 - 1
esp/src/eclwatch/ESPQuery.js

@@ -22,6 +22,7 @@ define([
     "dojo/_base/Deferred",
     "dojo/store/Observable",
     "dojo/Stateful",
+    "dojo/topic",
 
     "dojox/xml/parser",
 
@@ -30,7 +31,7 @@ define([
     "hpcc/ESPRequest",
     "hpcc/ESPUtil",
     "hpcc/ESPWorkunit"
-], function (declare, arrayUtil, lang, i18n, nlsHPCC, Deferred, Observable, Stateful,
+], function (declare, arrayUtil, lang, i18n, nlsHPCC, Deferred, Observable, Stateful, topic,
         parser,
         WsWorkunits, WsEcl, ESPRequest, ESPUtil, ESPWorkunit) {
 
@@ -101,6 +102,7 @@ define([
     });
 
     var Query = declare([ESPUtil.Singleton], {
+        i18n: nlsHPCC,
         constructor: function (args) {
             this.inherited(arguments);
             if (args) {
@@ -150,6 +152,20 @@ define([
             }
             return deferred.promise;
         },
+        showResetQueryStatsResponse: function (responses) {
+            var result = responses[0].WUQuerySetQueryActionResponse.Results.Result[0];
+            var sv = "Message";
+            var msg = result.Message;
+            if (result.Success == 0) {
+                sv = "Error";
+                msg = this.i18n.Exception + ": code=" + result.Code + " message=" + result.Message;
+            }
+            topic.publish("hpcc/brToaster", {
+                Severity: sv,
+                Source: "WsWorkunits.WUQuerysetQueryAction",
+                Exceptions: [{ Source: "ResetQueryStats", Message: msg }]
+            });
+        },
         doAction: function (action) {
             var context = this;
             return WsWorkunits.WUQuerysetQueryAction([{
@@ -158,6 +174,8 @@ define([
                 Name: this.Name
             }], action).then(function (responses) {
                 context.refresh();
+                if (action == 'ResetQueryStats')
+                    context.showResetQueryStatsResponse(responses);
                 return response;
             });
         },
@@ -167,6 +185,9 @@ define([
         setActivated: function (activated) {
             return this.doAction(activated ? "Activate" : "Deactivate");
         },
+        doReset: function () {
+            return this.doAction("ResetQueryStats");
+        },
         doDelete: function () {
             return this.doAction("Delete");
         }

+ 6 - 0
esp/src/eclwatch/QuerySetDetailsWidget.js

@@ -98,6 +98,12 @@ define([
                 activate: this.query.setActivated(activated)
             });
         },
+
+        _onReset:function(){
+            if (confirm(this.i18n.ResetThisQuery)) {
+                this.query.doReset();
+            }
+        },
         _onDelete: function (event) {
             if (confirm(this.i18n.DeleteSelectedWorkunits)) {
                 this.query.doDelete();

+ 1 - 0
esp/src/eclwatch/nls/hpcc.js

@@ -376,6 +376,7 @@ define({root:
     RequestSchema: "Request Schema",
     Reschedule: "Reschedule",
     Reset: "Reset",
+    ResetThisQuery: "Reset This Query?",
     ResetViewToSelection: "Reset View to Selection",
     Resource: "Resource",
     Resources: "Resources",

+ 2 - 1
esp/src/eclwatch/templates/QuerySetDetailsWidget.html

@@ -6,6 +6,7 @@
                     <div id="${id}Refresh" data-dojo-attach-event="onClick:_onRefresh" data-dojo-props='iconClass:"iconRefresh"' data-dojo-type="dijit.form.Button">${i18n.Refresh}</div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <div id="${id}Save" data-dojo-attach-event="onClick:_onSave" data-dojo-type="dijit.form.Button">${i18n.Save}</div>
+                    <div id="${id}Reset" data-dojo-attach-event="onClick:_onReset" data-dojo-type="dijit.form.Button">${i18n.Reset}</div>
                     <div id="${id}Delete" data-dojo-attach-event="onClick:_onDelete" data-dojo-type="dijit.form.Button">${i18n.Delete}</div>
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <div id="${id}NewPage" class="right" data-dojo-attach-event="onClick:_onNewPage" data-dojo-props="iconClass:'iconNewPage', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.OpenInNewPage}</div>
@@ -93,4 +94,4 @@
             </div>
         </div>
     </div>
-</div>
+</div>