Skip to content

Commit

Permalink
Report a node's "roles" setting in the /_cluster/allocation/explain r…
Browse files Browse the repository at this point in the history
…esponse
  • Loading branch information
DiannaHohensee committed Aug 16, 2023
1 parent 645a21f commit 7f79eb6
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,21 @@
- is_true: can_rebalance_cluster
- is_true: can_rebalance_to_other_node
- is_true: rebalance_explanation

---
"Cluster shard allocation explanation test includes node's roles":
- skip:
version: " - 8.9.99"
reason: The roles field was introduced in 8.10.0

- do:
indices.create:
index: test

- match: { acknowledged: true }

- do:
cluster.allocation_explain:
body: { "index": "test", "shard": 0, "primary": true }

- is_true: current_node.roles
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.elasticsearch.xcontent.json.JsonXContent;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -1150,6 +1151,49 @@ public void testCannotAllocateStaleReplicaExplanation() throws Exception {
}
}

public void testExplainRolesOutput() throws Exception {
logger.info("--> starting 1 node");
List<String> nodes = internalCluster().startNodes(1);

logger.info("--> shutting down node in order to restart with specific 'roles' setting");
Settings settings = internalCluster().dataPathSettings(nodes.get(0));
internalCluster().stopNode(nodes.get(0));

logger.info("--> restarting the stopped node with 'roles' setting added");
Settings roleSettings = Settings.builder().putList("node.roles", Arrays.asList("master", "data_hot", "ingest")).build();
internalCluster().startNode(Settings.builder().put(settings).put(roleSettings).build());
ensureStableCluster(1);
prepareIndex(1, 0);

boolean includeYesDecisions = randomBoolean();
boolean includeDiskInfo = randomBoolean();
ClusterAllocationExplanation explanation = runExplain(true, includeYesDecisions, includeDiskInfo);

DiscoveryNode currentNode = explanation.getCurrentNode();
assertTrue(currentNode.isMasterNode());
assertTrue(currentNode.isIngestNode());
assertTrue(currentNode.isHotDataNode());

try (XContentParser parser = getParser(explanation)) {
// Fast-forward to the "current_node" object, which contains "roles".
do {
parser.nextToken();
assertNotEquals(Token.END_OBJECT, parser.currentToken());
} while (parser.currentName() != "current_node");
assertEquals(Token.START_OBJECT, parser.nextToken());

// Fast-forward to "roles" field in the "current_node" object.
do {
parser.nextToken();
assertNotEquals(Token.END_OBJECT, parser.currentToken());
} while (parser.currentName() != "roles");

// Check that the "roles" reported are those set via the node Settings.
// Note: list() implicitly consumes the parser START_ARRAY and END_ARRAY tokens.
assertEquals(Arrays.asList("data_hot", "ingest", "master"), parser.list());
}
}

private void verifyClusterInfo(ClusterInfo clusterInfo, boolean includeDiskInfo, int numNodes) {
if (includeDiskInfo) {
assertThat(clusterInfo.getNodeMostAvailableDiskUsages().size(), greaterThanOrEqualTo(0));
Expand Down Expand Up @@ -1309,8 +1353,11 @@ private void verifyShardInfo(XContentParser parser, boolean primary, boolean inc
parser.currentName().equals("id")
|| parser.currentName().equals("name")
|| parser.currentName().equals("transport_address")
|| parser.currentName().equals("roles")
|| parser.currentName().equals("weight_ranking")
);
} else if (token == Token.START_ARRAY || token == Token.END_ARRAY) {
assertEquals("roles", parser.currentName());
} else {
assertTrue(token.isValue());
assertNotNull(parser.text());
Expand Down Expand Up @@ -1436,6 +1483,10 @@ private String verifyNodeDecisionPrologue(XContentParser parser) throws IOExcept
parser.nextToken();
assertNotNull(parser.text());
parser.nextToken();
assertEquals("roles", parser.currentName());
parser.nextToken();
assertNotEquals(0, parser.list().size());
parser.nextToken();
assertEquals("node_decision", parser.currentName());
parser.nextToken();
return nodeName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,15 @@ public boolean isRemoteClusterClient() {
return roles.contains(DiscoveryNodeRole.REMOTE_CLUSTER_CLIENT_ROLE);
}

/**
* Returns whether this node has a 'data_hot' role.
*
* @return true if the node has a hot data role.
*/
public boolean isHotDataNode() {
return roles.contains(DiscoveryNodeRole.DATA_HOT_NODE_ROLE);
}

/**
* Returns whether or not the node is a frozen only node, i.e., has data frozen role and no other data roles.
* @return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
package org.elasticsearch.cluster.routing.allocation;

import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodeRole;
import org.elasticsearch.cluster.routing.allocation.decider.Decision.Type;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
Expand Down Expand Up @@ -110,6 +111,13 @@ public static XContentBuilder discoveryNodeToXContent(DiscoveryNode node, boolea
}
builder.endObject();
}
if (node.getRoles().isEmpty() == false) {
builder.startArray("roles");
for (DiscoveryNodeRole role : node.getRoles()) {
builder.value(role.roleName());
}
builder.endArray();
}
return builder;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.elasticsearch.xcontent.XContentFactory;

import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.Locale;

Expand Down Expand Up @@ -109,6 +110,20 @@ public ShardAllocationDecision decideShardAllocation(ShardRouting shard, Routing
cae.getCurrentNode().getId(),
cae.getCurrentNode().getName(),
cae.getCurrentNode().getAddress(),
"data",
"data_cold",
"data_content",
"data_frozen",
"data_hot",
"data_warm",
"index",
"ingest",
"master",
"ml",
"remote_cluster_client",
"search",
"transform",
"voting_only",
explanation };
assertEquals(XContentHelper.stripWhitespace(Strings.format("""
{
Expand All @@ -120,7 +135,8 @@ public ShardAllocationDecision decideShardAllocation(ShardRouting shard, Routing
"current_node": {
"id": "%s",
"name": "%s",
"transport_address": "%s"
"transport_address": "%s",
"roles": ["%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s"]
},
"explanation": "%s"
}""", args)), Strings.toString(builder));
Expand Down

0 comments on commit 7f79eb6

Please sign in to comment.