View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.snapshot;
19  
20  import java.io.FileNotFoundException;
21  import java.io.IOException;
22  import java.util.Collections;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.apache.hadoop.conf.Configuration;
27  import org.apache.hadoop.fs.FSDataInputStream;
28  import org.apache.hadoop.fs.FSDataOutputStream;
29  import org.apache.hadoop.fs.FileSystem;
30  import org.apache.hadoop.fs.Path;
31  import org.apache.hadoop.fs.permission.FsPermission;
32  import org.apache.hadoop.hbase.HConstants;
33  import org.apache.hadoop.hbase.classification.InterfaceAudience;
34  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
35  import org.apache.hadoop.hbase.security.User;
36  import org.apache.hadoop.hbase.snapshot.SnapshotManifestV2;
37  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
38  import org.apache.hadoop.hbase.util.FSUtils;
39  
40  /**
41   * Utility class to help manage {@link SnapshotDescription SnapshotDesriptions}.
42   * <p>
43   * Snapshots are laid out on disk like this:
44   *
45   * <pre>
46   * /hbase/.snapshots
47   *          /.tmp                <---- working directory
48   *          /[snapshot name]     <----- completed snapshot
49   * </pre>
50   *
51   * A completed snapshot named 'completed' then looks like (multiple regions, servers, files, etc.
52   * signified by '...' on the same directory depth).
53   *
54   * <pre>
55   * /hbase/.snapshots/completed
56   *                   .snapshotinfo          <--- Description of the snapshot
57   *                   .tableinfo             <--- Copy of the tableinfo
58   *                    /.logs
59   *                        /[server_name]
60   *                            /... [log files]
61   *                         ...
62   *                   /[region name]           <---- All the region's information
63   *                   .regioninfo              <---- Copy of the HRegionInfo
64   *                      /[column family name]
65   *                          /[hfile name]     <--- name of the hfile in the real region
66   *                          ...
67   *                      ...
68   *                    ...
69   * </pre>
70   *
71   * Utility methods in this class are useful for getting the correct locations for different parts of
72   * the snapshot, as well as moving completed snapshots into place (see
73   * {@link #completeSnapshot}, and writing the
74   * {@link SnapshotDescription} to the working snapshot directory.
75   */
76  @InterfaceAudience.Private
77  public final class SnapshotDescriptionUtils {
78  
79    /**
80     * Filter that only accepts completed snapshot directories
81     */
82    public static class CompletedSnaphotDirectoriesFilter extends FSUtils.BlackListDirFilter {
83  
84      /**
85       * @param fs
86       */
87      public CompletedSnaphotDirectoriesFilter(FileSystem fs) {
88        super(fs, Collections.singletonList(SNAPSHOT_TMP_DIR_NAME));
89      }
90    }
91  
92    private static final Log LOG = LogFactory.getLog(SnapshotDescriptionUtils.class);
93    /**
94     * Version of the fs layout for a snapshot. Future snapshots may have different file layouts,
95     * which we may need to read in differently.
96     */
97    public static final int SNAPSHOT_LAYOUT_VERSION = SnapshotManifestV2.DESCRIPTOR_VERSION;
98  
99    // snapshot directory constants
100   /**
101    * The file contains the snapshot basic information and it is under the directory of a snapshot.
102    */
103   public static final String SNAPSHOTINFO_FILE = ".snapshotinfo";
104 
105   /** Temporary directory under the snapshot directory to store in-progress snapshots */
106   public static final String SNAPSHOT_TMP_DIR_NAME = ".tmp";
107 
108   /** This tag will be created in in-progess snapshots */
109   public static final String SNAPSHOT_IN_PROGRESS = ".inprogress";
110   // snapshot operation values
111   /** Default value if no start time is specified */
112   public static final long NO_SNAPSHOT_START_TIME_SPECIFIED = 0;
113 
114 
115   public static final String MASTER_SNAPSHOT_TIMEOUT_MILLIS = "hbase.snapshot.master.timeout.millis";
116 
117   /** By default, wait 300 seconds for a snapshot to complete */
118   public static final long DEFAULT_MAX_WAIT_TIME = 60000 * 5 ;
119 
120 
121   /**
122    * By default, check to see if the snapshot is complete (ms)
123    * @deprecated Use {@link #DEFAULT_MAX_WAIT_TIME} instead.
124    * */
125   @Deprecated
126   public static final int SNAPSHOT_TIMEOUT_MILLIS_DEFAULT = 60000 * 5;
127 
128   /**
129    * Conf key for # of ms elapsed before injecting a snapshot timeout error when waiting for
130    * completion.
131    * @deprecated Use {@link #MASTER_SNAPSHOT_TIMEOUT_MILLIS} instead.
132    */
133   @Deprecated
134   public static final String SNAPSHOT_TIMEOUT_MILLIS_KEY = "hbase.snapshot.master.timeoutMillis";
135 
136   private SnapshotDescriptionUtils() {
137     // private constructor for utility class
138   }
139 
140   /**
141    * @param conf {@link Configuration} from which to check for the timeout
142    * @param type type of snapshot being taken
143    * @param defaultMaxWaitTime Default amount of time to wait, if none is in the configuration
144    * @return the max amount of time the master should wait for a snapshot to complete
145    */
146   public static long getMaxMasterTimeout(Configuration conf, SnapshotDescription.Type type,
147       long defaultMaxWaitTime) {
148     String confKey;
149     switch (type) {
150     case DISABLED:
151     default:
152       confKey = MASTER_SNAPSHOT_TIMEOUT_MILLIS;
153     }
154     return Math.max(conf.getLong(confKey, defaultMaxWaitTime),
155         conf.getLong(SNAPSHOT_TIMEOUT_MILLIS_KEY, defaultMaxWaitTime));
156   }
157 
158   /**
159    * Get the snapshot root directory. All the snapshots are kept under this directory, i.e.
160    * ${hbase.rootdir}/.snapshot
161    * @param rootDir hbase root directory
162    * @return the base directory in which all snapshots are kept
163    */
164   public static Path getSnapshotRootDir(final Path rootDir) {
165     return new Path(rootDir, HConstants.SNAPSHOT_DIR_NAME);
166   }
167 
168   /**
169    * Get the directory for a specified snapshot. This directory is a sub-directory of snapshot root
170    * directory and all the data files for a snapshot are kept under this directory.
171    * @param snapshot snapshot being taken
172    * @param rootDir hbase root directory
173    * @return the final directory for the completed snapshot
174    */
175   public static Path getCompletedSnapshotDir(final SnapshotDescription snapshot, final Path rootDir) {
176     return getCompletedSnapshotDir(snapshot.getName(), rootDir);
177   }
178 
179   /**
180    * Get the directory for a completed snapshot. This directory is a sub-directory of snapshot root
181    * directory and all the data files for a snapshot are kept under this directory.
182    * @param snapshotName name of the snapshot being taken
183    * @param rootDir hbase root directory
184    * @return the final directory for the completed snapshot
185    */
186   public static Path getCompletedSnapshotDir(final String snapshotName, final Path rootDir) {
187     return getCompletedSnapshotDir(getSnapshotsDir(rootDir), snapshotName);
188   }
189 
190   /**
191    * Get the general working directory for snapshots - where they are built, where they are
192    * temporarily copied on export, etc.
193    * @param rootDir root directory of the HBase installation
194    * @return Path to the snapshot tmp directory, relative to the passed root directory
195    */
196   public static Path getWorkingSnapshotDir(final Path rootDir) {
197     return new Path(getSnapshotsDir(rootDir), SNAPSHOT_TMP_DIR_NAME);
198   }
199 
200   /**
201    * Get the directory to build a snapshot, before it is finalized
202    * @param snapshot snapshot that will be built
203    * @param rootDir root directory of the hbase installation
204    * @return {@link Path} where one can build a snapshot
205    */
206   public static Path getWorkingSnapshotDir(SnapshotDescription snapshot, final Path rootDir) {
207     return getCompletedSnapshotDir(getWorkingSnapshotDir(rootDir), snapshot.getName());
208   }
209 
210   /**
211    * Get the directory to build a snapshot, before it is finalized
212    * @param snapshotName name of the snapshot
213    * @param rootDir root directory of the hbase installation
214    * @return {@link Path} where one can build a snapshot
215    */
216   public static Path getWorkingSnapshotDir(String snapshotName, final Path rootDir) {
217     return getCompletedSnapshotDir(getWorkingSnapshotDir(rootDir), snapshotName);
218   }
219 
220   /**
221    * Get the directory to store the snapshot instance
222    * @param snapshotsDir hbase-global directory for storing all snapshots
223    * @param snapshotName name of the snapshot to take
224    * @return the final directory for the completed snapshot
225    */
226   private static final Path getCompletedSnapshotDir(final Path snapshotsDir, String snapshotName) {
227     return new Path(snapshotsDir, snapshotName);
228   }
229 
230   /**
231    * @param rootDir hbase root directory
232    * @return the directory for all completed snapshots;
233    */
234   public static final Path getSnapshotsDir(Path rootDir) {
235     return new Path(rootDir, HConstants.SNAPSHOT_DIR_NAME);
236   }
237 
238   /**
239    * Convert the passed snapshot description into a 'full' snapshot description based on default
240    * parameters, if none have been supplied. This resolves any 'optional' parameters that aren't
241    * supplied to their default values.
242    * @param snapshot general snapshot descriptor
243    * @param conf Configuration to read configured snapshot defaults if snapshot is not complete
244    * @return a valid snapshot description
245    * @throws IllegalArgumentException if the {@link SnapshotDescription} is not a complete
246    *           {@link SnapshotDescription}.
247    */
248   public static SnapshotDescription validate(SnapshotDescription snapshot, Configuration conf)
249       throws IllegalArgumentException {
250     if (!snapshot.hasTable()) {
251       throw new IllegalArgumentException(
252         "Descriptor doesn't apply to a table, so we can't build it.");
253     }
254 
255     // set the creation time, if one hasn't been set
256     long time = snapshot.getCreationTime();
257     if (time == SnapshotDescriptionUtils.NO_SNAPSHOT_START_TIME_SPECIFIED) {
258       time = EnvironmentEdgeManager.currentTime();
259       LOG.debug("Creation time not specified, setting to:" + time + " (current time:"
260           + EnvironmentEdgeManager.currentTime() + ").");
261       SnapshotDescription.Builder builder = snapshot.toBuilder();
262       builder.setCreationTime(time);
263       snapshot = builder.build();
264     }
265     return snapshot;
266   }
267 
268   /**
269    * Write the snapshot description into the working directory of a snapshot
270    * @param snapshot description of the snapshot being taken
271    * @param workingDir working directory of the snapshot
272    * @param fs {@link FileSystem} on which the snapshot should be taken
273    * @throws IOException if we can't reach the filesystem and the file cannot be cleaned up on
274    *           failure
275    */
276   public static void writeSnapshotInfo(SnapshotDescription snapshot, Path workingDir, FileSystem fs)
277       throws IOException {
278     FsPermission perms = FSUtils.getFilePermissions(fs, fs.getConf(),
279       HConstants.DATA_FILE_UMASK_KEY);
280     Path snapshotInfo = new Path(workingDir, SnapshotDescriptionUtils.SNAPSHOTINFO_FILE);
281     try {
282       FSDataOutputStream out = FSUtils.create(fs, snapshotInfo, perms, true);
283       try {
284         snapshot.writeTo(out);
285       } finally {
286         out.close();
287       }
288     } catch (IOException e) {
289       // if we get an exception, try to remove the snapshot info
290       if (!fs.delete(snapshotInfo, false)) {
291         String msg = "Couldn't delete snapshot info file: " + snapshotInfo;
292         LOG.error(msg);
293         throw new IOException(msg);
294       }
295     }
296   }
297 
298   /**
299    * Create in-progress tag under .tmp of in-progress snapshot
300    * */
301   public static void createInProgressTag(Path workingDir, FileSystem fs) throws IOException {
302     FsPermission perms = FSUtils.getFilePermissions(fs, fs.getConf(),
303       HConstants.DATA_FILE_UMASK_KEY);
304     Path snapshot_in_progress = new Path(workingDir, SnapshotDescriptionUtils.SNAPSHOT_IN_PROGRESS);
305     FSUtils.create(fs, snapshot_in_progress, perms, true);
306   }
307 
308   /**
309    * Read in the {@link org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription} stored for the snapshot in the passed directory
310    * @param fs filesystem where the snapshot was taken
311    * @param snapshotDir directory where the snapshot was stored
312    * @return the stored snapshot description
313    * @throws CorruptedSnapshotException if the
314    * snapshot cannot be read
315    */
316   public static SnapshotDescription readSnapshotInfo(FileSystem fs, Path snapshotDir)
317       throws CorruptedSnapshotException {
318     Path snapshotInfo = new Path(snapshotDir, SNAPSHOTINFO_FILE);
319     try {
320       FSDataInputStream in = null;
321       try {
322         in = fs.open(snapshotInfo);
323         SnapshotDescription desc = SnapshotDescription.parseFrom(in);
324         return desc;
325       } finally {
326         if (in != null) in.close();
327       }
328     } catch (IOException e) {
329       throw new CorruptedSnapshotException("Couldn't read snapshot info from:" + snapshotInfo, e);
330     }
331   }
332 
333   /**
334    * Move the finished snapshot to its final, publicly visible directory - this marks the snapshot
335    * as 'complete'.
336    * @param snapshot description of the snapshot being tabken
337    * @param rootdir root directory of the hbase installation
338    * @param workingDir directory where the in progress snapshot was built
339    * @param fs {@link FileSystem} where the snapshot was built
340    * @throws org.apache.hadoop.hbase.snapshot.SnapshotCreationException if the
341    * snapshot could not be moved
342    * @throws IOException the filesystem could not be reached
343    */
344   public static void completeSnapshot(SnapshotDescription snapshot, Path rootdir, Path workingDir,
345       FileSystem fs) throws SnapshotCreationException, IOException {
346     Path finishedDir = getCompletedSnapshotDir(snapshot, rootdir);
347     LOG.debug("Snapshot is done, just moving the snapshot from " + workingDir + " to "
348         + finishedDir);
349     if (!fs.rename(workingDir, finishedDir)) {
350       throw new SnapshotCreationException("Failed to move working directory(" + workingDir
351           + ") to completed directory(" + finishedDir + ").", snapshot);
352     }
353   }
354 
355   /**
356    * Check if the user is this table snapshot's owner
357    * @param snapshot the table snapshot description
358    * @param user the user
359    * @return true if the user is the owner of the snapshot,
360    *         false otherwise or the snapshot owner field is not present.
361    */
362   public static boolean isSnapshotOwner(final SnapshotDescription snapshot, final User user) {
363     if (user == null) return false;
364     if (!snapshot.hasOwner()) return false;
365     return snapshot.getOwner().equals(user.getShortName());
366   }
367 }