Browse Source

Merge branch 'closedown-5.0.x'

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 10 years ago
parent
commit
e20e915fe9
45 changed files with 488 additions and 241 deletions
  1. 9 1
      common/workunit/workunit.cpp
  2. 2 1
      dali/base/dautils.cpp
  3. 10 2
      ecl/eclagent/eclagent.cpp
  4. 56 19
      esp/eclwatch/ws_XSLT/dropzonefile.xslt
  5. 5 5
      esp/eclwatch/ws_XSLT/index.xslt
  6. 3 5
      esp/services/ws_fs/ws_fsService.cpp
  7. 5 5
      esp/src/eclwatch/ActivityWidget.js
  8. 2 2
      esp/src/eclwatch/DFUQueryWidget.js
  9. 6 6
      esp/src/eclwatch/DFUWUDetailsWidget.js
  10. 4 1
      esp/src/eclwatch/ESPDFUWorkunit.js
  11. 3 0
      esp/src/eclwatch/ESPLogicalFile.js
  12. 31 0
      esp/src/eclwatch/ESPWorkunit.js
  13. 2 2
      esp/src/eclwatch/EventScheduleWorkunitWidget.js
  14. 2 2
      esp/src/eclwatch/GetDFUWorkunitsWidget.js
  15. 18 3
      esp/src/eclwatch/GraphPageWidget.js
  16. 8 1
      esp/src/eclwatch/GraphTreeWidget.js
  17. 50 0
      esp/src/eclwatch/GraphWidget.js
  18. 2 2
      esp/src/eclwatch/GraphsWidget.js
  19. 19 4
      esp/src/eclwatch/LFDetailsWidget.js
  20. 3 1
      esp/src/eclwatch/LZBrowseWidget.js
  21. 7 7
      esp/src/eclwatch/LogsWidget.js
  22. 0 7
      esp/src/eclwatch/QuerySetErrorsWidget.js
  23. 4 4
      esp/src/eclwatch/QuerySetQueryWidget.js
  24. 2 2
      esp/src/eclwatch/ResourcesWidget.js
  25. 6 6
      esp/src/eclwatch/ResultsWidget.js
  26. 2 2
      esp/src/eclwatch/SearchResultsWidget.js
  27. 2 2
      esp/src/eclwatch/SourceFilesWidget.js
  28. 3 2
      esp/src/eclwatch/TimingPageWidget.js
  29. 16 11
      esp/src/eclwatch/TimingTreeMapWidget.js
  30. 0 18
      esp/src/eclwatch/UserQueryWidget.js
  31. 16 28
      esp/src/eclwatch/WUDetailsWidget.js
  32. 2 2
      esp/src/eclwatch/WUQueryWidget.js
  33. 6 0
      esp/src/eclwatch/nls/hpcc.js
  34. 11 11
      esp/src/eclwatch/templates/DFUQueryWidget.html
  35. 5 5
      esp/src/eclwatch/templates/GraphWidget.html
  36. 44 19
      esp/src/eclwatch/templates/LFDetailsWidget.html
  37. 10 10
      esp/src/eclwatch/templates/LZBrowseWidget.html
  38. 18 17
      initfiles/examples/embed/mysql-simple.ecl
  39. 1 1
      roxie/ccd/ccdfile.cpp
  40. 17 5
      system/jlib/jmutex.cpp
  41. 1 1
      system/jlib/jsocket.cpp
  42. 24 10
      testing/regress/README.rst
  43. 29 9
      testing/regress/ecl-test
  44. 6 0
      testing/regress/ecl-test.json
  45. 16 0
      testing/regress/hpcc/util/util.py

+ 9 - 1
common/workunit/workunit.cpp

