All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.hdfs.tools.offlineImageViewer.ImageLoaderCurrent Maven / Gradle / Ivy

There is a newer version: 3.4.0
Show newest version
/**
 * 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.hadoop.hdfs.tools.offlineImageViewer;

import java.io.DataInputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hdfs.protocol.DatanodeInfo.AdminStates;
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
import org.apache.hadoop.hdfs.protocol.LayoutFlags;
import org.apache.hadoop.hdfs.protocol.LayoutVersion.Feature;
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
import org.apache.hadoop.hdfs.server.namenode.FSImageSerialization;
import org.apache.hadoop.hdfs.server.namenode.NameNodeLayoutVersion;
import org.apache.hadoop.hdfs.tools.offlineImageViewer.ImageVisitor.ImageElement;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableUtils;
import org.apache.hadoop.io.compress.CompressionCodec;
import org.apache.hadoop.io.compress.CompressionCodecFactory;
import org.apache.hadoop.security.token.delegation.DelegationKey;

/**
 * ImageLoaderCurrent processes Hadoop FSImage files and walks over
 * them using a provided ImageVisitor, calling the visitor at each element
 * enumerated below.
 *
 * The only difference between v18 and v19 was the utilization of the
 * stickybit.  Therefore, the same viewer can reader either format.
 *
 * Versions -19 fsimage layout (with changes from -16 up):
 * Image version (int)
 * Namepsace ID (int)
 * NumFiles (long)
 * Generation stamp (long)
 * INodes (count = NumFiles)
 *  INode
 *    Path (String)
 *    Replication (short)
 *    Modification Time (long as date)
 *    Access Time (long) // added in -16
 *    Block size (long)
 *    Num blocks (int)
 *    Blocks (count = Num blocks)
 *      Block
 *        Block ID (long)
 *        Num bytes (long)
 *        Generation stamp (long)
 *    Namespace Quota (long)
 *    Diskspace Quota (long) // added in -18
 *    Permissions
 *      Username (String)
 *      Groupname (String)
 *      OctalPerms (short -> String)  // Modified in -19
 *    Symlink (String) // added in -23
 * NumINodesUnderConstruction (int)
 * INodesUnderConstruction (count = NumINodesUnderConstruction)
 *  INodeUnderConstruction
 *    Path (bytes as string)
 *    Replication (short)
 *    Modification time (long as date)
 *    Preferred block size (long)
 *    Num blocks (int)
 *    Blocks
 *      Block
 *        Block ID (long)
 *        Num bytes (long)
 *        Generation stamp (long)
 *    Permissions
 *      Username (String)
 *      Groupname (String)
 *      OctalPerms (short -> String)
 *    Client Name (String)
 *    Client Machine (String)
 *    NumLocations (int)
 *    DatanodeDescriptors (count = numLocations) // not loaded into memory
 *      short                                    // but still in file
 *      long
 *      string
 *      long
 *      int
 *      string
 *      string
 *      enum
 *    CurrentDelegationKeyId (int)
 *    NumDelegationKeys (int)
 *      DelegationKeys (count = NumDelegationKeys)
 *        DelegationKeyLength (vint)
 *        DelegationKey (bytes)
 *    DelegationTokenSequenceNumber (int)
 *    NumDelegationTokens (int)
 *    DelegationTokens (count = NumDelegationTokens)
 *      DelegationTokenIdentifier
 *        owner (String)
 *        renewer (String)
 *        realUser (String)
 *        issueDate (vlong)
 *        maxDate (vlong)
 *        sequenceNumber (vint)
 *        masterKeyId (vint)
 *      expiryTime (long)     
 *
 */
class ImageLoaderCurrent implements ImageLoader {
  protected final DateFormat dateFormat = 
                                      new SimpleDateFormat("yyyy-MM-dd HH:mm");
  private static int[] versions = { -16, -17, -18, -19, -20, -21, -22, -23,
      -24, -25, -26, -27, -28, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39,
      -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51 };
  private int imageVersion = 0;
  
  private final Map subtreeMap = new HashMap();
  private final Map dirNodeMap = new HashMap();

  /* (non-Javadoc)
   * @see ImageLoader#canProcessVersion(int)
   */
  @Override
  public boolean canLoadVersion(int version) {
    for(int v : versions)
      if(v == version) return true;

    return false;
  }

