Skip to content

Commit

Permalink
Add to and from XContent to ClusterBlock and ClusterBlocks
Browse files Browse the repository at this point in the history
Signed-off-by: Shivansh Arora <[email protected]>
  • Loading branch information
shiv0408 committed Jun 5, 2024
1 parent ba0df74 commit 251daa9
Show file tree
Hide file tree
Showing 5 changed files with 485 additions and 8 deletions.
106 changes: 101 additions & 5 deletions server/src/main/java/org/opensearch/cluster/block/ClusterBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,26 @@

package org.opensearch.cluster.block;

import org.opensearch.cluster.metadata.Metadata;
import org.opensearch.common.Nullable;
import org.opensearch.common.annotation.PublicApi;
import org.opensearch.common.util.set.Sets;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.common.io.stream.Writeable;
import org.opensearch.core.rest.RestStatus;
import org.opensearch.core.xcontent.ToXContentFragment;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.core.xcontent.XContentParser;

import java.io.IOException;
import java.util.EnumSet;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;

import static org.opensearch.cluster.metadata.Metadata.CONTEXT_MODE_API;
import static org.opensearch.cluster.metadata.Metadata.CONTEXT_MODE_PARAM;

/**
* Blocks the cluster for concurrency
Expand All @@ -54,6 +61,20 @@
@PublicApi(since = "1.0.0")
public class ClusterBlock implements Writeable, ToXContentFragment {

static final String KEY_UUID = "uuid";
static final String KEY_DESCRIPTION = "description";
static final String KEY_RETRYABLE = "retryable";
static final String KEY_DISABLE_STATE_PERSISTENCE = "disable_state_persistence";
static final String KEY_LEVELS = "levels";
static final String KEY_STATUS = "status";
static final String KEY_ALLOW_RELEASE_RESOURCES = "allow_release_resources";
private static final Set<String> VALID_FIELDS = Sets.newHashSet(
KEY_UUID,
KEY_DESCRIPTION,
KEY_RETRYABLE,
KEY_DISABLE_STATE_PERSISTENCE,
KEY_LEVELS
);
private final int id;
@Nullable
private final String uuid;
Expand Down Expand Up @@ -154,24 +175,99 @@ public boolean disableStatePersistence() {

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
Metadata.XContentContext context = Metadata.XContentContext.valueOf(params.param(CONTEXT_MODE_PARAM, CONTEXT_MODE_API));
builder.startObject(Integer.toString(id));
if (uuid != null) {
builder.field("uuid", uuid);
builder.field(KEY_UUID, uuid);
}
builder.field("description", description);
builder.field("retryable", retryable);
builder.field(KEY_DESCRIPTION, description);
builder.field(KEY_RETRYABLE, retryable);
if (disableStatePersistence) {
builder.field("disable_state_persistence", disableStatePersistence);
builder.field(KEY_DISABLE_STATE_PERSISTENCE, disableStatePersistence);
}
builder.startArray("levels");
builder.startArray(KEY_LEVELS);
for (ClusterBlockLevel level : levels) {
builder.value(level.name().toLowerCase(Locale.ROOT));
}
builder.endArray();
if (context == Metadata.XContentContext.GATEWAY) {
builder.field(KEY_STATUS, status);
builder.field(KEY_ALLOW_RELEASE_RESOURCES, allowReleaseResources);
}
builder.endObject();
return builder;
}

public static ClusterBlock fromXContent(XContentParser parser, int id) throws IOException {
String uuid = null;
String description = null;
boolean retryable = false;
boolean disableStatePersistence = false;
RestStatus status = null;
boolean allowReleaseResources = false;
EnumSet<ClusterBlockLevel> levels = EnumSet.noneOf(ClusterBlockLevel.class);
String currentFieldName = skipBlockID(parser);
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
switch (Objects.requireNonNull(currentFieldName)) {
case KEY_UUID:
uuid = parser.text();
break;
case KEY_DESCRIPTION:
description = parser.text();
break;
case KEY_RETRYABLE:
retryable = parser.booleanValue();
break;
case KEY_DISABLE_STATE_PERSISTENCE:
disableStatePersistence = parser.booleanValue();
break;
case KEY_STATUS:
status = RestStatus.valueOf(parser.text());
break;
case KEY_ALLOW_RELEASE_RESOURCES:
allowReleaseResources = parser.booleanValue();
break;
default:
throw new IllegalArgumentException("unknown field [" + currentFieldName + "]");
}
} else if (token == XContentParser.Token.START_ARRAY) {
if (currentFieldName.equals(KEY_LEVELS)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
levels.add(ClusterBlockLevel.fromString(parser.text(), Locale.ROOT));
}
} else {
throw new IllegalArgumentException("unknown field [" + currentFieldName + "]");
}
} else {
throw new IllegalArgumentException("unexpected token [" + token + "]");
}
}
return new ClusterBlock(id, uuid, description, retryable, disableStatePersistence, allowReleaseResources, status, levels);
}

private static String skipBlockID(XContentParser parser) throws IOException {
if (parser.currentToken() == null) {
parser.nextToken();
}
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
parser.nextToken();
if (parser.currentToken() == XContentParser.Token.FIELD_NAME) {
String currentFieldName = parser.currentName();
if (VALID_FIELDS.contains(currentFieldName)) {
return currentFieldName;
} else {
// we have hit block id, just move on
parser.nextToken();
}
}
}
return null;
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.opensearch.common.annotation.PublicApi;

import java.util.EnumSet;
import java.util.Locale;

/**
* What level to block the cluster
Expand All @@ -51,4 +52,11 @@ public enum ClusterBlockLevel {

public static final EnumSet<ClusterBlockLevel> ALL = EnumSet.allOf(ClusterBlockLevel.class);
public static final EnumSet<ClusterBlockLevel> READ_WRITE = EnumSet.of(READ, WRITE);

/*
* This method is used to convert a string to a ClusterBlockLevel.
* */
public static ClusterBlockLevel fromString(String level, Locale locale) {
return ClusterBlockLevel.valueOf(level.toUpperCase(locale));
}
}
102 changes: 100 additions & 2 deletions server/src/main/java/org/opensearch/cluster/block/ClusterBlocks.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.rest.RestStatus;
import org.opensearch.core.xcontent.ToXContent;
import org.opensearch.core.xcontent.ToXContentFragment;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.core.xcontent.XContentParser;
import org.opensearch.core.xcontent.XContentParserUtils;

