This is an automated email from the ASF dual-hosted git repository.

rexxiong pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/celeborn.git


The following commit(s) were added to refs/heads/main by this push:
     new 216152d03 [CELEBORN-1632] Support to apply ratis local raft_meta_conf 
command with RESTful api
216152d03 is described below

commit 216152d038e854f59cc6dd3201210ab8700035f9
Author: Wang, Fei <[email protected]>
AuthorDate: Thu Oct 24 16:09:18 2024 +0800

    [CELEBORN-1632] Support to apply ratis local raft_meta_conf command with 
RESTful api
    
    ### What changes were proposed in this pull request?
    Sub-task of CELEBORN-1628.
    
    Support to apply ratis local raft_meta_conf with RESTful api.
    
    See 
https://celeborn.apache.org/docs/latest/celeborn_ratis_shell/#local-raftmetaconf
    ```
    $ celeborn-ratis sh local raftMetaConf -peers 
<[P0_ID|]P0_HOST:P0_PORT,[P1_ID|]P1_HOST:P1_PORT,[P2_ID|]P2_HOST:P2_PORT> -path 
<PARENT_PATH_OF_RAFT_META_CONF>
    ```
    
    The implementation is same with 
https://github.com/apache/ratis/blob/e96ed1a33840385446f4e647864a169467da5ab7/ratis-shell/src/main/java/org/apache/ratis/shell/cli/sh/local/RaftMetaConfCommand.java#L122-L133
    
    ### Why are the changes needed?
    
    We have implemented the RESTful implementation for all the others 
ratis-shell command.
    
    <img width="1219" alt="image" 
src="https://github.com/user-attachments/assets/4367ddbd-3c55-449a-a1bc-75d6c18e8918";>
    
    | Ratis Shell               | RESTful api                        |
    |----------------------|---------------------------------|
    | election transfer    | `/ratis/election/transfer`      |
    | election stepDown    | `/ratis/election/step_down`     |
    | election pause       | `/ratis/election/pause`         |
    | election resume      | `/ratis/election/resume`        |
    | group info           | `/masters`                      |
    | peer add             | `/ratis/peer/add`               |
    | peer remove          | `/ratis/peer/remove`            |
    | peer setPriority     | `/ratis/peer/set_priority`      |
    | snapshot create      | `/ratis/snapshot/create`        |
    
    And the local raftMetaConf command is the last one.
    
    I closed the ticket CELEBORN-1632 before, I thought it is a local command 
and wonder whether it is necessary to implement it with RESTful api.
    
    But we have implemented all the others, so I decide to implement it as well.
    
    ### Does this PR introduce _any_ user-facing change?
    
    A new API.
    
    The implementation is same with 
https://github.com/apache/ratis/blob/e96ed1a33840385446f4e647864a169467da5ab7/ratis-shell/src/main/java/org/apache/ratis/shell/cli/sh/local/RaftMetaConfCommand.java#L122-L133
    
    ### How was this patch tested?
    
