1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.snapshot;
19
20 import java.io.IOException;
21 import java.net.URI;
22 import java.security.PrivilegedExceptionAction;
23 import java.util.Collections;
24 import java.util.concurrent.TimeUnit;
25
26 import com.google.common.collect.ListMultimap;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.conf.Configuration;
30 import org.apache.hadoop.fs.FSDataInputStream;
31 import org.apache.hadoop.fs.FSDataOutputStream;
32 import org.apache.hadoop.fs.FileSystem;
33 import org.apache.hadoop.fs.FileUtil;
34 import org.apache.hadoop.fs.Path;
35 import org.apache.hadoop.fs.permission.FsPermission;
36 import org.apache.hadoop.hbase.HConstants;
37 import org.apache.hadoop.hbase.TableName;
38 import org.apache.hadoop.hbase.classification.InterfaceAudience;
39 import org.apache.hadoop.hbase.client.Admin;
40 import org.apache.hadoop.hbase.client.Connection;
41 import org.apache.hadoop.hbase.client.ConnectionFactory;
42 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
43 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
44 import org.apache.hadoop.hbase.security.User;
45 import org.apache.hadoop.hbase.security.access.AccessControlLists;
46 import org.apache.hadoop.hbase.security.access.TablePermission;
47 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
48 import org.apache.hadoop.hbase.util.FSUtils;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 @InterfaceAudience.Private
87 public final class SnapshotDescriptionUtils {
88
89
90
91
92 public static class CompletedSnaphotDirectoriesFilter extends FSUtils.BlackListDirFilter {
93
94
95
96
97 public CompletedSnaphotDirectoriesFilter(FileSystem fs) {
98 super(fs, Collections.singletonList(SNAPSHOT_TMP_DIR_NAME));
99 }
100 }
101
102 private static final Log LOG = LogFactory.getLog(SnapshotDescriptionUtils.class);
103
104
105
106
107 public static final int SNAPSHOT_LAYOUT_VERSION = SnapshotManifestV2.DESCRIPTOR_VERSION;
108
109
110
111
112
113 public static final String SNAPSHOTINFO_FILE = ".snapshotinfo";
114
115
116 public static final String SNAPSHOT_TMP_DIR_NAME = ".tmp";
117
118
119
120
121
122 public static final String SNAPSHOT_WORKING_DIR = "hbase.snapshot.working.dir";
123
124
125
126 public static final long NO_SNAPSHOT_START_TIME_SPECIFIED = 0;
127
128
129 private static final long NO_SNAPSHOT_TTL_SPECIFIED = 0;
130
131 public static final String MASTER_SNAPSHOT_TIMEOUT_MILLIS = "hbase.snapshot.master.timeout.millis";
132
133
134 public static final long DEFAULT_MAX_WAIT_TIME = 60000 * 5 ;
135
136
137
138
139
140
141 @Deprecated
142 public static final int SNAPSHOT_TIMEOUT_MILLIS_DEFAULT = 60000 * 5;
143
144
145
146
147
148
149 @Deprecated
150 public static final String SNAPSHOT_TIMEOUT_MILLIS_KEY = "hbase.snapshot.master.timeoutMillis";
151
152 private SnapshotDescriptionUtils() {
153
154 }
155
156
157
158
159
160
161
162 public static long getMaxMasterTimeout(Configuration conf, SnapshotDescription.Type type,
163 long defaultMaxWaitTime) {
164 String confKey;
165 switch (type) {
166 case DISABLED:
167 default:
168 confKey = MASTER_SNAPSHOT_TIMEOUT_MILLIS;
169 }
170 return Math.max(conf.getLong(confKey, defaultMaxWaitTime),
171 conf.getLong(SNAPSHOT_TIMEOUT_MILLIS_KEY, defaultMaxWaitTime));
172 }
173
174
175
176
177
178
179
180 public static Path getSnapshotRootDir(final Path rootDir) {
181 return new Path(rootDir, HConstants.SNAPSHOT_DIR_NAME);
182 }
183
184
185
186
187
188
189
190
191 public static Path getCompletedSnapshotDir(final SnapshotDescription snapshot, final Path rootDir) {
192 return getCompletedSnapshotDir(snapshot.getName(), rootDir);
193 }
194
195
196
197
198
199
200
201
202 public static Path getCompletedSnapshotDir(final String snapshotName, final Path rootDir) {
203 return getSpecifiedSnapshotDir(getSnapshotsDir(rootDir), snapshotName);
204 }
205
206
207
208
209
210
211
212
213 public static Path getWorkingSnapshotDir(final Path rootDir, final Configuration conf) {
214 return new Path(conf.get(SNAPSHOT_WORKING_DIR,
215 getDefaultWorkingSnapshotDir(rootDir).toString()));
216 }
217
218
219
220
221
222
223
224
225 public static Path getWorkingSnapshotDir(SnapshotDescription snapshot, final Path rootDir,
226 Configuration conf) {
227 return getWorkingSnapshotDir(snapshot.getName(), rootDir, conf);
228 }
229
230
231
232
233
234
235
236
237 public static Path getWorkingSnapshotDir(String snapshotName, final Path rootDir,
238 Configuration conf) {
239 return getSpecifiedSnapshotDir(getWorkingSnapshotDir(rootDir, conf), snapshotName);
240 }
241
242
243
244
245
246
247
248 private static final Path getSpecifiedSnapshotDir(final Path snapshotsDir, String snapshotName) {
249 return new Path(snapshotsDir, snapshotName);
250 }
251
252
253
254
255
256 public static final Path getSnapshotsDir(Path rootDir) {
257 return new Path(rootDir, HConstants.SNAPSHOT_DIR_NAME);
258 }
259
260
261
262
263
264
265
266
267 public static boolean isSubDirectoryOf(final Path workingDir, final Path rootDir) {
268 return workingDir.toString().startsWith(rootDir.toString() + Path.SEPARATOR);
269 }
270
271
272
273
274
275
276
277
278 public static boolean isWithinDefaultWorkingDir(final Path workingDir, Configuration conf)
279 throws IOException {
280 Path defaultWorkingDir = getDefaultWorkingSnapshotDir(FSUtils.getRootDir(conf));
281 return workingDir.equals(defaultWorkingDir) || isSubDirectoryOf(workingDir, defaultWorkingDir);
282 }
283
284
285
286
287
288
289
290 private static Path getDefaultWorkingSnapshotDir(final Path rootDir) {
291 return new Path(getSnapshotsDir(rootDir), SNAPSHOT_TMP_DIR_NAME);
292 }
293
294
295
296
297
298
299
300
301
302
303
304 public static SnapshotDescription validate(SnapshotDescription snapshot, Configuration conf)
305 throws IllegalArgumentException, IOException {
306 if (!snapshot.hasTable()) {
307 throw new IllegalArgumentException(
308 "Descriptor doesn't apply to a table, so we can't build it.");
309 }
310
311
312 long time = snapshot.getCreationTime();
313 if (time == SnapshotDescriptionUtils.NO_SNAPSHOT_START_TIME_SPECIFIED) {
314 time = EnvironmentEdgeManager.currentTime();
315 LOG.debug("Creation time not specified, setting to:" + time + " (current time:"
316 + EnvironmentEdgeManager.currentTime() + ").");
317 SnapshotDescription.Builder builder = snapshot.toBuilder();
318 builder.setCreationTime(time);
319 snapshot = builder.build();
320 }
321 long ttl = snapshot.getTtl();
322
323 if (ttl == SnapshotDescriptionUtils.NO_SNAPSHOT_TTL_SPECIFIED ||
324 ttl > TimeUnit.MILLISECONDS.toSeconds(Long.MAX_VALUE)) {
325 final long defaultSnapshotTtl = conf.getLong(HConstants.DEFAULT_SNAPSHOT_TTL_CONFIG_KEY,
326 HConstants.DEFAULT_SNAPSHOT_TTL);
327 if (LOG.isDebugEnabled()) {
328 LOG.debug("Snapshot current TTL value: " + ttl + " resetting it to default value: " +
329 defaultSnapshotTtl);
330 }
331 ttl = defaultSnapshotTtl;
332 }
333 SnapshotDescription.Builder builder = snapshot.toBuilder();
334 builder.setTtl(ttl);
335 snapshot = builder.build();
336
337
338 if(isSecurityAvailable(conf)){
339 snapshot = writeAclToSnapshotDescription(snapshot, conf);
340 }
341
342 return snapshot;
343 }
344
345
346
347
348
349
350
351
352
353 public static void writeSnapshotInfo(SnapshotDescription snapshot, Path workingDir, FileSystem fs)
354 throws IOException {
355 FsPermission perms = FSUtils.getFilePermissions(fs, fs.getConf(),
356 HConstants.DATA_FILE_UMASK_KEY);
357 Path snapshotInfo = new Path(workingDir, SnapshotDescriptionUtils.SNAPSHOTINFO_FILE);
358 try {
359 FSDataOutputStream out = FSUtils.create(fs, snapshotInfo, perms, true);
360 try {
361 snapshot.writeTo(out);
362 } finally {
363 out.close();
364 }
365 } catch (IOException e) {
366
367 if (!fs.delete(snapshotInfo, false)) {
368 String msg = "Couldn't delete snapshot info file: " + snapshotInfo;
369 LOG.error(msg);
370 throw new IOException(msg);
371 }
372 }
373 }
374
375
376
377
378
379
380
381
382
383 public static SnapshotDescription readSnapshotInfo(FileSystem fs, Path snapshotDir)
384 throws CorruptedSnapshotException {
385 Path snapshotInfo = new Path(snapshotDir, SNAPSHOTINFO_FILE);
386 try {
387 FSDataInputStream in = null;
388 try {
389 in = fs.open(snapshotInfo);
390 SnapshotDescription desc = SnapshotDescription.parseFrom(in);
391 return desc;
392 } finally {
393 if (in != null) in.close();
394 }
395 } catch (IOException e) {
396 throw new CorruptedSnapshotException("Couldn't read snapshot info from:" + snapshotInfo, e);
397 }
398 }
399
400
401
402
403
404
405
406
407
408
409
410
411 public static void completeSnapshot(Path snapshotDir, Path workingDir, FileSystem fs,
412 FileSystem workingDirFs, Configuration conf) throws SnapshotCreationException, IOException {
413 LOG.debug("Snapshot is done, just moving the snapshot from " + workingDir + " to "
414 + snapshotDir);
415 if (!workingDirFs.exists(workingDir)) {
416 throw new IOException("Failed to moving nonexistent snapshot working directory "
417 + workingDir + " to " + snapshotDir);
418 }
419
420
421
422
423 URI workingURI = workingDirFs.getUri();
424 URI rootURI = fs.getUri();
425
426 if ((shouldSkipRenameSnapshotDirectories(workingURI, rootURI)
427 || !fs.rename(workingDir, snapshotDir))
428 && !FileUtil.copy(workingDirFs, workingDir, fs, snapshotDir, true, true, conf)) {
429 throw new SnapshotCreationException("Failed to copy working directory(" + workingDir
430 + ") to completed directory(" + snapshotDir + ").");
431 }
432 }
433
434 static boolean shouldSkipRenameSnapshotDirectories(URI workingURI, URI rootURI) {
435
436 if (workingURI.getScheme() == null &&
437 (rootURI.getScheme() != null && !rootURI.getScheme().equalsIgnoreCase("file"))) {
438 return true;
439 }
440 if (workingURI.getScheme() != null && !workingURI.getScheme().equals(rootURI.getScheme())) {
441 return true;
442 }
443
444
445 if (workingURI.getAuthority() == null && rootURI.getAuthority() != null) {
446 return true;
447 }
448 if (workingURI.getAuthority() != null &&
449 !workingURI.getAuthority().equals(rootURI.getAuthority())) {
450 return true;
451 }
452
453
454 if (workingURI.getUserInfo() == null && rootURI.getUserInfo() != null) {
455 return true;
456 }
457 if (workingURI.getUserInfo() != null &&
458 !workingURI.getUserInfo().equals(rootURI.getUserInfo())) {
459 return true;
460 }
461
462 return false;
463 }
464
465
466
467
468
469
470
471
472
473 public static boolean isSnapshotOwner(final SnapshotDescription snapshot, final User user) {
474 if (user == null) return false;
475 if (!snapshot.hasOwner()) return false;
476 return snapshot.getOwner().equals(user.getShortName());
477 }
478
479 public static boolean isSecurityAvailable(Configuration conf) throws IOException {
480 Connection conn = ConnectionFactory.createConnection(conf);
481 try {
482 Admin admin = conn.getAdmin();
483 try {
484 return admin.tableExists(AccessControlLists.ACL_TABLE_NAME);
485 } finally {
486 admin.close();
487 }
488 } finally {
489 conn.close();
490 }
491 }
492
493 private static SnapshotDescription writeAclToSnapshotDescription(
494 final SnapshotDescription snapshot, final Configuration conf) throws IOException {
495 ListMultimap<String, TablePermission> perms =
496 User.runAsLoginUser(new PrivilegedExceptionAction<ListMultimap<String, TablePermission>>() {
497 @Override
498 public ListMultimap<String, TablePermission> run() throws Exception {
499 return AccessControlLists.getTablePermissions(conf,
500 TableName.valueOf(snapshot.getTable()));
501 }
502 });
503 return snapshot.toBuilder().setUsersAndPermissions(ProtobufUtil.toUserTablePermissions(perms))
504 .build();
505 }
506 }