import java.io.IOException;
import java.util.Collections;
Expand All @@ -62,7 +67,7 @@
* @opensearch.api
*/
@PublicApi(since = "1.0.0")
public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> implements ToXContentFragment {
public static final ClusterBlocks EMPTY_CLUSTER_BLOCK = new ClusterBlocks(emptySet(), Map.of());

private final Set<ClusterBlock> global;
Expand Down Expand Up @@ -325,6 +330,16 @@ public static Diff<ClusterBlocks> readDiffFrom(StreamInput in) throws IOExceptio
return AbstractDiffable.readDiffFrom(ClusterBlocks::readFrom, in);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
Builder.toXContext(this, builder, params);
return builder;
}

public static ClusterBlocks fromXContent(XContentParser parser) throws IOException {
return Builder.fromXContent(parser);
}

/**
* An immutable level holder.
*
Expand Down Expand Up @@ -426,10 +441,16 @@ public Builder removeGlobalBlock(int blockId) {
}

public Builder addIndexBlock(String index, ClusterBlock block) {
prepareIndexForBlocks(index);
indices.get(index).add(block);
return this;
}

// initialize an index adding further blocks
private Builder prepareIndexForBlocks(String index) {
if (!indices.containsKey(index)) {
indices.put(index, new HashSet<>());
}
indices.get(index).add(block);
return this;
}

Expand Down Expand Up @@ -479,5 +500,82 @@ public ClusterBlocks build() {
}
return new ClusterBlocks(unmodifiableSet(new HashSet<>(global)), indicesBuilder);
}

public static void toXContext(ClusterBlocks blocks, XContentBuilder builder, ToXContent.Params params) throws IOException {
builder.startObject("blocks");
if (blocks.global().isEmpty() == false) {
builder.startObject("global");
for (ClusterBlock block : blocks.global()) {
block.toXContent(builder, params);
}
builder.endObject();
}

if (blocks.indices().isEmpty() == false) {
builder.startObject("indices");
for (Map.Entry<String, Set<ClusterBlock>> entry : blocks.indices().entrySet()) {
builder.startObject(entry.getKey());
for (ClusterBlock block : entry.getValue()) {
block.toXContent(builder, params);
}
builder.endObject();
}
builder.endObject();
}
builder.endObject();
}

public static ClusterBlocks fromXContent(XContentParser parser) throws IOException {
Builder builder = new Builder();
String currentFieldName = skipBlocksField(parser);
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser);
currentFieldName = parser.currentName();
parser.nextToken();
switch (currentFieldName) {
case "global":
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
currentFieldName = parser.currentName();
parser.nextToken();
builder.addGlobalBlock(ClusterBlock.fromXContent(parser, Integer.parseInt(currentFieldName)));
}
break;
case "indices":
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
String indexName = parser.currentName();
parser.nextToken();
// prepare for this index as we want to add this to ClusterBlocks even if there is no Block associated with it
builder.prepareIndexForBlocks(indexName);
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
currentFieldName = parser.currentName();
parser.nextToken();
builder.addIndexBlock(indexName, ClusterBlock.fromXContent(parser, Integer.parseInt(currentFieldName)));
}
}
break;
default:
throw new IllegalArgumentException("unknown field [" + currentFieldName + "]");
}
}
return builder.build();
}

private static String skipBlocksField(XContentParser parser) throws IOException {
if (parser.currentToken() == null) {
parser.nextToken();
}
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
parser.nextToken();
if (parser.currentToken() == XContentParser.Token.FIELD_NAME) {
if ("blocks".equals(parser.currentName())) {
parser.nextToken();
} else {
return parser.currentName();
}
}
}
return null;
}
}
}
Loading

0 comments on commit 251daa9

Please sign in to comment.