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