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.util.EnumSet;
021    import java.util.HashMap;
022    import java.util.Map;
023    
024    import org.apache.hadoop.classification.InterfaceAudience;
025    
026    /**
027     * This class tracks changes in the layout version of HDFS.
028     * 
029     * Layout version is changed for following reasons:
030     * <ol>
031     * <li>The layout of how namenode or datanode stores information 
032     * on disk changes.</li>
033     * <li>A new operation code is added to the editlog.</li>
034     * <li>Modification such as format of a record, content of a record 
035     * in editlog or fsimage.</li>
036     * </ol>
037     * <br>
038     * <b>How to update layout version:<br></b>
039     * When a change requires new layout version, please add an entry into
040     * {@link Feature} with a short enum name, new layout version and description
041     * of the change. Please see {@link Feature} for further details.
042     * <br>
043     */
044    @InterfaceAudience.Private
045    public class LayoutVersion {
046     
047      /**
048       * Enums for features that change the layout version.
049       * <br><br>
050       * To add a new layout version:
051       * <ul>
052       * <li>Define a new enum constant with a short enum name, the new layout version 
053       * and description of the added feature.</li>
054       * <li>When adding a layout version with an ancestor that is not same as
055       * its immediate predecessor, use the constructor where a spacific ancestor
056       * can be passed.
057       * </li>
058       * </ul>
059       */
060      public static enum Feature {
061        NAMESPACE_QUOTA(-16, "Support for namespace quotas"),
062        FILE_ACCESS_TIME(-17, "Support for access time on files"),
063        DISKSPACE_QUOTA(-18, "Support for disk space quotas"),
064        STICKY_BIT(-19, "Support for sticky bits"),
065        APPEND_RBW_DIR(-20, "Datanode has \"rbw\" subdirectory for append"),
066        ATOMIC_RENAME(-21, "Support for atomic rename"),
067        CONCAT(-22, "Support for concat operation"),
068        SYMLINKS(-23, "Support for symbolic links"),
069        DELEGATION_TOKEN(-24, "Support for delegation tokens for security"),
070        FSIMAGE_COMPRESSION(-25, "Support for fsimage compression"),
071        FSIMAGE_CHECKSUM(-26, "Support checksum for fsimage"),
072        REMOVE_REL13_DISK_LAYOUT_SUPPORT(-27, "Remove support for 0.13 disk layout"),
073        EDITS_CHESKUM(-28, "Support checksum for editlog"),
074        UNUSED(-29, "Skipped version"),
075        FSIMAGE_NAME_OPTIMIZATION(-30, "Store only last part of path in fsimage"),
076        RESERVED_REL20_203(-31, -19, "Reserved for release 0.20.203"),
077        RESERVED_REL20_204(-32, "Reserved for release 0.20.204"),
078        RESERVED_REL22(-33, -27, "Reserved for release 0.22"),
079        RESERVED_REL23(-34, -30, "Reserved for release 0.23"),
080        FEDERATION(-35, "Support for namenode federation"),
081        LEASE_REASSIGNMENT(-36, "Support for persisting lease holder reassignment"),
082        STORED_TXIDS(-37, "Transaction IDs are stored in edits log and image files"),
083        TXID_BASED_LAYOUT(-38, "File names in NN Storage are based on transaction IDs"), 
084        EDITLOG_OP_OPTIMIZATION(-39,
085            "Use LongWritable and ShortWritable directly instead of ArrayWritable of UTF8");
086        
087        final int lv;
088        final int ancestorLV;
089        final String description;
090        
091        /**
092         * Feature that is added at {@code currentLV}. 
093         * @param lv new layout version with the addition of this feature
094         * @param description description of the feature
095         */
096        Feature(final int lv, final String description) {
097          this(lv, lv + 1, description);
098        }
099    
100        /**
101         * Feature that is added at {@code currentLV}.
102         * @param lv new layout version with the addition of this feature
103         * @param ancestorLV layout version from which the new lv is derived
104         *          from.
105         * @param description description of the feature
106         */
107        Feature(final int lv, final int ancestorLV,
108            final String description) {
109          this.lv = lv;
110          this.ancestorLV = ancestorLV;
111          this.description = description;
112        }
113        
114        /** 
115         * Accessor method for feature layout version 
116         * @return int lv value
117         */
118        public int getLayoutVersion() {
119          return lv;
120        }
121    
122        /** 
123         * Accessor method for feature ancestor layout version 
124         * @return int ancestor LV value
125         */
126        public int getAncestorLayoutVersion() {
127          return ancestorLV;
128        }
129    
130        /** 
131         * Accessor method for feature description 
132         * @return String feature description 
133         */
134        public String getDescription() {
135          return description;
136        }
137      }
138      
139      // Build layout version and corresponding feature matrix
140      static final Map<Integer, EnumSet<Feature>>map = 
141        new HashMap<Integer, EnumSet<Feature>>();
142      
143      // Static initialization 
144      static {
145        initMap();
146      }
147      
148      /**
149       * Initialize the map of a layout version and EnumSet of {@link Feature}s 
150       * supported.
151       */
152      private static void initMap() {
153        // Go through all the enum constants and build a map of
154        // LayoutVersion <-> EnumSet of all supported features in that LayoutVersion
155        for (Feature f : Feature.values()) {
156          EnumSet<Feature> ancestorSet = map.get(f.ancestorLV);
157          if (ancestorSet == null) {
158            ancestorSet = EnumSet.noneOf(Feature.class); // Empty enum set
159            map.put(f.ancestorLV, ancestorSet);
160          }
161          EnumSet<Feature> featureSet = EnumSet.copyOf(ancestorSet);
162          featureSet.add(f);
163          map.put(f.lv, featureSet);
164        }
165        
166        // Special initialization for 0.20.203 and 0.20.204
167        // to add Feature#DELEGATION_TOKEN
168        specialInit(Feature.RESERVED_REL20_203.lv, Feature.DELEGATION_TOKEN);
169        specialInit(Feature.RESERVED_REL20_204.lv, Feature.DELEGATION_TOKEN);
170      }
171      
172      private static void specialInit(int lv, Feature f) {
173        EnumSet<Feature> set = map.get(lv);
174        set.add(f);
175      }
176      
177      /**
178       * Gets formatted string that describes {@link LayoutVersion} information.
179       */
180      public static String getString() {
181        final StringBuilder buf = new StringBuilder();
182        buf.append("Feature List:\n");
183        for (Feature f : Feature.values()) {
184          buf.append(f).append(" introduced in layout version ")
185              .append(f.lv).append(" (").
186          append(f.description).append(")\n");
187        }
188        
189        buf.append("\n\nLayoutVersion and supported features:\n");
190        for (Feature f : Feature.values()) {
191          buf.append(f.lv).append(": ").append(map.get(f.lv))
192              .append("\n");
193        }
194        return buf.toString();
195      }
196      
197      /**
198       * Returns true if a given feature is supported in the given layout version
199       * @param f Feature
200       * @param lv LayoutVersion
201       * @return true if {@code f} is supported in layout version {@code lv}
202       */
203      public static boolean supports(final Feature f, final int lv) {
204        final EnumSet<Feature> set =  map.get(lv);
205        return set != null && set.contains(f);
206      }
207      
208      /**
209       * Get the current layout version
210       */
211      public static int getCurrentLayoutVersion() {
212        Feature[] values = Feature.values();
213        return values[values.length - 1].lv;
214      }
215    }