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.IOException;
21 import java.util.Collections;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25 import org.apache.hadoop.conf.Configuration;
26 import org.apache.hadoop.fs.FSDataInputStream;
27 import org.apache.hadoop.fs.FSDataOutputStream;
28 import org.apache.hadoop.fs.FileSystem;
29 import org.apache.hadoop.fs.Path;
30 import org.apache.hadoop.fs.permission.FsPermission;
31 import org.apache.hadoop.hbase.HConstants;
32 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
33 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
34 import org.apache.hadoop.hbase.snapshot.SnapshotManifestV1;
35 import org.apache.hadoop.hbase.snapshot.SnapshotManifestV2;
36 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
37 import org.apache.hadoop.hbase.util.FSUtils;
38
39 /**
40 * Utility class to help manage {@link SnapshotDescription SnapshotDesriptions}.
41 * <p>
42 * Snapshots are laid out on disk like this:
43 *
44 * <pre>
45 * /hbase/.snapshots
46 * /.tmp <---- working directory
47 * /[snapshot name] <----- completed snapshot
48 * </pre>
49 *
50 * A completed snapshot named 'completed' then looks like (multiple regions, servers, files, etc.
51 * signified by '...' on the same directory depth).
52 *
53 * <pre>
54 * /hbase/.snapshots/completed
55 * .snapshotinfo <--- Description of the snapshot
56 * .tableinfo <--- Copy of the tableinfo
57 * /.logs
58 * /[server_name]
59 * /... [log files]
60 * ...
61 * /[region name] <---- All the region's information
62 * .regioninfo <---- Copy of the HRegionInfo
63 * /[column family name]
64 * /[hfile name] <--- name of the hfile in the real region
65 * ...
66 * ...
67 * ...
68 * </pre>
69 *
70 * Utility methods in this class are useful for getting the correct locations for different parts of
71 * the snapshot, as well as moving completed snapshots into place (see
72 * {@link #completeSnapshot}, and writing the
73 * {@link SnapshotDescription} to the working snapshot directory.
74 */
75 public class SnapshotDescriptionUtils {
76
77 /**
78 * Filter that only accepts completed snapshot directories
79 */
80 public static class CompletedSnaphotDirectoriesFilter extends FSUtils.BlackListDirFilter {
81
82 /**
83 * @param fs
84 */
85 public CompletedSnaphotDirectoriesFilter(FileSystem fs) {
86 super(fs, Collections.singletonList(SNAPSHOT_TMP_DIR_NAME));
87 }
88 }
89
90 private static final Log LOG = LogFactory.getLog(SnapshotDescriptionUtils.class);
91
92 /**
93 * Version of the fs layout for new snapshot.
94 * Each version of hbase should be able to read older formats.
95 */
96 public static final String SNAPSHOT_LAYOUT_CONF_KEY = "hbase.snapshot.format.version";
97 public static final int SNAPSHOT_LAYOUT_LATEST_FORMAT = 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 // snapshot operation values
108 /** Default value if no start time is specified */
109 public static final long NO_SNAPSHOT_START_TIME_SPECIFIED = 0;
110
111 public static final String MASTER_SNAPSHOT_TIMEOUT_MILLIS = "hbase.snapshot.master.timeout.millis";
112
113 /** By default, wait 60 seconds for a snapshot to complete */
114 public static final long DEFAULT_MAX_WAIT_TIME = 60000;
115
116 private SnapshotDescriptionUtils() {
117 // private constructor for utility class
118 }
119
120 public static int getDefaultSnapshotLayoutFormat(final Configuration conf) {
121 int layoutFormat = conf.getInt(SNAPSHOT_LAYOUT_CONF_KEY, SNAPSHOT_LAYOUT_LATEST_FORMAT);
122 if (layoutFormat >= SnapshotManifestV2.DESCRIPTOR_VERSION) {
123 return SnapshotManifestV2.DESCRIPTOR_VERSION;
124 }
125 return SnapshotManifestV1.DESCRIPTOR_VERSION;
126 }
127
128
129 /**
130 * @param conf {@link Configuration} from which to check for the timeout
131 * @param type type of snapshot being taken
132 * @param defaultMaxWaitTime Default amount of time to wait, if none is in the configuration
133 * @return the max amount of time the master should wait for a snapshot to complete
134 */
135 public static long getMaxMasterTimeout(Configuration conf, SnapshotDescription.Type type,
136 long defaultMaxWaitTime) {
137 String confKey;
138 switch (type) {
139 case DISABLED:
140 default:
141 confKey = MASTER_SNAPSHOT_TIMEOUT_MILLIS;
142 }
143 return conf.getLong(confKey, defaultMaxWaitTime);
144 }
145
146 /**
147 * Get the snapshot root directory. All the snapshots are kept under this directory, i.e.
148 * ${hbase.rootdir}/.snapshot
149 * @param rootDir hbase root directory
150 * @return the base directory in which all snapshots are kept
151 */
152 public static Path getSnapshotRootDir(final Path rootDir) {
153 return new Path(rootDir, HConstants.SNAPSHOT_DIR_NAME);
154 }
155
156 /**
157 * Get the directory for a specified snapshot. This directory is a sub-directory of snapshot root
158 * directory and all the data files for a snapshot are kept under this directory.
159 * @param snapshot snapshot being taken
160 * @param rootDir hbase root directory
161 * @return the final directory for the completed snapshot
162 */
163 public static Path getCompletedSnapshotDir(final SnapshotDescription snapshot, final Path rootDir) {
164 return getCompletedSnapshotDir(snapshot.getName(), rootDir);
165 }
166
167 /**
168 * Get the directory for a completed snapshot. This directory is a sub-directory of snapshot root
169 * directory and all the data files for a snapshot are kept under this directory.
170 * @param snapshotName name of the snapshot being taken
171 * @param rootDir hbase root directory
172 * @return the final directory for the completed snapshot
173 */
174 public static Path getCompletedSnapshotDir(final String snapshotName, final Path rootDir) {
175 return getCompletedSnapshotDir(getSnapshotsDir(rootDir), snapshotName);
176 }
177
178 /**
179 * Get the general working directory for snapshots - where they are built, where they are
180 * temporarily copied on export, etc.
181 * @param rootDir root directory of the HBase installation
182 * @return Path to the snapshot tmp directory, relative to the passed root directory
183 */
184 public static Path getWorkingSnapshotDir(final Path rootDir) {
185 return new Path(getSnapshotsDir(rootDir), SNAPSHOT_TMP_DIR_NAME);
186 }
187
188 /**
189 * Get the directory to build a snapshot, before it is finalized
190 * @param snapshot snapshot that will be built
191 * @param rootDir root directory of the hbase installation
192 * @return {@link Path} where one can build a snapshot
193 */
194 public static Path getWorkingSnapshotDir(SnapshotDescription snapshot, final Path rootDir) {
195 return getCompletedSnapshotDir(getWorkingSnapshotDir(rootDir), snapshot.getName());
196 }
197
198 /**
199 * Get the directory to build a snapshot, before it is finalized
200 * @param snapshotName name of the snapshot
201 * @param rootDir root directory of the hbase installation
202 * @return {@link Path} where one can build a snapshot
203 */
204 public static Path getWorkingSnapshotDir(String snapshotName, final Path rootDir) {
205 return getCompletedSnapshotDir(getWorkingSnapshotDir(rootDir), snapshotName);
206 }
207
208 /**
209 * Get the directory to store the snapshot instance
210 * @param snapshotsDir hbase-global directory for storing all snapshots
211 * @param snapshotName name of the snapshot to take
212 * @return the final directory for the completed snapshot
213 */
214 private static final Path getCompletedSnapshotDir(final Path snapshotsDir, String snapshotName) {
215 return new Path(snapshotsDir, snapshotName);
216 }
217
218 /**
219 * @param rootDir hbase root directory
220 * @return the directory for all completed snapshots;
221 */
222 public static final Path getSnapshotsDir(Path rootDir) {
223 return new Path(rootDir, HConstants.SNAPSHOT_DIR_NAME);
224 }
225
226 /**
227 * Convert the passed snapshot description into a 'full' snapshot description based on default
228 * parameters, if none have been supplied. This resolves any 'optional' parameters that aren't
229 * supplied to their default values.
230 * @param snapshot general snapshot descriptor
231 * @param conf Configuration to read configured snapshot defaults if snapshot is not complete
232 * @return a valid snapshot description
233 * @throws IllegalArgumentException if the {@link SnapshotDescription} is not a complete
234 * {@link SnapshotDescription}.
235 */
236 public static SnapshotDescription validate(SnapshotDescription snapshot, Configuration conf)
237 throws IllegalArgumentException {
238 if (!snapshot.hasTable()) {
239 throw new IllegalArgumentException(
240 "Descriptor doesn't apply to a table, so we can't build it.");
241 }
242
243 // set the creation time, if one hasn't been set
244 long time = snapshot.getCreationTime();
245 if (time == SnapshotDescriptionUtils.NO_SNAPSHOT_START_TIME_SPECIFIED) {
246 time = EnvironmentEdgeManager.currentTimeMillis();
247 LOG.debug("Creation time not specified, setting to:" + time + " (current time:"
248 + EnvironmentEdgeManager.currentTimeMillis() + ").");
249 SnapshotDescription.Builder builder = snapshot.toBuilder();
250 builder.setCreationTime(time);
251 snapshot = builder.build();
252 }
253 return snapshot;
254 }
255
256 /**
257 * Write the snapshot description into the working directory of a snapshot
258 * @param snapshot description of the snapshot being taken
259 * @param workingDir working directory of the snapshot
260 * @param fs {@link FileSystem} on which the snapshot should be taken
261 * @throws IOException if we can't reach the filesystem and the file cannot be cleaned up on
262 * failure
263 */
264 public static void writeSnapshotInfo(SnapshotDescription snapshot, Path workingDir, FileSystem fs)
265 throws IOException {
266 FsPermission perms = FSUtils.getFilePermissions(fs, fs.getConf(),
267 HConstants.DATA_FILE_UMASK_KEY);
268 Path snapshotInfo = new Path(workingDir, SnapshotDescriptionUtils.SNAPSHOTINFO_FILE);
269 try {
270 FSDataOutputStream out = FSUtils.create(fs, snapshotInfo, perms, true);
271 try {
272 snapshot.writeTo(out);
273 } finally {
274 out.close();
275 }
276 } catch (IOException e) {
277 // if we get an exception, try to remove the snapshot info
278 if (!fs.delete(snapshotInfo, false)) {
279 String msg = "Couldn't delete snapshot info file: " + snapshotInfo;
280 LOG.error(msg);
281 throw new IOException(msg);
282 }
283 }
284 }
285
286 /**
287 * Read in the {@link org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription} stored for the snapshot in the passed directory
288 * @param fs filesystem where the snapshot was taken
289 * @param snapshotDir directory where the snapshot was stored
290 * @return the stored snapshot description
291 * @throws CorruptedSnapshotException if the
292 * snapshot cannot be read
293 */
294 public static SnapshotDescription readSnapshotInfo(FileSystem fs, Path snapshotDir)
295 throws CorruptedSnapshotException {
296 Path snapshotInfo = new Path(snapshotDir, SNAPSHOTINFO_FILE);
297 try {
298 FSDataInputStream in = null;
299 try {
300 in = fs.open(snapshotInfo);
301 SnapshotDescription desc = SnapshotDescription.parseFrom(in);
302 return desc;
303 } finally {
304 if (in != null) in.close();
305 }
306 } catch (IOException e) {
307 throw new CorruptedSnapshotException("Couldn't read snapshot info from:" + snapshotInfo, e);
308 }
309 }
310
311 /**
312 * Move the finished snapshot to its final, publicly visible directory - this marks the snapshot
313 * as 'complete'.
314 * @param snapshot description of the snapshot being tabken
315 * @param rootdir root directory of the hbase installation
316 * @param workingDir directory where the in progress snapshot was built
317 * @param fs {@link FileSystem} where the snapshot was built
318 * @throws org.apache.hadoop.hbase.snapshot.SnapshotCreationException if the
319 * snapshot could not be moved
320 * @throws IOException the filesystem could not be reached
321 */
322 public static void completeSnapshot(SnapshotDescription snapshot, Path rootdir, Path workingDir,
323 FileSystem fs) throws SnapshotCreationException, IOException {
324 Path finishedDir = getCompletedSnapshotDir(snapshot, rootdir);
325 LOG.debug("Snapshot is done, just moving the snapshot from " + workingDir + " to "
326 + finishedDir);
327 if (!fs.rename(workingDir, finishedDir)) {
328 throw new SnapshotCreationException("Failed to move working directory(" + workingDir
329 + ") to completed directory(" + finishedDir + ").", snapshot);
330 }
331 }
332
333 }