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

pifta pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 6180d21  HDDS-6128. CLI tool that downloads all the block replicas and 
creates a manifest file (#2987)
6180d21 is described below

commit 6180d212b441bd73e1c6b715957b1a2abde1acff
Author: Zita Dombi <[email protected]>
AuthorDate: Tue Jan 25 22:14:19 2022 +0100

    HDDS-6128. CLI tool that downloads all the block replicas and creates a 
manifest file (#2987)
---
 .../ozone/client/protocol/ClientProtocol.java      |  17 ++
 .../apache/hadoop/ozone/client/rpc/RpcClient.java  |  63 ++++++
 .../apache/hadoop/ozone/debug/ReadReplicas.java    | 247 +++++++++++++++++++++
 3 files changed, 327 insertions(+)

diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
index 0431604..8502fec 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
@@ -27,6 +27,7 @@ import org.apache.hadoop.crypto.key.KeyProvider;
 import org.apache.hadoop.hdds.client.ReplicationConfig;
 import org.apache.hadoop.hdds.client.ReplicationFactor;
 import org.apache.hadoop.hdds.client.ReplicationType;
+import org.apache.hadoop.hdds.protocol.DatanodeDetails;
 import org.apache.hadoop.hdds.protocol.StorageType;
 import org.apache.hadoop.io.Text;
 import org.apache.hadoop.ozone.OzoneAcl;
@@ -42,6 +43,7 @@ import org.apache.hadoop.ozone.client.io.OzoneInputStream;
 import org.apache.hadoop.ozone.client.io.OzoneOutputStream;
 import org.apache.hadoop.ozone.om.OMConfigKeys;
 import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfo;
 import org.apache.hadoop.ozone.om.helpers.OmMultipartInfo;
 import org.apache.hadoop.ozone.om.helpers.OmMultipartUploadCompleteInfo;
 import org.apache.hadoop.ozone.om.helpers.OzoneFileStatus;
@@ -782,4 +784,19 @@ public interface ClientProtocol {
    */
   boolean setBucketOwner(String volumeName, String bucketName,
       String owner) throws IOException;
+
+  /**
+   * Reads every replica for all the blocks associated with a given key.
+   * @param volumeName Volume name.
+   * @param bucketName Bucket name.
+   * @param keyName Key name.
+   * @return For every OmKeyLocationInfo (represents a block) it is mapped
+   * every replica, which is constructed by the DatanodeDetails and an
+   * inputstream made from the block.
+   * @throws IOException
+   */
+  Map<OmKeyLocationInfo,
+      Map<DatanodeDetails, OzoneInputStream>> getKeysEveryReplicas(
+          String volumeName, String bucketName, String keyName)
+      throws IOException;
 }
diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
index 966e381..b9db113 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
@@ -31,6 +31,8 @@ import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -48,6 +50,7 @@ import org.apache.hadoop.hdds.client.ReplicationFactor;
 import org.apache.hadoop.hdds.client.ReplicationType;
 import org.apache.hadoop.hdds.conf.ConfigurationSource;
 import org.apache.hadoop.hdds.conf.StorageUnit;
+import org.apache.hadoop.hdds.protocol.DatanodeDetails;
 import org.apache.hadoop.hdds.protocol.StorageType;
 import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
 import org.apache.hadoop.hdds.scm.OzoneClientConfig;
@@ -55,6 +58,8 @@ import org.apache.hadoop.hdds.scm.ScmConfigKeys;
 import org.apache.hadoop.hdds.scm.XceiverClientFactory;
 import org.apache.hadoop.hdds.scm.XceiverClientManager;
 import org.apache.hadoop.hdds.scm.client.HddsClientUtils;
+import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
+import org.apache.hadoop.hdds.scm.pipeline.PipelineID;
 import org.apache.hadoop.hdds.tracing.TracingUtil;
 import org.apache.hadoop.hdds.utils.IOUtils;
 import org.apache.hadoop.io.Text;
@@ -89,6 +94,7 @@ import org.apache.hadoop.ozone.om.helpers.OmDeleteKeys;
 import org.apache.hadoop.ozone.om.helpers.OmKeyArgs;
 import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfo;
+import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfoGroup;
 import org.apache.hadoop.ozone.om.helpers.OmMultipartInfo;
 import org.apache.hadoop.ozone.om.helpers.OmMultipartUploadCompleteInfo;
 import org.apache.hadoop.ozone.om.helpers.OmMultipartUploadCompleteList;
@@ -921,6 +927,63 @@ public class RpcClient implements ClientProtocol {
   }
 
   @Override
+  public Map< OmKeyLocationInfo, Map<DatanodeDetails, OzoneInputStream> >
+      getKeysEveryReplicas(String volumeName,
+                         String bucketName,
+                         String keyName) throws IOException {
+
+    Map< OmKeyLocationInfo, Map<DatanodeDetails, OzoneInputStream> > result
+        = new LinkedHashMap<>();
+
+    verifyVolumeName(volumeName);
+    verifyBucketName(bucketName);
+    Preconditions.checkNotNull(keyName);
+    OmKeyArgs keyArgs = new OmKeyArgs.Builder()
+        .setVolumeName(volumeName)
+        .setBucketName(bucketName)
+        .setKeyName(keyName)
+        .setRefreshPipeline(true)
+        .setSortDatanodesInPipeline(topologyAwareReadEnabled)
+        .build();
+
+    OmKeyInfo keyInfo = ozoneManagerClient.lookupKey(keyArgs);
+    List<OmKeyLocationInfo> keyLocationInfos
+        = keyInfo.getLatestVersionLocations().getBlocksLatestVersionOnly();
+
+    for(OmKeyLocationInfo keyLocationInfo : keyLocationInfos) {
+      Map<DatanodeDetails, OzoneInputStream> blocks = new HashMap<>();
+
+      Pipeline pipelineBefore = keyLocationInfo.getPipeline();
+      List<DatanodeDetails> datanodes = pipelineBefore.getNodes();
+
+      for(DatanodeDetails dn : datanodes) {
+        List<DatanodeDetails> nodes = new ArrayList<>();
+        nodes.add(dn);
+        Pipeline pipeline
+            = new Pipeline.Builder(pipelineBefore).setNodes(nodes)
+            .setId(PipelineID.randomId()).build();
+        keyLocationInfo.setPipeline(pipeline);
+
+        List<OmKeyLocationInfo> keyLocationInfoList = new ArrayList<>();
+        keyLocationInfoList.add(keyLocationInfo);
+        OmKeyLocationInfoGroup keyLocationInfoGroup
+            = new OmKeyLocationInfoGroup(0, keyLocationInfoList);
+        List<OmKeyLocationInfoGroup> keyLocationInfoGroups = new ArrayList<>();
+        keyLocationInfoGroups.add(keyLocationInfoGroup);
+
+        keyInfo.setKeyLocationVersions(keyLocationInfoGroups);
+        OzoneInputStream is = createInputStream(keyInfo, Function.identity());
+
+        blocks.put(dn, is);
+      }
+
+      result.put(keyLocationInfo, blocks);
+    }
+
+    return result;
+  }
+
+  @Override
   public void deleteKey(
       String volumeName, String bucketName, String keyName, boolean recursive)
       throws IOException {
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/ReadReplicas.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/ReadReplicas.java
new file mode 100644
index 0000000..6a7f178
--- /dev/null
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/ReadReplicas.java
@@ -0,0 +1,247 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.ozone.debug;
+
+import com.google.gson.*;
+import org.apache.hadoop.hdds.cli.SubcommandWithParent;
+import org.apache.hadoop.hdds.client.BlockID;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.protocol.DatanodeDetails;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.client.OzoneClientException;
+import org.apache.hadoop.ozone.client.OzoneKeyDetails;
+import org.apache.hadoop.ozone.client.io.OzoneInputStream;
+import org.apache.hadoop.ozone.client.protocol.ClientProtocol;
+import org.apache.hadoop.ozone.client.rpc.RpcClient;
+import org.apache.hadoop.ozone.common.OzoneChecksumException;
+import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfo;
+import org.apache.hadoop.ozone.shell.OzoneAddress;
+import org.apache.hadoop.ozone.shell.keys.KeyHandler;
+import org.apache.ratis.thirdparty.io.grpc.StatusRuntimeException;
+import org.jetbrains.annotations.NotNull;
+import org.kohsuke.MetaInfServices;
+import picocli.CommandLine;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * Class that downloads every replica for all the blocks associated with a
+ * given key. It also generates a manifest file with information about the
+ * downloaded replicas.
+ */
[email protected](name = "read-replicas",
+    description = "Reads every replica for all the blocks associated with a " +
+        "given key.")
+@MetaInfServices(SubcommandWithParent.class)
+public class ReadReplicas extends KeyHandler implements SubcommandWithParent {
+
+  @CommandLine.Option(names = {"--outputDir", "-o"},
+      description = "Destination where the directory will be created" +
+          " for the downloaded replicas and the manifest file.",
+      defaultValue = "/opt/hadoop")
+  private String outputDir;
+
+  private static final String JSON_PROPERTY_FILE_NAME = "filename";
+  private static final String JSON_PROPERTY_FILE_SIZE = "datasize";
+  private static final String JSON_PROPERTY_FILE_BLOCKS = "blocks";
+  private static final String JSON_PROPERTY_BLOCK_INDEX = "blockIndex";
+  private static final String JSON_PROPERTY_BLOCK_CONTAINERID = "containerId";
+  private static final String JSON_PROPERTY_BLOCK_LOCALID = "localId";
+  private static final String JSON_PROPERTY_BLOCK_LENGTH = "length";
+  private static final String JSON_PROPERTY_BLOCK_OFFSET = "offset";
+  private static final String JSON_PROPERTY_BLOCK_REPLICAS = "replicas";
+  private static final String JSON_PROPERTY_REPLICA_HOSTNAME = "hostname";
+  private static final String JSON_PROPERTY_REPLICA_UUID = "uuid";
+  private static final String JSON_PROPERTY_REPLICA_EXCEPTION = "exception";
+
+  private ClientProtocol clientProtocol;
+  private ClientProtocol clientProtocolWithoutChecksum;
+
+  @Override
+  public Class<?> getParentType() {
+    return OzoneDebug.class;
+  }
+
+  @Override
+  protected void execute(OzoneClient client, OzoneAddress address)
+      throws IOException, OzoneClientException {
+
+    boolean isChecksumVerifyEnabled
+        = getConf().getBoolean("ozone.client.verify.checksum", true);
+    OzoneConfiguration configuration = new OzoneConfiguration(getConf());
+    configuration.setBoolean("ozone.client.verify.checksum",
+        !isChecksumVerifyEnabled);
+
+    if(isChecksumVerifyEnabled) {
+      clientProtocol = client.getObjectStore().getClientProxy();
+      clientProtocolWithoutChecksum = new RpcClient(configuration, null);
+    } else {
+      clientProtocol = new RpcClient(configuration, null);
+      clientProtocolWithoutChecksum = client.getObjectStore().getClientProxy();
+    }
+
+    address.ensureKeyAddress();
+    String volumeName = address.getVolumeName();
+    String bucketName = address.getBucketName();
+    String keyName = address.getKeyName();
+
+    String directoryName = createDirectory(volumeName, bucketName, keyName);
+
+    OzoneKeyDetails keyInfoDetails
+        = clientProtocol.getKeyDetails(volumeName, bucketName, keyName);
+
+    Map<OmKeyLocationInfo, Map<DatanodeDetails, OzoneInputStream>> replicas
+        = clientProtocol.getKeysEveryReplicas(volumeName, bucketName, keyName);
+
+    Map<OmKeyLocationInfo, Map<DatanodeDetails, OzoneInputStream>>
+        replicasWithoutChecksum = clientProtocolWithoutChecksum
+        .getKeysEveryReplicas(volumeName, bucketName, keyName);
+
+    JsonObject result = new JsonObject();
+    result.addProperty(JSON_PROPERTY_FILE_NAME,
+        volumeName + "/" + bucketName + "/" + keyName);
+    result.addProperty(JSON_PROPERTY_FILE_SIZE, keyInfoDetails.getDataSize());
+
+    JsonArray blocks = new JsonArray();
+    downloadReplicasAndCreateManifest(keyName, replicas,
+        replicasWithoutChecksum, directoryName, blocks);
+    result.add(JSON_PROPERTY_FILE_BLOCKS, blocks);
+
+    Gson gson = new GsonBuilder().setPrettyPrinting().create();
+    String prettyJson = gson.toJson(result);
+
+    String manifestFileName = keyName + "_manifest";
+    System.out.println("Writing manifest file : " + manifestFileName);
+    File manifestFile
+        = new File(outputDir + "/" + directoryName + "/" + manifestFileName);
+    Files.write(manifestFile.toPath(),
+        prettyJson.getBytes(StandardCharsets.UTF_8));
+  }
+
+  private void downloadReplicasAndCreateManifest(
+      String keyName,
+      Map<OmKeyLocationInfo, Map<DatanodeDetails, OzoneInputStream>> replicas,
+      Map<OmKeyLocationInfo, Map<DatanodeDetails, OzoneInputStream>>
+          replicasWithoutChecksum,
+      String directoryName, JsonArray blocks) throws IOException {
+    int blockIndex = 0;
+
+    for (Map.Entry<OmKeyLocationInfo, Map<DatanodeDetails, OzoneInputStream>>
+        block : replicas.entrySet()) {
+      JsonObject blockJson = new JsonObject();
+      JsonArray replicasJson = new JsonArray();
+
+      blockIndex += 1;
+      blockJson.addProperty(JSON_PROPERTY_BLOCK_INDEX, blockIndex);
+      blockJson.addProperty(JSON_PROPERTY_BLOCK_CONTAINERID,
+          block.getKey().getContainerID());
+      blockJson.addProperty(JSON_PROPERTY_BLOCK_LOCALID,
+          block.getKey().getLocalID());
+      blockJson.addProperty(JSON_PROPERTY_BLOCK_LENGTH,
+          block.getKey().getLength());
+      blockJson.addProperty(JSON_PROPERTY_BLOCK_OFFSET,
+          block.getKey().getOffset());
+
+      for (Map.Entry<DatanodeDetails, OzoneInputStream>
+          replica : block.getValue().entrySet()) {
+        JsonObject replicaJson = new JsonObject();
+
+        replicaJson.addProperty(JSON_PROPERTY_REPLICA_HOSTNAME,
+            replica.getKey().getHostName());
+        replicaJson.addProperty(JSON_PROPERTY_REPLICA_UUID,
+            replica.getKey().getUuidString());
+
+        OzoneInputStream is = replica.getValue();
+        String fileName = keyName + "_block" + blockIndex + "_" +
+            replica.getKey().getHostName();
+        System.out.println("Writing : " + fileName);
+        File replicaFile
+            = new File(outputDir + "/" + directoryName + "/" + fileName);
+
+        try {
+          Files.copy(is, replicaFile.toPath(),
+              StandardCopyOption.REPLACE_EXISTING);
+        } catch (IOException e) {
+          Throwable cause = e.getCause();
+          replicaJson.addProperty(JSON_PROPERTY_REPLICA_EXCEPTION,
+              e.getMessage());
+          if(cause instanceof OzoneChecksumException) {
+            BlockID blockID = block.getKey().getBlockID();
+            String datanodeUUID = replica.getKey().getUuidString();
+            is = getInputStreamWithoutChecksum(replicasWithoutChecksum,
+                datanodeUUID, blockID);
+            Files.copy(is, replicaFile.toPath(),
+                StandardCopyOption.REPLACE_EXISTING);
+          } else if(cause instanceof StatusRuntimeException) {
+            break;
+          }
+        } finally {
+          is.close();
+        }
+        replicasJson.add(replicaJson);
+      }
+      blockJson.add(JSON_PROPERTY_BLOCK_REPLICAS, replicasJson);
+      blocks.add(blockJson);
+    }
+  }
+
+  private OzoneInputStream getInputStreamWithoutChecksum(
+      Map<OmKeyLocationInfo, Map<DatanodeDetails, OzoneInputStream>>
+          replicasWithoutChecksum, String datanodeUUID, BlockID blockID) {
+    OzoneInputStream is = new OzoneInputStream();
+    for (Map.Entry<OmKeyLocationInfo, Map<DatanodeDetails, OzoneInputStream>>
+        block : replicasWithoutChecksum.entrySet()) {
+      if(block.getKey().getBlockID().equals(blockID)) {
+        for (Map.Entry<DatanodeDetails, OzoneInputStream>
+            replica : block.getValue().entrySet()) {
+          if(replica.getKey().getUuidString().equals(datanodeUUID)) {
+            is = replica.getValue();
+          }
+        }
+      }
+    }
+    return is;
+  }
+
+  @NotNull
+  private String createDirectory(String volumeName, String bucketName,
+                                 String keyName) throws IOException {
+    String fileSuffix
+        = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
+    String directoryName = volumeName + "_" + bucketName + "_" + keyName +
+        "_" + fileSuffix;
+    System.out.println("Creating directory : " + directoryName);
+    File dir = new File(outputDir + "/" + directoryName);
+    if (!dir.exists()){
+      if(dir.mkdir()) {
+        System.out.println("Successfully created!");
+      } else {
+        throw new IOException(String.format(
+            "Failed to create directory %s.", dir));
+      }
+    }
+    return directoryName;
+  }
+}

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to