001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hdfs.server.blockmanagement;
019
020import org.apache.hadoop.classification.InterfaceAudience;
021import org.apache.hadoop.hdfs.protocol.Block;
022import org.apache.hadoop.hdfs.server.namenode.NameNode;
023import org.apache.hadoop.ipc.Server;
024
025import java.util.*;
026
027/**
028 * Stores information about all corrupt blocks in the File System.
029 * A Block is considered corrupt only if all of its replicas are
030 * corrupt. While reporting replicas of a Block, we hide any corrupt
031 * copies. These copies are removed once Block is found to have 
032 * expected number of good replicas.
033 * Mapping: Block -> TreeSet<DatanodeDescriptor> 
034 */
035
036@InterfaceAudience.Private
037public class CorruptReplicasMap{
038
039  /** The corruption reason code */
040  public static enum Reason {
041    NONE,                // not specified.
042    ANY,                 // wildcard reason
043    GENSTAMP_MISMATCH,   // mismatch in generation stamps
044    SIZE_MISMATCH,       // mismatch in sizes
045    INVALID_STATE,       // invalid state
046    CORRUPTION_REPORTED  // client or datanode reported the corruption
047  }
048
049  private final SortedMap<Block, Map<DatanodeDescriptor, Reason>> corruptReplicasMap =
050    new TreeMap<Block, Map<DatanodeDescriptor, Reason>>();
051
052  /**
053   * Mark the block belonging to datanode as corrupt.
054   *
055   * @param blk Block to be added to CorruptReplicasMap
056   * @param dn DatanodeDescriptor which holds the corrupt replica
057   * @param reason a textual reason (for logging purposes)
058   * @param reasonCode the enum representation of the reason
059   */
060  void addToCorruptReplicasMap(Block blk, DatanodeDescriptor dn,
061      String reason, Reason reasonCode) {
062    Map <DatanodeDescriptor, Reason> nodes = corruptReplicasMap.get(blk);
063    if (nodes == null) {
064      nodes = new HashMap<DatanodeDescriptor, Reason>();
065      corruptReplicasMap.put(blk, nodes);
066    }
067    
068    String reasonText;
069    if (reason != null) {
070      reasonText = " because " + reason;
071    } else {
072      reasonText = "";
073    }
074    
075    if (!nodes.keySet().contains(dn)) {
076      NameNode.blockStateChangeLog.info("BLOCK NameSystem.addToCorruptReplicasMap: "+
077                                   blk.getBlockName() +
078                                   " added as corrupt on " + dn +
079                                   " by " + Server.getRemoteIp() +
080                                   reasonText);
081    } else {
082      NameNode.blockStateChangeLog.info("BLOCK NameSystem.addToCorruptReplicasMap: "+
083                                   "duplicate requested for " + 
084                                   blk.getBlockName() + " to add as corrupt " +
085                                   "on " + dn +
086                                   " by " + Server.getRemoteIp() +
087                                   reasonText);
088    }
089    // Add the node or update the reason.
090    nodes.put(dn, reasonCode);
091  }
092
093  /**
094   * Remove Block from CorruptBlocksMap
095   *
096   * @param blk Block to be removed
097   */
098  void removeFromCorruptReplicasMap(Block blk) {
099    if (corruptReplicasMap != null) {
100      corruptReplicasMap.remove(blk);
101    }
102  }
103
104  /**
105   * Remove the block at the given datanode from CorruptBlockMap
106   * @param blk block to be removed
107   * @param datanode datanode where the block is located
108   * @return true if the removal is successful; 
109             false if the replica is not in the map
110   */ 
111  boolean removeFromCorruptReplicasMap(Block blk, DatanodeDescriptor datanode) {
112    return removeFromCorruptReplicasMap(blk, datanode, Reason.ANY);
113  }
114
115  boolean removeFromCorruptReplicasMap(Block blk, DatanodeDescriptor datanode,
116      Reason reason) {
117    Map <DatanodeDescriptor, Reason> datanodes = corruptReplicasMap.get(blk);
118    if (datanodes==null)
119      return false;
120
121    // if reasons can be compared but don't match, return false.
122    Reason storedReason = datanodes.get(datanode);
123    if (reason != Reason.ANY && storedReason != null &&
124        reason != storedReason) {
125      return false;
126    }
127
128    if (datanodes.remove(datanode) != null) { // remove the replicas
129      if (datanodes.isEmpty()) {
130        // remove the block if there is no more corrupted replicas
131        corruptReplicasMap.remove(blk);
132      }
133      return true;
134    }
135    return false;
136  }
137    
138
139  /**
140   * Get Nodes which have corrupt replicas of Block
141   * 
142   * @param blk Block for which nodes are requested
143   * @return collection of nodes. Null if does not exists
144   */
145  Collection<DatanodeDescriptor> getNodes(Block blk) {
146    Map <DatanodeDescriptor, Reason> nodes = corruptReplicasMap.get(blk);
147    if (nodes == null)
148      return null;
149    return nodes.keySet();
150  }
151
152  /**
153   * Check if replica belonging to Datanode is corrupt
154   *
155   * @param blk Block to check
156   * @param node DatanodeDescriptor which holds the replica
157   * @return true if replica is corrupt, false if does not exists in this map
158   */
159  boolean isReplicaCorrupt(Block blk, DatanodeDescriptor node) {
160    Collection<DatanodeDescriptor> nodes = getNodes(blk);
161    return ((nodes != null) && (nodes.contains(node)));
162  }
163
164  int numCorruptReplicas(Block blk) {
165    Collection<DatanodeDescriptor> nodes = getNodes(blk);
166    return (nodes == null) ? 0 : nodes.size();
167  }
168  
169  int size() {
170    return corruptReplicasMap.size();
171  }
172
173  /**
174   * Return a range of corrupt replica block ids. Up to numExpectedBlocks 
175   * blocks starting at the next block after startingBlockId are returned
176   * (fewer if numExpectedBlocks blocks are unavailable). If startingBlockId 
177   * is null, up to numExpectedBlocks blocks are returned from the beginning.
178   * If startingBlockId cannot be found, null is returned.
179   *
180   * @param numExpectedBlocks Number of block ids to return.
181   *  0 <= numExpectedBlocks <= 100
182   * @param startingBlockId Block id from which to start. If null, start at
183   *  beginning.
184   * @return Up to numExpectedBlocks blocks from startingBlockId if it exists
185   *
186   */
187  long[] getCorruptReplicaBlockIds(int numExpectedBlocks,
188                                   Long startingBlockId) {
189    if (numExpectedBlocks < 0 || numExpectedBlocks > 100) {
190      return null;
191    }
192    
193    Iterator<Block> blockIt = corruptReplicasMap.keySet().iterator();
194    
195    // if the starting block id was specified, iterate over keys until
196    // we find the matching block. If we find a matching block, break
197    // to leave the iterator on the next block after the specified block. 
198    if (startingBlockId != null) {
199      boolean isBlockFound = false;
200      while (blockIt.hasNext()) {
201        Block b = blockIt.next();
202        if (b.getBlockId() == startingBlockId) {
203          isBlockFound = true;
204          break; 
205        }
206      }
207      
208      if (!isBlockFound) {
209        return null;
210      }
211    }
212
213    ArrayList<Long> corruptReplicaBlockIds = new ArrayList<Long>();
214
215    // append up to numExpectedBlocks blockIds to our list
216    for(int i=0; i<numExpectedBlocks && blockIt.hasNext(); i++) {
217      corruptReplicaBlockIds.add(blockIt.next().getBlockId());
218    }
219    
220    long[] ret = new long[corruptReplicaBlockIds.size()];
221    for(int i=0; i<ret.length; i++) {
222      ret[i] = corruptReplicaBlockIds.get(i);
223    }
224    
225    return ret;
226  }  
227}