diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/prometheusutil/ClusterMetricsRegistry.java b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/prometheusutil/ClusterMetricsRegistry.java index 462b2328812b..787b6e22bc5c 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/prometheusutil/ClusterMetricsRegistry.java +++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/prometheusutil/ClusterMetricsRegistry.java @@ -48,5 +48,17 @@ public ClusterMetricsRegistry() { .help("The total number of nodes in this cluster") .labelNames("instance") .register(registry)); + + nameToGaugeMap.put("IS_PRIMARY_NODE", Gauge.build() + .name("cluster_is_primary_node") + .help("Whether this NiFi instance is the Primary Node. Values are 0 or 1") + .labelNames("instance") + .register(registry)); + + nameToGaugeMap.put("IS_CLUSTER_COORDINATOR", Gauge.build() + .name("cluster_is_cluster_coordinator") + .help("Whether this NiFi instance is the Cluster Coordinator. Values are 0 or 1") + .labelNames("instance") + .register(registry)); } } diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/prometheusutil/PrometheusMetricsUtil.java b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/prometheusutil/PrometheusMetricsUtil.java index 60c7d4d223af..1d799a0ab69f 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/prometheusutil/PrometheusMetricsUtil.java +++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/prometheusutil/PrometheusMetricsUtil.java @@ -500,12 +500,15 @@ public static void createVersionInfoMetrics(final VersionInfoRegistry versionInf } public static CollectorRegistry createClusterMetrics(final ClusterMetricsRegistry clusterMetricsRegistry, final String instId, final boolean isClustered, final boolean isConnectedToCluster, - final int connectedNodeCount, final int totalNodeCount) { + final int connectedNodeCount, final int totalNodeCount, + final boolean isPrimaryNode, final boolean isClusterCoordinator) { final String instanceId = StringUtils.isEmpty(instId) ? DEFAULT_LABEL_STRING : instId; clusterMetricsRegistry.setDataPoint(isClustered ? 1 : 0, "IS_CLUSTERED", instanceId); clusterMetricsRegistry.setDataPoint(isConnectedToCluster ? 1 : 0, "IS_CONNECTED_TO_CLUSTER", instanceId); clusterMetricsRegistry.setDataPoint(connectedNodeCount, "CONNECTED_NODE_COUNT", instanceId); clusterMetricsRegistry.setDataPoint(totalNodeCount, "TOTAL_NODE_COUNT", instanceId); + clusterMetricsRegistry.setDataPoint(isPrimaryNode ? 1 : 0, "IS_PRIMARY_NODE", instanceId); + clusterMetricsRegistry.setDataPoint(isClusterCoordinator ? 1 : 0, "IS_CLUSTER_COORDINATOR", instanceId); return clusterMetricsRegistry.getRegistry(); } diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java index b178d39d6769..12a81009a53f 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java +++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java @@ -8175,7 +8175,8 @@ protected Collection populateFlowMetrics(FlowMetricsRep } final boolean isClustered = clusterCoordinator != null; final boolean isConnectedToCluster = isClustered() && clusterCoordinator.isConnected(); - PrometheusMetricsUtil.createClusterMetrics(clusterMetricsRegistry, instanceId, isClustered, isConnectedToCluster, connectedNodeCount, totalNodeCount); + PrometheusMetricsUtil.createClusterMetrics(clusterMetricsRegistry, instanceId, isClustered, isConnectedToCluster, connectedNodeCount, totalNodeCount, + controllerFacade.isPrimary(), controllerFacade.isClusterCoordinator()); Collection metricsRegistries = Arrays.asList( nifiMetricsRegistry, jvmMetricsRegistry, diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java index e9a2436aa615..d33b4156dfc3 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java +++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java @@ -494,6 +494,20 @@ public boolean isClustered() { return flowController.isClustered(); } + /** + * @return true if this node is the Primary Node + */ + public boolean isPrimary() { + return flowController.isPrimary(); + } + + /** + * @return true if this node is the Cluster Coordinator + */ + public boolean isClusterCoordinator() { + return flowController.isClusterCoordinator(); + } + /** * Gets the name of this controller. * diff --git a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestFlowResource.java b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestFlowResource.java index 8356406c31ab..9a5a7335eb11 100644 --- a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestFlowResource.java +++ b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestFlowResource.java @@ -124,6 +124,12 @@ public class TestFlowResource { private static final int COMPONENT_TYPE_VALUE_INDEX = 1; private static final String CLUSTER_TYPE_LABEL = "cluster"; private static final String CLUSTER_LABEL_KEY = "instance"; + private static final String IS_CLUSTERED_LABEL_KEY = "cluster_is_clustered"; + private static final String IS_CONNECTED_TO_CLUSTER_LABEL_KEY = "cluster_is_connected_to_cluster"; + private static final String CONNECTED_NODE_COUNT_LABEL_KEY = "cluster_connected_node_count"; + private static final String TOTAL_NODE_COUNT_LABEL_KEY = "cluster_total_node_count"; + private static final String IS_PRIMARY_NODE_LABEL_KEY = "cluster_is_primary_node"; + private static final String IS_CLUSTER_COORDINATOR_LABEL_KEY = "cluster_is_cluster_coordinator"; private static final String SAMPLE_REGISTRY_ID = "0e87642a-7720-4799-a3bd-04db74b86e85"; private static final String SAMPLE_BRANCH_ID_A = "c302f541-976e-4c51-952d-345516444e3d"; private static final String SAMPLE_BUCKET_ID_A = "23da421d-a8da-4fa3-939e-658d8f35b972"; @@ -276,13 +282,20 @@ public void testGetFlowMetricsPrometheusAsJson() throws IOException { assertTrue(metrics.containsKey(ROOT_FIELD_NAME)); final List registryList = metrics.get(ROOT_FIELD_NAME); - assertEquals(13, registryList.size()); + assertEquals(15, registryList.size()); - final Map result = getResult(registryList); - assertEquals(3L, result.get(SAMPLE_NAME_JVM)); - assertEquals(4L, result.get(SAMPLE_LABEL_VALUES_PROCESS_GROUP)); - assertEquals(2L, result.get(SAMPLE_LABEL_VALUES_ROOT_PROCESS_GROUP)); - assertEquals(4L, result.get(CLUSTER_LABEL_KEY)); + final Map sampleCounts = getSampleCounts(registryList); + assertEquals(3L, sampleCounts.get(SAMPLE_NAME_JVM)); + assertEquals(4L, sampleCounts.get(SAMPLE_LABEL_VALUES_PROCESS_GROUP)); + assertEquals(2L, sampleCounts.get(SAMPLE_LABEL_VALUES_ROOT_PROCESS_GROUP)); + assertEquals(6L, sampleCounts.get(CLUSTER_LABEL_KEY)); + + assertSample(registryList, IS_CLUSTERED_LABEL_KEY, 1.0); + assertSample(registryList, IS_CONNECTED_TO_CLUSTER_LABEL_KEY, 1.0); + assertSample(registryList, CONNECTED_NODE_COUNT_LABEL_KEY, 2.0); + assertSample(registryList, TOTAL_NODE_COUNT_LABEL_KEY, 3.0); + assertSample(registryList, IS_PRIMARY_NODE_LABEL_KEY, 1.0); + assertSample(registryList, IS_CLUSTER_COORDINATOR_LABEL_KEY, 0.0); } @Test @@ -301,8 +314,8 @@ public void testGetFlowMetricsPrometheusAsJsonSampleName() throws IOException { final List registryList = metrics.get(ROOT_FIELD_NAME); assertEquals(3, registryList.size()); - final Map result = getResult(registryList); - assertEquals(3L, result.get(SAMPLE_NAME_JVM)); + final Map sampleCounts = getSampleCounts(registryList); + assertEquals(3L, sampleCounts.get(SAMPLE_NAME_JVM)); } @Test @@ -321,8 +334,8 @@ public void testGetFlowMetricsPrometheusAsJsonSampleNameStartsWithPattern() thro final List registryList = metrics.get(ROOT_FIELD_NAME); assertEquals(2, registryList.size()); - final Map result = getResult(registryList); - assertEquals(2L, result.get(SAMPLE_NAME_JVM)); + final Map sampleCounts = getSampleCounts(registryList); + assertEquals(2L, sampleCounts.get(SAMPLE_NAME_JVM)); } @Test @@ -341,8 +354,8 @@ public void testGetFlowMetricsPrometheusAsJsonSampleLabelValue() throws IOExcept final List registryList = metrics.get(ROOT_FIELD_NAME); assertEquals(2, registryList.size()); - final Map result = getResult(registryList); - assertEquals(2L, result.get(SAMPLE_LABEL_VALUES_ROOT_PROCESS_GROUP)); + final Map sampleCounts = getSampleCounts(registryList); + assertEquals(2L, sampleCounts.get(SAMPLE_LABEL_VALUES_ROOT_PROCESS_GROUP)); } @Test @@ -362,9 +375,9 @@ public void testGetFlowMetricsPrometheusAsJsonSampleNameAndSampleLabelValue() th final List registryList = metrics.get(ROOT_FIELD_NAME); assertEquals(5, registryList.size()); - final Map result = getResult(registryList); - assertEquals(3L, result.get(SAMPLE_NAME_JVM)); - assertEquals(2L, result.get(SAMPLE_LABEL_VALUES_ROOT_PROCESS_GROUP)); + final Map sampleCounts = getSampleCounts(registryList); + assertEquals(3L, sampleCounts.get(SAMPLE_NAME_JVM)); + assertEquals(2L, sampleCounts.get(SAMPLE_LABEL_VALUES_ROOT_PROCESS_GROUP)); } @Test @@ -753,14 +766,14 @@ private Map> convertJsonResponseToMap(final Response respon return mapper.readValue(json, typeReference); } - private Map getResult(final List registries) { - return registries.stream() + private Map getSampleCounts(final List samples) { + return samples.stream() .collect(Collectors.groupingBy( - sample -> getResultKey(sample), + sample -> getSampleKey(sample), Collectors.counting())); } - private String getResultKey(final Sample sample) { + private String getSampleKey(final Sample sample) { if (sample.labelNames.contains(COMPONENT_TYPE_LABEL)) { return sample.labelValues.get(COMPONENT_TYPE_VALUE_INDEX); } @@ -770,6 +783,15 @@ private String getResultKey(final Sample sample) { return SAMPLE_NAME_JVM; } + private void assertSample(final List samples, final String sampleName, final double expectedValue) { + final Sample sample = samples.stream() + .filter(s -> sampleName.equals(s.name)) + .findFirst() + .orElse(null); + assertNotNull(sample, String.format("Sample [%s] not found", sampleName)); + assertEquals(expectedValue, sample.value); + } + private static List getCollectorRegistriesForJson() { final List registryList = new ArrayList<>(); @@ -836,6 +858,8 @@ private static CollectorRegistry getClusterMetricsRegistry() { clusterMetricsRegistry.setDataPoint(1, "IS_CONNECTED_TO_CLUSTER", "B1Id"); clusterMetricsRegistry.setDataPoint(2, "CONNECTED_NODE_COUNT", "B1Id"); clusterMetricsRegistry.setDataPoint(3, "TOTAL_NODE_COUNT", "B1Id"); + clusterMetricsRegistry.setDataPoint(1, "IS_PRIMARY_NODE", "B1Id"); + clusterMetricsRegistry.setDataPoint(0, "IS_CLUSTER_COORDINATOR", "B1Id"); return clusterMetricsRegistry.getRegistry(); }