/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation: version 3 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package functionaltests.dataspaces; import static com.google.common.truth.Truth.assertThat; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.StandardOpenOption; import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.rules.Timeout; import org.objectweb.proactive.api.PAActiveObject; import org.ow2.proactive.scheduler.common.job.JobId; import org.ow2.proactive.scheduler.common.task.TaskId; import org.ow2.proactive.scheduler.common.task.dataspaces.InputAccessMode; import org.ow2.proactive.scheduler.common.task.dataspaces.InputSelector; import org.ow2.proactive.scheduler.common.task.dataspaces.OutputAccessMode; import org.ow2.proactive.scheduler.common.task.dataspaces.OutputSelector; import org.ow2.proactive.scheduler.core.DataSpaceServiceStarter; import org.ow2.proactive.scheduler.job.JobIdImpl; import org.ow2.proactive.scheduler.job.TaskDataSpaceApplication; import org.ow2.proactive.scheduler.task.TaskIdImpl; import org.ow2.proactive.scheduler.task.data.TaskProActiveDataspaces; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import functionaltests.utils.RMTHelper; /** * Test the integration of {@link TaskProActiveDataspaces} with * {@link DataSpaceServiceStarter} and {@link TaskDataSpaceApplication}. * <p> * {@link TaskProActiveDataspaces} is usually used on node side to manage * files from the scratch space per Task whereas {@link DataSpaceServiceStarter} * and {@link TaskDataSpaceApplication} generally depict the dataspace parts * instantiated on server side to respectively create default spaces and to * manage the transfer of files per Task from default spaces to nodes. * <p> * All the classes mentioned previously assume they are used from a ProActive * virtual node. It explains why server and node side of the dataspaces are * represented in this test by Active Objects. Besides, the server side * is deployed in a new JVM to prevent ProActive programming to make some * optimizations that could hide problems. * * @author ActiveEon Team */ public class TaskProActiveDataspacesIntegrationTest { @Rule public TemporaryFolder folder = new TemporaryFolder(); @Rule public Timeout globalTimeout = Timeout.seconds(600); private Process schedulerDataspaceProcess; private SchedulerDataspace schedulerDataspace; private Process nodeDataspaceProcess; private NodeDataspace nodeDataspace; private File globalSpace; private File userSpace; private File inputSpace; private File outputSpace; private File scratchSpace; @Before public void setUp() throws Exception { String rootPath = folder.getRoot().getAbsolutePath(); System.out.println("Scheduler folder for dataspaces is " + rootPath); String owner = "bobot"; JobId jobId = JobIdImpl.makeJobId("42"); TaskId taskId = TaskIdImpl.createTaskId(jobId, "taskId", 0); schedulerDataspaceProcess = spawnNewJvm(SchedulerDataspace.class, rootPath); String schedulerDataspaceUrl = readLine(schedulerDataspaceProcess.getInputStream()); schedulerDataspace = PAActiveObject.lookupActive(SchedulerDataspace.class, schedulerDataspaceUrl); schedulerDataspace.init(jobId, taskId, owner); String namingServiceUrl = schedulerDataspace.getNamingServiceUrl(); nodeDataspaceProcess = spawnNewJvm(NodeDataspace.class); String nodeDataspaceUrl = readLine(nodeDataspaceProcess.getInputStream()); nodeDataspace = PAActiveObject.lookupActive(NodeDataspace.class, nodeDataspaceUrl); nodeDataspace.init(taskId, namingServiceUrl); // prints the path to the scratch folder of the node System.out.println(readLine(nodeDataspaceProcess.getInputStream())); globalSpace = nodeDataspace.getGlobalSpace(); userSpace = nodeDataspace.getUserSpace(); inputSpace = nodeDataspace.getInputSpace(); outputSpace = nodeDataspace.getOutputSpace(); scratchSpace = nodeDataspace.getScratchFolder(); } private Process spawnNewJvm(Class<?> clazz, String... params) throws Exception { String classpath = RMTHelper.testClasspath(); String className = clazz.getCanonicalName(); String javaHome = System.getProperty("java.home"); String javaBin = javaHome + File.separator + "bin" + File.separator + "java"; String javaPolicy = getSystemProperty("java.security.policy"); String paRmHome = getSystemProperty("pa.rm.home"); String paSchedulerHome = getSystemProperty("pa.scheduler.home"); ProcessBuilder builder = new ProcessBuilder(concat(new String[] { javaBin, "-cp", classpath, "-Djava.security.policy=" + javaPolicy, "-Dpa.rm.home=" + paRmHome, "-Dpa.scheduler.home=" + paSchedulerHome, className }, params)); return builder.start(); } private String readLine(InputStream iStream) throws IOException { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(iStream)); return bufferedReader.readLine(); } private String getSystemProperty(String name) { String property = System.getProperty(name); if (property == null) { throw new IllegalArgumentException("Missing system property: " + name); } return property; } private String[] concat(String[] a, String[] b) { int l1 = a.length; int l2 = b.length; String[] result = new String[l1 + l2]; System.arraycopy(a, 0, result, 0, l1); System.arraycopy(b, 0, result, l1, l2); return result; } @After public void tearDown() throws Exception { PAActiveObject.terminateActiveObject(nodeDataspace, true); PAActiveObject.terminateActiveObject(schedulerDataspace, true); nodeDataspaceProcess.destroy(); schedulerDataspaceProcess.destroy(); } @Test public void testSimpleCopyFromInputSpacesToScratch() throws Exception { createFile(globalSpace, "globalSpace.txt", "globalSpace"); createFile(userSpace, "userSpace.txt", "userSpace"); createFile(inputSpace, "inputSpace.txt", "inputSpace"); createFile(outputSpace, "outputSpace.txt", "outputSpace"); List<InputSelector> inputSelectors = ImmutableList.of(createInputSelector(InputAccessMode.TransferFromGlobalSpace, "globalSpace.txt"), createInputSelector(InputAccessMode.TransferFromUserSpace, "userSpace.txt"), createInputSelector(InputAccessMode.TransferFromInputSpace, "inputSpace.txt"), createInputSelector(InputAccessMode.TransferFromOutputSpace, "outputSpace.txt")); nodeDataspace.copyInputDataToScratch(inputSelectors); assertThat(scratchSpace.list()).hasLength(4); assertThat(existsAndMatches(scratchSpace, "globalSpace.txt", "globalSpace")).isTrue(); assertThat(existsAndMatches(scratchSpace, "userSpace.txt", "userSpace")).isTrue(); assertThat(existsAndMatches(scratchSpace, "inputSpace.txt", "inputSpace")).isTrue(); assertThat(existsAndMatches(scratchSpace, "outputSpace.txt", "outputSpace")).isTrue(); } @Test public void testSimpleCopyFromScratchToOutputSpaces() throws Exception { createFile(scratchSpace, "outputGlobalSpace.txt", "outputGlobalSpace"); createFile(scratchSpace, "outputUserSpace.txt", "outputUserSpace"); createFile(scratchSpace, "outputOutputSpace.txt", "outputOutputSpace"); List<OutputSelector> outputSelectors = ImmutableList.of(createOutputSelector(OutputAccessMode.TransferToGlobalSpace, "outputGlobalSpace.txt"), createOutputSelector(OutputAccessMode.TransferToUserSpace, "outputUserSpace.txt"), createOutputSelector(OutputAccessMode.TransferToOutputSpace, "outputOutputSpace.txt")); nodeDataspace.copyScratchDataToOutput(outputSelectors); assertThat(globalSpace.list()).hasLength(1); assertThat(existsAndMatches(globalSpace, "outputGlobalSpace.txt", "outputGlobalSpace")).isTrue(); assertThat(userSpace.list()).hasLength(1); assertThat(existsAndMatches(userSpace, "outputUserSpace.txt", "outputUserSpace")).isTrue(); assertThat(outputSpace.list()).hasLength(1); assertThat(existsAndMatches(outputSpace, "outputOutputSpace.txt", "outputOutputSpace")).isTrue(); } @Test public void testParallelCopyFromInputSpaceToScratch() throws Exception { createFilesForParallelCopy(globalSpace, 10, 10, 100); List<InputSelector> inputSelectors = ImmutableList.of(createInputSelector(InputAccessMode.TransferFromGlobalSpace, "**/*")); nodeDataspace.copyInputDataToScratch(inputSelectors); assertThat(countFiles(scratchSpace)).isEqualTo(10000); } @Test public void testParallelCopyFromScratchToOutputSpace() throws Exception { createFilesForParallelCopy(scratchSpace, 10, 10, 100); List<OutputSelector> outputSelectors = ImmutableList.of(createOutputSelector(OutputAccessMode.TransferToOutputSpace, "**/*")); nodeDataspace.copyScratchDataToOutput(outputSelectors); assertThat(countFiles(outputSpace)).isEqualTo(10000); } @Test public void testPrecedenceCopyFromInputSpacesToScratch1() throws Exception { String filename = createFilesForTestingPrecedence(); List<InputSelector> inputSelectors = ImmutableList.of(createInputSelector(InputAccessMode.TransferFromUserSpace, filename), createInputSelector(InputAccessMode.TransferFromGlobalSpace, filename), createInputSelector(InputAccessMode.TransferFromOutputSpace, filename), createInputSelector(InputAccessMode.TransferFromInputSpace, filename)); testPrecedenceCopyFromInputSpacesToScratch(inputSelectors, filename, "outputSpace"); } @Test public void testPrecedenceCopyFromInputSpacesToScratch2() throws Exception { String filename = createFilesForTestingPrecedence(); List<InputSelector> inputSelectors = ImmutableList.of(createInputSelector(InputAccessMode.TransferFromUserSpace, filename), createInputSelector(InputAccessMode.TransferFromInputSpace, filename), createInputSelector(InputAccessMode.TransferFromGlobalSpace, filename)); testPrecedenceCopyFromInputSpacesToScratch(inputSelectors, filename, "inputSpace"); } @Test public void testPrecedenceCopyFromInputSpacesToScratch3() throws Exception { String filename = createFilesForTestingPrecedence(); List<InputSelector> inputSelectors = ImmutableList.of(createInputSelector(InputAccessMode.TransferFromUserSpace, filename), createInputSelector(InputAccessMode.TransferFromGlobalSpace, filename)); testPrecedenceCopyFromInputSpacesToScratch(inputSelectors, filename, "userSpace"); } private void createFilesForParallelCopy(File root, int breadth, int depth, int nbFilesPerLevel) throws IOException, InterruptedException { File subpath; for (int b = 0; b < breadth; b++) { subpath = new File(root, "breadth" + b); for (int d = 0; d < depth; d++) { subpath = new File(subpath, "depth" + d); for (int i = 0; i < nbFilesPerLevel; i++) { Files.createDirectories(subpath.toPath()); File file = new File(subpath, "file" + i); Files.createFile(file.toPath()); } } } } private int countFiles(File directory) { int count = 0; File[] files = directory.listFiles(); if (files == null) { return 0; } for (File file : files) { if (file.isFile()) { count++; } if (file.isDirectory()) { count += countFiles(file); } } return count; } private String createFilesForTestingPrecedence() throws Exception { String filename = "test.txt"; createFile(globalSpace, filename, "globalSpace"); createFile(userSpace, filename, "userSpace"); createFile(inputSpace, filename, "inputSpace"); createFile(outputSpace, filename, "outputSpace"); return filename; } private void testPrecedenceCopyFromInputSpacesToScratch(List<InputSelector> inputSelectors, String filename, String expectedContent) throws Exception { nodeDataspace.copyInputDataToScratch(inputSelectors); assertThat(scratchSpace.list()).hasLength(1); assertThat(existsAndMatches(scratchSpace, filename, expectedContent)).isTrue(); } private void createFile(File space, String relativePath, String content) throws Exception { createFile(space, relativePath, Optional.of(content)); } private void createFile(File space, String relativePath, Optional<String> content) throws Exception { File file = new File(space, relativePath); Files.createFile(file.toPath()); if (content.isPresent()) { writeFile(file, content.get()); } } private void writeFile(File file, String fileContent) throws Exception { writeFile(file, fileContent, Charset.defaultCharset()); } private void writeFile(File file, String fileContent, Charset charset) throws Exception { Files.write(file.toPath(), fileContent.getBytes(charset), StandardOpenOption.CREATE); } private InputSelector createInputSelector(InputAccessMode inputAccessMode, String... includes) { org.objectweb.proactive.extensions.dataspaces.vfs.selector.FileSelector fileSelector = new org.objectweb.proactive.extensions.dataspaces.vfs.selector.FileSelector(includes); return new InputSelector(fileSelector, inputAccessMode); } private OutputSelector createOutputSelector(OutputAccessMode outputAccessMode, String... includes) { org.objectweb.proactive.extensions.dataspaces.vfs.selector.FileSelector fileSelector = new org.objectweb.proactive.extensions.dataspaces.vfs.selector.FileSelector(includes); return new OutputSelector(fileSelector, outputAccessMode); } private boolean existsAndMatches(File space, String relativePath, String expectedContent) throws IOException { return existsAndMatches(space, relativePath, Optional.of(expectedContent)); } private boolean existsAndMatches(File space, String relativePath, Optional<String> expectedContent) throws IOException { File file = new File(space, relativePath); if (expectedContent.isPresent()) { String content = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset()); return expectedContent.get().equals(content) && file.exists(); } return file.exists(); } }