diff --git a/nifi-api/src/main/java/org/apache/nifi/controller/status/ProcessorStatus.java b/nifi-api/src/main/java/org/apache/nifi/controller/status/ProcessorStatus.java index 5a4f64a908..808f2f6f3c 100644 --- a/nifi-api/src/main/java/org/apache/nifi/controller/status/ProcessorStatus.java +++ b/nifi-api/src/main/java/org/apache/nifi/controller/status/ProcessorStatus.java @@ -16,6 +16,8 @@ */ package org.apache.nifi.controller.status; +import org.apache.nifi.scheduling.ExecutionNode; + import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -29,6 +31,7 @@ public class ProcessorStatus implements Cloneable { private String name; private String type; private RunStatus runStatus; + private ExecutionNode executionNode; private int inputCount; private long inputBytes; private int outputCount; @@ -90,6 +93,14 @@ public class ProcessorStatus implements Cloneable { this.runStatus = runStatus; } + public ExecutionNode getExecutionNode() { + return executionNode; + } + + public void setExecutionNode(ExecutionNode executionNode) { + this.executionNode = executionNode; + } + public void setInputCount(final int inputCount) { this.inputCount = inputCount; } @@ -244,6 +255,7 @@ public class ProcessorStatus implements Cloneable { clonedObj.averageLineageDuration = averageLineageDuration; clonedObj.flowFilesRemoved = flowFilesRemoved; clonedObj.runStatus = runStatus; + clonedObj.executionNode = executionNode; clonedObj.type = type; clonedObj.counters = counters == null ? null : new HashMap<>(counters); return clonedObj; @@ -262,6 +274,8 @@ public class ProcessorStatus implements Cloneable { builder.append(type); builder.append(", runStatus="); builder.append(runStatus); + builder.append(", executionNode="); + builder.append(executionNode); builder.append(", inputCount="); builder.append(inputCount); builder.append(", inputBytes="); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/status/ProcessorStatusSnapshotDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/status/ProcessorStatusSnapshotDTO.java index df999b1895..44a9d203d4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/status/ProcessorStatusSnapshotDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/status/ProcessorStatusSnapshotDTO.java @@ -31,6 +31,7 @@ public class ProcessorStatusSnapshotDTO implements Cloneable { private String name; private String type; private String runStatus; + private String executionNode; private Long bytesRead = 0L; private Long bytesWritten = 0L; @@ -103,6 +104,18 @@ public class ProcessorStatusSnapshotDTO implements Cloneable { this.runStatus = runStatus; } + @ApiModelProperty( + value = "Indicates the node where the process will execute.", + allowableValues = "ALL, PRIMARY" + ) + public String getExecutionNode() { + return executionNode; + } + + public void setExecutionNode(String executionNode) { + this.executionNode = executionNode; + } + /** * @return The total count and size of flow files that have been accepted in the last five minutes */ @@ -280,6 +293,7 @@ public class ProcessorStatusSnapshotDTO implements Cloneable { other.setType(getType()); other.setRunStatus(getRunStatus()); + other.setExecutionNode(getExecutionNode()); other.setBytesRead(getBytesRead()); other.setBytesWritten(getBytesWritten()); other.setFlowFilesIn(getFlowFilesIn()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java index 56a230ff87..acc3102aef 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java @@ -2976,6 +2976,7 @@ public class FlowController implements EventAccess, ControllerServiceProvider, R status.setRunStatus(RunStatus.Stopped); } + status.setExecutionNode(procNode.getExecutionNode()); status.setActiveThreadCount(processScheduler.getActiveThreadCount(procNode)); return status; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java index 1a4d52b20d..f995f6bcd3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java @@ -1106,6 +1106,7 @@ public final class DtoFactory { // determine the run status snapshot.setRunStatus(procStatus.getRunStatus().toString()); + snapshot.setExecutionNode(procStatus.getExecutionNode().toString()); snapshot.setActiveThreadCount(procStatus.getActiveThreadCount()); snapshot.setType(procStatus.getType()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css index 18a32b2a3e..f5c1a6f4d7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css @@ -66,7 +66,6 @@ g.component rect.body.unauthorized { fill: #f4f6f7; } - g.component rect.border { stroke: rgba(0,0,0,0.25); stroke-width: 1; @@ -187,7 +186,7 @@ text.processor-icon { font-size: 30px; } -circle.restricted-background { +circle.restricted-background, circle.is-primary-background { visibility: hidden; fill: rgba(255, 255, 255, 0.90); } @@ -199,6 +198,12 @@ text.restricted { fill: #ba554a; } +text.is-primary { + visibility: hidden; + font-size: 12px; + font-weight: bold; +} + text.run-status-icon { font-size: 13px; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/summary.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/summary.css index 4e0e3f9909..e8ef890f7d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/summary.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/summary.css @@ -103,6 +103,23 @@ input.search-nodes { color: #888; } +div.is-primary-icon { + color: #000; + font-family: Roboto; + font-size: 10px; + font-weight: bold; + line-height: 14px; + width: 14px; + height: 14px; + background-color: rgba(255, 255, 255, 0.9); + border: 1px solid #aaa; + border-radius: 7px; + margin-right: 3px; + margin-top: 3px; + float: left; + text-align: center; +} + /* system diagnostics dialog */ #system-diagnostics-refresh-container{ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js index 648c661c15..1d9b4b2c0a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js @@ -23,9 +23,10 @@ 'd3', 'nf.Common', 'nf.Client', + 'nf.ClusterSummary', 'nf.CanvasUtils'], - function ($, d3, nfCommon, nfClient, nfCanvasUtils) { - return (nf.Processor = factory($, d3, nfCommon, nfClient, nfCanvasUtils)); + function ($, d3, nfCommon, nfClient, nfClusterSummary, nfCanvasUtils) { + return (nf.Processor = factory($, d3, nfCommon, nfClient, nfClusterSummary, nfCanvasUtils)); }); } else if (typeof exports === 'object' && typeof module === 'object') { module.exports = (nf.Processor = @@ -33,15 +34,17 @@ require('d3'), require('nf.Common'), require('nf.Client'), + require('nf.ClusterSummary'), require('nf.CanvasUtils'))); } else { nf.Processor = factory(root.$, root.d3, root.nf.Common, root.nf.Client, + root.nf.ClusterSummary, root.nf.CanvasUtils); } -}(this, function ($, d3, nfCommon, nfClient, nfCanvasUtils) { +}(this, function ($, d3, nfCommon, nfClient, nfClusterSummary, nfCanvasUtils) { 'use strict'; var nfConnectable; @@ -181,6 +184,27 @@ }) .text('\uf132'); + // is primary icon background + processor.append('circle') + .attr({ + 'r': 9, + 'cx': 38, + 'cy': 36, + 'class': 'is-primary-background' + }); + + // is primary icon + processor.append('text') + .attr({ + 'x': 34.75, + 'y': 40, + 'class': 'is-primary' + }) + .text('P') + .append('title').text(function (d) { + return 'This component is only scheduled to execute on the Primary Node'; + }); + // make processors selectable processor.call(nfSelectable.activate).call(nfContextMenu.activate).call(nfQuickSelect.activate); }; @@ -686,11 +710,15 @@ // restricted component indicator processor.select('circle.restricted-background').style('visibility', showRestricted); processor.select('text.restricted').style('visibility', showRestricted); + + // is primary component indicator + processor.select('circle.is-primary-background').style('visibility', showIsPrimary); + processor.select('text.is-primary').style('visibility', showIsPrimary); }); }; /** - * Returns whether the resticted indicator should be shown for a given + * Returns whether the resticted indicator should be shown for a given component * @param d * @returns {*} */ @@ -700,7 +728,16 @@ } return d.component.restricted ? 'visible' : 'hidden'; - } + }; + + /** + * Returns whether the is primary indicator should be shown for a given component + * @param d + * @returns {*} + */ + var showIsPrimary = function (d) { + return nfClusterSummary.isClustered() && d.status.aggregateSnapshot.executionNode === 'PRIMARY' ? 'visible' : 'hidden'; + }; /** * Updates the stats for the processors in the specified selection. diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js index 4a1c954486..1cf0e49fef 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js @@ -276,6 +276,19 @@ return markup; }; + // formatter for name + var nameFormatter = function (row, cell, value, columnDef, dataContext) { + var markup = ''; + + if (isClustered && dataContext.executionNode === 'PRIMARY') { + markup += '
P
'; + } + + markup += nfCommon.escapeHtml(value); + + return markup; + }; + // formatter for io var ioFormatter = function (row, cell, value, columnDef, dataContext) { return nfCommon.escapeHtml(dataContext.read) + ' / ' + nfCommon.escapeHtml(dataContext.written); @@ -326,9 +339,9 @@ id: 'name', field: 'name', name: 'Name', + formatter: nameFormatter, sortable: true, - resizable: true, - formatter: nfCommon.genericValueFormatter + resizable: true }; var runStatusColumn = { id: 'runStatus',