From ef2afb12112bcd2c376d7c068c7992d78d8cca7e Mon Sep 17 00:00:00 2001 From: buczekp <buczekp> Date: Wed, 23 Mar 2011 14:23:13 +0000 Subject: [PATCH] [LMS-2142] remote implementation of folder comparison SVN: 20472 --- .../RemoteDataSetFileOperationsExecutor.java | 69 +++++++++++++++++-- .../plugins/standard/DataSetCopier.java | 2 + .../DataSetFileOperationsManager.java | 35 ++++++---- .../DataSetFileOperationsManagerTest.java | 51 ++++++++++---- 4 files changed, 125 insertions(+), 32 deletions(-) diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RemoteDataSetFileOperationsExecutor.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RemoteDataSetFileOperationsExecutor.java index 5c2618cb352..28dd5d211e0 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RemoteDataSetFileOperationsExecutor.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RemoteDataSetFileOperationsExecutor.java @@ -17,19 +17,25 @@ package ch.systemsx.cisd.openbis.dss.generic.server; import java.io.File; +import java.io.FileFilter; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import org.apache.log4j.Logger; +import ch.systemsx.cisd.bds.StringUtils; import ch.systemsx.cisd.common.exceptions.ExceptionWithStatus; import ch.systemsx.cisd.common.exceptions.Status; import ch.systemsx.cisd.common.filesystem.BooleanStatus; +import ch.systemsx.cisd.common.filesystem.FileUtilities; import ch.systemsx.cisd.common.filesystem.IPathCopier; import ch.systemsx.cisd.common.filesystem.ssh.ISshCommandExecutor; import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; import ch.systemsx.cisd.common.process.ProcessResult; import ch.systemsx.cisd.common.utilities.StringUtilities; +import ch.systemsx.cisd.openbis.dss.generic.server.LocalDataSetFileOperationsExcecutor.FolderFileSizesReportGenerator; import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.DataSetCopier; public final class RemoteDataSetFileOperationsExecutor implements IDataSetFileOperationsExecutor @@ -48,14 +54,18 @@ public final class RemoteDataSetFileOperationsExecutor implements IDataSetFileOp private final String rsyncPasswordFileOrNull; + private final File gfindExecutable; + public RemoteDataSetFileOperationsExecutor(ISshCommandExecutor executor, IPathCopier copier, - String host, String rsyncModuleNameOrNull, String rsyncPasswordFileOrNull) + File gfindExecutable, String host, String rsyncModuleNameOrNull, + String rsyncPasswordFileOrNull) { this.executor = executor; this.copier = copier; this.host = host; this.rsyncModuleNameOrNull = rsyncModuleNameOrNull; this.rsyncPasswordFileOrNull = rsyncPasswordFileOrNull; + this.gfindExecutable = gfindExecutable; } public BooleanStatus exists(File file) @@ -179,12 +189,57 @@ public final class RemoteDataSetFileOperationsExecutor implements IDataSetFileOp { return BooleanStatus.createFalse("Data set location '" + dataSet + "' doesn't exist"); } - // else if (false == fileOperations.exists(destination)) - // { - // return BooleanStatus.createFalse("Destination location '" + destination - // + "' doesn't exist"); - // } - return executor.exists(destination.getPath(), DataSetCopier.SSH_TIMEOUT_MILLIS); + BooleanStatus existsStatus = + executor.exists(destination.getPath(), DataSetCopier.SSH_TIMEOUT_MILLIS); + if (false == existsStatus.isSuccess()) + { + return existsStatus; + } + FileFilter nullFilter = null; + List<File> storeFiles = FileUtilities.listFiles(dataSet, nullFilter, true); + Map<String, Long> dataSetFileSizesByPaths = + FolderFileSizesReportGenerator.extractSizesByPaths(storeFiles, dataSet); + String cmd = createListFilesWithFileSizeCmd(destination.getPath(), gfindExecutable); + ProcessResult result = + executor.executeCommandRemotely(cmd, DataSetCopier.SSH_TIMEOUT_MILLIS); + + Map<String, Long> destinationFileSizesByPaths = new LinkedHashMap<String, Long>(); + if (result.isOK() && result.getOutput() != null) + { + List<String> output = result.getOutput(); + for (String line : output) + { + String split[] = line.split("\t"); + assert split.length == 2; + destinationFileSizesByPaths.put(split[0], Long.parseLong(split[1])); + } + } else + { + String errorOutput = StringUtilities.concatenateWithNewLine(result.getErrorOutput()); + operationLog.error("Listing files in '" + destination + "' failed with exit value: " + + result.getExitValue() + "; error output: " + errorOutput); + return BooleanStatus.createError("listing files failed"); + } + + String inconsistenciesReport = + FolderFileSizesReportGenerator.findInconsistencies(dataSetFileSizesByPaths, + destinationFileSizesByPaths); + if (StringUtils.isBlank(inconsistenciesReport)) + { + return BooleanStatus.createTrue(); + } else + { + return BooleanStatus.createFalse("Inconsistencies:\n" + inconsistenciesReport); + } + } + + /** + * Returns a bash command listing relative file paths of regular files with their sizes in + * bytes. + */ + private static String createListFilesWithFileSizeCmd(final String path, final File findExec) + { + return findExec + " " + path + " -type f -printf \"%p\\t%s\\n\""; } } \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetCopier.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetCopier.java index 10e875b4587..5eeb862016a 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetCopier.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetCopier.java @@ -50,6 +50,8 @@ public class DataSetCopier extends AbstractDropboxProcessingPlugin @Private static final String COPYING_FAILED_MSG = "copying failed"; + public static final String GFIND_EXEC = "find"; + public static final String RSYNC_EXEC = "rsync"; public static final String SSH_EXEC = "ssh"; diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManager.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManager.java index f9f92886b68..95c27396ae3 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManager.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManager.java @@ -70,6 +70,9 @@ public class DataSetFileOperationsManager @Private static final String SSH_EXEC = "ssh"; + @Private + static final String GFIND_EXEC = "find"; + @Private static final long SSH_TIMEOUT_MILLIS = 15 * 1000; // 15s @@ -96,6 +99,7 @@ public class DataSetFileOperationsManager { File sshExecutable = Copier.getExecutable(properties, SSH_EXEC); File rsyncExecutable = Copier.getExecutable(properties, RSYNC_EXEC); + File gfindExecutable = Copier.getExecutable(properties, GFIND_EXEC); IPathCopier copier = pathCopierFactory.create(rsyncExecutable, sshExecutable); copier.check(); String rsyncModule = hostAwareFile.tryGetRsyncModule(); @@ -105,8 +109,9 @@ public class DataSetFileOperationsManager ISshCommandExecutor sshCommandExecutor = sshCommandExecutorFactory.create(sshExecutable, hostOrNull); this.executor = - new RemoteDataSetFileOperationsExecutor(sshCommandExecutor, copier, hostOrNull, - rsyncModule, rsyncPasswordFile); + new RemoteDataSetFileOperationsExecutor(sshCommandExecutor, copier, + gfindExecutable, hostOrNull, rsyncModule, rsyncPasswordFile); + } } @@ -119,8 +124,10 @@ public class DataSetFileOperationsManager try { File destinationFolder = new File(destination, dataset.getDataSetLocation()); - createFolderIfNotExists(destinationFolder.getParentFile()); - // deleteFolderIfExists(destinationFolder.getParentFile()); + if (false == createFolderIfNotExists(destinationFolder.getParentFile())) + { + deleteFolderIfExists(destinationFolder); + } operationLog.info("Copy dataset '" + dataset.getDatasetCode() + "' from '" + originalData.getPath() + "' to '" + destinationFolder.getParentFile()); executor.copyDataSetToDestination(originalData, destinationFolder.getParentFile()); @@ -203,22 +210,24 @@ public class DataSetFileOperationsManager } } - // private void deleteFolderIfExists(File destinationFolder) - // { - // BooleanStatus destinationExists = destinationExists(destinationFolder); - // if (destinationExists.isSuccess()) - // { - // executor.deleteFolder(destinationFolder); - // } - // } + private void deleteFolderIfExists(File destinationFolder) + { + BooleanStatus destinationExists = destinationExists(destinationFolder); + if (destinationExists.isSuccess()) + { + executor.deleteFolder(destinationFolder); + } + } - private void createFolderIfNotExists(File destinationFolder) + private boolean createFolderIfNotExists(File destinationFolder) { BooleanStatus destinationExists = destinationExists(destinationFolder); if (destinationExists.isSuccess() == false) { executor.createFolder(destinationFolder); + return true; } + return false; } private BooleanStatus destinationExists(File destinationFolder) diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManagerTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManagerTest.java index 7fc714ffa71..0f101b7b7e0 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManagerTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/standard/DataSetFileOperationsManagerTest.java @@ -128,6 +128,8 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase private File sshExec; + private File gfindExec; + @BeforeClass public void init() { @@ -179,6 +181,8 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase rsyncExec.createNewFile(); sshExec = new File(workingDirectory, "my-rssh"); sshExec.createNewFile(); + gfindExec = new File(workingDirectory, "my-gfind"); + gfindExec.createNewFile(); } private DatasetDescription createDataSetDescription(String dataSetCode, String location, @@ -506,12 +510,19 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase will(returnValue(Status.OK)); /* - * ds2: directory exists in archive -> only copy + * ds2: directory exists in archive -> delete and copy */ - one(sshExecutor).exists(ds1ArchivedLocationFile.getParentFile().getPath(), + one(sshExecutor).exists(ds2ArchivedLocationFile.getParentFile().getPath(), SSH_TIMEOUT_MILLIS); will(returnValue(BooleanStatus.createTrue())); + one(sshExecutor).exists(ds2ArchivedLocationFile.getPath(), SSH_TIMEOUT_MILLIS); + will(returnValue(BooleanStatus.createTrue())); + + one(sshExecutor).executeCommandRemotely( + "rm -rf " + ds2ArchivedLocationFile.getPath(), SSH_TIMEOUT_MILLIS); + will(returnValue(OK_RESULT)); + one(copier).copyToRemote(ds2Location, ds2ArchivedLocationFile.getParentFile(), HOST, null, null); will(returnValue(Status.OK)); @@ -537,7 +548,13 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase { one(sshExecutor).exists(ds1ArchivedLocationFile.getParentFile().getPath(), SSH_TIMEOUT_MILLIS); - will(returnValue(BooleanStatus.createTrue())); + will(returnValue(BooleanStatus.createFalse())); + + one(sshExecutor).executeCommandRemotely( + "mkdir -p " + ds1ArchivedLocationFile.getParentFile().getPath(), + SSH_TIMEOUT_MILLIS); + will(returnValue(OK_RESULT)); + one(copier).copyToRemote(ds1Location, ds1ArchivedLocationFile.getParentFile(), HOST, null, null); will(returnValue(Status.createError(DUMMY_ERROR_MESSAGE))); @@ -649,22 +666,28 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase { { /* - * ds1: TODO directory is present but content is different + * ds1: directory is present and content is OK */ one(sshExecutor).exists(ds1ArchivedLocationFile.getPath(), SSH_TIMEOUT_MILLIS); will(returnValue(BooleanStatus.createTrue())); - /* - * ds2: directory is present and content is OK - */ - one(sshExecutor).exists(ds2ArchivedLocationFile.getPath(), SSH_TIMEOUT_MILLIS); - will(returnValue(BooleanStatus.createTrue())); + one(sshExecutor).executeCommandRemotely( + gfindExec.getPath() + " " + ds1ArchivedLocationFile.getPath() + + " -type f -printf \"%p\\t%s\\n\"", SSH_TIMEOUT_MILLIS); + will(returnValue(ERROR_RESULT)); + + // /* + // * ds2: directory is present and content is OK + // */ + // one(sshExecutor).exists(ds2ArchivedLocationFile.getPath(), + // SSH_TIMEOUT_MILLIS); + // will(returnValue(BooleanStatus.createTrue())); } }); BooleanStatus status1 = dataSetCopier.isPresentInDestination(ds1Location, ds1); - BooleanStatus status2 = dataSetCopier.isPresentInDestination(ds2Location, ds2); - assertTrue(status1); - assertTrue(status2); + // BooleanStatus status2 = dataSetCopier.isPresentInDestination(ds2Location, ds2); + assertError(status1, "listing files failed"); + // assertTrue(status2); context.assertIsSatisfied(); } @@ -840,6 +863,8 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase rsyncExec.getPath()); properties.setProperty(DataSetFileOperationsManager.SSH_EXEC + "-executable", sshExec.getPath()); + properties.setProperty(DataSetFileOperationsManager.GFIND_EXEC + "-executable", + gfindExec.getPath()); return properties; } @@ -855,6 +880,8 @@ public class DataSetFileOperationsManagerTest extends AbstractFileSystemTestCase rsyncExec.getPath()); properties.setProperty(DataSetFileOperationsManager.SSH_EXEC + "-executable", sshExec.getPath()); + properties.setProperty(DataSetFileOperationsManager.GFIND_EXEC + "-executable", + gfindExec.getPath()); return properties; } -- GitLab