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