  /* (non-Javadoc)
   * @see ImageLoader#processImage(java.io.DataInputStream, ImageVisitor, boolean)
   */
  @Override
  public void loadImage(DataInputStream in, ImageVisitor v,
      boolean skipBlocks) throws IOException {
    boolean done = false;
    try {
      v.start();
      v.visitEnclosingElement(ImageElement.FS_IMAGE);

      imageVersion = in.readInt();
      if( !canLoadVersion(imageVersion))
        throw new IOException("Cannot process fslayout version " + imageVersion);
      if (NameNodeLayoutVersion.supports(Feature.ADD_LAYOUT_FLAGS, imageVersion)) {
        LayoutFlags.read(in);
      }

      v.visit(ImageElement.IMAGE_VERSION, imageVersion);
      v.visit(ImageElement.NAMESPACE_ID, in.readInt());

      long numInodes = in.readLong();

      v.visit(ImageElement.GENERATION_STAMP, in.readLong());

      if (NameNodeLayoutVersion.supports(Feature.SEQUENTIAL_BLOCK_ID, imageVersion)) {
        v.visit(ImageElement.GENERATION_STAMP_V2, in.readLong());
        v.visit(ImageElement.GENERATION_STAMP_V1_LIMIT, in.readLong());
        v.visit(ImageElement.LAST_ALLOCATED_BLOCK_ID, in.readLong());
      }

      if (NameNodeLayoutVersion.supports(Feature.STORED_TXIDS, imageVersion)) {
        v.visit(ImageElement.TRANSACTION_ID, in.readLong());
      }
      
      if (NameNodeLayoutVersion.supports(Feature.ADD_INODE_ID, imageVersion)) {
        v.visit(ImageElement.LAST_INODE_ID, in.readLong());
      }
      
      boolean supportSnapshot = NameNodeLayoutVersion.supports(Feature.SNAPSHOT,
          imageVersion);
      if (supportSnapshot) {
        v.visit(ImageElement.SNAPSHOT_COUNTER, in.readInt());
        int numSnapshots = in.readInt();
        v.visit(ImageElement.NUM_SNAPSHOTS_TOTAL, numSnapshots);
        for (int i = 0; i < numSnapshots; i++) {
          processSnapshot(in, v);
        }
      }
      
      if (NameNodeLayoutVersion.supports(Feature.FSIMAGE_COMPRESSION, imageVersion)) {
        boolean isCompressed = in.readBoolean();
        v.visit(ImageElement.IS_COMPRESSED, String.valueOf(isCompressed));
        if (isCompressed) {
          String codecClassName = Text.readString(in);
          v.visit(ImageElement.COMPRESS_CODEC, codecClassName);
          CompressionCodecFactory codecFac = new CompressionCodecFactory(
              new Configuration());
          CompressionCodec codec = codecFac.getCodecByClassName(codecClassName);
          if (codec == null) {
            throw new IOException("Image compression codec not supported: "
                + codecClassName);
          }
          in = new DataInputStream(codec.createInputStream(in));
        }
      }
      processINodes(in, v, numInodes, skipBlocks, supportSnapshot);
      subtreeMap.clear();
      dirNodeMap.clear();

      processINodesUC(in, v, skipBlocks);

      if (NameNodeLayoutVersion.supports(Feature.DELEGATION_TOKEN, imageVersion)) {
        processDelegationTokens(in, v);
      }
      
      if (NameNodeLayoutVersion.supports(Feature.CACHING, imageVersion)) {
        processCacheManagerState(in, v);
      }
      v.leaveEnclosingElement(); // FSImage
      done = true;
    } finally {
      if (done) {
        v.finish();
      } else {
        v.finishAbnormally();
      }
    }
  }

  /**
   * Process CacheManager state from the fsimage.
   */
  private void processCacheManagerState(DataInputStream in, ImageVisitor v)
      throws IOException {
    v.visit(ImageElement.CACHE_NEXT_ENTRY_ID, in.readLong());
    final int numPools = in.readInt();
    for (int i=0; i 0) {
      numInodes -= processDirectory(in, v, skipBlocks);
    }
  }
  
  private int processDirectory(DataInputStream in, ImageVisitor v,
     boolean skipBlocks) throws IOException {
    String parentName = FSImageSerialization.readString(in);
    return processChildren(in, v, skipBlocks, parentName);
  }
  
  /**
   * Process image with local path name and snapshot support
   * 
   * @param in image stream
   * @param v visitor
   * @param skipBlocks skip blocks or not
   */
  private void processLocalNameINodesWithSnapshot(DataInputStream in,
      ImageVisitor v, boolean skipBlocks) throws IOException {
    // process root
    processINode(in, v, skipBlocks, "", false);
    processDirectoryWithSnapshot(in, v, skipBlocks);
  }
  
