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 java.io.DataInput;
021    import java.io.DataOutput;
022    import java.io.IOException;
023    import java.util.Date;
024    
025    import org.apache.hadoop.classification.InterfaceAudience;
026    import org.apache.hadoop.classification.InterfaceStability;
027    import org.apache.hadoop.hdfs.DFSUtil;
028    import org.apache.hadoop.io.Text;
029    import org.apache.hadoop.io.Writable;
030    import org.apache.hadoop.io.WritableFactories;
031    import org.apache.hadoop.io.WritableFactory;
032    import org.apache.hadoop.io.WritableUtils;
033    import org.apache.hadoop.net.NetUtils;
034    import org.apache.hadoop.net.NetworkTopology;
035    import org.apache.hadoop.net.Node;
036    import org.apache.hadoop.net.NodeBase;
037    import org.apache.hadoop.util.StringUtils;
038    
039    import org.apache.avro.reflect.Nullable;
040    
041    /** 
042     * DatanodeInfo represents the status of a DataNode.
043     * This object is used for communication in the
044     * Datanode Protocol and the Client Protocol.
045     */
046    @InterfaceAudience.Private
047    @InterfaceStability.Evolving
048    public class DatanodeInfo extends DatanodeID implements Node {
049      protected long capacity;
050      protected long dfsUsed;
051      protected long remaining;
052      protected long blockPoolUsed;
053      protected long lastUpdate;
054      protected int xceiverCount;
055      protected String location = NetworkTopology.DEFAULT_RACK;
056    
057      /** HostName as supplied by the datanode during registration as its 
058       * name. Namenode uses datanode IP address as the name.
059       */
060      @Nullable
061      protected String hostName = null;
062      
063      // administrative states of a datanode
064      public enum AdminStates {
065        NORMAL("In Service"), 
066        DECOMMISSION_INPROGRESS("Decommission In Progress"), 
067        DECOMMISSIONED("Decommissioned");
068    
069        final String value;
070    
071        AdminStates(final String v) {
072          this.value = v;
073        }
074    
075        public String toString() {
076          return value;
077        }
078      }
079    
080      @Nullable
081      protected AdminStates adminState;
082    
083    
084      public DatanodeInfo() {
085        super();
086        adminState = null;
087      }
088      
089      public DatanodeInfo(DatanodeInfo from) {
090        super(from);
091        this.capacity = from.getCapacity();
092        this.dfsUsed = from.getDfsUsed();
093        this.remaining = from.getRemaining();
094        this.blockPoolUsed = from.getBlockPoolUsed();
095        this.lastUpdate = from.getLastUpdate();
096        this.xceiverCount = from.getXceiverCount();
097        this.location = from.getNetworkLocation();
098        this.adminState = from.adminState;
099        this.hostName = from.hostName;
100      }
101    
102      public DatanodeInfo(DatanodeID nodeID) {
103        super(nodeID);
104        this.capacity = 0L;
105        this.dfsUsed = 0L;
106        this.remaining = 0L;
107        this.blockPoolUsed = 0L;
108        this.lastUpdate = 0L;
109        this.xceiverCount = 0;
110        this.adminState = null;    
111      }
112      
113      protected DatanodeInfo(DatanodeID nodeID, String location, String hostName) {
114        this(nodeID);
115        this.location = location;
116        this.hostName = hostName;
117      }
118    
119      /** Constructor */
120      public DatanodeInfo(final String name, final String storageID,
121          final int infoPort, final int ipcPort,
122          final long capacity, final long dfsUsed, final long remaining,
123          final long blockPoolUsed, final long lastUpdate, final int xceiverCount,
124          final String networkLocation, final String hostName,
125          final AdminStates adminState) {
126        super(name, storageID, infoPort, ipcPort);
127    
128        this.capacity = capacity;
129        this.dfsUsed = dfsUsed;
130        this.remaining = remaining;
131        this.blockPoolUsed = blockPoolUsed;
132        this.lastUpdate = lastUpdate;
133        this.xceiverCount = xceiverCount;
134        this.location = networkLocation;
135        this.hostName = hostName;
136        this.adminState = adminState;
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      /** rack name */
209      public synchronized String getNetworkLocation() {return location;}
210        
211      /** Sets the rack name */
212      public synchronized void setNetworkLocation(String location) {
213        this.location = NodeBase.normalize(location);
214      }
215      
216      public String getHostName() {
217        return (hostName == null || hostName.length()==0) ? getHost() : hostName;
218      }
219      
220      public void setHostName(String host) {
221        hostName = host;
222      }
223      
224      /** A formatted string for reporting the status of the DataNode. */
225      public String getDatanodeReport() {
226        StringBuilder buffer = new StringBuilder();
227        long c = getCapacity();
228        long r = getRemaining();
229        long u = getDfsUsed();
230        long nonDFSUsed = getNonDfsUsed();
231        float usedPercent = getDfsUsedPercent();
232        float remainingPercent = getRemainingPercent();
233        String hostName = NetUtils.getHostNameOfIP(name);
234    
235        buffer.append("Name: "+ name);
236        if(hostName != null)
237          buffer.append(" (" + hostName + ")");
238        buffer.append("\n");
239    
240        if (!NetworkTopology.DEFAULT_RACK.equals(location)) {
241          buffer.append("Rack: "+location+"\n");
242        }
243        buffer.append("Decommission Status : ");
244        if (isDecommissioned()) {
245          buffer.append("Decommissioned\n");
246        } else if (isDecommissionInProgress()) {
247          buffer.append("Decommission in progress\n");
248        } else {
249          buffer.append("Normal\n");
250        }
251        buffer.append("Configured Capacity: "+c+" ("+StringUtils.byteDesc(c)+")"+"\n");
252        buffer.append("DFS Used: "+u+" ("+StringUtils.byteDesc(u)+")"+"\n");
253        buffer.append("Non DFS Used: "+nonDFSUsed+" ("+StringUtils.byteDesc(nonDFSUsed)+")"+"\n");
254        buffer.append("DFS Remaining: " +r+ " ("+StringUtils.byteDesc(r)+")"+"\n");
255        buffer.append("DFS Used%: "+StringUtils.limitDecimalTo2(usedPercent)+"%\n");
256        buffer.append("DFS Remaining%: "+StringUtils.limitDecimalTo2(remainingPercent)+"%\n");
257        buffer.append("Last contact: "+new Date(lastUpdate)+"\n");
258        return buffer.toString();
259      }
260    
261      /** A formatted string for printing the status of the DataNode. */
262      public String dumpDatanode() {
263        StringBuilder buffer = new StringBuilder();
264        long c = getCapacity();
265        long r = getRemaining();
266        long u = getDfsUsed();
267        buffer.append(name);
268        if (!NetworkTopology.DEFAULT_RACK.equals(location)) {
269          buffer.append(" "+location);
270        }
271        if (isDecommissioned()) {
272          buffer.append(" DD");
273        } else if (isDecommissionInProgress()) {
274          buffer.append(" DP");
275        } else {
276          buffer.append(" IN");
277        }
278        buffer.append(" " + c + "(" + StringUtils.byteDesc(c)+")");
279        buffer.append(" " + u + "(" + StringUtils.byteDesc(u)+")");
280        buffer.append(" " + StringUtils.limitDecimalTo2(((1.0*u)/c)*100)+"%");
281        buffer.append(" " + r + "(" + StringUtils.byteDesc(r)+")");
282        buffer.append(" " + new Date(lastUpdate));
283        return buffer.toString();
284      }
285    
286      /**
287       * Start decommissioning a node.
288       * old state.
289       */
290      public void startDecommission() {
291        adminState = AdminStates.DECOMMISSION_INPROGRESS;
292      }
293    
294      /**
295       * Stop decommissioning a node.
296       * old state.
297       */
298      public void stopDecommission() {
299        adminState = null;
300      }
301    
302      /**
303       * Returns true if the node is in the process of being decommissioned
304       */
305      public boolean isDecommissionInProgress() {
306        return adminState == AdminStates.DECOMMISSION_INPROGRESS;
307      }
308    
309      /**
310       * Returns true if the node has been decommissioned.
311       */
312      public boolean isDecommissioned() {
313        return adminState == AdminStates.DECOMMISSIONED;
314      }
315    
316      /**
317       * Sets the admin state to indicate that decommission is complete.
318       */
319      public void setDecommissioned() {
320        adminState = AdminStates.DECOMMISSIONED;
321      }
322    
323      /**
324       * Retrieves the admin state of this node.
325       */
326      public AdminStates getAdminState() {
327        if (adminState == null) {
328          return AdminStates.NORMAL;
329        }
330        return adminState;
331      }
332    
333      /**
334       * Sets the admin state of this node.
335       */
336      protected void setAdminState(AdminStates newState) {
337        if (newState == AdminStates.NORMAL) {
338          adminState = null;
339        }
340        else {
341          adminState = newState;
342        }
343      }
344    
345      private transient int level; //which level of the tree the node resides
346      private transient Node parent; //its parent
347    
348      /** Return this node's parent */
349      public Node getParent() { return parent; }
350      public void setParent(Node parent) {this.parent = parent;}
351       
352      /** Return this node's level in the tree.
353       * E.g. the root of a tree returns 0 and its children return 1
354       */
355      public int getLevel() { return level; }
356      public void setLevel(int level) {this.level = level;}
357    
358      /////////////////////////////////////////////////
359      // Writable
360      /////////////////////////////////////////////////
361      static {                                      // register a ctor
362        WritableFactories.setFactory
363          (DatanodeInfo.class,
364           new WritableFactory() {
365             public Writable newInstance() { return new DatanodeInfo(); }
366           });
367      }
368    
369      /** {@inheritDoc} */
370      public void write(DataOutput out) throws IOException {
371        super.write(out);
372    
373        //TODO: move it to DatanodeID once DatanodeID is not stored in FSImage
374        out.writeShort(ipcPort);
375    
376        out.writeLong(capacity);
377        out.writeLong(dfsUsed);
378        out.writeLong(remaining);
379        out.writeLong(blockPoolUsed);
380        out.writeLong(lastUpdate);
381        out.writeInt(xceiverCount);
382        Text.writeString(out, location);
383        Text.writeString(out, hostName == null? "": hostName);
384        WritableUtils.writeEnum(out, getAdminState());
385      }
386    
387      /** {@inheritDoc} */
388      public void readFields(DataInput in) throws IOException {
389        super.readFields(in);
390    
391        //TODO: move it to DatanodeID once DatanodeID is not stored in FSImage
392        this.ipcPort = in.readShort() & 0x0000ffff;
393    
394        this.capacity = in.readLong();
395        this.dfsUsed = in.readLong();
396        this.remaining = in.readLong();
397        this.blockPoolUsed = in.readLong();
398        this.lastUpdate = in.readLong();
399        this.xceiverCount = in.readInt();
400        this.location = Text.readString(in);
401        this.hostName = Text.readString(in);
402        setAdminState(WritableUtils.readEnum(in, AdminStates.class));
403      }
404    
405      /** Read a DatanodeInfo */
406      public static DatanodeInfo read(DataInput in) throws IOException {
407        final DatanodeInfo d = new DatanodeInfo();
408        d.readFields(in);
409        return d;
410      }
411    
412      @Override
413      public int hashCode() {
414        // Super implementation is sufficient
415        return super.hashCode();
416      }
417      
418      @Override
419      public boolean equals(Object obj) {
420        // Sufficient to use super equality as datanodes are uniquely identified
421        // by DatanodeID
422        return (this == obj) || super.equals(obj);
423      }
424    }