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 java.util.ArrayList;
021import java.util.BitSet;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.Iterator;
027import java.util.LinkedList;
028import java.util.List;
029import java.util.Map;
030import java.util.Queue;
031import java.util.Set;
032
033import com.google.common.annotations.VisibleForTesting;
034
035import com.google.common.collect.ImmutableList;
036import org.apache.commons.logging.Log;
037import org.apache.commons.logging.LogFactory;
038import org.apache.hadoop.classification.InterfaceAudience;
039import org.apache.hadoop.classification.InterfaceStability;
040import org.apache.hadoop.fs.StorageType;
041import org.apache.hadoop.hdfs.protocol.Block;
042import org.apache.hadoop.hdfs.protocol.DatanodeID;
043import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
044import org.apache.hadoop.hdfs.server.namenode.CachedBlock;
045import org.apache.hadoop.hdfs.server.protocol.BlockReportContext;
046import org.apache.hadoop.hdfs.server.protocol.DatanodeStorage;
047import org.apache.hadoop.hdfs.server.protocol.DatanodeStorage.State;
048import org.apache.hadoop.hdfs.server.protocol.StorageReport;
049import org.apache.hadoop.hdfs.server.protocol.VolumeFailureSummary;
050import org.apache.hadoop.hdfs.util.EnumCounters;
051import org.apache.hadoop.hdfs.util.LightWeightHashSet;
052import org.apache.hadoop.util.IntrusiveCollection;
053import org.apache.hadoop.util.Time;
054
055/**
056 * This class extends the DatanodeInfo class with ephemeral information (eg
057 * health, capacity, what blocks are associated with the Datanode) that is
058 * private to the Namenode, ie this class is not exposed to clients.
059 */
060@InterfaceAudience.Private
061@InterfaceStability.Evolving
062public class DatanodeDescriptor extends DatanodeInfo {
063  public static final Log LOG = LogFactory.getLog(DatanodeDescriptor.class);
064  public static final DatanodeDescriptor[] EMPTY_ARRAY = {};
065
066  // Stores status of decommissioning.
067  // If node is not decommissioning, do not use this object for anything.
068  public final DecommissioningStatus decommissioningStatus = new DecommissioningStatus();
069
070  private long curBlockReportId = 0;
071
072  private BitSet curBlockReportRpcsSeen = null;
073
074  public int updateBlockReportContext(BlockReportContext context) {
075    if (curBlockReportId != context.getReportId()) {
076      curBlockReportId = context.getReportId();
077      curBlockReportRpcsSeen = new BitSet(context.getTotalRpcs());
078    }
079    curBlockReportRpcsSeen.set(context.getCurRpc());
080    return curBlockReportRpcsSeen.cardinality();
081  }
082
083  public void clearBlockReportContext() {
084    curBlockReportId = 0;
085    curBlockReportRpcsSeen = null;
086  }
087
088  /** Block and targets pair */
089  @InterfaceAudience.Private
090  @InterfaceStability.Evolving
091  public static class BlockTargetPair {
092    public final Block block;
093    public final DatanodeStorageInfo[] targets;    
094
095    BlockTargetPair(Block block, DatanodeStorageInfo[] targets) {
096      this.block = block;
097      this.targets = targets;
098    }
099  }
100
101  /** A BlockTargetPair queue. */
102  private static class BlockQueue<E> {
103    private final Queue<E> blockq = new LinkedList<E>();
104
105    /** Size of the queue */
106    synchronized int size() {return blockq.size();}
107
108    /** Enqueue */
109    synchronized boolean offer(E e) { 
110      return blockq.offer(e);
111    }
112
113    /** Dequeue */
114    synchronized List<E> poll(int numBlocks) {
115      if (numBlocks <= 0 || blockq.isEmpty()) {
116        return null;
117      }
118
119      List<E> results = new ArrayList<E>();
120      for(; !blockq.isEmpty() && numBlocks > 0; numBlocks--) {
121        results.add(blockq.poll());
122      }
123      return results;
124    }
125
126    /**
127     * Returns <tt>true</tt> if the queue contains the specified element.
128     */
129    boolean contains(E e) {
130      return blockq.contains(e);
131    }
132
133    synchronized void clear() {
134      blockq.clear();
135    }
136  }
137
138  private final Map<String, DatanodeStorageInfo> storageMap = 
139      new HashMap<String, DatanodeStorageInfo>();
140
141  /**
142   * A list of CachedBlock objects on this datanode.
143   */
144  public static class CachedBlocksList extends IntrusiveCollection<CachedBlock> {
145    public enum Type {
146      PENDING_CACHED,
147      CACHED,
148      PENDING_UNCACHED
149    }
150
151    private final DatanodeDescriptor datanode;
152
153    private final Type type;
154
155    CachedBlocksList(DatanodeDescriptor datanode, Type type) {
156      this.datanode = datanode;
157      this.type = type;
158    }
159
160    public DatanodeDescriptor getDatanode() {
161      return datanode;
162    }
163
164    public Type getType() {
165      return type;
166    }
167  }
168
169  /**
170   * The blocks which we want to cache on this DataNode.
171   */
172  private final CachedBlocksList pendingCached = 
173      new CachedBlocksList(this, CachedBlocksList.Type.PENDING_CACHED);
174
175  /**
176   * The blocks which we know are cached on this datanode.
177   * This list is updated by periodic cache reports.
178   */
179  private final CachedBlocksList cached = 
180      new CachedBlocksList(this, CachedBlocksList.Type.CACHED);
181
182  /**
183   * The blocks which we want to uncache on this DataNode.
184   */
185  private final CachedBlocksList pendingUncached = 
186      new CachedBlocksList(this, CachedBlocksList.Type.PENDING_UNCACHED);
187
188  public CachedBlocksList getPendingCached() {
189    return pendingCached;
190  }
191
192  public CachedBlocksList getCached() {
193    return cached;
194  }
195
196  public CachedBlocksList getPendingUncached() {
197    return pendingUncached;
198  }
199
200  /**
201   * The time when the last batch of caching directives was sent, in
202   * monotonic milliseconds.
203   */
204  private long lastCachingDirectiveSentTimeMs;
205
206  // isAlive == heartbeats.contains(this)
207  // This is an optimization, because contains takes O(n) time on Arraylist
208  public boolean isAlive = false;
209  public boolean needKeyUpdate = false;
210
211  private boolean forceRegistration = false;
212
213  // A system administrator can tune the balancer bandwidth parameter
214  // (dfs.balance.bandwidthPerSec) dynamically by calling
215  // "dfsadmin -setBalanacerBandwidth <newbandwidth>", at which point the
216  // following 'bandwidth' variable gets updated with the new value for each
217  // node. Once the heartbeat command is issued to update the value on the
218  // specified datanode, this value will be set back to 0.
219  private long bandwidth;
220
221  /** A queue of blocks to be replicated by this datanode */
222  private final BlockQueue<BlockTargetPair> replicateBlocks = new BlockQueue<BlockTargetPair>();
223  /** A queue of blocks to be recovered by this datanode */
224  private final BlockQueue<BlockInfoContiguousUnderConstruction> recoverBlocks =
225                                new BlockQueue<BlockInfoContiguousUnderConstruction>();
226  /** A set of blocks to be invalidated by this datanode */
227  private final LightWeightHashSet<Block> invalidateBlocks = new LightWeightHashSet<Block>();
228
229  /* Variables for maintaining number of blocks scheduled to be written to
230   * this storage. This count is approximate and might be slightly bigger
231   * in case of errors (e.g. datanode does not report if an error occurs
232   * while writing the block).
233   */
234  private EnumCounters<StorageType> currApproxBlocksScheduled
235      = new EnumCounters<StorageType>(StorageType.class);
236  private EnumCounters<StorageType> prevApproxBlocksScheduled
237      = new EnumCounters<StorageType>(StorageType.class);
238  private long lastBlocksScheduledRollTime = 0;
239  private static final int BLOCKS_SCHEDULED_ROLL_INTERVAL = 600*1000; //10min
240  private int volumeFailures = 0;
241  private VolumeFailureSummary volumeFailureSummary = null;
242  
243  /** 
244   * When set to true, the node is not in include list and is not allowed
245   * to communicate with the namenode
246   */
247  private boolean disallowed = false;
248
249  // The number of replication work pending before targets are determined
250  private int PendingReplicationWithoutTargets = 0;
251
252  // HB processing can use it to tell if it is the first HB since DN restarted
253  private boolean heartbeatedSinceRegistration = false;
254
255  /**
256   * DatanodeDescriptor constructor
257   * @param nodeID id of the data node
258   */
259  public DatanodeDescriptor(DatanodeID nodeID) {
260    super(nodeID);
261    updateHeartbeatState(StorageReport.EMPTY_ARRAY, 0L, 0L, 0, 0, null);
262  }
263
264  /**
265   * DatanodeDescriptor constructor
266   * @param nodeID id of the data node
267   * @param networkLocation location of the data node in network
268   */
269  public DatanodeDescriptor(DatanodeID nodeID, 
270                            String networkLocation) {
271    super(nodeID, networkLocation);
272    updateHeartbeatState(StorageReport.EMPTY_ARRAY, 0L, 0L, 0, 0, null);
273  }
274
275  @VisibleForTesting
276  public DatanodeStorageInfo getStorageInfo(String storageID) {
277    synchronized (storageMap) {
278      return storageMap.get(storageID);
279    }
280  }
281  @VisibleForTesting
282  public DatanodeStorageInfo[] getStorageInfos() {
283    synchronized (storageMap) {
284      final Collection<DatanodeStorageInfo> storages = storageMap.values();
285      return storages.toArray(new DatanodeStorageInfo[storages.size()]);
286    }
287  }
288
289  public StorageReport[] getStorageReports() {
290    final DatanodeStorageInfo[] infos = getStorageInfos();
291    final StorageReport[] reports = new StorageReport[infos.length];
292    for(int i = 0; i < infos.length; i++) {
293      reports[i] = infos[i].toStorageReport();
294    }
295    return reports;
296  }
297
298  boolean hasStaleStorages() {
299    synchronized (storageMap) {
300      for (DatanodeStorageInfo storage : storageMap.values()) {
301        if (storage.areBlockContentsStale()) {
302          return true;
303        }
304      }
305      return false;
306    }
307  }
308
309  static final private List<DatanodeStorageInfo> EMPTY_STORAGE_INFO_LIST =
310      ImmutableList.of();
311
312  List<DatanodeStorageInfo> removeZombieStorages() {
313    List<DatanodeStorageInfo> zombies = null;
314    synchronized (storageMap) {
315      Iterator<Map.Entry<String, DatanodeStorageInfo>> iter =
316          storageMap.entrySet().iterator();
317      while (iter.hasNext()) {
318        Map.Entry<String, DatanodeStorageInfo> entry = iter.next();
319        DatanodeStorageInfo storageInfo = entry.getValue();
320        if (storageInfo.getLastBlockReportId() != curBlockReportId) {
321          LOG.info(storageInfo.getStorageID() + " had lastBlockReportId 0x" +
322              Long.toHexString(storageInfo.getLastBlockReportId()) +
323              ", but curBlockReportId = 0x" +
324              Long.toHexString(curBlockReportId));
325          iter.remove();
326          if (zombies == null) {
327            zombies = new LinkedList<DatanodeStorageInfo>();
328          }
329          zombies.add(storageInfo);
330        }
331        storageInfo.setLastBlockReportId(0);
332      }
333    }
334    return zombies == null ? EMPTY_STORAGE_INFO_LIST : zombies;
335  }
336
337  /**
338   * Remove block from the list of blocks belonging to the data-node. Remove
339   * data-node from the block.
340   */
341  boolean removeBlock(BlockInfoContiguous b) {
342    final DatanodeStorageInfo s = b.findStorageInfo(this);
343    // if block exists on this datanode
344    if (s != null) {
345      return s.removeBlock(b);
346    }
347    return false;
348  }
349  
350  /**
351   * Remove block from the list of blocks belonging to the data-node. Remove
352   * data-node from the block.
353   */
354  boolean removeBlock(String storageID, BlockInfoContiguous b) {
355    DatanodeStorageInfo s = getStorageInfo(storageID);
356    if (s != null) {
357      return s.removeBlock(b);
358    }
359    return false;
360  }
361
362  public void resetBlocks() {
363    setCapacity(0);
364    setRemaining(0);
365    setBlockPoolUsed(0);
366    setDfsUsed(0);
367    setXceiverCount(0);
368    this.invalidateBlocks.clear();
369    this.volumeFailures = 0;
370    // pendingCached, cached, and pendingUncached are protected by the
371    // FSN lock.
372    this.pendingCached.clear();
373    this.cached.clear();
374    this.pendingUncached.clear();
375  }
376  
377  public void clearBlockQueues() {
378    synchronized (invalidateBlocks) {
379      this.invalidateBlocks.clear();
380      this.recoverBlocks.clear();
381      this.replicateBlocks.clear();
382    }
383    // pendingCached, cached, and pendingUncached are protected by the
384    // FSN lock.
385    this.pendingCached.clear();
386    this.cached.clear();
387    this.pendingUncached.clear();
388  }
389
390  public int numBlocks() {
391    int blocks = 0;
392    for (DatanodeStorageInfo entry : getStorageInfos()) {
393      blocks += entry.numBlocks();
394    }
395    return blocks;
396  }
397
398  /**
399   * Updates stats from datanode heartbeat.
400   */
401  public void updateHeartbeat(StorageReport[] reports, long cacheCapacity,
402      long cacheUsed, int xceiverCount, int volFailures,
403      VolumeFailureSummary volumeFailureSummary) {
404    updateHeartbeatState(reports, cacheCapacity, cacheUsed, xceiverCount,
405        volFailures, volumeFailureSummary);
406    heartbeatedSinceRegistration = true;
407  }
408
409  /**
410   * process datanode heartbeat or stats initialization.
411   */
412  public void updateHeartbeatState(StorageReport[] reports, long cacheCapacity,
413      long cacheUsed, int xceiverCount, int volFailures,
414      VolumeFailureSummary volumeFailureSummary) {
415    long totalCapacity = 0;
416    long totalRemaining = 0;
417    long totalBlockPoolUsed = 0;
418    long totalDfsUsed = 0;
419    Set<DatanodeStorageInfo> failedStorageInfos = null;
420
421    // Decide if we should check for any missing StorageReport and mark it as
422    // failed. There are different scenarios.
423    // 1. When DN is running, a storage failed. Given the current DN
424    //    implementation doesn't add recovered storage back to its storage list
425    //    until DN restart, we can assume volFailures won't decrease
426    //    during the current DN registration session.
427    //    When volumeFailures == this.volumeFailures, it implies there is no
428    //    state change. No need to check for failed storage. This is an
429    //    optimization.  Recent versions of the DataNode report a
430    //    VolumeFailureSummary containing the date/time of the last volume
431    //    failure.  If that's available, then we check that instead for greater
432    //    accuracy.
433    // 2. After DN restarts, volFailures might not increase and it is possible
434    //    we still have new failed storage. For example, admins reduce
435    //    available storages in configuration. Another corner case
436    //    is the failed volumes might change after restart; a) there
437    //    is one good storage A, one restored good storage B, so there is
438    //    one element in storageReports and that is A. b) A failed. c) Before
439    //    DN sends HB to NN to indicate A has failed, DN restarts. d) After DN
440    //    restarts, storageReports has one element which is B.
441    final boolean checkFailedStorages;
442    if (volumeFailureSummary != null && this.volumeFailureSummary != null) {
443      checkFailedStorages = volumeFailureSummary.getLastVolumeFailureDate() >
444          this.volumeFailureSummary.getLastVolumeFailureDate();
445    } else {
446      checkFailedStorages = (volFailures > this.volumeFailures) ||
447          !heartbeatedSinceRegistration;
448    }
449
450    if (checkFailedStorages) {
451      LOG.info("Number of failed storage changes from "
452          + this.volumeFailures + " to " + volFailures);
453      failedStorageInfos = new HashSet<DatanodeStorageInfo>(
454          storageMap.values());
455    }
456
457    setCacheCapacity(cacheCapacity);
458    setCacheUsed(cacheUsed);
459    setXceiverCount(xceiverCount);
460    setLastUpdate(Time.now());
461    setLastUpdateMonotonic(Time.monotonicNow());
462    this.volumeFailures = volFailures;
463    this.volumeFailureSummary = volumeFailureSummary;
464    for (StorageReport report : reports) {
465      DatanodeStorageInfo storage = updateStorage(report.getStorage());
466      if (checkFailedStorages) {
467        failedStorageInfos.remove(storage);
468      }
469
470      storage.receivedHeartbeat(report);
471      totalCapacity += report.getCapacity();
472      totalRemaining += report.getRemaining();
473      totalBlockPoolUsed += report.getBlockPoolUsed();
474      totalDfsUsed += report.getDfsUsed();
475    }
476    rollBlocksScheduled(getLastUpdateMonotonic());
477
478    // Update total metrics for the node.
479    setCapacity(totalCapacity);
480    setRemaining(totalRemaining);
481    setBlockPoolUsed(totalBlockPoolUsed);
482    setDfsUsed(totalDfsUsed);
483    if (checkFailedStorages) {
484      updateFailedStorage(failedStorageInfos);
485    }
486
487    if (storageMap.size() != reports.length) {
488      pruneStorageMap(reports);
489    }
490  }
491
492  /**
493   * Remove stale storages from storageMap. We must not remove any storages
494   * as long as they have associated block replicas.
495   */
496  private void pruneStorageMap(final StorageReport[] reports) {
497    if (LOG.isDebugEnabled()) {
498      LOG.debug("Number of storages reported in heartbeat=" + reports.length +
499                    "; Number of storages in storageMap=" + storageMap.size());
500    }
501
502    HashMap<String, DatanodeStorageInfo> excessStorages;
503
504    synchronized (storageMap) {
505      // Init excessStorages with all known storages.
506      excessStorages = new HashMap<String, DatanodeStorageInfo>(storageMap);
507
508      // Remove storages that the DN reported in the heartbeat.
509      for (final StorageReport report : reports) {
510        excessStorages.remove(report.getStorage().getStorageID());
511      }
512
513      // For each remaining storage, remove it if there are no associated
514      // blocks.
515      for (final DatanodeStorageInfo storageInfo : excessStorages.values()) {
516        if (storageInfo.numBlocks() == 0) {
517          storageMap.remove(storageInfo.getStorageID());
518          LOG.info("Removed storage " + storageInfo + " from DataNode" + this);
519        } else if (LOG.isDebugEnabled()) {
520          // This can occur until all block reports are received.
521          LOG.debug("Deferring removal of stale storage " + storageInfo +
522                        " with " + storageInfo.numBlocks() + " blocks");
523        }
524      }
525    }
526  }
527
528  private void updateFailedStorage(
529      Set<DatanodeStorageInfo> failedStorageInfos) {
530    for (DatanodeStorageInfo storageInfo : failedStorageInfos) {
531      if (storageInfo.getState() != DatanodeStorage.State.FAILED) {
532        LOG.info(storageInfo + " failed.");
533        storageInfo.setState(DatanodeStorage.State.FAILED);
534      }
535    }
536  }
537
538  private static class BlockIterator implements Iterator<BlockInfoContiguous> {
539    private int index = 0;
540    private final List<Iterator<BlockInfoContiguous>> iterators;
541    
542    private BlockIterator(final DatanodeStorageInfo... storages) {
543      List<Iterator<BlockInfoContiguous>> iterators = new ArrayList<Iterator<BlockInfoContiguous>>();
544      for (DatanodeStorageInfo e : storages) {
545        iterators.add(e.getBlockIterator());
546      }
547      this.iterators = Collections.unmodifiableList(iterators);
548    }
549
550    @Override
551    public boolean hasNext() {
552      update();
553      return !iterators.isEmpty() && iterators.get(index).hasNext();
554    }
555
556    @Override
557    public BlockInfoContiguous next() {
558      update();
559      return iterators.get(index).next();
560    }
561    
562    @Override
563    public void remove() {
564      throw new UnsupportedOperationException("Remove unsupported.");
565    }
566    
567    private void update() {
568      while(index < iterators.size() - 1 && !iterators.get(index).hasNext()) {
569        index++;
570      }
571    }
572  }
573
574  Iterator<BlockInfoContiguous> getBlockIterator() {
575    return new BlockIterator(getStorageInfos());
576  }
577  Iterator<BlockInfoContiguous> getBlockIterator(final String storageID) {
578    return new BlockIterator(getStorageInfo(storageID));
579  }
580
581  void incrementPendingReplicationWithoutTargets() {
582    PendingReplicationWithoutTargets++;
583  }
584
585  void decrementPendingReplicationWithoutTargets() {
586    PendingReplicationWithoutTargets--;
587  }
588
589  /**
590   * Store block replication work.
591   */
592  void addBlockToBeReplicated(Block block, DatanodeStorageInfo[] targets) {
593    assert(block != null && targets != null && targets.length > 0);
594    replicateBlocks.offer(new BlockTargetPair(block, targets));
595  }
596
597  /**
598   * Store block recovery work.
599   */
600  void addBlockToBeRecovered(BlockInfoContiguousUnderConstruction block) {
601    if(recoverBlocks.contains(block)) {
602      // this prevents adding the same block twice to the recovery queue
603      BlockManager.LOG.info(block + " is already in the recovery queue");
604      return;
605    }
606    recoverBlocks.offer(block);
607  }
608
609  /**
610   * Store block invalidation work.
611   */
612  void addBlocksToBeInvalidated(List<Block> blocklist) {
613    assert(blocklist != null && blocklist.size() > 0);
614    synchronized (invalidateBlocks) {
615      for(Block blk : blocklist) {
616        invalidateBlocks.add(blk);
617      }
618    }
619  }
620
621  /**
622   * The number of work items that are pending to be replicated
623   */
624  int getNumberOfBlocksToBeReplicated() {
625    return PendingReplicationWithoutTargets + replicateBlocks.size();
626  }
627
628  /**
629   * The number of block invalidation items that are pending to 
630   * be sent to the datanode
631   */
632  int getNumberOfBlocksToBeInvalidated() {
633    synchronized (invalidateBlocks) {
634      return invalidateBlocks.size();
635    }
636  }
637
638  public List<BlockTargetPair> getReplicationCommand(int maxTransfers) {
639    return replicateBlocks.poll(maxTransfers);
640  }
641
642  public BlockInfoContiguousUnderConstruction[] getLeaseRecoveryCommand(int maxTransfers) {
643    List<BlockInfoContiguousUnderConstruction> blocks = recoverBlocks.poll(maxTransfers);
644    if(blocks == null)
645      return null;
646    return blocks.toArray(new BlockInfoContiguousUnderConstruction[blocks.size()]);
647  }
648
649  /**
650   * Remove the specified number of blocks to be invalidated
651   */
652  public Block[] getInvalidateBlocks(int maxblocks) {
653    synchronized (invalidateBlocks) {
654      Block[] deleteList = invalidateBlocks.pollToArray(new Block[Math.min(
655          invalidateBlocks.size(), maxblocks)]);
656      return deleteList.length == 0 ? null : deleteList;
657    }
658  }
659
660  /**
661   * Return the sum of remaining spaces of the specified type. If the remaining
662   * space of a storage is less than minSize, it won't be counted toward the
663   * sum.
664   *
665   * @param t The storage type. If null, the type is ignored.
666   * @param minSize The minimum free space required.
667   * @return the sum of remaining spaces that are bigger than minSize.
668   */
669  public long getRemaining(StorageType t, long minSize) {
670    long remaining = 0;
671    for (DatanodeStorageInfo s : getStorageInfos()) {
672      if (s.getState() == State.NORMAL &&
673          (t == null || s.getStorageType() == t)) {
674        long r = s.getRemaining();
675        if (r >= minSize) {
676          remaining += r;
677        }
678      }
679    }
680    return remaining;
681  }
682
683  /**
684   * @return Approximate number of blocks currently scheduled to be written 
685   * to the given storage type of this datanode.
686   */
687  public int getBlocksScheduled(StorageType t) {
688    return (int)(currApproxBlocksScheduled.get(t)
689        + prevApproxBlocksScheduled.get(t));
690  }
691
692  /**
693   * @return Approximate number of blocks currently scheduled to be written 
694   * to this datanode.
695   */
696  public int getBlocksScheduled() {
697    return (int)(currApproxBlocksScheduled.sum()
698        + prevApproxBlocksScheduled.sum());
699  }
700
701  /** Increment the number of blocks scheduled. */
702  void incrementBlocksScheduled(StorageType t) {
703    currApproxBlocksScheduled.add(t, 1);;
704  }
705  
706  /** Decrement the number of blocks scheduled. */
707  void decrementBlocksScheduled(StorageType t) {
708    if (prevApproxBlocksScheduled.get(t) > 0) {
709      prevApproxBlocksScheduled.subtract(t, 1);
710    } else if (currApproxBlocksScheduled.get(t) > 0) {
711      currApproxBlocksScheduled.subtract(t, 1);
712    } 
713    // its ok if both counters are zero.
714  }
715  
716  /** Adjusts curr and prev number of blocks scheduled every few minutes. */
717  private void rollBlocksScheduled(long now) {
718    if (now - lastBlocksScheduledRollTime > BLOCKS_SCHEDULED_ROLL_INTERVAL) {
719      prevApproxBlocksScheduled.set(currApproxBlocksScheduled);
720      currApproxBlocksScheduled.reset();
721      lastBlocksScheduledRollTime = now;
722    }
723  }
724  
725  @Override
726  public int hashCode() {
727    // Super implementation is sufficient
728    return super.hashCode();
729  }
730  
731  @Override
732  public boolean equals(Object obj) {
733    // Sufficient to use super equality as datanodes are uniquely identified
734    // by DatanodeID
735    return (this == obj) || super.equals(obj);
736  }
737
738  /** Decommissioning status */
739  public class DecommissioningStatus {
740    private int underReplicatedBlocks;
741    private int decommissionOnlyReplicas;
742    private int underReplicatedInOpenFiles;
743    private long startTime;
744    
745    synchronized void set(int underRep,
746        int onlyRep, int underConstruction) {
747      if (isDecommissionInProgress() == false) {
748        return;
749      }
750      underReplicatedBlocks = underRep;
751      decommissionOnlyReplicas = onlyRep;
752      underReplicatedInOpenFiles = underConstruction;
753    }
754
755    /** @return the number of under-replicated blocks */
756    public synchronized int getUnderReplicatedBlocks() {
757      if (isDecommissionInProgress() == false) {
758        return 0;
759      }
760      return underReplicatedBlocks;
761    }
762    /** @return the number of decommission-only replicas */
763    public synchronized int getDecommissionOnlyReplicas() {
764      if (isDecommissionInProgress() == false) {
765        return 0;
766      }
767      return decommissionOnlyReplicas;
768    }
769    /** @return the number of under-replicated blocks in open files */
770    public synchronized int getUnderReplicatedInOpenFiles() {
771      if (isDecommissionInProgress() == false) {
772        return 0;
773      }
774      return underReplicatedInOpenFiles;
775    }
776    /** Set start time */
777    public synchronized void setStartTime(long time) {
778      startTime = time;
779    }
780    /** @return start time */
781    public synchronized long getStartTime() {
782      if (isDecommissionInProgress() == false) {
783        return 0;
784      }
785      return startTime;
786    }
787  }  // End of class DecommissioningStatus
788
789  /**
790   * Set the flag to indicate if this datanode is disallowed from communicating
791   * with the namenode.
792   */
793  public void setDisallowed(boolean flag) {
794    disallowed = flag;
795  }
796  /** Is the datanode disallowed from communicating with the namenode? */
797  public boolean isDisallowed() {
798    return disallowed;
799  }
800
801  /**
802   * @return number of failed volumes in the datanode.
803   */
804  public int getVolumeFailures() {
805    return volumeFailures;
806  }
807
808  /**
809   * Returns info about volume failures.
810   *
811   * @return info about volume failures, possibly null
812   */
813  public VolumeFailureSummary getVolumeFailureSummary() {
814    return volumeFailureSummary;
815  }
816
817  /**
818   * @param nodeReg DatanodeID to update registration for.
819   */
820  @Override
821  public void updateRegInfo(DatanodeID nodeReg) {
822    super.updateRegInfo(nodeReg);
823    
824    // must re-process IBR after re-registration
825    for(DatanodeStorageInfo storage : getStorageInfos()) {
826      storage.setBlockReportCount(0);
827    }
828    heartbeatedSinceRegistration = false;
829    forceRegistration = false;
830  }
831
832  /**
833   * @return balancer bandwidth in bytes per second for this datanode
834   */
835  public long getBalancerBandwidth() {
836    return this.bandwidth;
837  }
838
839  /**
840   * @param bandwidth balancer bandwidth in bytes per second for this datanode
841   */
842  public void setBalancerBandwidth(long bandwidth) {
843    this.bandwidth = bandwidth;
844  }
845
846  @Override
847  public String dumpDatanode() {
848    StringBuilder sb = new StringBuilder(super.dumpDatanode());
849    int repl = replicateBlocks.size();
850    if (repl > 0) {
851      sb.append(" ").append(repl).append(" blocks to be replicated;");
852    }
853    int inval = invalidateBlocks.size();
854    if (inval > 0) {
855      sb.append(" ").append(inval).append(" blocks to be invalidated;");      
856    }
857    int recover = recoverBlocks.size();
858    if (recover > 0) {
859      sb.append(" ").append(recover).append(" blocks to be recovered;");
860    }
861    return sb.toString();
862  }
863
864  DatanodeStorageInfo updateStorage(DatanodeStorage s) {
865    synchronized (storageMap) {
866      DatanodeStorageInfo storage = storageMap.get(s.getStorageID());
867      if (storage == null) {
868        LOG.info("Adding new storage ID " + s.getStorageID() +
869                 " for DN " + getXferAddr());
870        storage = new DatanodeStorageInfo(this, s);
871        storageMap.put(s.getStorageID(), storage);
872      } else if (storage.getState() != s.getState() ||
873                 storage.getStorageType() != s.getStorageType()) {
874        // For backwards compatibility, make sure that the type and
875        // state are updated. Some reports from older datanodes do
876        // not include these fields so we may have assumed defaults.
877        storage.updateFromStorage(s);
878        storageMap.put(storage.getStorageID(), storage);
879      }
880      return storage;
881    }
882  }
883
884  /**
885   * @return   The time at which we last sent caching directives to this 
886   *           DataNode, in monotonic milliseconds.
887   */
888  public long getLastCachingDirectiveSentTimeMs() {
889    return this.lastCachingDirectiveSentTimeMs;
890  }
891
892  /**
893   * @param time  The time at which we last sent caching directives to this 
894   *              DataNode, in monotonic milliseconds.
895   */
896  public void setLastCachingDirectiveSentTimeMs(long time) {
897    this.lastCachingDirectiveSentTimeMs = time;
898  }
899  
900  /**
901   * checks whether atleast first block report has been received
902   * @return
903   */
904  public boolean checkBlockReportReceived() {
905    if(this.getStorageInfos().length == 0) {
906      return false;
907    }
908    for(DatanodeStorageInfo storageInfo: this.getStorageInfos()) {
909      if(storageInfo.getBlockReportCount() == 0 )
910        return false;
911    }
912    return true;
913  }
914
915  public void setForceRegistration(boolean force) {
916    forceRegistration = force;
917  }
918
919  public boolean isRegistered() {
920    return isAlive && !forceRegistration;
921  }
922}
923