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 }