![image](https://github.com/user-attachments/assets/088d8523-e5f5-4546-9159-e12191fd8a29)
    
![image](https://github.com/user-attachments/assets/ce9c4284-fd61-45de-93e7-d38e3b6afac9)
    <img width="960" alt="image" 
src="https://github.com/user-attachments/assets/b302a680-baea-4709-b77f-a2b1946b8dff";>
    
    <img width="1471" alt="image" 
src="https://github.com/user-attachments/assets/4bf090ba-c6f4-4f49-aa57-8dd2c897ff30";>
    <img width="871" alt="image" 
src="https://github.com/user-attachments/assets/9959072c-5e96-48f5-911e-546c05a0c443";>
    
    Closes #2829 from turboFei/local_raft_conf.
    
    Authored-by: Wang, Fei <[email protected]>
    Signed-off-by: Shuang <[email protected]>
---
 .../deploy/master/http/api/v1/RatisResource.scala  |  90 +++++++++++++-
 .../apache/celeborn/rest/v1/master/RatisApi.java   | 138 +++++++++++++++++++++
 .../v1/model/RatisLocalRaftMetaConfRequest.java    | 120 ++++++++++++++++++
 .../src/main/openapi3/master_rest_v1.yaml          |  44 +++++++
 4 files changed, 388 insertions(+), 4 deletions(-)

diff --git 
a/master/src/main/scala/org/apache/celeborn/service/deploy/master/http/api/v1/RatisResource.scala
 
b/master/src/main/scala/org/apache/celeborn/service/deploy/master/http/api/v1/RatisResource.scala
index 44e4ab1ff..04e94c4ba 100644
--- 
a/master/src/main/scala/org/apache/celeborn/service/deploy/master/http/api/v1/RatisResource.scala
+++ 
b/master/src/main/scala/org/apache/celeborn/service/deploy/master/http/api/v1/RatisResource.scala
@@ -17,27 +17,32 @@
 
 package org.apache.celeborn.service.deploy.master.http.api.v1
 
-import javax.ws.rs.{BadRequestException, Consumes, Path, POST, Produces}
-import javax.ws.rs.core.MediaType
+import java.nio.charset.StandardCharsets
+import java.nio.file.Paths
+import javax.ws.rs.{BadRequestException, Consumes, GET, NotFoundException, 
Path, POST, Produces}
+import javax.ws.rs.core.{MediaType, Response}
 
 import scala.collection.JavaConverters._
 
 import io.swagger.v3.oas.annotations.media.{Content, Schema}
 import io.swagger.v3.oas.annotations.responses.ApiResponse
 import io.swagger.v3.oas.annotations.tags.Tag
+import org.apache.commons.io.IOUtils
+import org.apache.ratis.proto.RaftProtos.{LogEntryProto, 
RaftConfigurationProto, RaftPeerProto, RaftPeerRole}
 import org.apache.ratis.protocol.{LeaderElectionManagementRequest, 
RaftClientReply, RaftPeer, SetConfigurationRequest, SnapshotManagementRequest, 
TransferLeadershipRequest}
 import org.apache.ratis.rpc.CallId
+import org.apache.ratis.server.storage.RaftStorageDirectory
+import org.apache.ratis.thirdparty.com.google.protobuf.ByteString
 
 import org.apache.celeborn.common.CelebornConf
 import org.apache.celeborn.common.internal.Logging
-import org.apache.celeborn.rest.v1.model.{HandleResponse, 
RatisElectionTransferRequest, RatisPeerAddRequest, RatisPeerRemoveRequest, 
RatisPeerSetPriorityRequest}
+import org.apache.celeborn.rest.v1.model.{HandleResponse, 
RatisElectionTransferRequest, RatisLocalRaftMetaConfRequest, 
RatisPeerAddRequest, RatisPeerRemoveRequest, RatisPeerSetPriorityRequest}
 import org.apache.celeborn.server.common.http.api.ApiRequestContext
 import org.apache.celeborn.service.deploy.master.Master
 import 
org.apache.celeborn.service.deploy.master.clustermeta.ha.{HAMasterMetaManager, 
HARaftServer}
 import 
org.apache.celeborn.service.deploy.master.http.api.MasterHttpResourceUtils.{ensureMasterHAEnabled,
 ensureMasterIsLeader}
 
 @Tag(name = "Ratis")
-@Produces(Array(MediaType.APPLICATION_JSON))
 @Consumes(Array(MediaType.APPLICATION_JSON))
 class RatisResource extends ApiRequestContext with Logging {
   private def master = httpService.asInstanceOf[Master]
@@ -51,6 +56,7 @@ class RatisResource extends ApiRequestContext with Logging {
     description = "Transfer the group leader to the specified server.")
   @POST
   @Path("/election/transfer")
+  @Produces(Array(MediaType.APPLICATION_JSON))
   def electionTransfer(request: RatisElectionTransferRequest): HandleResponse =
     ensureMasterIsLeader(master) {
       transferLeadership(request.getPeerAddress)
@@ -64,6 +70,7 @@ class RatisResource extends ApiRequestContext with Logging {
     description = "Make the group leader step down its leadership.")
   @POST
   @Path("/election/step_down")
+  @Produces(Array(MediaType.APPLICATION_JSON))
   def electionStepDown(): HandleResponse = ensureMasterIsLeader(master) {
     transferLeadership(null)
   }
@@ -77,6 +84,7 @@ class RatisResource extends ApiRequestContext with Logging {
       " Then, the current server would not start a leader election.")
   @POST
   @Path("/election/pause")
+  @Produces(Array(MediaType.APPLICATION_JSON))
   def electionPause(): HandleResponse = ensureMasterHAEnabled(master) {
     applyElectionOp(new LeaderElectionManagementRequest.Pause)
   }
@@ -89,6 +97,7 @@ class RatisResource extends ApiRequestContext with Logging {
     description = "Resume leader election at the current server.")
   @POST
   @Path("/election/resume")
+  @Produces(Array(MediaType.APPLICATION_JSON))
   def electionResume(): HandleResponse = ensureMasterHAEnabled(master) {
     applyElectionOp(new LeaderElectionManagementRequest.Resume)
   }
@@ -101,6 +110,7 @@ class RatisResource extends ApiRequestContext with Logging {
     description = "Add new peers to the raft group.")
   @POST
   @Path("/peer/add")
+  @Produces(Array(MediaType.APPLICATION_JSON))
   def peerAdd(request: RatisPeerAddRequest): HandleResponse =
     ensureLeaderElectionMemberMajorityAddEnabled(master) {
       if (request.getPeers.isEmpty) {
@@ -146,6 +156,7 @@ class RatisResource extends ApiRequestContext with Logging {
     description = "Remove peers from the raft group.")
   @POST
   @Path("/peer/remove")
+  @Produces(Array(MediaType.APPLICATION_JSON))
   def peerRemove(request: RatisPeerRemoveRequest): HandleResponse =
     ensureLeaderElectionMemberMajorityAddEnabled(master) {
       if (request.getPeers.isEmpty) {
@@ -183,6 +194,7 @@ class RatisResource extends ApiRequestContext with Logging {
     description = "Set the priority of the peers in the raft group.")
   @POST
   @Path("/peer/set_priority")
+  @Produces(Array(MediaType.APPLICATION_JSON))
   def peerSetPriority(request: RatisPeerSetPriorityRequest): HandleResponse =
     ensureLeaderElectionMemberMajorityAddEnabled(master) {
       if (request.getAddressPriorities.isEmpty) {
@@ -218,6 +230,7 @@ class RatisResource extends ApiRequestContext with Logging {
     description = "Trigger the current server to take snapshot.")
   @POST
   @Path("/snapshot/create")
+  @Produces(Array(MediaType.APPLICATION_JSON))
   def createSnapshot(): HandleResponse = ensureMasterHAEnabled(master) {
     val request = SnapshotManagementRequest.newCreate(
       ratisServer.getClientId,
@@ -235,6 +248,75 @@ class RatisResource extends ApiRequestContext with Logging 
{
     }
   }
 
+  @ApiResponse(
+    responseCode = "200",
+    content = Array(new Content(
+      mediaType = MediaType.APPLICATION_OCTET_STREAM,
+      schema = new Schema(implementation = classOf[Response]))),
+    description = "Get the raft-meta.conf file of the current server.")
+  @GET
+  @Path("/local/raft_meta_conf")
+  @Produces(Array(MediaType.APPLICATION_OCTET_STREAM))
+  def getLocalRaftMetaConf(): Response = ensureMasterHAEnabled(master) {
+    val raftMetaConfFile = Paths.get(
+      master.conf.haMasterRatisStorageDir,
+      ratisServer.getGroupId.getUuid.toString,
+      RaftStorageDirectory.CURRENT_DIR_NAME,
+      "raft-meta.conf")
+
+    if (!raftMetaConfFile.toFile.exists()) {
+      throw new NotFoundException(s"File $raftMetaConfFile not found.")
+    }
+
+    Response.ok(IOUtils.toByteArray(raftMetaConfFile.toUri))
+      .header("Content-Disposition", "attachment; filename=\"raft-meta.conf\"")
+      .build()
+  }
+
+  @ApiResponse(
+    responseCode = "200",
+    content = Array(new Content(
+      mediaType = MediaType.APPLICATION_OCTET_STREAM,
+      schema = new Schema(implementation = classOf[Response]))),
+    description = "Generate a new-raft-meta.conf file based on original 
raft-meta.conf" +
+      " and new peers, which is used to move a raft node to a new node.")
+  @POST
+  @Path("/local/raft_meta_conf")
+  @Produces(Array(MediaType.APPLICATION_OCTET_STREAM))
+  def generateNewRaftMetaConf(request: RatisLocalRaftMetaConfRequest): 
Response =
+    ensureMasterHAEnabled(master) {
+      if (request.getPeers.isEmpty) {
+        throw new BadRequestException("No peers specified.")
+      }
+
+      val groupInfo = ratisServer.getGroupInfo
+
+      val existingPeers = getRaftPeers().map(_.getRaftPeerProto)
+      val newPeers = request.getPeers.asScala.map { peer =>
+        RaftPeerProto.newBuilder()
+          
.setId(ByteString.copyFrom(peer.getId.getBytes(StandardCharsets.UTF_8)))
+          .setAddress(peer.getAddress)
+          .setStartupRole(RaftPeerRole.FOLLOWER)
+          .build()
+      }
+
+      val remainingPeers =
+        existingPeers.filterNot(p => newPeers.exists(_.getId.toStringUtf8 == 
p.getId.toStringUtf8))
+      val allPeers = remainingPeers ++ newPeers
+
+      val newIndex = groupInfo.getLogIndex + 1
+      logInfo(s"Generating new-raft-meta.conf with remaining peers:" +
+        s" $remainingPeers and new peers: $newPeers, index: $newIndex.")
+
+      val generateLogEntryProto = LogEntryProto.newBuilder()
+        .setConfigurationEntry(RaftConfigurationProto.newBuilder()
+          .addAllPeers(allPeers.asJava).build())
+        .setIndex(newIndex).build()
+      Response.ok(generateLogEntryProto.toByteArray)
+        .header("Content-Disposition", "attachment; 
filename=\"new-raft-meta.conf\"")
+        .build()
+    }
+
   private def transferLeadership(peerAddress: String): HandleResponse = {
     val newLeaderId = Option(peerAddress).map { addr =>
       getRaftPeers().find(_.getAddress == addr).map(_.getId).getOrElse(
diff --git 
a/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/master/RatisApi.java
 
b/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/master/RatisApi.java
index 60640c615..8c42ca7f7 100644
--- 
a/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/master/RatisApi.java
+++ 
b/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/master/RatisApi.java
@@ -25,8 +25,10 @@ import org.apache.celeborn.rest.v1.master.invoker.BaseApi;
 import org.apache.celeborn.rest.v1.master.invoker.Configuration;
 import org.apache.celeborn.rest.v1.master.invoker.Pair;
 
+import java.io.File;
 import org.apache.celeborn.rest.v1.model.HandleResponse;
 import org.apache.celeborn.rest.v1.model.RatisElectionTransferRequest;
+import org.apache.celeborn.rest.v1.model.RatisLocalRaftMetaConfRequest;
 import org.apache.celeborn.rest.v1.model.RatisPeerAddRequest;
 import org.apache.celeborn.rest.v1.model.RatisPeerRemoveRequest;
 import org.apache.celeborn.rest.v1.model.RatisPeerSetPriorityRequest;
@@ -186,6 +188,142 @@ public class RatisApi extends BaseApi {
     );
   }
 
+  /**
+   * 
+   * Generate a new-raft-meta.conf file based on original raft-meta.conf and 
new peers, which is used to move a raft node to a new node.
+   * @param ratisLocalRaftMetaConfRequest  (optional)
+   * @return File
+   * @throws ApiException if fails to make API call
+   */
+  public File generateNewRaftMetaConf(RatisLocalRaftMetaConfRequest 
ratisLocalRaftMetaConfRequest) throws ApiException {
+    return this.generateNewRaftMetaConf(ratisLocalRaftMetaConfRequest, 
Collections.emptyMap());
+  }
+
+
+  /**
+   * 
+   * Generate a new-raft-meta.conf file based on original raft-meta.conf and 
new peers, which is used to move a raft node to a new node.
+   * @param ratisLocalRaftMetaConfRequest  (optional)
+   * @param additionalHeaders additionalHeaders for this call
+   * @return File
+   * @throws ApiException if fails to make API call
+   */
+  public File generateNewRaftMetaConf(RatisLocalRaftMetaConfRequest 
ratisLocalRaftMetaConfRequest, Map<String, String> additionalHeaders) throws 
ApiException {
+    Object localVarPostBody = ratisLocalRaftMetaConfRequest;
+    
+    // create path and map variables
+    String localVarPath = "/api/v1/ratis/local/raft_meta_conf";
+
+    StringJoiner localVarQueryStringJoiner = new StringJoiner("&");
+    String localVarQueryParameterBaseName;
+    List<Pair> localVarQueryParams = new ArrayList<Pair>();
+    List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>();
+    Map<String, String> localVarHeaderParams = new HashMap<String, String>();
+    Map<String, String> localVarCookieParams = new HashMap<String, String>();
+    Map<String, Object> localVarFormParams = new HashMap<String, Object>();
+
+    
+    localVarHeaderParams.putAll(additionalHeaders);
+
+    
+    
+    final String[] localVarAccepts = {
+      "application/octet-stream"
+    };
+    final String localVarAccept = 
apiClient.selectHeaderAccept(localVarAccepts);
+
+    final String[] localVarContentTypes = {
+      "application/json"
+    };
+    final String localVarContentType = 
apiClient.selectHeaderContentType(localVarContentTypes);
+
+    String[] localVarAuthNames = new String[] { "basic" };
+
+    TypeReference<File> localVarReturnType = new TypeReference<File>() {};
+    return apiClient.invokeAPI(
+        localVarPath,
+        "POST",
+        localVarQueryParams,
+        localVarCollectionQueryParams,
+        localVarQueryStringJoiner.toString(),
+        localVarPostBody,
+        localVarHeaderParams,
+        localVarCookieParams,
+        localVarFormParams,
+        localVarAccept,
+        localVarContentType,
+        localVarAuthNames,
+        localVarReturnType
+    );
+  }
+
+  /**
+   * 
+   * Get the raft-meta.conf file of the current server.
+   * @return File
+   * @throws ApiException if fails to make API call
+   */
+  public File getLocalRaftMetaConf() throws ApiException {
+    return this.getLocalRaftMetaConf(Collections.emptyMap());
+  }
+
+
+  /**
+   * 
+   * Get the raft-meta.conf file of the current server.
+   * @param additionalHeaders additionalHeaders for this call
+   * @return File
+   * @throws ApiException if fails to make API call
+   */
+  public File getLocalRaftMetaConf(Map<String, String> additionalHeaders) 
throws ApiException {
+    Object localVarPostBody = null;
+    
+    // create path and map variables
+    String localVarPath = "/api/v1/ratis/local/raft_meta_conf";
+
+    StringJoiner localVarQueryStringJoiner = new StringJoiner("&");
+    String localVarQueryParameterBaseName;
+    List<Pair> localVarQueryParams = new ArrayList<Pair>();
+    List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>();
+    Map<String, String> localVarHeaderParams = new HashMap<String, String>();
+    Map<String, String> localVarCookieParams = new HashMap<String, String>();
+    Map<String, Object> localVarFormParams = new HashMap<String, Object>();
+
+    
+    localVarHeaderParams.putAll(additionalHeaders);
+
+    
+    
+    final String[] localVarAccepts = {
+      "application/octet-stream"
+    };
+    final String localVarAccept = 
apiClient.selectHeaderAccept(localVarAccepts);
+
+    final String[] localVarContentTypes = {
+      
+    };
+    final String localVarContentType = 
apiClient.selectHeaderContentType(localVarContentTypes);
+
+    String[] localVarAuthNames = new String[] { "basic" };
+
+    TypeReference<File> localVarReturnType = new TypeReference<File>() {};
+    return apiClient.invokeAPI(
+        localVarPath,
+        "GET",
+        localVarQueryParams,
+        localVarCollectionQueryParams,
+        localVarQueryStringJoiner.toString(),
+        localVarPostBody,
+        localVarHeaderParams,
+        localVarCookieParams,
+        localVarFormParams,
+        localVarAccept,
+        localVarContentType,
+        localVarAuthNames,
+        localVarReturnType
+    );
+  }
+
   /**
    * 
    * Pause leader election at the current server. Then, the current server 
would not start a leader election.
diff --git 
a/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/model/RatisLocalRaftMetaConfRequest.java
 
b/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/model/RatisLocalRaftMetaConfRequest.java
new file mode 100644
index 000000000..d78c760ff
--- /dev/null
+++ 
b/openapi/openapi-client/src/main/java/org/apache/celeborn/rest/v1/model/RatisLocalRaftMetaConfRequest.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.celeborn.rest.v1.model;
+
+import java.util.Objects;
+import java.util.Arrays;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import com.fasterxml.jackson.annotation.JsonValue;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.celeborn.rest.v1.model.RatisPeer;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+
+/**
+ * RatisLocalRaftMetaConfRequest
+ */
+@JsonPropertyOrder({
+  RatisLocalRaftMetaConfRequest.JSON_PROPERTY_PEERS
+})
[email protected](value = 
"org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator 
version: 7.8.0")
+public class RatisLocalRaftMetaConfRequest {
+  public static final String JSON_PROPERTY_PEERS = "peers";
+  private List<RatisPeer> peers = new ArrayList<>();
+
+  public RatisLocalRaftMetaConfRequest() {
+  }
+
+  public RatisLocalRaftMetaConfRequest peers(List<RatisPeer> peers) {
+    
+    this.peers = peers;
+    return this;
+  }
+
+  public RatisLocalRaftMetaConfRequest addPeersItem(RatisPeer peersItem) {
+    if (this.peers == null) {
+      this.peers = new ArrayList<>();
+    }
+    this.peers.add(peersItem);
+    return this;
+  }
+
+  /**
+   * The new peers to generate a new-raft-meta.conf file with existing peers.
+   * @return peers
+   */
+  @javax.annotation.Nonnull
+  @JsonProperty(JSON_PROPERTY_PEERS)
+  @JsonInclude(value = JsonInclude.Include.ALWAYS)
+
+  public List<RatisPeer> getPeers() {
+    return peers;
+  }
+
+
+  @JsonProperty(JSON_PROPERTY_PEERS)
+  @JsonInclude(value = JsonInclude.Include.ALWAYS)
+  public void setPeers(List<RatisPeer> peers) {
+    this.peers = peers;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    RatisLocalRaftMetaConfRequest ratisLocalRaftMetaConfRequest = 
(RatisLocalRaftMetaConfRequest) o;
+    return Objects.equals(this.peers, ratisLocalRaftMetaConfRequest.peers);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(peers);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("class RatisLocalRaftMetaConfRequest {\n");
+    sb.append("    peers: ").append(toIndentedString(peers)).append("\n");
+    sb.append("}");
+    return sb.toString();
+  }
+
+  /**
+   * Convert the given object to string with each line indented by 4 spaces
+   * (except the first line).
+   */
+  private String toIndentedString(Object o) {
+    if (o == null) {
+      return "null";
+    }
+    return o.toString().replace("\n", "\n    ");
+  }
+
+}
+
diff --git a/openapi/openapi-client/src/main/openapi3/master_rest_v1.yaml 
b/openapi/openapi-client/src/main/openapi3/master_rest_v1.yaml
index 60528efcb..a4b2802ce 100644
--- a/openapi/openapi-client/src/main/openapi3/master_rest_v1.yaml
+++ b/openapi/openapi-client/src/main/openapi3/master_rest_v1.yaml
@@ -448,6 +448,39 @@ paths:
               schema:
                 $ref: '#/components/schemas/HandleResponse'
 
+  /api/v1/ratis/local/raft_meta_conf:
+    get:
+      tags:
+        - Ratis
+      operationId: getLocalRaftMetaConf
+      description: Get the raft-meta.conf file of the current server.
+      responses:
+        "200":
+          description: The request was successful.
+          content:
+            application/octet-stream:
+              schema:
+                type: string
+                format: binary
+    post:
+      tags:
+        - Ratis
+      operationId: generateNewRaftMetaConf
+      description: Generate a new-raft-meta.conf file based on original 
raft-meta.conf and new peers, which is used to move a raft node to a new node.
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/RatisLocalRaftMetaConfRequest'
+      responses:
+        "200":
+          description: The request was successful.
+          content:
+            application/octet-stream:
+              schema:
+                type: string
+                format: binary
+
 components:
   schemas:
     ConfigData:
@@ -1027,6 +1060,17 @@ components:
           additionalProperties:
             type: integer
 
+    RatisLocalRaftMetaConfRequest:
+      type: object
+      properties:
+        peers:
+          type: array
+          description: The new peers to generate a new-raft-meta.conf file 
with existing peers.
+          items:
+            $ref: '#/components/schemas/RatisPeer'
+      required:
+        - peers
+
     HandleResponse:
       type: object
       properties:

Reply via email to