  /**
   * Process directories when snapshot is supported.
   */
  private void processDirectoryWithSnapshot(DataInputStream in, ImageVisitor v,
      boolean skipBlocks) throws IOException {
    // 1. load dir node id
    long inodeId = in.readLong();
    
    String dirName = dirNodeMap.remove(inodeId);
    Boolean visitedRef = subtreeMap.get(inodeId);
    if (visitedRef != null) {
      if (visitedRef.booleanValue()) { // the subtree has been visited
        return;
      } else { // first time to visit
        subtreeMap.put(inodeId, true);
      }
    } // else the dir is not linked by a RefNode, thus cannot be revisited
    
    // 2. load possible snapshots
    processSnapshots(in, v, dirName);
    // 3. load children nodes
    processChildren(in, v, skipBlocks, dirName);
    // 4. load possible directory diff list
    processDirectoryDiffList(in, v, dirName);
    // recursively process sub-directories
    final int numSubTree = in.readInt();
    for (int i = 0; i < numSubTree; i++) {
      processDirectoryWithSnapshot(in, v, skipBlocks);
    }
  }
  
  /**
   * Process snapshots of a snapshottable directory
   */
  private void processSnapshots(DataInputStream in, ImageVisitor v,
      String rootName) throws IOException {
    final int numSnapshots = in.readInt();
    if (numSnapshots >= 0) {
      v.visitEnclosingElement(ImageElement.SNAPSHOTS,
          ImageElement.NUM_SNAPSHOTS, numSnapshots);
      for (int i = 0; i < numSnapshots; i++) {
        // process snapshot
        v.visitEnclosingElement(ImageElement.SNAPSHOT);
        v.visit(ImageElement.SNAPSHOT_ID, in.readInt());
        v.leaveEnclosingElement();
      }
      v.visit(ImageElement.SNAPSHOT_QUOTA, in.readInt());
      v.leaveEnclosingElement();
    }
  }
  
  private void processSnapshot(DataInputStream in, ImageVisitor v)
      throws IOException {
    v.visitEnclosingElement(ImageElement.SNAPSHOT);
    v.visit(ImageElement.SNAPSHOT_ID, in.readInt());
    // process root of snapshot
    v.visitEnclosingElement(ImageElement.SNAPSHOT_ROOT);
    processINode(in, v, true, "", false);
    v.leaveEnclosingElement();
    v.leaveEnclosingElement();
  }
  
