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 com.google.common.annotations.VisibleForTesting;
21 import java.io.IOException;
22 import java.net.URI;
23 import java.security.PrivilegedExceptionAction;
24 import java.util.Collections;
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 public static final String MASTER_SNAPSHOT_TIMEOUT_MILLIS = "hbase.snapshot.master.timeout.millis";
130
131
132 public static final long DEFAULT_MAX_WAIT_TIME = 60000 * 5 ;
133
134
135
136
137
138
139 @Deprecated
140 public static final int SNAPSHOT_TIMEOUT_MILLIS_DEFAULT = 60000 * 5;
141
142
143
144
145
146
147 @Deprecated
148 public static final String SNAPSHOT_TIMEOUT_MILLIS_KEY = "hbase.snapshot.master.timeoutMillis";
149
150 private SnapshotDescriptionUtils() {
151
152 }
153
154
155
156
157
158
159
160 public static long getMaxMasterTimeout(Configuration conf, SnapshotDescription.Type type,
161 long defaultMaxWaitTime) {
162 String confKey;
163 switch (type) {
164 case DISABLED:
165 default:
166 confKey = MASTER_SNAPSHOT_TIMEOUT_MILLIS;
167 }
168 return Math.max(conf.getLong(confKey, defaultMaxWaitTime),
169 conf.getLong(SNAPSHOT_TIMEOUT_MILLIS_KEY, defaultMaxWaitTime));
170 }
171
172
173
174
175
176
177
178 public static Path getSnapshotRootDir(final Path rootDir) {
179 return new Path(rootDir, HConstants.SNAPSHOT_DIR_NAME);
180 }
181
182
183
184
185
186
187
188
189 public static Path getCompletedSnapshotDir(final SnapshotDescription snapshot, final Path rootDir) {
190 return getCompletedSnapshotDir(snapshot.getName(), rootDir);
191 }
192
193
194
195
196
197
198
199
200 public static Path getCompletedSnapshotDir(final String snapshotName, final Path rootDir) {
201 return getSpecifiedSnapshotDir(getSnapshotsDir(rootDir), snapshotName);
202 }
203
204
205
206
207
208
209
210
211 public static Path getWorkingSnapshotDir(final Path rootDir, final Configuration conf) {
212 return new Path(conf.get(SNAPSHOT_WORKING_DIR,
213 getDefaultWorkingSnapshotDir(rootDir).toString()));
214 }
215
216
217
218
219
220
221
222
223 public static Path getWorkingSnapshotDir(SnapshotDescription snapshot, final Path rootDir,
224 Configuration conf) {
225 return getWorkingSnapshotDir(snapshot.getName(), rootDir, conf);
226 }
227
228
229
230
231
232
233
234
235 public static Path getWorkingSnapshotDir(String snapshotName, final Path rootDir,
236 Configuration conf) {
237 return getSpecifiedSnapshotDir(getWorkingSnapshotDir(rootDir, conf), snapshotName);
238 }
239
240
241
242
243
244
245
246 private static final Path getSpecifiedSnapshotDir(final Path snapshotsDir, String snapshotName) {
247 return new Path(snapshotsDir, snapshotName);
248 }
249
250
251
252
253
254 public static final Path getSnapshotsDir(Path rootDir) {
255 return new Path(rootDir, HConstants.SNAPSHOT_DIR_NAME);
256 }
257
258
259
260
261
262
263
264
265 public static boolean isSubDirectoryOf(final Path workingDir, final Path rootDir) {
266 return workingDir.toString().startsWith(rootDir.toString() + Path.SEPARATOR);
267 }
268
269
270
271
272
273
274
275
276 public static boolean isWithinDefaultWorkingDir(final Path workingDir, Configuration conf)
277 throws IOException {
278 Path defaultWorkingDir = getDefaultWorkingSnapshotDir(FSUtils.getRootDir(conf));
279 return workingDir.equals(defaultWorkingDir) || isSubDirectoryOf(workingDir, defaultWorkingDir);
280 }
281
282
283
284
285
286
287
288 private static Path getDefaultWorkingSnapshotDir(final Path rootDir) {
289 return new Path(getSnapshotsDir(rootDir), SNAPSHOT_TMP_DIR_NAME);
290 }
291
292
293
294
295
296
297
298
299
300
301
302 public static SnapshotDescription validate(SnapshotDescription snapshot, Configuration conf)
303 throws IllegalArgumentException, IOException {
304 if (!snapshot.hasTable()) {
305 throw new IllegalArgumentException(
306 "Descriptor doesn't apply to a table, so we can't build it.");
307 }
308
309
310 long time = snapshot.getCreationTime();
311 if (time == SnapshotDescriptionUtils.NO_SNAPSHOT_START_TIME_SPECIFIED) {
312 time = EnvironmentEdgeManager.currentTime();
313 LOG.debug("Creation time not specified, setting to:" + time + " (current time:"
314 + EnvironmentEdgeManager.currentTime() + ").");
315 SnapshotDescription.Builder builder = snapshot.toBuilder();
316 builder.setCreationTime(time);
317 snapshot = builder.build();
318 }
319
320
321 if(isSecurityAvailable(conf)){
322 snapshot = writeAclToSnapshotDescription(snapshot, conf);
323 }
324
325 return snapshot;
326 }
327
328
329
330
331
332
333
334
335
336 public static void writeSnapshotInfo(SnapshotDescription snapshot, Path workingDir, FileSystem fs)
337 throws IOException {
338 FsPermission perms = FSUtils.getFilePermissions(fs, fs.getConf(),
339 HConstants.DATA_FILE_UMASK_KEY);
340 Path snapshotInfo = new Path(workingDir, SnapshotDescriptionUtils.SNAPSHOTINFO_FILE);
341 try {
342 FSDataOutputStream out = FSUtils.create(fs, snapshotInfo, perms, true);
343 try {
344 snapshot.writeTo(out);
345 } finally {
346 out.close();
347 }
348 } catch (IOException e) {
349
350 if (!fs.delete(snapshotInfo, false)) {
351 String msg = "Couldn't delete snapshot info file: " + snapshotInfo;
352 LOG.error(msg);
353 throw new IOException(msg);
354 }
355 }
356 }
357
358
359
360
361
362
363
364
365
366 public static SnapshotDescription readSnapshotInfo(FileSystem fs, Path snapshotDir)
367 throws CorruptedSnapshotException {
368 Path snapshotInfo = new Path(snapshotDir, SNAPSHOTINFO_FILE);
369 try {
370 FSDataInputStream in = null;
371 try {
372 in = fs.open(snapshotInfo);
373 SnapshotDescription desc = SnapshotDescription.parseFrom(in);
374 return desc;
375 } finally {
376 if (in != null) in.close();
377 }
378 } catch (IOException e) {
379 throw new CorruptedSnapshotException("Couldn't read snapshot info from:" + snapshotInfo, e);
380 }
381 }
382
383
384
385
386
387
388
389
390
391
392
393
394 public static void completeSnapshot(Path snapshotDir, Path workingDir, FileSystem fs,
395 FileSystem workingDirFs, Configuration conf) throws SnapshotCreationException, IOException {
396 LOG.debug("Snapshot is done, just moving the snapshot from " + workingDir + " to "
397 + snapshotDir);
398 if (!workingDirFs.exists(workingDir)) {
399 throw new IOException("Failed to moving nonexistent snapshot working directory "
400 + workingDir + " to " + snapshotDir);
401 }
402
403
404
405
406 URI workingURI = workingDirFs.getUri();
407 URI rootURI = fs.getUri();
408
409 if ((shouldSkipRenameSnapshotDirectories(workingURI, rootURI)
410 || !fs.rename(workingDir, snapshotDir))
411 && !FileUtil.copy(workingDirFs, workingDir, fs, snapshotDir, true, true, conf)) {
412 throw new SnapshotCreationException("Failed to copy working directory(" + workingDir
413 + ") to completed directory(" + snapshotDir + ").");
414 }
415 }
416
417 @VisibleForTesting
418 static boolean shouldSkipRenameSnapshotDirectories(URI workingURI, URI rootURI) {
419
420 if (workingURI.getScheme() == null &&
421 (rootURI.getScheme() != null && !rootURI.getScheme().equalsIgnoreCase("file"))) {
422 return true;
423 }
424 if (workingURI.getScheme() != null && !workingURI.getScheme().equals(rootURI.getScheme())) {
425 return true;
426 }
427
428
429 if (workingURI.getAuthority() == null && rootURI.getAuthority() != null) {
430 return true;
431 }
432 if (workingURI.getAuthority() != null &&
433 !workingURI.getAuthority().equals(rootURI.getAuthority())) {
434 return true;
435 }
436
437
438 if (workingURI.getUserInfo() == null && rootURI.getUserInfo() != null) {
439 return true;
440 }
441 if (workingURI.getUserInfo() != null &&
442 !workingURI.getUserInfo().equals(rootURI.getUserInfo())) {
443 return true;
444 }
445
446 return false;
447 }
448
449
450
451
452
453
454
455
456
457 public static boolean isSnapshotOwner(final SnapshotDescription snapshot, final User user) {
458 if (user == null) return false;
459 if (!snapshot.hasOwner()) return false;
460 return snapshot.getOwner().equals(user.getShortName());
461 }
462
463 public static boolean isSecurityAvailable(Configuration conf) throws IOException {
464 Connection conn = ConnectionFactory.createConnection(conf);
465 try {
466 Admin admin = conn.getAdmin();
467 try {
468 return admin.tableExists(AccessControlLists.ACL_TABLE_NAME);
469 } finally {
470 admin.close();
471 }
472 } finally {
473 conn.close();
474 }
475 }
476
477 private static SnapshotDescription writeAclToSnapshotDescription(
478 final SnapshotDescription snapshot, final Configuration conf) throws IOException {
479 ListMultimap<String, TablePermission> perms =
480 User.runAsLoginUser(new PrivilegedExceptionAction<ListMultimap<String, TablePermission>>() {
481 @Override
482 public ListMultimap<String, TablePermission> run() throws Exception {
483 return AccessControlLists.getTablePermissions(conf,
484 TableName.valueOf(snapshot.getTable()));
485 }
486 });
487 return snapshot.toBuilder().setUsersAndPermissions(ProtobufUtil.toUserTablePermissions(perms))
488 .build();
489 }
490 }