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