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     */
018    package org.apache.hadoop.hdfs.protocol;
019    
020    import org.apache.hadoop.classification.InterfaceAudience;
021    import org.apache.hadoop.classification.InterfaceStability;
022    import org.apache.hadoop.hdfs.DFSConfigKeys;
023    import org.apache.hadoop.hdfs.DFSUtil;
024    import org.apache.hadoop.net.NetUtils;
025    import org.apache.hadoop.net.NetworkTopology;
026    import org.apache.hadoop.net.Node;
027    import org.apache.hadoop.net.NodeBase;
028    import org.apache.hadoop.util.StringUtils;
029    import org.apache.hadoop.util.Time;
030    
031    import java.util.Date;
032    
033    import 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
042    public 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    }