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.protocol;
019
020import org.apache.hadoop.classification.InterfaceAudience;
021import org.apache.hadoop.classification.InterfaceStability;
022import org.apache.hadoop.hdfs.DFSConfigKeys;
023import org.apache.hadoop.hdfs.DFSUtil;
024import org.apache.hadoop.net.NetUtils;
025import org.apache.hadoop.net.NetworkTopology;
026import org.apache.hadoop.net.Node;
027import org.apache.hadoop.net.NodeBase;
028import org.apache.hadoop.util.StringUtils;
029import org.apache.hadoop.util.Time;
030
031import java.util.Date;
032import java.util.LinkedList;
033import java.util.List;
034
035import static org.apache.hadoop.hdfs.DFSUtil.percent2String;
036
037/** 
038 * This class extends the primary identifier of a Datanode with ephemeral
039 * state, eg usage information, current administrative state, and the
040 * network location that is communicated to clients.
041 */
042@InterfaceAudience.Private
043@InterfaceStability.Evolving
044public class DatanodeInfo extends DatanodeID implements Node {
045  private long capacity;
046  private long dfsUsed;
047  private long remaining;
048  private long blockPoolUsed;
049  private long cacheCapacity;
050  private long cacheUsed;
051  private long lastUpdate;
052  private long lastUpdateMonotonic;
053  private int xceiverCount;
054  private String location = NetworkTopology.DEFAULT_RACK;
055  private String softwareVersion;
056  private List<String> dependentHostNames = new LinkedList<String>();
057  
058  
059  // Datanode administrative states
060  public enum AdminStates {
061    NORMAL("In Service"), 
062    DECOMMISSION_INPROGRESS("Decommission In Progress"), 
063    DECOMMISSIONED("Decommissioned");
064
065    final String value;
066
067    AdminStates(final String v) {
068      this.value = v;
069    }
070
071    @Override
072    public String toString() {
073      return value;
074    }
075    
076    public static AdminStates fromValue(final String value) {
077      for (AdminStates as : AdminStates.values()) {
078        if (as.value.equals(value)) return as;
079      }
080      return NORMAL;
081    }
082  }
083
084  protected AdminStates adminState;
085
086  public DatanodeInfo(DatanodeInfo from) {
087    super(from);
088    this.capacity = from.getCapacity();
089    this.dfsUsed = from.getDfsUsed();
090    this.remaining = from.getRemaining();
091    this.blockPoolUsed = from.getBlockPoolUsed();
092    this.cacheCapacity = from.getCacheCapacity();
093    this.cacheUsed = from.getCacheUsed();
094    this.lastUpdate = from.getLastUpdate();
095    this.lastUpdateMonotonic = from.getLastUpdateMonotonic();
096    this.xceiverCount = from.getXceiverCount();
097    this.location = from.getNetworkLocation();
098    this.adminState = from.getAdminState();
099  }
100
101  public DatanodeInfo(DatanodeID nodeID) {
102    super(nodeID);
103    this.capacity = 0L;
104    this.dfsUsed = 0L;
105    this.remaining = 0L;
106    this.blockPoolUsed = 0L;
107    this.cacheCapacity = 0L;
108    this.cacheUsed = 0L;
109    this.lastUpdate = 0L;
110    this.lastUpdateMonotonic = 0L;
111    this.xceiverCount = 0;
112    this.adminState = null;    
113  }
114  
115  public DatanodeInfo(DatanodeID nodeID, String location) {
116    this(nodeID);
117    this.location = location;
118  }
119  
120  public DatanodeInfo(DatanodeID nodeID, String location,
121      final long capacity, final long dfsUsed, final long remaining,
122      final long blockPoolUsed, final long cacheCapacity, final long cacheUsed,
123      final long lastUpdate, final long lastUpdateMonotonic,
124      final int xceiverCount, final AdminStates adminState) {
125    this(nodeID.getIpAddr(), nodeID.getHostName(), nodeID.getDatanodeUuid(),
126        nodeID.getXferPort(), nodeID.getInfoPort(), nodeID.getInfoSecurePort(),
127        nodeID.getIpcPort(), capacity, dfsUsed, remaining, blockPoolUsed,
128        cacheCapacity, cacheUsed, lastUpdate, lastUpdateMonotonic,
129        xceiverCount, location, adminState);
130  }
131
132  /** Constructor */
133  public DatanodeInfo(final String ipAddr, final String hostName,
134      final String datanodeUuid, final int xferPort, final int infoPort,
135      final int infoSecurePort, final int ipcPort,
136      final long capacity, final long dfsUsed, final long remaining,
137      final long blockPoolUsed, final long cacheCapacity, final long cacheUsed,
138      final long lastUpdate, final long lastUpdateMonotonic,
139      final int xceiverCount, final String networkLocation,
140      final AdminStates adminState) {
141    super(ipAddr, hostName, datanodeUuid, xferPort, infoPort,
142            infoSecurePort, ipcPort);
143    this.capacity = capacity;
144    this.dfsUsed = dfsUsed;
145    this.remaining = remaining;
146    this.blockPoolUsed = blockPoolUsed;
147    this.cacheCapacity = cacheCapacity;
148    this.cacheUsed = cacheUsed;
149    this.lastUpdate = lastUpdate;
150    this.lastUpdateMonotonic = lastUpdateMonotonic;
151    this.xceiverCount = xceiverCount;
152    this.location = networkLocation;
153    this.adminState = adminState;
154  }
155  
156  /** Network location name */
157  @Override
158  public String getName() {
159    return getXferAddr();
160  }
161  
162  /** The raw capacity. */
163  public long getCapacity() { return capacity; }
164  
165  /** The used space by the data node. */
166  public long getDfsUsed() { return dfsUsed; }
167
168  /** The used space by the block pool on data node. */
169  public long getBlockPoolUsed() { return blockPoolUsed; }
170
171  /** The used space by the data node. */
172  public long getNonDfsUsed() { 
173    long nonDFSUsed = capacity - dfsUsed - remaining;
174    return nonDFSUsed < 0 ? 0 : nonDFSUsed;
175  }
176
177  /** The used space by the data node as percentage of present capacity */
178  public float getDfsUsedPercent() { 
179    return DFSUtil.getPercentUsed(dfsUsed, capacity);
180  }
181
182  /** The raw free space. */
183  public long getRemaining() { return remaining; }
184
185  /** Used space by the block pool as percentage of present capacity */
186  public float getBlockPoolUsedPercent() {
187    return DFSUtil.getPercentUsed(blockPoolUsed, capacity);
188  }
189  
190  /** The remaining space as percentage of configured capacity. */
191  public float getRemainingPercent() { 
192    return DFSUtil.getPercentRemaining(remaining, capacity);
193  }
194
195  /**
196   * @return Amount of cache capacity in bytes
197   */
198  public long getCacheCapacity() {
199    return cacheCapacity;
200  }
201
202  /**
203   * @return Amount of cache used in bytes
204   */
205  public long getCacheUsed() {
206    return cacheUsed;
207  }
208
209  /**
210   * @return Cache used as a percentage of the datanode's total cache capacity
211   */
212  public float getCacheUsedPercent() {
213    return DFSUtil.getPercentUsed(cacheUsed, cacheCapacity);
214  }
215
216  /**
217   * @return Amount of cache remaining in bytes
218   */
219  public long getCacheRemaining() {
220    return cacheCapacity - cacheUsed;
221  }
222
223  /**
224   * @return Cache remaining as a percentage of the datanode's total cache
225   * capacity
226   */
227  public float getCacheRemainingPercent() {
228    return DFSUtil.getPercentRemaining(getCacheRemaining(), cacheCapacity);
229  }
230
231  /**
232   * Get the last update timestamp.
233   * Return value is suitable for Date conversion.
234   */
235  public long getLastUpdate() { return lastUpdate; }
236
237  /** 
238   * The time when this information was accurate. <br>
239   * Ps: So return value is ideal for calculation of time differences.
240   * Should not be used to convert to Date.  
241   */
242  public long getLastUpdateMonotonic() { return lastUpdateMonotonic;}
243
244  /**
245   * Set lastUpdate monotonic time
246   */
247  public void setLastUpdateMonotonic(long lastUpdateMonotonic) {
248    this.lastUpdateMonotonic = lastUpdateMonotonic;
249  }
250
251  /** number of active connections */
252  public int getXceiverCount() { return xceiverCount; }
253
254  /** Sets raw capacity. */
255  public void setCapacity(long capacity) { 
256    this.capacity = capacity; 
257  }
258  
259  /** Sets the used space for the datanode. */
260  public void setDfsUsed(long dfsUsed) {
261    this.dfsUsed = dfsUsed;
262  }
263
264  /** Sets raw free space. */
265  public void setRemaining(long remaining) { 
266    this.remaining = remaining; 
267  }
268
269  /** Sets block pool used space */
270  public void setBlockPoolUsed(long bpUsed) { 
271    this.blockPoolUsed = bpUsed; 
272  }
273
274  /** Sets cache capacity. */
275  public void setCacheCapacity(long cacheCapacity) {
276    this.cacheCapacity = cacheCapacity;
277  }
278
279  /** Sets cache used. */
280  public void setCacheUsed(long cacheUsed) {
281    this.cacheUsed = cacheUsed;
282  }
283
284  /** Sets time when this information was accurate. */
285  public void setLastUpdate(long lastUpdate) { 
286    this.lastUpdate = lastUpdate; 
287  }
288
289  /** Sets number of active connections */
290  public void setXceiverCount(int xceiverCount) { 
291    this.xceiverCount = xceiverCount; 
292  }
293
294  /** network location */
295  public synchronized String getNetworkLocation() {return location;}
296    
297  /** Sets the network location */
298  public synchronized void setNetworkLocation(String location) {
299    this.location = NodeBase.normalize(location);
300  }
301  
302  /** Add a hostname to a list of network dependencies */
303  public void addDependentHostName(String hostname) {
304    dependentHostNames.add(hostname);
305  }
306  
307  /** List of Network dependencies */
308  public List<String> getDependentHostNames() {
309    return dependentHostNames;
310  }
311  
312  /** Sets the network dependencies */
313  public void setDependentHostNames(List<String> dependencyList) {
314    dependentHostNames = dependencyList;
315  }
316    
317  /** A formatted string for reporting the status of the DataNode. */
318  public String getDatanodeReport() {
319    StringBuilder buffer = new StringBuilder();
320    long c = getCapacity();
321    long r = getRemaining();
322    long u = getDfsUsed();
323    long nonDFSUsed = getNonDfsUsed();
324    float usedPercent = getDfsUsedPercent();
325    float remainingPercent = getRemainingPercent();
326    long cc = getCacheCapacity();
327    long cr = getCacheRemaining();
328    long cu = getCacheUsed();
329    float cacheUsedPercent = getCacheUsedPercent();
330    float cacheRemainingPercent = getCacheRemainingPercent();
331    String lookupName = NetUtils.getHostNameOfIP(getName());
332
333    buffer.append("Name: "+ getName());
334    if (lookupName != null) {
335      buffer.append(" (" + lookupName + ")");
336    }
337    buffer.append("\n");
338    buffer.append("Hostname: " + getHostName() + "\n");
339
340    if (!NetworkTopology.DEFAULT_RACK.equals(location)) {
341      buffer.append("Rack: "+location+"\n");
342    }
343    buffer.append("Decommission Status : ");
344    if (isDecommissioned()) {
345      buffer.append("Decommissioned\n");
346    } else if (isDecommissionInProgress()) {
347      buffer.append("Decommission in progress\n");
348    } else {
349      buffer.append("Normal\n");
350    }
351    buffer.append("Configured Capacity: "+c+" ("+StringUtils.byteDesc(c)+")"+"\n");
352    buffer.append("DFS Used: "+u+" ("+StringUtils.byteDesc(u)+")"+"\n");
353    buffer.append("Non DFS Used: "+nonDFSUsed+" ("+StringUtils.byteDesc(nonDFSUsed)+")"+"\n");
354    buffer.append("DFS Remaining: " +r+ " ("+StringUtils.byteDesc(r)+")"+"\n");
355    buffer.append("DFS Used%: "+percent2String(usedPercent) + "\n");
356    buffer.append("DFS Remaining%: "+percent2String(remainingPercent) + "\n");
357    buffer.append("Configured Cache Capacity: "+cc+" ("+StringUtils.byteDesc(cc)+")"+"\n");
358    buffer.append("Cache Used: "+cu+" ("+StringUtils.byteDesc(cu)+")"+"\n");
359    buffer.append("Cache Remaining: " +cr+ " ("+StringUtils.byteDesc(cr)+")"+"\n");
360    buffer.append("Cache Used%: "+percent2String(cacheUsedPercent) + "\n");
361    buffer.append("Cache Remaining%: "+percent2String(cacheRemainingPercent) + "\n");
362    buffer.append("Xceivers: "+getXceiverCount()+"\n");
363    buffer.append("Last contact: "+new Date(lastUpdate)+"\n");
364    return buffer.toString();
365  }
366
367  /** A formatted string for printing the status of the DataNode. */
368  public String dumpDatanode() {
369    StringBuilder buffer = new StringBuilder();
370    long c = getCapacity();
371    long r = getRemaining();
372    long u = getDfsUsed();
373    float usedPercent = getDfsUsedPercent();
374    long cc = getCacheCapacity();
375    long cr = getCacheRemaining();
376    long cu = getCacheUsed();
377    float cacheUsedPercent = getCacheUsedPercent();
378    buffer.append(getName());
379    if (!NetworkTopology.DEFAULT_RACK.equals(location)) {
380      buffer.append(" "+location);
381    }
382    if (isDecommissioned()) {
383      buffer.append(" DD");
384    } else if (isDecommissionInProgress()) {
385      buffer.append(" DP");
386    } else {
387      buffer.append(" IN");
388    }
389    buffer.append(" " + c + "(" + StringUtils.byteDesc(c)+")");
390    buffer.append(" " + u + "(" + StringUtils.byteDesc(u)+")");
391    buffer.append(" " + percent2String(usedPercent));
392    buffer.append(" " + r + "(" + StringUtils.byteDesc(r)+")");
393    buffer.append(" " + cc + "(" + StringUtils.byteDesc(cc)+")");
394    buffer.append(" " + cu + "(" + StringUtils.byteDesc(cu)+")");
395    buffer.append(" " + percent2String(cacheUsedPercent));
396    buffer.append(" " + cr + "(" + StringUtils.byteDesc(cr)+")");
397    buffer.append(" " + new Date(lastUpdate));
398    return buffer.toString();
399  }
400
401  /**
402   * Start decommissioning a node.
403   * old state.
404   */
405  public void startDecommission() {
406    adminState = AdminStates.DECOMMISSION_INPROGRESS;
407  }
408
409  /**
410   * Stop decommissioning a node.
411   * old state.
412   */
413  public void stopDecommission() {
414    adminState = null;
415  }
416
417  /**
418   * Returns true if the node is in the process of being decommissioned
419   */
420  public boolean isDecommissionInProgress() {
421    return adminState == AdminStates.DECOMMISSION_INPROGRESS;
422  }
423
424  /**
425   * Returns true if the node has been decommissioned.
426   */
427  public boolean isDecommissioned() {
428    return adminState == AdminStates.DECOMMISSIONED;
429  }
430
431  /**
432   * Sets the admin state to indicate that decommission is complete.
433   */
434  public void setDecommissioned() {
435    adminState = AdminStates.DECOMMISSIONED;
436  }
437
438  /**
439   * Retrieves the admin state of this node.
440   */
441  public AdminStates getAdminState() {
442    if (adminState == null) {
443      return AdminStates.NORMAL;
444    }
445    return adminState;
446  }
447 
448  /**
449   * Check if the datanode is in stale state. Here if 
450   * the namenode has not received heartbeat msg from a 
451   * datanode for more than staleInterval (default value is
452   * {@link DFSConfigKeys#DFS_NAMENODE_STALE_DATANODE_INTERVAL_DEFAULT}),
453   * the datanode will be treated as stale node.
454   * 
455   * @param staleInterval
456   *          the time interval for marking the node as stale. If the last
457   *          update time is beyond the given time interval, the node will be
458   *          marked as stale.
459   * @return true if the node is stale
460   */
461  public boolean isStale(long staleInterval) {
462    return (Time.monotonicNow() - lastUpdateMonotonic) >= staleInterval;
463  }
464  
465  /**
466   * Sets the admin state of this node.
467   */
468  protected void setAdminState(AdminStates newState) {
469    if (newState == AdminStates.NORMAL) {
470      adminState = null;
471    }
472    else {
473      adminState = newState;
474    }
475  }
476
477  private transient int level; //which level of the tree the node resides
478  private transient Node parent; //its parent
479
480  /** Return this node's parent */
481  @Override
482  public Node getParent() { return parent; }
483  @Override
484  public void setParent(Node parent) {this.parent = parent;}
485   
486  /** Return this node's level in the tree.
487   * E.g. the root of a tree returns 0 and its children return 1
488   */
489  @Override
490  public int getLevel() { return level; }
491  @Override
492  public void setLevel(int level) {this.level = level;}
493
494  @Override
495  public int hashCode() {
496    // Super implementation is sufficient
497    return super.hashCode();
498  }
499  
500  @Override
501  public boolean equals(Object obj) {
502    // Sufficient to use super equality as datanodes are uniquely identified
503    // by DatanodeID
504    return (this == obj) || super.equals(obj);
505  }
506
507  public String getSoftwareVersion() {
508    return softwareVersion;
509  }
510
511  public void setSoftwareVersion(String softwareVersion) {
512    this.softwareVersion = softwareVersion;
513  }
514}