  private void processDirectoryDiffList(DataInputStream in, ImageVisitor v,
      String currentINodeName) throws IOException {
    final int numDirDiff = in.readInt();
    if (numDirDiff >= 0) {
      v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFFS,
          ImageElement.NUM_SNAPSHOT_DIR_DIFF, numDirDiff);
      for (int i = 0; i < numDirDiff; i++) {
        // process directory diffs in reverse chronological oder
        processDirectoryDiff(in, v, currentINodeName); 
      }
      v.leaveEnclosingElement();
    }
  }
  
  private void processDirectoryDiff(DataInputStream in, ImageVisitor v,
      String currentINodeName) throws IOException {
    v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFF);
    int snapshotId = in.readInt();
    v.visit(ImageElement.SNAPSHOT_DIFF_SNAPSHOTID, snapshotId);
    v.visit(ImageElement.SNAPSHOT_DIR_DIFF_CHILDREN_SIZE, in.readInt());
    
    // process snapshotINode
    boolean useRoot = in.readBoolean();
    if (!useRoot) {
      if (in.readBoolean()) {
        v.visitEnclosingElement(ImageElement.SNAPSHOT_INODE_DIRECTORY_ATTRIBUTES);
        if (NameNodeLayoutVersion.supports(Feature.OPTIMIZE_SNAPSHOT_INODES, imageVersion)) {
          processINodeDirectoryAttributes(in, v, currentINodeName);
        } else {
          processINode(in, v, true, currentINodeName, true);
        }
        v.leaveEnclosingElement();
      }
    }
    
    // process createdList
    int createdSize = in.readInt();
    v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFF_CREATEDLIST,
        ImageElement.SNAPSHOT_DIR_DIFF_CREATEDLIST_SIZE, createdSize);
    for (int i = 0; i < createdSize; i++) {
      String createdNode = FSImageSerialization.readString(in);
      v.visit(ImageElement.SNAPSHOT_DIR_DIFF_CREATED_INODE, createdNode);
    }
    v.leaveEnclosingElement();
    
    // process deletedList
    int deletedSize = in.readInt();
    v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFF_DELETEDLIST,
        ImageElement.SNAPSHOT_DIR_DIFF_DELETEDLIST_SIZE, deletedSize);
    for (int i = 0; i < deletedSize; i++) {
      v.visitEnclosingElement(ImageElement.SNAPSHOT_DIR_DIFF_DELETED_INODE);
      processINode(in, v, false, currentINodeName, true);
      v.leaveEnclosingElement();
    }
    v.leaveEnclosingElement();
    v.leaveEnclosingElement();
  }

  private void processINodeDirectoryAttributes(DataInputStream in, ImageVisitor v,
      String parentName) throws IOException {
    final String pathName = readINodePath(in, parentName);
    v.visit(ImageElement.INODE_PATH, pathName);
    processPermission(in, v);
    v.visit(ImageElement.MODIFICATION_TIME, formatDate(in.readLong()));

    v.visit(ImageElement.NS_QUOTA, in.readLong());
    v.visit(ImageElement.DS_QUOTA, in.readLong());
  }

  /** Process children under a directory */
  private int processChildren(DataInputStream in, ImageVisitor v,
      boolean skipBlocks, String parentName) throws IOException {
    int numChildren = in.readInt();
    for (int i = 0; i < numChildren; i++) {
      processINode(in, v, skipBlocks, parentName, false);
    }
    return numChildren;
  }
  
  /**
   * Process image with full path name
   * 
   * @param in image stream
   * @param v visitor
   * @param numInodes number of indoes to read
   * @param skipBlocks skip blocks or not
   * @throws IOException if there is any error occurs
   */
  private void processFullNameINodes(DataInputStream in, ImageVisitor v,
      long numInodes, boolean skipBlocks) throws IOException {
    for(long i = 0; i < numInodes; i++) {
      processINode(in, v, skipBlocks, null, false);
    }
  }
 
  private String readINodePath(DataInputStream in, String parentName)
      throws IOException {
    String pathName = FSImageSerialization.readString(in);
    if (parentName != null) {  // local name
      pathName = "/" + pathName;
      if (!"/".equals(parentName)) { // children of non-root directory
        pathName = parentName + pathName;
      }
    }
    return pathName;
  }

  /**
   * Process an INode
   * 
   * @param in image stream
   * @param v visitor
   * @param skipBlocks skip blocks or not
   * @param parentName the name of its parent node
   * @param isSnapshotCopy whether or not the inode is a snapshot copy
   * @throws IOException
   */
  private void processINode(DataInputStream in, ImageVisitor v,
      boolean skipBlocks, String parentName, boolean isSnapshotCopy)
      throws IOException {
    boolean supportSnapshot = 
        NameNodeLayoutVersion.supports(Feature.SNAPSHOT, imageVersion);
    boolean supportInodeId = 
        NameNodeLayoutVersion.supports(Feature.ADD_INODE_ID, imageVersion);
    
    v.visitEnclosingElement(ImageElement.INODE);
    final String pathName = readINodePath(in, parentName);
    v.visit(ImageElement.INODE_PATH, pathName);

    long inodeId = HdfsConstants.GRANDFATHER_INODE_ID;
    if (supportInodeId) {
      inodeId = in.readLong();
      v.visit(ImageElement.INODE_ID, inodeId);
    }
    v.visit(ImageElement.REPLICATION, in.readShort());
    v.visit(ImageElement.MODIFICATION_TIME, formatDate(in.readLong()));
    if(NameNodeLayoutVersion.supports(Feature.FILE_ACCESS_TIME, imageVersion))
      v.visit(ImageElement.ACCESS_TIME, formatDate(in.readLong()));
    v.visit(ImageElement.BLOCK_SIZE, in.readLong());
    int numBlocks = in.readInt();

    processBlocks(in, v, numBlocks, skipBlocks);
    
    if (numBlocks >= 0) { // File
      if (supportSnapshot) {
        // make sure subtreeMap only contains entry for directory
        subtreeMap.remove(inodeId);
        // process file diffs
        processFileDiffList(in, v, parentName);
        if (isSnapshotCopy) {
          boolean underConstruction = in.readBoolean();
          if (underConstruction) {
            v.visit(ImageElement.CLIENT_NAME,
                FSImageSerialization.readString(in));
            v.visit(ImageElement.CLIENT_MACHINE,
                FSImageSerialization.readString(in));
          }
        }
      }
      processPermission(in, v);
    } else if (numBlocks == -1) { // Directory
      if (supportSnapshot && supportInodeId) {
        dirNodeMap.put(inodeId, pathName);
      }

      v.visit(ImageElement.NS_QUOTA, in.readLong());
      if (NameNodeLayoutVersion.supports(Feature.DISKSPACE_QUOTA,
          imageVersion)) {
        v.visit(ImageElement.DS_QUOTA, in.readLong());
      }

      if (supportSnapshot) {
        boolean snapshottable = in.readBoolean();
        if (!snapshottable) {
          boolean withSnapshot = in.readBoolean();
          v.visit(ImageElement.IS_WITHSNAPSHOT_DIR, Boolean.toString(withSnapshot));
        } else {
          v.visit(ImageElement.IS_SNAPSHOTTABLE_DIR, Boolean.toString(snapshottable));
        }
      }
      processPermission(in, v);
    } else if (numBlocks == -2) {
      v.visit(ImageElement.SYMLINK, Text.readString(in));
      processPermission(in, v);
    } else if (numBlocks == -3) { // reference node
      final boolean isWithName = in.readBoolean();
      int snapshotId = in.readInt();
      if (isWithName) {
        v.visit(ImageElement.SNAPSHOT_LAST_SNAPSHOT_ID, snapshotId);
      } else {
        v.visit(ImageElement.SNAPSHOT_DST_SNAPSHOT_ID, snapshotId);
      }
      
      final boolean firstReferred = in.readBoolean();
      if (firstReferred) {
        // if a subtree is linked by multiple "parents", the corresponding dir
        // must be referred by a reference node. we put the reference node into
        // the subtreeMap here and let its value be false. when we later visit
        // the subtree for the first time, we change the value to true.
        subtreeMap.put(inodeId, false);
        v.visitEnclosingElement(ImageElement.SNAPSHOT_REF_INODE);
        processINode(in, v, skipBlocks, parentName, isSnapshotCopy);
        v.leaveEnclosingElement();  // referred inode    
      } else {
        v.visit(ImageElement.SNAPSHOT_REF_INODE_ID, in.readLong());
      }
    }

    v.leaveEnclosingElement(); // INode
  }

  private void processINodeFileAttributes(DataInputStream in, ImageVisitor v,
      String parentName) throws IOException {
    final String pathName = readINodePath(in, parentName);
    v.visit(ImageElement.INODE_PATH, pathName);
    processPermission(in, v);
    v.visit(ImageElement.MODIFICATION_TIME, formatDate(in.readLong()));
    if(NameNodeLayoutVersion.supports(Feature.FILE_ACCESS_TIME, imageVersion)) {
      v.visit(ImageElement.ACCESS_TIME, formatDate(in.readLong()));
    }

    v.visit(ImageElement.REPLICATION, in.readShort());
    v.visit(ImageElement.BLOCK_SIZE, in.readLong());
  }
  
  private void processFileDiffList(DataInputStream in, ImageVisitor v,
      String currentINodeName) throws IOException {
    final int size = in.readInt();
    if (size >= 0) {
      v.visitEnclosingElement(ImageElement.SNAPSHOT_FILE_DIFFS,
          ImageElement.NUM_SNAPSHOT_FILE_DIFF, size);
      for (int i = 0; i < size; i++) {
        processFileDiff(in, v, currentINodeName);
      }
      v.leaveEnclosingElement();
    }
  }
  
  private void processFileDiff(DataInputStream in, ImageVisitor v,
      String currentINodeName) throws IOException {
    int snapshotId = in.readInt();
    v.visitEnclosingElement(ImageElement.SNAPSHOT_FILE_DIFF,
        ImageElement.SNAPSHOT_DIFF_SNAPSHOTID, snapshotId);
    v.visit(ImageElement.SNAPSHOT_FILE_SIZE, in.readLong());
    if (in.readBoolean()) {
      v.visitEnclosingElement(ImageElement.SNAPSHOT_INODE_FILE_ATTRIBUTES);
      if (NameNodeLayoutVersion.supports(Feature.OPTIMIZE_SNAPSHOT_INODES, imageVersion)) {
        processINodeFileAttributes(in, v, currentINodeName);
      } else {
        processINode(in, v, true, currentINodeName, true);
      }
      v.leaveEnclosingElement();
    }
    v.leaveEnclosingElement();
  }
  
  /**
   * Helper method to format dates during processing.
   * @param date Date as read from image file
   * @return String version of date format
   */
  private String formatDate(long date) {
    return dateFormat.format(new Date(date));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy