Skip to content

Commit 74ddf69

Browse files
authored
HDFS-16911. Distcp with snapshot diff to support Ozone filesystem. (#5364)
1 parent 3e2ae1d commit 74ddf69

File tree

2 files changed

+140
-37
lines changed

2 files changed

+140
-37
lines changed

hadoop-tools/hadoop-distcp/src/main/java/org/apache/hadoop/tools/DistCpSync.java

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,19 @@
2020
import org.apache.hadoop.classification.VisibleForTesting;
2121
import org.apache.hadoop.HadoopIllegalArgumentException;
2222
import org.apache.hadoop.conf.Configuration;
23+
import org.apache.hadoop.fs.CommonPathCapabilities;
2324
import org.apache.hadoop.fs.FileStatus;
2425
import org.apache.hadoop.fs.FileSystem;
2526
import org.apache.hadoop.fs.Path;
2627
import org.apache.hadoop.hdfs.DFSUtilClient;
27-
import org.apache.hadoop.hdfs.DistributedFileSystem;
2828
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
2929
import org.apache.hadoop.hdfs.protocol.SnapshotDiffReport;
30-
import org.apache.hadoop.hdfs.web.WebHdfsFileSystem;
3130
import org.apache.hadoop.tools.CopyListing.InvalidInputException;
3231

3332
import java.io.FileNotFoundException;
3433
import java.io.IOException;
34+
import java.lang.reflect.InvocationTargetException;
35+
import java.lang.reflect.Method;
3536
import java.util.Arrays;
3637
import java.util.List;
3738
import java.util.Random;
@@ -106,20 +107,7 @@ private boolean preSyncCheck() throws IOException {
106107
final FileSystem snapshotDiffFs = isRdiff() ? tgtFs : srcFs;
107108
final Path snapshotDiffDir = isRdiff() ? targetDir : sourceDir;
108109

109-
// currently we require both the source and the target file system are
110-
// DistributedFileSystem or (S)WebHdfsFileSystem.
111-
if (!(srcFs instanceof DistributedFileSystem
112-
|| srcFs instanceof WebHdfsFileSystem)) {
113-
throw new IllegalArgumentException("Unsupported source file system: "
114-
+ srcFs.getScheme() + "://. " +
115-
"Supported file systems: hdfs://, webhdfs:// and swebhdfs://.");
116-
}
117-
if (!(tgtFs instanceof DistributedFileSystem
118-
|| tgtFs instanceof WebHdfsFileSystem)) {
119-
throw new IllegalArgumentException("Unsupported target file system: "
120-
+ tgtFs.getScheme() + "://. " +
121-
"Supported file systems: hdfs://, webhdfs:// and swebhdfs://.");
122-
}
110+
checkFilesystemSupport(sourceDir,targetDir,srcFs, tgtFs);
123111

124112
// make sure targetFS has no change between from and the current states
125113
if (!checkNoChange(tgtFs, targetDir)) {
@@ -165,6 +153,42 @@ private boolean preSyncCheck() throws IOException {
165153
return true;
166154
}
167155

156+
/**
157+
* Check if the source and target filesystems support snapshots.
158+
*/
159+
private void checkFilesystemSupport(Path sourceDir, Path targetDir,
160+
FileSystem srcFs, FileSystem tgtFs) throws IOException {
161+
if (!srcFs.hasPathCapability(sourceDir,
162+
CommonPathCapabilities.FS_SNAPSHOTS)) {
163+
throw new UnsupportedOperationException(
164+
"The source file system " + srcFs.getScheme()
165+
+ " does not support snapshot.");
166+
}
167+
if (!tgtFs.hasPathCapability(targetDir,
168+
CommonPathCapabilities.FS_SNAPSHOTS)) {
169+
throw new UnsupportedOperationException(
170+
"The target file system " + tgtFs.getScheme()
171+
+ " does not support snapshot.");
172+
}
173+
try {
174+
getSnapshotDiffReportMethod(srcFs);
175+
} catch (NoSuchMethodException e) {
176+
throw new UnsupportedOperationException(
177+
"The source file system " + srcFs.getScheme()
178+
+ " does not support getSnapshotDiffReport",
179+
e);
180+
}
181+
try {
182+
getSnapshotDiffReportMethod(tgtFs);
183+
} catch (NoSuchMethodException e) {
184+
throw new UnsupportedOperationException(
185+
"The target file system " + tgtFs.getScheme()
186+
+ " does not support getSnapshotDiffReport",
187+
e);
188+
}
189+
190+
}
191+
168192
public boolean sync() throws IOException {
169193
if (!preSyncCheck()) {
170194
return false;
@@ -211,21 +235,10 @@ private boolean getAllDiffs() throws IOException {
211235
context.getTargetPath() : context.getSourcePaths().get(0);
212236

213237
try {
214-
SnapshotDiffReport report = null;
215-
FileSystem fs = ssDir.getFileSystem(conf);
216238
final String from = getSnapshotName(context.getFromSnapshot());
217239
final String to = getSnapshotName(context.getToSnapshot());
218-
if (fs instanceof DistributedFileSystem) {
219-
DistributedFileSystem dfs = (DistributedFileSystem)fs;
220-
report = dfs.getSnapshotDiffReport(ssDir, from, to);
221-
} else if (fs instanceof WebHdfsFileSystem) {
222-
WebHdfsFileSystem webHdfs = (WebHdfsFileSystem)fs;
223-
report = webHdfs.getSnapshotDiffReport(ssDir, from, to);
224-
} else {
225-
throw new IllegalArgumentException("Unsupported file system: " +
226-
fs.getScheme() + "://. " +
227-
"Supported file systems: hdfs://, webhdfs:// and swebhdfs://.");
228-
}
240+
SnapshotDiffReport report =
241+
getSnapshotDiffReport(ssDir.getFileSystem(conf), ssDir, from, to);
229242

230243
this.diffMap = new EnumMap<>(SnapshotDiffReport.DiffType.class);
231244
for (SnapshotDiffReport.DiffType type :
@@ -286,6 +299,36 @@ private boolean getAllDiffs() throws IOException {
286299
return false;
287300
}
288301

302+
/**
303+
* Check if the filesystem implementation has a method named
304+
* getSnapshotDiffReport.
305+
*/
306+
private static Method getSnapshotDiffReportMethod(FileSystem fs)
307+
throws NoSuchMethodException {
308+
return fs.getClass().getMethod(
309+
"getSnapshotDiffReport", Path.class, String.class, String.class);
310+
}
311+
312+
/**
313+
* Get the snapshotDiff b/w the fromSnapshot & toSnapshot for the given
314+
* filesystem.
315+
*/
316+
private static SnapshotDiffReport getSnapshotDiffReport(
317+
final FileSystem fs,
318+
final Path snapshotDir,
319+
final String fromSnapshot,
320+
final String toSnapshot) throws IOException {
321+
try {
322+
return (SnapshotDiffReport) getSnapshotDiffReportMethod(fs).invoke(
323+
fs, snapshotDir, fromSnapshot, toSnapshot);
324+
} catch (InvocationTargetException e) {
325+
throw new IOException(e.getCause());
326+
} catch (NoSuchMethodException|IllegalAccessException e) {
327+
throw new IllegalArgumentException(
328+
"Failed to invoke getSnapshotDiffReport.", e);
329+
}
330+
}
331+
289332
private String getSnapshotName(String name) {
290333
return Path.CUR_DIR.equals(name) ? "" : name;
291334
}
@@ -327,14 +370,7 @@ private void deleteTargetTmpDir(FileSystem targetFs,
327370
private boolean checkNoChange(FileSystem fs, Path path) {
328371
try {
329372
final String from = getSnapshotName(context.getFromSnapshot());
330-
SnapshotDiffReport targetDiff = null;
331-
if (fs instanceof DistributedFileSystem) {
332-
DistributedFileSystem dfs = (DistributedFileSystem)fs;
333-
targetDiff = dfs.getSnapshotDiffReport(path, from, "");
334-
} else {
335-
WebHdfsFileSystem webHdfs = (WebHdfsFileSystem)fs;
336-
targetDiff = webHdfs.getSnapshotDiffReport(path, from, "");
337-
}
373+
SnapshotDiffReport targetDiff = getSnapshotDiffReport(fs, path, from, "");
338374
if (!targetDiff.getDiffList().isEmpty()) {
339375
DistCp.LOG.warn("The target has been modified since snapshot "
340376
+ context.getFromSnapshot());

hadoop-tools/hadoop-distcp/src/test/java/org/apache/hadoop/tools/TestDistCpSync.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import org.apache.hadoop.fs.FileSystem;
2424
import org.apache.hadoop.fs.Path;
2525
import org.apache.hadoop.fs.contract.ContractTestUtils;
26+
import org.apache.hadoop.fs.RawLocalFileSystem;
27+
import org.apache.hadoop.fs.CommonPathCapabilities;
2628
import org.apache.hadoop.hdfs.DFSTestUtil;
2729
import org.apache.hadoop.hdfs.DistributedFileSystem;
2830
import org.apache.hadoop.hdfs.HdfsConfiguration;
@@ -38,6 +40,7 @@
3840
import org.apache.hadoop.mapreduce.Mapper;
3941
import org.apache.hadoop.security.Credentials;
4042
import org.apache.hadoop.test.GenericTestUtils;
43+
import org.apache.hadoop.test.LambdaTestUtils;
4144
import org.apache.hadoop.tools.mapred.CopyMapper;
4245
import org.junit.After;
4346
import org.junit.Assert;
@@ -47,6 +50,7 @@
4750
import java.io.IOException;
4851
import java.io.FileWriter;
4952
import java.io.BufferedWriter;
53+
import java.net.URI;
5054
import java.nio.file.Files;
5155
import java.util.Arrays;
5256
import java.util.ArrayList;
@@ -56,6 +60,9 @@
5660
import java.util.Map;
5761
import java.util.regex.Pattern;
5862

63+
import static org.apache.hadoop.fs.impl.PathCapabilitiesSupport.validatePathCapabilityArgs;
64+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
65+
5966
public class TestDistCpSync {
6067
private MiniDFSCluster cluster;
6168
private final Configuration conf = new HdfsConfiguration();
@@ -89,6 +96,7 @@ public void setUp() throws Exception {
8996

9097
conf.set(DistCpConstants.CONF_LABEL_TARGET_WORK_PATH, target.toString());
9198
conf.set(DistCpConstants.CONF_LABEL_TARGET_FINAL_PATH, target.toString());
99+
conf.setClass("fs.dummy.impl", DummyFs.class, FileSystem.class);
92100
}
93101

94102
@After
@@ -1276,4 +1284,63 @@ private void snapshotDiffWithPaths(Path sourceFSPath,
12761284
verifyCopyByFs(sourceFS, targetFS, sourceFS.getFileStatus(sourceFSPath),
12771285
targetFS.getFileStatus(targetFSPath), false);
12781286
}
1287+
1288+
@Test
1289+
public void testSyncSnapshotDiffWithLocalFileSystem() throws Exception {
1290+
String[] args = new String[]{"-update", "-diff", "s1", "s2",
1291+
"file:///source", "file:///target"};
1292+
LambdaTestUtils.intercept(
1293+
UnsupportedOperationException.class,
1294+
"The source file system file does not support snapshot",
1295+
() -> new DistCp(conf, OptionsParser.parse(args)).execute());
1296+
}
1297+
1298+
@Test
1299+
public void testSyncSnapshotDiffWithDummyFileSystem() {
1300+
String[] args =
1301+
new String[] { "-update", "-diff", "s1", "s2", "dummy:///source",
1302+
"dummy:///target" };
1303+
try {
1304+
FileSystem dummyFs = FileSystem.get(URI.create("dummy:///"), conf);
1305+
assertThat(dummyFs).isInstanceOf(DummyFs.class);
1306+
new DistCp(conf, OptionsParser.parse(args)).execute();
1307+
} catch (UnsupportedOperationException e) {
1308+
throw e;
1309+
} catch (Exception e) {
1310+
// can expect other exceptions as source and target paths
1311+
// are not created.
1312+
}
1313+
}
1314+
1315+
public static class DummyFs extends RawLocalFileSystem {
1316+
public DummyFs() {
1317+
super();
1318+
}
1319+
1320+
public URI getUri() {
1321+
return URI.create("dummy:///");
1322+
}
1323+
1324+
@Override
1325+
public boolean hasPathCapability(Path path, String capability)
1326+
throws IOException {
1327+
switch (validatePathCapabilityArgs(makeQualified(path), capability)) {
1328+
case CommonPathCapabilities.FS_SNAPSHOTS:
1329+
return true;
1330+
default:
1331+
return super.hasPathCapability(path, capability);
1332+
}
1333+
}
1334+
1335+
@Override
1336+
public FileStatus getFileStatus(Path f) throws IOException {
1337+
return new FileStatus();
1338+
}
1339+
1340+
public SnapshotDiffReport getSnapshotDiffReport(final Path snapshotDir,
1341+
final String fromSnapshot, final String toSnapshot) {
1342+
return new SnapshotDiffReport(snapshotDir.getName(), fromSnapshot,
1343+
toSnapshot, new ArrayList<SnapshotDiffReport.DiffReportEntry>());
1344+
}
1345+
}
12791346
}

0 commit comments

Comments
 (0)