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