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