@@ -2197,8 +2197,16 @@ public:
     {
         StringBuffer wuidStr(wuid);
         wuidStr.trim();
-        if (!wuidStr.length())
+        if (wuidStr.length() && ('w' == wuidStr.charAt(0)))
+            wuidStr.setCharAt(0, 'W');
+
+        if (!wuidStr.length() || ('W' != wuidStr.charAt(0)))
+        {
+            if (workUnitTraceLevel > 1)
+                PrintLog("openWorkUnit %s invalid WUID", nullText(wuidStr.str()));
+
             return NULL;
+        }
 
         if (workUnitTraceLevel > 1)
             PrintLog("openWorkUnit %s", wuidStr.str());

+ 2 - 1
dali/base/dautils.cpp

@@ -1992,7 +1992,8 @@ IRemoteConnection *getElementsPaged( IElementsPager *elementsPager,
     if (hint&&*hint)
     {
         elem.setown(QUERYINTERFACE(pagedElementsCache->get(owner,*hint),CPECacheElem)); // NB: removes from cache in process, added back at end
-        postfilter = elem->postFilter; // reuse cached postfilter
+        if (elem)
+            postfilter = elem->postFilter; // reuse cached postfilter
     }
     else
     {

+ 10 - 2
ecl/eclagent/eclagent.cpp

@@ -731,10 +731,18 @@ IConstWUResult *EclAgent::getResultForGet(const char *name, unsigned sequence)
 
 IConstWUResult *EclAgent::getExternalResult(const char * wuid, const char *name, unsigned sequence)
 {
+    LOG(MCsetresult, unknownJob, "EclAgent::getExternalResult(wuid:'%s',name='%s',sequence:%d)", nullText(wuid), nullText(name), sequence);
     Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
     Owned<IConstWorkUnit> externalWU = factory->openWorkUnit(wuid, false);
-    externalWU->remoteCheckAccess(queryUserDescriptor(), false);
-    return getWorkUnitResult(externalWU, name, sequence);
+    if (externalWU)
+    {
+        externalWU->remoteCheckAccess(queryUserDescriptor(), false);
+        return getWorkUnitResult(externalWU, name, sequence);
+    }
+    else
+    {
+        fail(0, "Missing or invalid workunit name in getExternalResult()");
+    }
 }
 
 void EclAgent::outputFormattedResult(const char * name, unsigned sequence, bool close)

+ 56 - 19
esp/eclwatch/ws_XSLT/dropzonefile.xslt

@@ -38,6 +38,7 @@
         <title>EclWatch</title>
         <script language="JavaScript1.2">
           var initialPath = '<xsl:value-of select="/DropZoneFilesResponse/Path"/>';
+          var currentNetAddress = '<xsl:value-of select="/DropZoneFilesResponse/NetAddress"/>';
           <xsl:text disable-output-escaping="yes"><![CDATA[
             var intervalId = 0;
             var hideLoading = 1;
@@ -45,7 +46,7 @@
             function onChangeMachine(resetPath)
             {
               machineDropDown = document.forms['DropZoneForm'].machine;
-              if (machineDropDown.selectedIndex >=0)
+              if (machineDropDown.selectedIndex > 0)
               {
                 selected=machineDropDown.options[machineDropDown.selectedIndex];               
                 document.forms['DropZoneForm'].NetAddress.value=selected.value;
@@ -65,6 +66,18 @@
 
                 document.location.href = "/FileSpray/DropZoneFiles?NetAddress=" + selected.value + "&OS=" + sourceOS+ "&Path=" + directory;
               }
+              else
+              {
+                document.forms['DropZoneForm'].NetAddress.value = "";
+                document.forms['DropZoneForm'].Directory.value = "";
+                var chooseFileButton = document.getElementById('FilesToUpload');
+                var uploadBtn = document.getElementById('UploadBtn');
+                if (chooseFileButton)
+                  chooseFileButton.disabled =  true;
+                if (uploadBtn)
+                  uploadBtn.disabled =  true;
+                document.forms['DropZoneFileForm'].style.display="none";
+              }
             }         
 
             function doBlink() 
@@ -183,12 +196,26 @@
             function getSelectedDropZone()
             {
               machineDropDown = document.forms['DropZoneForm'].machine;
-              if (machineDropDown.selectedIndex >=0)
+              if ((machineDropDown.selectedIndex < 1) && (machineDropDown.length > 1) && (currentNetAddress != ""))
+              {
+                for (i=1; i<machineDropDown.length; i++)
+                {
+                  if (machineDropDown.options[i].value == currentNetAddress)
+                  {
+                    machineDropDown.selectedIndex = i;
+                    break;
+                  }
+                }
+              }
+              if (machineDropDown.selectedIndex > 0)
               {
                 selected=machineDropDown.options[machineDropDown.selectedIndex];               
                 document.forms['DropZoneForm'].NetAddress.value=selected.value;
                 pos = selected.title.indexOf(';');
-                directory = selected.title.substring(0, pos);
+                if (initialPath == '')
+                  directory = selected.title.substring(0, pos);
+                else
+                  directory = initialPath;
                 linux = selected.title.substring(pos+1);
                 sourceOS = linux == 'true' ? 1: 0;
 
@@ -200,6 +227,16 @@
 
                 url0 = "/FileSpray/UploadFile?upload_&NetAddress=" + selected.value + "&OS=" + sourceOS + "&Path=" + directory;
               }
+              else
+              {
+                document.forms['DropZoneForm'].Directory.value = "";
+                var chooseFileButton = document.getElementById('FilesToUpload');
+                var uploadBtn = document.getElementById('UploadBtn');
+                if (chooseFileButton)
+                  chooseFileButton.disabled =  true;
+                if (uploadBtn)
+                  uploadBtn.disabled =  true;
+              }
             }
 
             function onLoad()
@@ -207,9 +244,6 @@
               initSelection('resultsTable');
 
               getSelectedDropZone();
-              document.forms['DropZoneForm'].Directory.value = initialPath;
-              document.forms['DropZoneFileForm'].Path.value = initialPath;
-              url0 = "/FileSpray/UploadFile?upload_&NetAddress=" + selected.value + "&OS=" + sourceOS + "&Path=" + initialPath;
             }   
           ]]></xsl:text>
         </script>
@@ -232,6 +266,7 @@
                         <td>Machine/dropzone:</td>
                         <td>
                           <select name="machine" id="machine" onchange="onChangeMachine(true)" onblur="onChangeMachine(true)">
+                            <option>(Select a dropzone.)</option>
                             <xsl:for-each select="DropZones/DropZone">
                               <option>
                                 <xsl:variable name="curip" select="NetAddress"/>
@@ -284,19 +319,21 @@
               </tr>
               <tr>
                 <td>
-                  <form id="DropZoneFileForm" action="/FileSpray/DeleteDropZoneFiles" method="post">
-                    <input type="hidden" name="NetAddress" value="{$netAddress0}"/>
-                    <input type="hidden" name="Path" value="{$path0}"/>
-                    <input type="hidden" name="OS" value="{$os0}"/>
-                    <xsl:choose>
-                      <xsl:when test="not(Files/PhysicalFileStruct[1])">
-                        <br/><br/>No file found in this dropzone.<br/><br/>
-                      </xsl:when>
-                      <xsl:otherwise>
-                        <xsl:apply-templates select="Files"/>
-                      </xsl:otherwise>
-                    </xsl:choose>
-                  </form>
+                  <xsl:if test="string-length($netAddress0)">
+                    <form id="DropZoneFileForm" action="/FileSpray/DeleteDropZoneFiles" method="post">
+                      <input type="hidden" name="NetAddress" value="{$netAddress0}"/>
+                      <input type="hidden" name="Path" value="{$path0}"/>
+                      <input type="hidden" name="OS" value="{$os0}"/>
+                      <xsl:choose>
+                        <xsl:when test="not(Files/PhysicalFileStruct[1])">
+                          <br/><br/>No file found in this dropzone.<br/><br/>
+                        </xsl:when>
+                        <xsl:otherwise>
+                          <xsl:apply-templates select="Files"/>
+                        </xsl:otherwise>
+                      </xsl:choose>
+                    </form>
+                  </xsl:if>
                 </td>
               </tr>
             </table>

+ 5 - 5
esp/eclwatch/ws_XSLT/index.xslt

@@ -889,8 +889,8 @@
         <xsl:param name="queue"/>
         <xsl:param name="thorlcr" select="'0'"/>
 
-        <xsl:variable name="active1" select="$workunits[State='running' and $queue = QueueName]"/>
-        <xsl:variable name="active2" select="$workunits[State='running' and $queue != QueueName]"/>
+        <xsl:variable name="active1" select="$workunits[starts-with(State,'running') and $queue = QueueName]"/>
+        <xsl:variable name="active2" select="$workunits[starts-with(State,'running') and $queue != QueueName]"/>
         <tbody>
             <xsl:choose>
                 <xsl:when test="$workunits[1]">
@@ -938,7 +938,7 @@
                         </tr>
                     </xsl:if>
 
-                    <xsl:for-each select="$workunits[State!='running']">
+                    <xsl:for-each select="$workunits[not(starts-with(State,'running'))]">
                         <tr>
                             <xsl:apply-templates select=".">
                                 <xsl:with-param name="cluster" select="$cluster"/>
@@ -1017,7 +1017,7 @@
         <xsl:variable name="active">
             <xsl:choose>
                 <xsl:when test="Priority='high'">highpriority</xsl:when>
-                <xsl:when test="State='running'">active</xsl:when>
+                <xsl:when test="starts-with(State,'running')">active</xsl:when>
                 <xsl:when test="Priority='normal'">normalpriority</xsl:when>
                 <xsl:when test="Priority='low'">lowpriority</xsl:when>
                 <xsl:otherwise></xsl:otherwise>
@@ -1044,7 +1044,7 @@
             </xsl:variable>
             <a href="javascript:go('{$href-method}?{$href-wuidparam}={Wuid}')">
                 <xsl:choose>
-                    <xsl:when test="State='running'">
+                    <xsl:when test="starts-with(State,'running')">
                         <b>
                             <xsl:value-of select="Wuid"/>
                         </b>

+ 3 - 5
esp/services/ws_fs/ws_fsService.cpp

@@ -3038,11 +3038,6 @@ bool CFileSprayEx::onDropZoneFiles(IEspContext &context, IEspDropZoneFilesReques
                 aDropZone->setPath(dir.str());
                 aDropZone->setName(pszName);
                 aDropZone->setNetAddress(sNetAddr.str());
-                if (netAddressStr.length() < 1)
-                {
-                    netAddressStr = sNetAddr;
-                    directoryStr = dir;
-                }
 
                 dropZoneList.append(*aDropZone.getClear());
             }
@@ -3051,6 +3046,9 @@ bool CFileSprayEx::onDropZoneFiles(IEspContext &context, IEspDropZoneFilesReques
         if (dropZoneList.ordinality())
             resp.setDropZones(dropZoneList);
 
+        if (netAddressStr.length() < 1)
+            return true;
+
         char pathSep = '/';
         if (osStr && *osStr)
         {

+ 5 - 5
esp/src/eclwatch/ActivityWidget.js

@@ -290,12 +290,12 @@ define([
                             var img = row.getStateImage();
                             if (context.activity.isInstanceOfQueue(row)) {
                                 if (row.ClusterType === 3) {
-                                    return "<img src='" + img + "'/>&nbsp;<a href='#' class='" + context.id + "RowClick'>" + _name + "</a>";
+                                    return "<img src='" + img + "'/>&nbsp;<a href='#' class='dgrid-row-url'>" + _name + "</a>";
                                 } else {
                                     return "<img src='" + img + "'/>&nbsp;" + _name;
                                 }
                             }
-                            return "<img src='" + img + "'/>&nbsp;<a href='#' class='" + context.id + "RowClick'>" + row.Wuid + "</a>";
+                            return "<img src='" + img + "'/>&nbsp;<a href='#' class='dgrid-row-url'>" + row.Wuid + "</a>";
                         }
                     }),
                     GID: {
@@ -303,7 +303,7 @@ define([
                         formatter: function (_gid, row) {
                             if (context.activity.isInstanceOfWorkunit(row)) {
                                 if (row.GraphName) {
-                                    return "<a href='#' class='" + context.id + "GraphClick'>" + row.GraphName + "-" + row.GID + "</a>";
+                                    return "<a href='#' class='dgrid-row-url2'>" + row.GraphName + "-" + row.GID + "</a>";
                                 }
                             }
                             return "";
@@ -339,7 +339,7 @@ define([
                 }
             }, domID);
 
-            on(document, "." + this.id + "RowClick:click", function (evt) {
+            retVal.on(".dgrid-row-url:click", function (evt) {
                 if (context._onRowDblClick) {
                     var row = retVal.row(evt).data;
                     context._onRowDblClick(row, {
@@ -348,7 +348,7 @@ define([
                 }
             });
 
-            on(document, "." + this.id + "GraphClick:click", function (evt) {
+            retVal.on(".dgrid-row-url2:click", function (evt) {
                 if (context._onRowDblClick) {
                     var row = retVal.row(evt).data;
                     context._onRowDblClick(row, {

+ 2 - 2
esp/src/eclwatch/DFUQueryWidget.js

@@ -438,7 +438,7 @@ define([
                             if (row.__hpcc_isDir) {
                                 return name;
                             }
-                            return (row.getStateImageHTML ? row.getStateImageHTML() + "&nbsp;" : "") + "<a href='#' rowIndex=" + row + " class='" + context.id + "LogicalNameClick'>" + name + "</a>";
+                            return (row.getStateImageHTML ? row.getStateImageHTML() + "&nbsp;" : "") + "<a href='#' class='dgrid-row-url'>" + name + "</a>";
                         },
                         renderExpando: function (level, hasChildren, expanded, object) {
                             var dir = this.grid.isRTL ? "right" : "left";
@@ -462,7 +462,7 @@ define([
             }, this.id + "WorkunitsGrid");
 
             var context = this;
-            on(document, "." + context.id + "LogicalNameClick:click", function (evt) {
+            this.workunitsGrid.on(".dgrid-row-url:click", function (evt) {
                 if (context._onRowDblClick) {
                     var item = context.workunitsGrid.row(evt).data;
                     context._onRowDblClick(item);

+ 6 - 6
esp/src/eclwatch/DFUWUDetailsWidget.js

@@ -283,13 +283,13 @@ define([
 
         refreshActionState: function () {
             this.setDisabled(this.id + "AutoRefresh", this.wu.isComplete(), "iconAutoRefresh", "iconAutoRefreshDisabled");
-            registry.byId(this.id + "Save").set("disabled", !this.wu.isComplete());
-            registry.byId(this.id + "Delete").set("disabled", !this.wu.isComplete());
-            registry.byId(this.id + "Abort").set("disabled", this.wu.isComplete());
-            registry.byId(this.id + "Resubmit").set("disabled", !this.wu.isComplete());
+            registry.byId(this.id + "Save").set("disabled", !this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "Delete").set("disabled", !this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "Abort").set("disabled", this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "Resubmit").set("disabled", !this.wu.isComplete() || this.wu.isDeleted());
             registry.byId(this.id + "Modify").set("disabled", true);  //TODO
-            registry.byId(this.id + "JobName").set("readOnly", !this.wu.isComplete());
-            registry.byId(this.id + "isProtected").set("readOnly", !this.wu.isComplete());
+            registry.byId(this.id + "JobName").set("readOnly", !this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "isProtected").set("readOnly", !this.wu.isComplete() || this.wu.isDeleted());
 
             this.summaryWidget.set("iconClass", this.wu.getStateIconClass());
             dom.byId(this.id + "StateIdImage").src = this.wu.getStateImage();

+ 4 - 1
esp/src/eclwatch/ESPDFUWorkunit.js

@@ -154,6 +154,9 @@ define([
         isComplete: function () {
             return this.hasCompleted;
         },
+        isDeleted: function () {
+            return this.State === 999;
+        },
         monitor: function (callback) {
             if (callback) {
                 callback(this);
@@ -257,7 +260,7 @@ define([
             return dojoConfig.getImageURL("unlocked.png");
         },
         getStateIconClass: function () {
-            switch (this.StateID) {
+            switch (this.State) {
                 case 1:
                     return "iconWarning";
                 case 2:

+ 3 - 0
esp/src/eclwatch/ESPLogicalFile.js

@@ -380,6 +380,9 @@ define([
         },
         getStateImageHTML: function () {
             return dojoConfig.getImageHTML(this.getStateImageName());
+        },
+        isDeleted: function () {
+            return this.StateID === 999;
         }
     });
 

+ 31 - 0
esp/src/eclwatch/ESPWorkunit.js

@@ -171,6 +171,34 @@ define([
         _GraphsSetter: function (Graphs) {
             this.set("graphs", Graphs.ECLGraph);
         },
+
+        //  Calculated "Helpers"  ---
+        _HelpersSetter: function (Helpers) {
+            this.set("helpers", Helpers.ECLHelpFile);
+            this.refreshHelpersCount();
+        },
+        _ThorLogListSetter: function (ThorLogList) {
+            this.set("thorLogInfo", ThorLogList.ThorLogInfo);
+            this.refreshHelpersCount();
+        },
+        _HasArchiveQuerySetter: function (HasArchiveQuery) {
+            this.set("hasArchiveQuery", HasArchiveQuery);
+            this.refreshHelpersCount();
+        },
+        refreshHelpersCount: function () {
+            var helpersCount = 2;   //  ECL + Workunit XML are also helpers...
+            if (this.helpers) {
+                helpersCount += this.helpers.length;
+            }
+            if (this.thorLogList) {
+                helpersCount += this.thorLogList.length;
+            }
+            if (this.hasArchiveQuery) {
+                helpersCount += 1;
+            }
+            this.set("helpersCount", helpersCount);
+        },
+
         //  ---  ---  ---
         onCreate: function () {
         },
@@ -188,6 +216,9 @@ define([
         isComplete: function () {
             return this.hasCompleted;
         },
+        isDeleted: function () {
+            return this.StateID === 999;
+        },
         monitor: function (callback) {
             if (callback) {
                 callback(this);

+ 2 - 2
esp/src/eclwatch/EventScheduleWorkunitWidget.js

@@ -140,7 +140,7 @@ define([
                     Wuid: {
                         label: this.i18n.Workunit, width: 180, sortable: true,
                         formatter: function (Wuid) {
-                            return "<a href='#' class='" + context.id + "WuidClick'>" + Wuid + "</a>";
+                            return "<a href='#' class='dgrid-row-url'>" + Wuid + "</a>";
                         }
                     },
                     Cluster: { label: this.i18n.Cluster, width: 100, sortable: true },
@@ -152,7 +152,7 @@ define([
                 }
             }, this.id + "EventGrid");
 
-            on(document, "." + context.id + "WuidClick:click", function (evt) {
+            this.eventGrid.on(".dgrid-row-url:click", function (evt) {
                 if (context._onRowDblClick) {
                     var item = context.eventGrid.row(evt).data;
                     context._onRowDblClick(item);

+ 2 - 2
esp/src/eclwatch/GetDFUWorkunitsWidget.js

@@ -333,7 +333,7 @@ define([
                         width: 180,
                         formatter: function (ID, idx) {
                             var wu = ESPDFUWorkunit.Get(ID);
-                            return "<img src='" + wu.getStateImage() + "'>&nbsp;<a href='#' rowIndex=" + idx + " class='" + context.id + "IDClick'>" + ID + "</a>";
+                            return "<img src='" + wu.getStateImage() + "'>&nbsp;<a href='#' class='dgrid-row-url'>" + ID + "</a>";
                         }
                     },
                     Command: {
@@ -355,7 +355,7 @@ define([
             }, this.id + "WorkunitsGrid");
 
             var context = this;
-            on(document, "." + context.id + "IDClick:click", function (evt) {
+            this.workunitsGrid.on(".dgrid-row-url:click", function (evt) {
                 if (context._onRowDblClick) {
                     var item = context.workunitsGrid.row(evt).data;
                     context._onRowDblClick(item.ID);

+ 18 - 3
esp/src/eclwatch/GraphPageWidget.js

@@ -150,7 +150,9 @@ define([
             };
             this.overview.onDoubleClick = function (globalID, keyState) {
                 var mainItem = context.main.getItem(globalID);
-                context.main.centerOnItem(mainItem, true);
+                if (mainItem) {
+                    context.main.centerOnItem(mainItem, true);
+                }
             };
 
             this.main = registry.byId(this.id + "MainGraphWidget");
@@ -436,8 +438,15 @@ define([
                         initialSelection = [this.params.SubGraphId];
                     }
                 }
+                var mainRoot = [0];
                 this.setOverviewRootItems([0], initialSelection);
-                this.setMainRootItems(initialSelection);
+                var complexityInfo = this.global.getComplexityInfo();
+                if (complexityInfo.isComplex()) {
+                    if (confirm(lang.replace(this.i18n.ComplexityWarning, complexityInfo) + "\n" + this.i18n.ManualOverviewSelection)) {
+                        mainRoot = [];
+                    }
+                }
+                this.setMainRootItems(mainRoot, initialSelection);
                 this.setLocalRootItems([]);
                 this.loadSubgraphs();
                 this.loadVertices();
@@ -635,7 +644,13 @@ define([
                 }
             }
             if (sourceControl != this.local) {
-                this.setLocalRootItems(selectedGlobalIDs);
+                switch (sourceControl) {
+                    case this.overview:
+                        this.setLocalRootItems([]);
+                        break;
+                    default:
+                        this.setLocalRootItems(selectedGlobalIDs);
+                }
             }
 
             var propertiesDom = dom.byId(this.id + "Properties");

+ 8 - 1
esp/src/eclwatch/GraphTreeWidget.js

@@ -431,7 +431,14 @@ define([
             if (this.global.loadXGMML(xgmml, false, this.graphTimers)) {
                 this.global.setMessage("...");  //  Just in case it decides to render  ---
                 var initialSelection = [];
-                this.setMainRootItems(initialSelection);
+                var mainRoot = [0];
+                var complexityInfo = this.global.getComplexityInfo();
+                if (complexityInfo.isComplex()) {
+                    if (confirm(lang.replace(this.i18n.ComplexityWarning, complexityInfo) + "\n" + this.i18n.ManualTreeSelection)) {
+                        mainRoot = [];
+                    }
+                }
+                this.setMainRootItems(mainRoot, initialSelection);
                 this.loadTree();
                 this.loadSubgraphs();
                 this.loadVertices();

+ 50 - 0
esp/src/eclwatch/GraphWidget.js

@@ -263,6 +263,7 @@ define([
                                         this.graphViewHistory.getLatest().navigateTo(this);
                                     }
                                     deferred.resolve("Layout Complete.");
+                                    this.refreshRootState(context.selectedGlobalIDs);
                                 };
                                 if (this.svg) {
                                     targetGraphWidget.startCachedLayout(this.svg);
@@ -277,6 +278,7 @@ define([
                             targetGraphWidget.setSelectedAsGlobalID(context.selectedGlobalIDs);
                             targetGraphWidget.setMessage("");
                             deferred.resolve("XGMML Did Not Change.");
+                            targetGraphWidget.refreshRootState(context.selectedGlobalIDs);
                         }
                     }));
                 }));
@@ -596,6 +598,35 @@ define([
                 return this.loadXGMML(xgmml, true);
             },
 
+            getTypeSummary: function (gloablIDs) {
+                var retVal = {
+                    Graph: 0,
+                    Cluster: 0,
+                    Vertex: 0,
+                    Edge: 0,
+                    Unknown: 0
+                };
+                arrayUtil.forEach(gloablIDs, function (item) {
+                    retVal[this.getGlobalType(item)]++;
+                }, this);
+                return retVal;
+            },
+
+            getGlobalType: function(globalID) {
+                if (this.hasPlugin()) {
+                    return this._plugin.getGlobalType(this._plugin.getItem(globalID));
+                }
+                return "Unknown";
+            },
+
+            getComplexityInfo: function () {
+                return {
+                    threshold: 200,
+                    activityCount: this.getVertices().length,
+                    isComplex: function () { return this.activityCount > this.threshold; }
+                };
+            },
+
             centerOn: function (globalID) {
                 if (this.hasPlugin()) {
                     var item = this.getItem(globalID);
@@ -1103,6 +1134,13 @@ define([
                 return [];
             },
 
+            getVertices: function () {
+                if (this.hasPlugin()) {
+                    return this._plugin.getVertices();
+                }
+                return [];
+            },
+
             getVerticesWithProperties: function () {
                 if (this.hasPlugin()) {
                     return this._plugin.getVerticesWithProperties();
@@ -1152,6 +1190,18 @@ define([
                 this.setDisabled(this.id + "Next", !this.graphViewHistory.hasNext(), "iconRight", "iconRightDisabled");
                 var selection = this.getSelection();
                 this.setDisabled(this.id + "SyncSelection", !this.getSelection().length, "iconSync", "iconSyncDisabled");
+            },
+
+            refreshRootState: function (selectedGlobalIDs) {
+                var depthDisabled = false;
+                var distanceDisabled = false;
+                if (selectedGlobalIDs) {
+                    var typeSummary = this.getTypeSummary(selectedGlobalIDs);
+                    depthDisabled = !selectedGlobalIDs.length || !(typeSummary.Graph || typeSummary.Cluster);
+                    distanceDisabled = !(typeSummary.Vertex || typeSummary.Edge);
+                }
+                this.setDisabled(this.id + "Depth", depthDisabled);
+                this.setDisabled(this.id + "Distance", distanceDisabled);
             }
         });
     });

+ 2 - 2
esp/src/eclwatch/GraphsWidget.js

@@ -123,7 +123,7 @@ define([
                     Name: {
                         label: this.i18n.Name, width: 72, sortable: true,
                         formatter: function (Name, row) {
-                            return "<a href='#' class='" + context.id + "GraphClick'>" + Name + "</a>";
+                            return "<a href='#' class='dgrid-row-url'>" + Name + "</a>";
                         }
                     },
                     Label: { label: this.i18n.Label, sortable: true },
@@ -146,7 +146,7 @@ define([
                 context.syncSelectionFrom(context.grid);
             });
 
-            on(document, "." + this.id + "GraphClick:click", function (evt) {
+            retVal.on(".dgrid-row-url:click", function (evt) {
                 if (context._onRowDblClick) {
                     var row = retVal.row(evt).data;
                     context._onRowDblClick(row);

+ 19 - 4
esp/src/eclwatch/LFDetailsWidget.js

@@ -51,7 +51,8 @@ define([
     "dijit/TooltipDialog",
     "dijit/form/ValidationTextBox",
     "dijit/form/CheckBox",
-    
+    "dijit/Fieldset",
+
     "hpcc/TableContainer"
 
 ], function (exports, declare, lang, i18n, nlsHPCC, arrayUtil, dom, domAttr, domClass, domForm, query,
@@ -153,8 +154,13 @@ define([
         _onDesprayOk: function (event) {
             if (this.desprayForm.validate()) {
                 var context = this;
+                var request = domForm.toObject(this.id + "DesprayForm");
+                if (!context.endsWith(request.destPath, "/")) {
+                    request.destPath += "/";
+                }
+                request.destPath += registry.byId(this.id + "DesprayTargetName").get("value");
                 this.logicalFile.despray({
-                    request: domForm.toObject(this.id + "DesprayForm")
+                    request: request
                 }).then(function (response) {
                     context._handleResponse("DesprayResponse.wuid", response);
                 });
@@ -186,7 +192,8 @@ define([
                 DropZones: true,
                 callback: function (value, item) {
                     context.updateInput("DesprayTargetIPAddress", null, item.machine.Netaddress);
-                    context.updateInput("DesprayTargetPath", null, item.machine.Directory + "/" + context.logicalFile.getLeaf());
+                    context.updateInput("DesprayTargetPath", null, item.machine.Directory);
+                    context.updateInput("DesprayTargetName", null, context.logicalFile.getLeaf());
                 }
             });
         },
@@ -291,6 +298,7 @@ define([
                 this.summaryWidget.set("iconClass", this.logicalFile.getStateIconClass());
                 domClass.remove(this.id + "StateIdImage");
                 domClass.add(this.id + "StateIdImage", this.logicalFile.getStateIconClass());
+                this.refreshActionState();
             }
         },
 
@@ -309,7 +317,14 @@ define([
                 this.addChild(retVal);
             }
             return retVal;
-        }
+        },
 
+        refreshActionState: function () {
+            registry.byId(this.id + "Save").set("disabled", this.logicalFile.isDeleted());
+            registry.byId(this.id + "Delete").set("disabled", this.logicalFile.isDeleted());
+            registry.byId(this.id + "CopyDropDown").set("disabled", this.logicalFile.isDeleted());
+            registry.byId(this.id + "RenameDropDown").set("disabled", this.logicalFile.isDeleted());
+            registry.byId(this.id + "DesprayDropDown").set("disabled", this.logicalFile.isDeleted());
+        }
     });
 });

+ 3 - 1
esp/src/eclwatch/LZBrowseWidget.js

@@ -475,10 +475,12 @@ define([
                         label: this.i18n.Name,
                         collapseOnRefresh: true,
                         sortable: false,
-                        formatter: function (name, row) {
+                        formatter: function (_name, row) {
                             var img = "";
+                            var name = _name;
                             if (row.isDir === undefined) {
                                 img = dojoConfig.getImageHTML("server.png");
+                                name += " [//" + row.NetAddress + row.Path + "]";
                             } else if (row.isDir) {
                                 img = dojoConfig.getImageHTML("folder.png");
                             } else {

+ 7 - 7
esp/src/eclwatch/LogsWidget.js

@@ -152,7 +152,7 @@ define([
                             width: 117,
                             formatter: function (Type, row) {
                                 if (context.canShowContent(Type)) {
-                                    return "<a href='#' rowIndex=" + row.id + " class='" + context.id + "HelperClick'>" + Type + "</a>";
+                                    return "<a href='#' class='dgrid-row-url'>" + Type + "</a>";
                                 }
                                 return Type;
                             }
@@ -166,7 +166,7 @@ define([
                         }
                     }
                 }, domID);
-                on(document, "." + this.id + "HelperClick:click", function (evt) {
+                retVal.on(".dgrid-row-url:click", function (evt) {
                     if (context._onRowDblClick) {
                         var row = context.grid.row(evt).data;
                         context._onRowDblClick(row);
@@ -226,17 +226,17 @@ define([
                             Type: "Workunit XML",
                             FileSize: response.WUXMLSize
                         });
-                        if (response.HasArchiveQuery) {
+                        if (response.hasArchiveQuery) {
                             context.logData.push({
                                 id: "A:0",
                                 Type: "Archive Query"
                             });
                         }
-                        if (response.Helpers && response.Helpers.ECLHelpFile) {
-                            context.loadHelpers(response.Helpers.ECLHelpFile);
+                        if (response.helpers) {
+                            context.loadHelpers(response.helpers);
                         }
-                        if (response.ThorLogList && response.ThorLogList.ThorLogInfo) {
-                            context.loadThorLogInfo(response.ThorLogList.ThorLogInfo);
+                        if (response.thorLogList) {
+                            context.loadThorLogInfo(response.thorLogList);
                         }
                         context.store.setData(context.logData);
                         context.grid.refresh();

+ 0 - 7
esp/src/eclwatch/QuerySetErrorsWidget.js

@@ -58,13 +58,6 @@ define([
                     State: { label: this.i18n.State, width: 108, sortable: false }
                 }
             }, domID);
-
-            on(document, "." + this.id + "WuidClick:click", function (evt) {
-                if (context._onRowDblClick) {
-                    var row = retVal.row(evt).data;
-                    context._onRowDblClick(row);
-                }
-            });
             return retVal;
         },
 

+ 4 - 4
esp/src/eclwatch/QuerySetQueryWidget.js

@@ -345,7 +345,7 @@ define([
                     Id: {
                         label: this.i18n.ID,
                         formatter: function (Id, idx) {
-                            return "<a href='#' rowIndex=" + idx + " class='" + context.id + "IdClick'>" + Id + "</a>";
+                            return "<a href='#' class='dgrid-row-url'>" + Id + "</a>";
                         }
                     },
                     Name: {
@@ -361,7 +361,7 @@ define([
                         width: 180,
                         label: this.i18n.WUID,
                         formatter: function (Wuid, idx) {
-                            return "<a href='#' rowIndex=" + idx + " class='" + context.id + "WuidClick'>" + Wuid + "</a>";
+                            return "<a href='#' class='dgrid-row-url2'>" + Wuid + "</a>";
                         }
                     },
                     Dll: {
@@ -380,13 +380,13 @@ define([
                     }
                 }
             }, this.id + "QuerySetGrid");
-            on(document, "." + context.id + "IdClick:click", function (evt) {
+            this.querySetGrid.on(".dgrid-row-url:click", function (evt) {
                 if (context._onRowDblClick) {
                     var item = context.querySetGrid.row(evt).data;
                     context._onRowDblClick(item);
                 }
             });
-            on(document, "." + context.id + "WuidClick:click", function (evt) {
+            this.querySetGrid.on(".dgrid-row-url2:click", function (evt) {
                 if (context._onRowDblClick) {
                     var item = context.querySetGrid.row(evt).data;
                     context._onRowDblClick(item, true);

+ 2 - 2
esp/src/eclwatch/ResourcesWidget.js

@@ -87,13 +87,13 @@ define([
                     DisplayPath: {
                         label: this.i18n.Name, sortable: true,
                         formatter: function (url, row) {
-                            return "<a href='#' class='" + context.id + "URLClick'>" + url + "</a>";
+                            return "<a href='#' class='dgrid-row-url'>" + url + "</a>";
                         }
                     }
                 }
             }, domID);
 
-            on(document, "." + this.id + "URLClick:click", function (evt) {
+            retVal.on(".dgrid-row-url:click", function (evt) {
                 if (context._onRowDblClick) {
                     var row = retVal.row(evt).data;
                     context._onRowDblClick(row);

+ 6 - 6
esp/src/eclwatch/ResultsWidget.js

@@ -97,13 +97,13 @@ define([
                     Name: {
                         label: this.i18n.Name, width: 180, sortable: true,
                         formatter: function (Name, idx) {
-                            return "<a href='#' rowIndex=" + idx + " class='" + context.id + "ResultClick'>" + Name + "</a>";
+                            return "<a href='#' class='dgrid-row-url'>" + Name + "</a>";
                         }
                     },
                     FileName: {
                         label: this.i18n.FileName, sortable: true,
                         formatter: function (FileName, idx) {
-                            return "<a href='#' rowIndex=" + idx + " class='" + context.id + "FileClick'>" + FileName + "</a>";
+                            return "<a href='#' class='dgrid-row-url2'>" + FileName + "</a>";
                         }
                     },
                     Value: {
@@ -116,7 +116,7 @@ define([
                         formatter: function (ResultViews, idx) {
                             var retVal = "";
                             arrayUtil.forEach(ResultViews, function (item, idx) {
-                                retVal += "<a href='#' viewName=" + encodeURIComponent(item) + " class='" + context.id + "ViewClick'>" + item + "</a>&nbsp;";
+                                retVal += "<a href='#' viewName=" + encodeURIComponent(item) + " class='dgrid-row-url3'>" + item + "</a>&nbsp;";
                             });
                             return retVal;
                         }
@@ -125,19 +125,19 @@ define([
             }, domID);
 
             var context = this;
-            on(document, "." + this.id + "ResultClick:click", function (evt) {
+            retVal.on(".dgrid-row-url:click", function (evt) {
                 if (context._onRowDblClick) {
                     var row = context.grid.row(evt).data;
                     context._onRowDblClick(row);
                 }
             });
-            on(document, "." + this.id + "FileClick:click", function (evt) {
+            retVal.on(".dgrid-row-url2:click", function (evt) {
                 if (context._onRowDblClick) {
                     var row = context.grid.row(evt).data;
                     context._onRowDblClickFile(row);
                 }
             });
-            on(document, "." + this.id + "ViewClick:click", function (evt) {
+            retVal.on(".dgrid-row-url3:click", function (evt) {
                 if (context._onRowDblClick) {
                     var row = context.grid.row(evt).data;
                     context._onRowDblClickView(row, evt.srcElement.getAttribute("viewName"));

+ 2 - 2
esp/src/eclwatch/SearchResultsWidget.js

@@ -132,14 +132,14 @@ define([
                     Summary: {
                         label: this.i18n.Who, sortable: true,
                         formatter: function (summary, idx) {
-                            return "<a href='#' rowIndex=" + idx + " class='" + context.id + "SearchResultClick'>" + summary + "</a>";
+                            return "<a href='#' class='dgrid-row-url'>" + summary + "</a>";
                         }
                     }
                 }
             }, domID);
 
             var context = this;
-            on(document, "." + this.id + "SearchResultClick:click", function (evt) {
+            retVal.on(".dgrid-row-url:click", function (evt) {
                 if (context._onRowDblClick) {
                     var row = retVal.row(evt).data;
                     context._onRowDblClick(row);

+ 2 - 2
esp/src/eclwatch/SourceFilesWidget.js

@@ -67,7 +67,7 @@ define([
                     Name: {
                         label: "Name", sortable: true,
                         formatter: function (Name, row) {
-                            return dojoConfig.getImageHTML(row.IsSuperFile ? "folder_table.png" : "file.png") + "&nbsp;<a href='#' rowIndex=" + row + " class='" + context.id + "SourceFileClick'>" + Name + "</a>";
+                            return dojoConfig.getImageHTML(row.IsSuperFile ? "folder_table.png" : "file.png") + "&nbsp;<a href='#' class='dgrid-row-url'>" + Name + "</a>";
                         }
                     },
                     Count: { label: "Usage", width: 72, sortable: true }
@@ -75,7 +75,7 @@ define([
             }, domID);
 
             var context = this;
-            on(document, "." + this.id + "SourceFileClick:click", function (evt) {
+            retVal.on("." + this.id + "dgrid-row-url:click", function (evt) {
                 if (context._onRowDblClick) {
                     var row = context.grid.row(evt).data;
                     context._onRowDblClick(row);

+ 3 - 2
esp/src/eclwatch/TimingPageWidget.js

@@ -104,7 +104,7 @@ define([
                             sortable: true,
                             formatter: function (Name, row) {
                                 if (row.GraphName) {
-                                    return "<a href='#' class='" + context.id + "GraphClick'>" + Name + "</a>";
+                                    return "<a href='#' class='dgrid-row-url'>" + Name + "</a>";
                                 }
                                 return Name;
                             }
@@ -117,7 +117,7 @@ define([
                     context.syncSelectionFrom(context.grid);
                 });
 
-                on(document, "." + this.id + "GraphClick:click", function (evt) {
+                retVal.on(".dgrid-row-url:click", function (evt) {
                     if (context._onRowDblClick) {
                         var row = retVal.row(evt).data;
                         context._onRowDblClick(row);
@@ -155,6 +155,7 @@ define([
                         onGetTimers: function (timers) {
                             context.store.setData(timers);
                             context.grid.refresh();
+                            context.timingTreeMap.loadTimers(timers);
                         }
                     });
                 }

+ 16 - 11
esp/src/eclwatch/TimingTreeMapWidget.js

@@ -94,18 +94,15 @@ define([
                 var context = this;
                 if (params.Wuid) {
                     this.wu = ESPWorkunit.Get(params.Wuid);
-
-                    this.wu.fetchTimers(function (timers) {
-                        context.timers = timers;
-                        context.loadTimers(timers, params.query);
+                    var monitorCount = 4;
+                    this.wu.monitor(function () {
+                        if (context.wu.isComplete() || ++monitorCount % 5 == 0) {
+                            context.refreshTreeMap();
+                        }
                     });
                 }
             },
 
-            setQuery: function (query) {
-                this.loadTimers(this.timers, query);
-            },
-
             getSelected: function () {
                 return this.treeMap.selectedItems;
             },
@@ -158,13 +155,21 @@ define([
                 }
             },
 
-            loadTimers: function (timers, query) {
+            refreshTreeMap: function () {
+                var context = this;
+                this.wu.fetchTimers(function (timers) {
+                    context.timers = timers;
+                    context.loadTimers(timers);
+                });
+            },
+
+            loadTimers: function (timers) {
                 this.largestValue = 0;
                 var timerData = [];
                 if (timers) {
                     for (var i = 0; i < timers.length; ++i) {
-                        if (query.graphsOnly) {
-                            if (timers[i].SubGraphId && (query.graphName === "*" || query.graphName === timers[i].GraphName) && (query.subGraphId === "*" || query.subGraphId === timers[i].SubGraphId)) {
+                        if (this.params.query.graphsOnly) {
+                            if (timers[i].SubGraphId && (this.params.query.graphName === "*" || this.params.query.graphName === timers[i].GraphName) && (this.params.query.subGraphId === "*" || this.params.query.subGraphId === timers[i].SubGraphId)) {
                                 timerData.push(lang.mixin({
                                     __hpcc_prefix: timers[i].GraphName
                                 }, timers[i]));

+ 0 - 18
esp/src/eclwatch/UserQueryWidget.js

@@ -394,12 +394,6 @@ define([
                 }
             }, this.id + "GroupsGrid");
             var context = this;
-            on(document, ".WuidClick:click", function (evt) {
-                if (context._onGroupsRowDblClick) {
-                    var item = context.groupsGrid.row(evt).data;
-                    context._onGroupsRowDblClick(item.name);
-                }
-            });
             this.groupsGrid.on(".dgrid-row:dblclick", function (evt) {
                 if (context._onGroupsRowDblClick) {
                     var item = context.groupsGrid.row(evt).data;
@@ -492,12 +486,6 @@ define([
                 }
             }, this.id + "UsersGrid");
             var context = this;
-            on(document, ".WuidClick:click", function (evt) {
-                if (context._onUsersRowDblClick) {
-                    var item = context.usersGrid.row(evt).data;
-                    context._onUsersRowDblClick(item.username,item.fullname,item.passwordexpiration);
-                }
-            });
             this.usersGrid.on(".dgrid-row:dblclick", function (evt) {
                 if (context._onUsersRowDblClick) {
                     var item = context.usersGrid.row(evt).data;
@@ -606,12 +594,6 @@ define([
                 }
             }, this.id + "PermissionsGrid");
             var context = this;
-            on(document, ".WuidClick:click", function (evt) {
-                if (context._onPermissionsRowDblClick) {
-                    var item = context.permissionsGrid.row(evt).data;
-                    context._onPermissionsRowDblClick(item.username, item.fullname, item.passwordexpiration);
-                }
-            });
             this.permissionsGrid.on(".dgrid-row:dblclick", function (evt) {
                 if (context._onPermissionsRowDblClick) {
                     var item = context.permissionsGrid.row(evt).data;

+ 16 - 28
esp/src/eclwatch/WUDetailsWidget.js

@@ -421,6 +421,9 @@ define([
             } else if (name === "resourceURLCount" && newValue) {
                 this.widget._Resources.set("title", this.i18n.Resources + " (" + newValue + ")");
                 this.setDisabled(this.widget._Resources.id, false);
+            } else if (name === "helpersCount" && newValue) {
+                this.logsWidget.set("title", this.i18n.Helpers + " (" + newValue + ")");
+                this.setDisabled(this.logsWidget.id, false);
             } else if (name === "Archived") {
                 this.refreshActionState();
             } else if (name === "StateID") {
@@ -444,19 +447,20 @@ define([
         refreshActionState: function () {
             var isArchived = this.wu.get("Archived");
             this.setDisabled(this.id + "AutoRefresh", isArchived || this.wu.isComplete(), "iconAutoRefresh", "iconAutoRefreshDisabled");
-            registry.byId(this.id + "Save").set("disabled", isArchived || !this.wu.isComplete());
-            registry.byId(this.id + "Delete").set("disabled", isArchived || !this.wu.isComplete());
+            registry.byId(this.id + "Save").set("disabled", isArchived || !this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "Delete").set("disabled", isArchived || !this.wu.isComplete() || this.wu.isDeleted());
             registry.byId(this.id + "Restore").set("disabled", !isArchived);
-            registry.byId(this.id + "SetToFailed").set("disabled", isArchived || this.wu.isComplete());
-            registry.byId(this.id + "Abort").set("disabled", isArchived || this.wu.isComplete());
-            registry.byId(this.id + "Clone").set("disabled", isArchived || !this.wu.isComplete());
-            registry.byId(this.id + "Resubmit").set("disabled", isArchived || !this.wu.isComplete());
-            registry.byId(this.id + "Recover").set("disabled", isArchived || !this.wu.isComplete());
-            registry.byId(this.id + "Publish").set("disabled", isArchived || !this.wu.isComplete());
-
-            registry.byId(this.id + "Jobname").set("readOnly", !this.wu.isComplete());
-            registry.byId(this.id + "Description").set("readOnly", !this.wu.isComplete());
-            registry.byId(this.id + "Protected").set("readOnly", !this.wu.isComplete());
+            registry.byId(this.id + "SetToFailed").set("disabled", isArchived || this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "Abort").set("disabled", isArchived || this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "Clone").set("disabled", isArchived || !this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "Resubmit").set("disabled", isArchived || !this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "Recover").set("disabled", isArchived || !this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "Publish").set("disabled", isArchived || !this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "ZapReport").set("disabled", this.wu.isDeleted());
+
+            registry.byId(this.id + "Jobname").set("readOnly", !this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "Description").set("readOnly", !this.wu.isComplete() || this.wu.isDeleted());
+            registry.byId(this.id + "Protected").set("readOnly", !this.wu.isComplete() || this.wu.isDeleted());
 
             this.summaryWidget.set("iconClass", this.wu.getStateIconClass());
             domClass.remove(this.id + "StateIdImage");
@@ -468,22 +472,6 @@ define([
             if (this.wu.isComplete()) {
                 this.wu.getInfo({
                     onGetVariables: function (response) {
-                    },
-
-                    onAfterSend: function (response) {
-                        var helpersCount = 0;
-                        if (response.Helpers && response.Helpers.ECLHelpFile) {
-                            helpersCount += response.Helpers.ECLHelpFile.length;
-                        }
-                        if (response.ThorLogList && response.ThorLogList.ThorLogInfo) {
-                            helpersCount += response.ThorLogList.ThorLogInfo.length;
-                        }
-                        if (response.HasArchiveQuery) {
-                            helpersCount += 1;
-                        }
-
-                        context.logsWidget.set("title", context.i18n.Helpers + " (" + helpersCount + ")");
-                        context.setDisabled(context.logsWidget.id, false);
                     }
                 });
             }

+ 2 - 2
esp/src/eclwatch/WUQueryWidget.js

@@ -387,7 +387,7 @@ define([
                         label: this.i18n.WUID, width: 180,
                         formatter: function (Wuid, idx) {
                             var wu = ESPWorkunit.Get(Wuid);
-                            return wu.getStateImageHTML() + "&nbsp;<a href='#' rowIndex=" + idx + " class='" + context.id + "WuidClick'>" + Wuid + "</a>";
+                            return wu.getStateImageHTML() + "&nbsp;<a href='#' class='dgrid-row-url'>" + Wuid + "</a>";
                         }
                     },
                     Owner: { label: this.i18n.Owner, width: 90 },
@@ -400,7 +400,7 @@ define([
             }, this.id + "WorkunitsGrid");
 
             var context = this;
-            on(document, "." + context.id + "WuidClick:click", function (evt) {
+            this.workunitsGrid.on(".dgrid-row-url:click", function (evt) {
                 if (context._onRowDblClick) {
                     var item = context.workunitsGrid.row(evt).data;
                     context._onRowDblClick(item.Wuid);

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

@@ -56,6 +56,7 @@ define({root:
     Command: "Command",
     Comment: "Comment",
     Completed: "Completed",
+    ComplexityWarning: "More than {threshold} activities ({activityCount}) - suppress initial display?",
     Component: "Component",
     Compress: "Compress",
     Compressed: "Compressed",
@@ -92,6 +93,7 @@ define({root:
     DenyRead: "<center>Deny<br>Read</center>",
     DenyWrite: "<center>Deny<br>Write</center>",
     Depth: "Depth",
+    DepthTooltip: "Maximum Subgraph Depth",
     Deschedule: "Deschedule",
     DescheduleSelectedWorkunits: "Deschedule Selected Workunits?",
     Description: "Description",
@@ -104,6 +106,7 @@ define({root:
     DisableScopeScanConfirm: "Are you sure you want to disable Scope Scans?  Changes will revert to configuration settings on DALI reboot.",
     DiskUsage: "Disk Usage",
     Distance: "Distance",
+    DistanceTooltip: "Maximum Activity Neighbourhood Distance",
     Dll: "Dll",
     DOT: "DOT",
     DOTAttributes: "DOT Attributes",
@@ -232,6 +235,8 @@ define({root:
     log_analysis_1: "log_analysis_1*",
     Low: "Low",
     ManualCopy: "Press Ctrl+C",
+    ManualOverviewSelection: "(Manual overview selection will be required)",
+    ManualTreeSelection: "(Manual tree selection will be required)",
     Mappings: "Mappings",
     Mask: "Mask",
     Max: "Max",
@@ -370,6 +375,7 @@ define({root:
     RequestSchema: "Request Schema",
     Reschedule: "Reschedule",
     Reset: "Reset",
+    ResetViewToSelection: "Reset View to Selection",
     Resource: "Resource",
     Resources: "Resources",
     ResponseSchema: "Response Schema",

+ 11 - 11
esp/src/eclwatch/templates/DFUQueryWidget.html

@@ -13,7 +13,7 @@
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}ImportForm" style="width: 460px;" onsubmit="return false;" data-dojo-props="region: 'bottom'" data-dojo-type="dijit.form.Form">
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Source</legend>
+                                    <legend>${i18n.Source}</legend>
                                     <div data-dojo-type="hpcc.TableContainer">
                                         <input name="sourceDali" title="${i18n.Dali}:" style="width:100%" required="true" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
                                         <input name="srcusername" title="${i18n.UserID}:" style="width:100%" required="true" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
@@ -22,14 +22,14 @@
                                     </div>
                                 </div>
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Target</legend>
+                                    <legend>${i18n.Target}</legend>
                                     <div data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}ImportTargetSelect" title="${i18n.Group}:" name="destGroup" style="width:100%" data-dojo-type="TargetSelectWidget" />
                                         <input name="destLogicalName" title="${i18n.LogicalName}:" style="width:100%" required="true" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
                                     </div>
                                 </div>
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Options</legend>
+                                    <legend>${i18n.Options}</legend>
                                     <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
                                         <input title="${i18n.Overwrite}:" name="overwrite" data-dojo-type="dijit.form.CheckBox" />
                                         <input title="${i18n.NoSplit}:" name="nosplit" data-dojo-type="dijit.form.CheckBox" />
@@ -51,7 +51,7 @@
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}CopyForm" style="width: 460px;" onsubmit="return false;" data-dojo-props="region: 'bottom'" data-dojo-type="dijit.form.Form">
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Target</legend>
+                                    <legend>${i18n.Target}</legend>
                                     <div data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}CopyTargetSelect" title="${i18n.Group}:" name="destGroup" style="width:100%;" data-dojo-type="TargetSelectWidget" style="display: inline-block; vertical-align: middle" />
                                     </div>
@@ -59,7 +59,7 @@
                                     </div>
                                 </div>
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Options</legend>
+                                    <legend>${i18n.Options}</legend>
                                     <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}CopyTargetOverwrite" title="${i18n.Overwrite}:" name="overwrite" data-dojo-type="dijit.form.CheckBox" />
                                         <input id="${id}CopyTargetNoSplit" title="${i18n.NoSplit}:" name="nosplit" data-dojo-type="dijit.form.CheckBox" />
@@ -79,12 +79,12 @@
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}RenameForm" style="width: 460px;" onsubmit="return false;" data-dojo-type="dijit.form.Form">
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Target</legend>
+                                    <legend>${i18n.Target}</legend>
                                     <div id="${id}RenameGrid" data-dojo-type="SelectionGridWidget">
                                     </div>
                                 </div>
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Options</legend>
+                                    <legend>${i18n.Options}</legend>
                                     <div data-dojo-props="cols:1" data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}RenameTargetOverwrite" title="${i18n.Overwrite}:" name="overwrite" data-dojo-type="dijit.form.CheckBox" />
                                     </div>
@@ -100,7 +100,7 @@
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}AddToSuperfileForm" style="width:480px" onsubmit="return false;" data-dojo-type="dijit.form.Form" >
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Target</legend>
+                                    <legend>${i18n.Target}</legend>
                                     <div class="dijitDialogPaneContentArea" data-dojo-props="cols:1" data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}AddToSuperfileTargetName" name="Superfile" title="${i18n.SuperFile}:" style="width:100%;" required="true" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
                                     </div>
@@ -108,7 +108,7 @@
                                     </div>
                                 </div>
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Options</legend>
+                                    <legend>${i18n.Options}</legend>
                                     <div class="dijitDialogPaneContentArea" data-dojo-props="cols:1" data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}AddToSuperfileTargetAppend" name="ExistingFile" title="${i18n.Append}:" data-dojo-type="dijit.form.CheckBox" />
                                     </div>
@@ -124,7 +124,7 @@
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}DesprayForm" style="width: 460px;" onsubmit="return false;" data-dojo-type="dijit.form.Form">
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Target</legend>
+                                    <legend>${i18n.Target}</legend>
                                     <div data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}DesprayTargetSelect" title="${i18n.DropZone}:" name="destGroup" style="width: 100%;" data-dojo-type="TargetSelectWidget" />
                                         <input id="${id}DesprayTargetIPAddress" title="${i18n.IPAddress}:" name="destIP" style="width: 100%;" required="true" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
@@ -135,7 +135,7 @@
                                     </div>
                                 </div>
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Options</legend>
+                                    <legend>${i18n.Options}</legend>
                                     <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}DesprayTargetOverwrite" title="${i18n.Overwrite}:" name="overwrite" data-dojo-type="dijit.form.CheckBox" />
                                         <input id="${id}DesprayTargetUseSingleConnection" title="${i18n.UseSingleConnection}:" name="SingleConnection" data-dojo-type="dijit.form.CheckBox" />

+ 5 - 5
esp/src/eclwatch/templates/GraphWidget.html

@@ -11,12 +11,12 @@
                 <div data-dojo-attach-event="onClick:_onClickZoomWidth" data-dojo-props="iconClass:'iconZoomWidth', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.ZoomWidth}</div>
                 <span data-dojo-type="dijit.ToolbarSeparator"></span>
                 <span data-dojo-attach-point="containerNode"></span>
-                <div title="${i18n.Depth}" data-dojo-props="iconClass:'iconDepth', showLabel:false, disabled: true" data-dojo-type="dijit.form.Button"></div>
-                <input id="${id}Depth" style="width: 60px" value="2" title="${i18n.Depth}" data-dojo-attach-event="onChange:_onDepthChange" data-dojo-props="placeHolder:'${i18n.Depth}', intermediateChanges:true, constraints:{min:0,max:1000}" data-dojo-type="dijit.form.NumberSpinner" />
-                <div id="${id}DistanceLabel" title="${i18n.Distance}" data-dojo-props="iconClass:'iconDistance', showLabel:false, disabled: true" data-dojo-type="dijit.form.Button"></div>
-                <input id="${id}Distance" style="width: 60px" value="2" title="${i18n.Distance}" data-dojo-attach-event="onChange:_onDistanceChange" data-dojo-props="placeHolder:'${i18n.Depth}', intermediateChanges:true, constraints:{min:0,max:1000}" data-dojo-type="dijit.form.NumberSpinner" />
+                <div title="${i18n.DepthTooltip}" data-dojo-props="iconClass:'iconDepth', showLabel:false, disabled: true" data-dojo-type="dijit.form.Button"></div>
+                <input id="${id}Depth" style="width: 60px" value="2" title="${i18n.DepthTooltip}" data-dojo-attach-event="onChange:_onDepthChange" data-dojo-props="intermediateChanges:true, constraints:{min:0,max:1000}" data-dojo-type="dijit.form.NumberSpinner" />
+                <div id="${id}DistanceLabel" title="${i18n.DistanceTooltip}" data-dojo-props="iconClass:'iconDistance', showLabel:false, disabled: true" data-dojo-type="dijit.form.Button"></div>
+                <input id="${id}Distance" style="width: 60px" value="2" title="${i18n.DistanceTooltip}" data-dojo-attach-event="onChange:_onDistanceChange" data-dojo-props="intermediateChanges:true, constraints:{min:0,max:1000}" data-dojo-type="dijit.form.NumberSpinner" />
                 <span id="${id}SyncSelectionSplitter" data-dojo-type="dijit.ToolbarSeparator"></span>
-                <div id="${id}SyncSelection" data-dojo-attach-event="onClick:_onSyncSelection" data-dojo-props="iconClass:'iconSync', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.SyncSelection}</div>
+                <div id="${id}SyncSelection" data-dojo-attach-event="onClick:_onSyncSelection" data-dojo-props="iconClass:'iconSync', showLabel:false" data-dojo-type="dijit.form.Button">${i18n.ResetViewToSelection}</div>
             </div>
         </div>
         <div id="${id}GraphContentPane" class="${baseClass}GraphContentPane" style="padding: 0px; overflow: hidden; background-color: #707070" data-dojo-props="region: 'center'" data-dojo-type="dijit.layout.ContentPane">

+ 44 - 19
esp/src/eclwatch/templates/LFDetailsWidget.html

@@ -12,15 +12,23 @@
                         <span>${i18n.Copy}</span>
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}CopyForm" style="width: 460px;" onsubmit="return false;" data-dojo-type="dijit.form.Form">
-                                <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
-                                    <input id="${id}CopyTargetSelect" title="${i18n.Group}:" name="destGroup" colspan="2" style="width:100%;" data-dojo-type="TargetSelectWidget" style="display: inline-block; vertical-align: middle" />
-                                    <input id="${id}CopyTargetName"  title="${i18n.TargetName}:" name="destLogicalName" colspan="2" style="width:100%;" required="true" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
-                                    <input id="${id}CopyTargetOverwrite" title="${i18n.Overwrite}:" name="overwrite" data-dojo-type="dijit.form.CheckBox" />
-                                    <input id="${id}CopyTargetReplicate" title="${i18n.Replicate}:" name="replicate" data-dojo-type="dijit.form.CheckBox" />
-                                    <input id="${id}CopyTargetNoSplit" title="${i18n.NoSplit}:" name="nosplit" data-dojo-type="dijit.form.CheckBox" />
-                                    <input id="${id}CopyTargetCompress" title="${i18n.Compress}:" name="compress" data-dojo-type="dijit.form.CheckBox" />
-                                    <input id="${id}CopyTargetWrap" title="${i18n.Wrap}:" name="Wrap" data-dojo-type="dijit.form.CheckBox" />
-                                    <input id="${id}CopyTargetRetainSuperfileStructure" title="${i18n.RetainSuperfileStructure}:" name="superCopy" data-dojo-type="dijit.form.CheckBox" />
+                                <div data-dojo-type="dijit.Fieldset">
+                                    <legend>${i18n.Target}</legend>
+                                    <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
+                                        <input id="${id}CopyTargetSelect" title="${i18n.Group}:" name="destGroup" colspan="2" style="width:100%;" data-dojo-type="TargetSelectWidget" style="display: inline-block; vertical-align: middle" />
+                                        <input id="${id}CopyTargetName" title="${i18n.TargetName}:" name="destLogicalName" colspan="2" style="width:100%;" required="true" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
+                                    </div>
+                                </div>
+                                <div data-dojo-type="dijit.Fieldset">
+                                    <legend>${i18n.Options}</legend>
+                                    <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
+                                        <input id="${id}CopyTargetOverwrite" title="${i18n.Overwrite}:" name="overwrite" data-dojo-type="dijit.form.CheckBox" />
+                                        <input id="${id}CopyTargetReplicate" title="${i18n.Replicate}:" name="replicate" data-dojo-type="dijit.form.CheckBox" />
+                                        <input id="${id}CopyTargetNoSplit" title="${i18n.NoSplit}:" name="nosplit" data-dojo-type="dijit.form.CheckBox" />
+                                        <input id="${id}CopyTargetCompress" title="${i18n.Compress}:" name="compress" data-dojo-type="dijit.form.CheckBox" />
+                                        <input id="${id}CopyTargetWrap" title="${i18n.Wrap}:" name="Wrap" data-dojo-type="dijit.form.CheckBox" />
+                                        <input id="${id}CopyTargetRetainSuperfileStructure" title="${i18n.RetainSuperfileStructure}:" name="superCopy" data-dojo-type="dijit.form.CheckBox" />
+                                    </div>
                                 </div>
                                 <div class="dijitDialogPaneActionBar">
                                     <button type="submit" data-dojo-attach-event="onClick:_onCopyOk" data-dojo-type="dijit.form.Button">${i18n.Copy}</button>
@@ -32,9 +40,17 @@
                         <span>${i18n.Rename}</span>
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}RenameForm" style="width: 460px;" onsubmit="return false;" data-dojo-type="dijit.form.Form">
-                                <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
-                                    <input id="${id}RenameTargetName" title="${i18n.TargetName}:" style="width: 100%;" name="dstname" colspan="2" required="true" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
-                                    <input id="${id}RenameTargetOverwrite" title="${i18n.Overwrite}:" name="overwrite" data-dojo-type="dijit.form.CheckBox" />
+                                <div data-dojo-type="dijit.Fieldset">
+                                    <legend>${i18n.Target}</legend>
+                                    <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
+                                        <input id="${id}RenameTargetName" title="${i18n.TargetName}:" style="width: 100%;" name="dstname" colspan="2" required="true" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
+                                    </div>
+                                </div>
+                                <div data-dojo-type="dijit.Fieldset">
+                                    <legend>${i18n.Options}</legend>
+                                    <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
+                                        <input id="${id}RenameTargetOverwrite" title="${i18n.Overwrite}:" name="overwrite" data-dojo-type="dijit.form.CheckBox" />
+                                    </div>
                                 </div>
                                 <div class="dijitDialogPaneActionBar">
                                     <button type="submit" data-dojo-attach-event="onClick:_onRenameOk" data-dojo-type="dijit.form.Button">${i18n.Rename}</button>
@@ -46,13 +62,22 @@
                         <span>${i18n.Despray}</span>
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}DesprayForm" style="width: 460px;" onsubmit="return false;" data-dojo-type="dijit.form.Form">
-                                <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
-                                    <input id="${id}DesprayTargetSelect" title="${i18n.DropZone}:" name="destGroup" colspan="2" style="width: 100%;" data-dojo-type="TargetSelectWidget" />
-                                    <input id="${id}DesprayTargetIPAddress" title="${i18n.IPAddress}:" name="destIP" colspan="2" required="true" style="width: 100%;" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
-                                    <input id="${id}DesprayTargetPath" title="${i18n.Path}:" name="destPath" colspan="2" required="true" style="width: 100%;" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
-                                    <input id="${id}DesprayTargetSplitPrefix" title="${i18n.SplitPrefix}:" name="splitprefix" colspan="2" style="width: 100%;" data-dojo-props="trim: true" data-dojo-type="dijit.form.TextBox" />
-                                    <input id="${id}DesprayTargetOverwrite" title="${i18n.Overwrite}:" name="overwrite" data-dojo-type="dijit.form.CheckBox" />
-                                    <input id="${id}DesprayTargetUseSingleConnection" title="${i18n.UseSingleConnection}:" name="SingleConnection" data-dojo-type="dijit.form.CheckBox" />
+                                <div data-dojo-type="dijit.Fieldset">
+                                    <legend>${i18n.Target}</legend>
+                                    <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
+                                        <input id="${id}DesprayTargetSelect" title="${i18n.DropZone}:" name="destGroup" colspan="2" style="width: 100%;" data-dojo-type="TargetSelectWidget" />
+                                        <input id="${id}DesprayTargetIPAddress" title="${i18n.IPAddress}:" name="destIP" colspan="2" required="true" style="width: 100%;" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
+                                        <input id="${id}DesprayTargetPath" title="${i18n.Path}:" name="destPath" colspan="2" required="true" style="width: 100%;" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
+                                        <input id="${id}DesprayTargetName" title="${i18n.TargetName}:" colspan="2" required="true" style="width: 100%;" data-dojo-props="trim: true" data-dojo-type="dijit.form.ValidationTextBox" />
+                                        <input id="${id}DesprayTargetSplitPrefix" title="${i18n.SplitPrefix}:" name="splitprefix" colspan="2" style="width: 100%;" data-dojo-props="trim: true" data-dojo-type="dijit.form.TextBox" />
+                                    </div>
+                                </div>
+                                <div data-dojo-type="dijit.Fieldset">
+                                    <legend>${i18n.Options}</legend>
+                                    <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
+                                        <input id="${id}DesprayTargetOverwrite" title="${i18n.Overwrite}:" name="overwrite" data-dojo-type="dijit.form.CheckBox" />
+                                        <input id="${id}DesprayTargetUseSingleConnection" title="${i18n.UseSingleConnection}:" name="SingleConnection" data-dojo-type="dijit.form.CheckBox" />
+                                    </div>
                                 </div>
                                 <div class="dijitDialogPaneActionBar">
                                     <button type="submit" data-dojo-attach-event="onClick:_onDesprayOk" data-dojo-type="dijit.form.Button">${i18n.Despray}</button>

+ 10 - 10
esp/src/eclwatch/templates/LZBrowseWidget.html

@@ -33,7 +33,7 @@
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}SprayFixedForm" style="width: 530px;" onsubmit="return false;" data-dojo-type="dijit.form.Form">
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Target</legend>
+                                    <legend>${i18n.Target}</legend>
                                     <div data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}SprayFixedDestination" title="${i18n.Group}:" style="width: 95%;" name="destGroup" data-dojo-type="TargetSelectWidget" />
                                         <input id="${id}SprayFixedDestinationNamePrefix" title="${i18n.NamePrefix}:" style="width: 95%;" name="namePrefix" data-dojo-props="trim: true, placeHolder:'${i18n.NamePrefixPlaceholder}'" data-dojo-type="dijit.form.TextBox" />
@@ -42,7 +42,7 @@
                                     </div>
                                 </div>
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Options</legend>
+                                    <legend>${i18n.Options}</legend>
                                     <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
                                         <input title="${i18n.Overwrite}:" name="overwrite" data-dojo-type="dijit.form.CheckBox" />
                                         <input title="${i18n.Replicate}:" name="replicate" data-dojo-type="dijit.form.CheckBox" />
@@ -62,7 +62,7 @@
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}SprayDelimitedForm" style="width: 530px;" onsubmit="return false;" data-dojo-type="dijit.form.Form">
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Target</legend>
+                                    <legend>${i18n.Target}</legend>
                                     <div data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}SprayDelimitedDestination" title="${i18n.Group}:" style="width: 95%;" name="destGroup" data-dojo-type="TargetSelectWidget" />
                                         <input title="${i18n.NamePrefix}:" style="width: 95%;" name="namePrefix" data-dojo-props="trim: true, placeHolder:'${i18n.NamePrefixPlaceholder}'" data-dojo-type="dijit.form.TextBox" />
@@ -71,7 +71,7 @@
                                     </div>
                                 </div>
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Options</legend>
+                                    <legend>${i18n.Options}</legend>
                                     <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
                                         <select id="${id}sourceFormat" title="${i18n.Format}:" name="sourceFormat" colspan="2" data-dojo-type="dijit.form.Select">
                                             <option value="1">ASCII</option>
@@ -110,7 +110,7 @@
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}SprayXmlForm" style="width: 530px;" data-dojo-type="dijit.form.Form">
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Target</legend>
+                                    <legend>${i18n.Target}</legend>
                                     <div data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}SprayXmlDestinationSelect" title="${i18n.Group}:" style="width: 95%;" name="destGroup" data-dojo-type="TargetSelectWidget" />
                                         <input title="${i18n.NamePrefix}:" style="width: 95%;" name="namePrefix" data-dojo-props="trim: true, placeHolder:'${i18n.NamePrefixPlaceholder}'" data-dojo-type="dijit.form.TextBox" />
@@ -119,7 +119,7 @@
                                     </div>
                                 </div>
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Options</legend>
+                                    <legend>${i18n.Options}</legend>
                                     <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
                                         <select id="${id}xmlsourceFormat" title="${i18n.Format}:" name="sourceFormat" colspan="2" data-dojo-type="dijit.form.Select">
                                             <option value="1">ASCII</option>
@@ -151,7 +151,7 @@
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}SprayVariableForm" style="width: 530px;" onsubmit="return false;" data-dojo-type="dijit.form.Form">
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Target</legend>
+                                    <legend>${i18n.Target}</legend>
                                     <div data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}SprayVariableDestination" title="${i18n.Group}:" style="width: 95%;" name="destGroup" data-dojo-type="TargetSelectWidget" />
                                         <input title="${i18n.NamePrefix}:" style="width: 95%;" name="namePrefix" data-dojo-props="trim: true, placeHolder:'${i18n.NamePrefixPlaceholder}'" data-dojo-type="dijit.form.TextBox" />
@@ -160,7 +160,7 @@
                                     </div>
                                 </div>
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Options</legend>
+                                    <legend>${i18n.Options}</legend>
                                     <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
                                         <select title="${i18n.VariableSourceType}:" name="sourceFormat" colspan="2" data-dojo-type="dijit.form.Select">
                                             <option value="recfmv" selected="true">recfmv</option>
@@ -186,7 +186,7 @@
                         <div data-dojo-type="dijit.TooltipDialog">
                             <div id="${id}SprayBlobForm" style="width: 530px;" onsubmit="return false;" data-dojo-type="dijit.form.Form">
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Target</legend>
+                                    <legend>${i18n.Target}</legend>
                                     <div data-dojo-type="hpcc.TableContainer">
                                         <input id="${id}SprayBlobDestination" title="${i18n.Group}:" style="width: 95%;" name="destGroup" data-dojo-type="TargetSelectWidget" />
                                         <input title="${i18n.TargetName}:" style="width: 95%;" name="destLogicalName" data-dojo-props="trim: true, placeHolder:'${i18n.TargetNamePlaceholder}'" required="true" data-dojo-type="dijit.form.ValidationTextBox" />
@@ -195,7 +195,7 @@
                                     </div>
                                 </div>
                                 <div data-dojo-type="dijit.Fieldset">
-                                    <legend>Options</legend>
+                                    <legend>${i18n.Options}</legend>
                                     <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
                                         <input title="${i18n.BlobPrefix}:" style="width: 95%;" name="prefix" data-dojo-props="trim: true, placeHolder:'${i18n.PrefixPlaceholder}'" colspan="2" data-dojo-type="dijit.form.TextBox" />
                                         <input title="${i18n.Overwrite}:" name="overwrite" data-dojo-type="dijit.form.CheckBox" />

+ 18 - 17
initfiles/examples/embed/mysql-simple.ecl

@@ -26,24 +26,25 @@ init := DATASET([{'name1', 1, true, 1.2, 3.4, D'aa55aa55', 1234567.89, U'Straße
                  {'name2', 2, false, 5.6, 7.8, D'00', -1234567.89, U'là', U'là'}], childrec);
 
 // Set up the MySQL database
+// Enable remote access to your MySQL DB Server to use its DNS name or IP, else use localhost or 127.0.0.1
 
-drop() := EMBED(mysql : user('rchapman'),database('test'))
+drop() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   DROP TABLE IF EXISTS tbl1;
 ENDEMBED;
 
-create() := EMBED(mysql : user('rchapman'),database('test'))
+create() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   CREATE TABLE tbl1 ( name VARCHAR(20), value INT, boolval TINYINT, r8 DOUBLE, r4 FLOAT, d BLOB, ddd DECIMAL(10,2), u1 VARCHAR(10), u2 VARCHAR(10) );
 ENDEMBED;
 
 // Initialize the MySQL table, passing in the ECL dataset to provide the rows
 
-initialize(dataset(childrec) values) := EMBED(mysql : user('rchapman'),database('test'))
+initialize(dataset(childrec) values) := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   INSERT INTO tbl1 values (?, ?, ?, ?, ?, ?, ?, ?, ?);
 ENDEMBED;
 
 // Add an additional row containing NULL values, to test that they are handled properly when read in ECL
 
-initializeNulls() := EMBED(mysql : user('rchapman'),database('test'))
+initializeNulls() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   INSERT INTO tbl1 (name) values ('nulls');
 ENDEMBED;
 
@@ -55,13 +56,13 @@ ENDEMBED;
 
 // Returning a dataset
 
-dataset(childrec) testMySQLDS() := EMBED(mysql : user('rchapman'),database('test'))
+dataset(childrec) testMySQLDS() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT * from tbl1;
 ENDEMBED;
 
 // Returning a single row
 
-childrec testMySQLRow() := EMBED(mysql : user('rchapman'),database('test'))
+childrec testMySQLRow() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT * from tbl1 LIMIT 1;
 ENDEMBED;
 
@@ -76,45 +77,45 @@ childrec testMySQLParms(
    real4 r4,
    DATA d,
    UTF8 u1,
-   UNICODE8 u2) := EMBED(mysql : user('rchapman'),database('test'))
+   UNICODE8 u2) := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT * from tbl1 WHERE name=? AND value=? AND boolval=? AND r8=? AND r4=? AND d=? AND u1=? AND u2=?;
 ENDEMBED;
 
 // Returning scalars
 
-string testMySQLString() := EMBED(mysql : user('rchapman'),database('test'))
+string testMySQLString() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT max(name) from tbl1;
 ENDEMBED;
 
-dataset(childrec) testMySQLStringParam(string filter) := EMBED(mysql : user('rchapman'),database('test'))
+dataset(childrec) testMySQLStringParam(string filter) := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT * from tbl1 where name = ?;
 ENDEMBED;
 
-integer testMySQLInt() := EMBED(mysql : user('rchapman'),database('test'))
+integer testMySQLInt() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT max(value) from tbl1;
 ENDEMBED;
 
-boolean testMySQLBool() := EMBED(mysql : user('rchapman'),database('test'))
+boolean testMySQLBool() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT max(boolval) from tbl1;
 ENDEMBED;
 
-real8 testMySQLReal8() := EMBED(mysql : user('rchapman'),database('test'))
+real8 testMySQLReal8() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT max(r8) from tbl1;
 ENDEMBED;
 
-real4 testMySQLReal4() := EMBED(mysql : user('rchapman'),database('test'))
+real4 testMySQLReal4() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT max(r4) from tbl1;
 ENDEMBED;
 
-data testMySQLData() := EMBED(mysql : user('rchapman'),database('test'))
+data testMySQLData() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT max(d) from tbl1;
 ENDEMBED;
 
-UTF8 testMySQLUtf8() := EMBED(mysql : user('rchapman'),database('test'))
+UTF8 testMySQLUtf8() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT max(u1) from tbl1;
 ENDEMBED;
 
-UNICODE testMySQLUnicode() := EMBED(mysql : user('rchapman'),database('test'))
+UNICODE testMySQLUnicode() := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT max(u2) from tbl1;
 ENDEMBED;
 
@@ -128,7 +129,7 @@ stringrec extractName(childrec l) := TRANSFORM
   SELF := l;
 END;
 
-dataset(childrec) testMySQLDSParam(dataset(stringrec) inrecs) := EMBED(mysql : user('rchapman'),database('test'))
+dataset(childrec) testMySQLDSParam(dataset(stringrec) inrecs) := EMBED(mysql : user('rchapman'),database('test'),server('127.0.0.1'))
   SELECT * from tbl1 where name = ?;
 ENDEMBED;
 

+ 1 - 1
roxie/ccd/ccdfile.cpp

@@ -691,7 +691,7 @@ class CRoxieFileCache : public CInterface, implements ICopyFileProgress, impleme
                     throw MakeStringException(ROXIE_FILE_ERROR, "Local file %s does not match DFS information", localLocation);
                 else
                 {
-                    if (traceLevel > 2)
+                    if (traceLevel >= 2)
                     {
                         DBGLOG("Failed to open file at any of the following %d local locations:", localLocations.length());
                         ForEachItemIn(local_idx, localLocations)

+ 17 - 5
system/jlib/jmutex.cpp

@@ -20,6 +20,7 @@
 #include "jmutex.hpp"
 #include "jsuperhash.hpp"
 #include "jmisc.hpp"
+#include "jfile.hpp"
 
 #include <stdio.h>
 #include <assert.h>
@@ -210,14 +211,25 @@ static void unlock_file(const char *lfpath)
     ERRLOG("NamedMutex cannot unlock file (%d)",errno);
 }
 
+static CriticalSection lockPrefixCS;
+static StringBuffer lockPrefix;
 NamedMutex::NamedMutex(const char *name)
 {
-    const char * pfx = "/var/lock/JLIBMUTEX_";
-    mutexfname = (char *)malloc(strlen(name)+strlen(pfx)+1);
-    strcpy(mutexfname,pfx);
-    strcat(mutexfname,name);
+    {
+        CriticalBlock b(lockPrefixCS);
+        if (0 == lockPrefix.length())
+        {
+            if (!getConfigurationDirectory(NULL, "lock", NULL, NULL, lockPrefix))
+                throw MakeStringException(0, "Failed to get lock directory from environment");
+        }
+        addPathSepChar(lockPrefix);
+        lockPrefix.append("JLIBMUTEX_");
+    }
+    StringBuffer tmp(lockPrefix);
+    tmp.append("JLIBMUTEX_").append(name);
+    mutexfname = tmp.detach();
 }
-    
+
 NamedMutex::~NamedMutex()
 {
     free(mutexfname);

+ 1 - 1
system/jlib/jsocket.cpp

@@ -2664,7 +2664,7 @@ IpAddress & queryHostIP()
     if (cachehostip.isNull()) {
         if (!cachehostip.ipset(GetCachedHostName())) {
             cachehostip.ipset(queryLocalIP());          
-            printf("hostname %s not resolved, using localhost\n",GetCachedHostName()); // don't use jlog in case recursive
+            // printf("hostname %s not resolved, using localhost\n",GetCachedHostName()); // don't use jlog in case recursive
         }
     }
     return cachehostip;

+ 24 - 10
testing/regress/README.rst

@@ -92,13 +92,13 @@ Command:
 Result:
 
 |
-|       usage: ecl-test setup [-h] [--target [target_cluster | all]]
+|       usage: ecl-test setup [-h] [--target [target_cluster_list | all]]
 |                             [--pq threadNumber]
 |
 |       optional arguments:
 |        -h, --help               show this help message and exit
-|        --target [target_cluster | all], -t [target_cluster | all]
-|                                 target cluster for single query run. If target = 'all' then run query on all clusters. Default value is thor.
+|        --target [target_cluster_list | all], -t [target_cluster_list | all]
+|                                 run the setup on target cluster(s). If target = 'all' then run setup on all clusters. If undefined the config 'defaultSetupClusters' value will be used.
 |        --pq threadNumber        parallel query execution with threadNumber threads. (If threadNumber is '-1' on a single node system then threadNumber = numberOfLocalCore * 2)
 |
 
@@ -112,14 +112,14 @@ Command:
 Result:
 
 |
-|       usage: ecl-test run [-h] [--target [target_cluster | all]] [--publish] 
+|       usage: ecl-test run [-h] [--target [target_cluster_list | all]] [--publish]
 |                           [--pq threadNumber]
 |                           [--runclass [class]] [--excludeclass [class]]
 |
 |       optional arguments:
 |        -h, --help               show this help message and exit
-|        --target [target_cluster | all], -t [target_cluster | all]
-|                                 target cluster for single query run. If target = 'all' then run query on all clusters. Default value is thor.
+|        --target [target_cluster_list | all], -t [target_cluster_list | all]
+|                                 run the suite on target cluster(s). If target = 'all' then run suite on all clusters. If undefined the config 'defaultTargetClusters' value will be used.
 |        --publish, -p            publish compiled query instead of run.
 |        --pq threadNumber        parallel query execution with threadNumber threads. (If threadNumber is '-1' on a single node system then threadNumber = numberOfLocalCore * 2)
 |        --runclass class[,class,...], -r class[,class,...]
@@ -138,7 +138,7 @@ Command:
 Result:
 
 |
-|       usage: ecl-test query [-h] [--target [target_cluster | all]] [--publish]
+|       usage: ecl-test query [-h] [--target [target_cluster_list | all]] [--publish]
 |                             [--pq threadNumber]
 |                             ECL_query [ECL_query ...]
 |
@@ -147,8 +147,8 @@ Result:
 |
 |       optional arguments:
 |        -h, --help               show this help message and exit
-|        --target [target_cluster | all], -t [target_cluster | all]
-|                                 target cluster for single query run. If target = 'all' then run query on all clusters. Default value is thor.
+|        --target [target_cluster_list | all], -t [target_cluster_list | all]
+|                                 run the query on target cluster(s). If target = 'all' then run query on all clusters. If undefined the config 'defaultTargetClusters' value will be used.
 |        --publish, -p            publish compiled query instead of run.
 |        --pq threadNumber        parallel query execution for multiple test cases specified in CLI with threadNumber threads. (If threadNumber is '-1' on a single node system then threadNumber = numberOfLocalCore * 2 )
 |
@@ -596,7 +596,21 @@ So if you have a new test case and it works well on all clusters (or some of the
         "timeout":"720",                                - Default test case timeout in sec. Can be override by command line parameter or //timeout tag in ECL file
         "maxAttemptCount":"3"                           - Max retry count to reset timeout if a testcase in any early stage (compiled, blocked) of execution pipeline.
 
-Optionally the config file can contain a section of default values for stored parameters like this:
+Optionally the config file can contain some sections of default values:
+
+If the -t | --target command line parameter is omitted then the regression test engine uses the default target(s) from one of these default definitions. If undefined, then the engine uses the first cluster from the Cluster array.
+
+        "defaultSetupClusters": [
+            "hthor",
+            "thor3"
+        ]
+
+        "defaultTargetClusters": [
+            "thor",
+            "thor3"
+        ]
+
+For stored parameters:
 
     "Params":[
                 "querya.ecl:param1=value1,param2=value2",

+ 29 - 9
testing/regress/ecl-test

@@ -28,7 +28,7 @@ import glob
 from hpcc.util import argparse
 from hpcc.regression.regress import Regression
 from hpcc.util.ecl.file import ECLFile
-from hpcc.util.util import checkPqParam, getVersionNumbers, checkXParam, convertPath, getRealIPAddress, parentPath
+from hpcc.util.util import checkPqParam, getVersionNumbers, checkXParam, convertPath, getRealIPAddress, parentPath, checkClusters
 from hpcc.common.error import Error
 
 # For coverage
@@ -170,15 +170,15 @@ class RegressMain:
 
         parser_setup = subparsers.add_parser('setup', help='setup help')
         parser_setup.set_defaults(func='setup')
-        parser_setup.add_argument('--target', '-t', help="Run the setup on target cluster. If target = 'all' then run setup on all clusters. Default value is thor.",
-                                nargs='?', type=str,  default='thor', metavar="target_cluster | all")
+        parser_setup.add_argument('--target', '-t', help="Run the setup on target cluster(s). If target = 'all' then run setup on all clusters. If not defined then default value(s) come from config (ecl-test.json by default).",
+                                nargs='?', type=str,  default='', metavar="target_cluster_list | all")
         parser_setup.add_argument('--pq', help="Parallel query execution with threadNumber threads. (If threadNumber is '-1' on a single node system then threadNumber = numberOfLocalCore * 2 )",
                                 type=checkPqParam,  default = 0,   metavar="threadNumber")
 
         parser_run = subparsers.add_parser('run', help='run help')
         parser_run.set_defaults(func='run')
-        parser_run.add_argument('--target', '-t', help="Run the cluster suite. If target = 'all' then run suite on all clusters. Default value is thor.",
-                                nargs='?', type=str,  default='thor', metavar="target_cluster | all")
+        parser_run.add_argument('--target', '-t', help="Run the cluster(s) suite. If target = 'all' then run suite on all clusters. If not defined then default value(s) come from config (ecl-test.json by default).",
+                                nargs='?', type=str,  default='', metavar="target_cluster_list | all")
         parser_run.add_argument('--publish', '-p', help="Publish compiled query instead of run.",
                                 action='store_true')
         parser_run.add_argument('--pq', help="Parallel query execution with threadNumber threads. (If threadNumber is '-1' on a single node system then threadNumber = numberOfLocalCore * 2 )",
@@ -192,8 +192,8 @@ class RegressMain:
         parser_query.set_defaults(func='query')
         parser_query.add_argument('query', help="One or more ECL file(s). It can contain wildcards. (mandatory).",
                                   nargs='+', metavar="ECL_query")
-        parser_query.add_argument('--target', '-t', help="Target cluster for query to run. If target = 'all' then run query on all clusters. Default value is thor.",
-                                nargs='?', default='thor', metavar="target_cluster | all")
+        parser_query.add_argument('--target', '-t', help="Target cluster(s) for query to run. If target = 'all' then run query on all clusters. If not defined then default value(s) come from config (ecl-test.json by default).",
+                                nargs='?', default='', metavar="target_cluster_list | all")
         parser_query.add_argument('--publish', '-p', help="Publish compiled query instead of run.",
                                 action='store_true')
         parser_query.add_argument('--pq', help="Parallel query execution with threadNumber threads. (If threadNumber is '-1' on a single node system then threadNumber = numberOfLocalCore * 2 )",
@@ -224,11 +224,31 @@ class RegressMain:
 
             self.targetClusters = []
             if 'target' in self.args:
-                if 'all' == self.args.target:
+                if '' == self.args.target:
+                    # Target not specified, use default from config
+                    try:
+                        if self.args.func == 'setup':
+                            defaultTargets = self.regress.config.defaultSetupClusters
+                            targetSet='defaultSetupClusters'
+                        else:
+                            defaultTargets = self.regress.config.defaultTargetClusters
+                            targetSet='defaultTargetClusters'
+
+                        self.targetClusters = checkClusters(defaultTargets, targetSet)
+
+                    except AttributeError:
+                        # It seems there is no defaultSetupClusters|defaultTargetClusters array in the config file
+                        # use the first one of cluster list in config file
+                        self.targetClusters.append(self.regress.config.Clusters[0])
+                elif 'all' == self.args.target:
                     for cluster in self.regress.config.Clusters:
                         self.targetClusters.append(str(cluster))
                 else:
-                    if self.args.target in self.regress.config.Clusters:
+                    if ',' in self.args.target:
+                        # target is a list, process it
+                        targets = self.args.target.split(',')
+                        self.targetClusters = checkClusters(targets, 'target')
+                    elif self.args.target in self.regress.config.Clusters:
                         self.targetClusters.append(self.args.target)
                     else:
                         logging.error("%s. Unknown target cluster:'%s'!" % (1,  self.args.target))

+ 6 - 0
testing/regress/ecl-test.json

@@ -24,6 +24,12 @@
         ],
         "timeout":"720",
         "maxAttemptCount":"3",
+        "defaultSetupClusters": [
+            "all"
+        ],
+        "defaultTargetClusters": [
+            "all"
+        ],
         "Params":[
             "PassTest.ecl:bla='A value'"
         ]

+ 16 - 0
testing/regress/hpcc/util/util.py

@@ -158,3 +158,19 @@ def getRealIPAddress():
         pass
 
     return ipAddress
+
+def checkClusters(clusters,  targetSet):
+    targetClusters =[]
+    if 'all' in clusters:
+        for cluster in gConfig.Clusters:
+            targetClusters.append(str(cluster))
+    else:
+        for cluster in clusters:
+            cluster = cluster.strip()
+            if cluster in gConfig.Clusters:
+                targetClusters.append(cluster)
+            else:
+                logging.error("%s. Unknown cluster:'%s' in %s:'%s'!" % (1,  cluster,  targetSet,  clusters))
+                raise Error("4000")
+
+    return  targetClusters