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