/** * This work was created by participants in the DataONE project, and is * jointly copyrighted by participating institutions in DataONE. For * more information on DataONE, see our web site at http://dataone.org. * * Copyright ${year} * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.dataone.integration.it.functional; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.commons.io.IOUtils; import org.dataone.client.D1Client; import org.dataone.client.v1.MNode; import org.dataone.client.auth.CertificateManager; import org.dataone.integration.ContextAwareTestCaseDataone; import org.dataone.integration.ExampleUtilities; import org.dataone.service.exceptions.NotFound; import org.dataone.service.exceptions.ServiceFailure; import org.dataone.service.types.v1.AccessPolicy; import org.dataone.service.types.v1.AccessRule; import org.dataone.service.types.v1.Identifier; import org.dataone.service.types.v1.Node; import org.dataone.service.types.v1.NodeReference; import org.dataone.service.types.v1.Permission; import org.dataone.service.types.v1.Replica; import org.dataone.service.types.v1.ReplicationPolicy; import org.dataone.service.types.v1.Subject; import org.dataone.service.types.v1.SystemMetadata; import org.junit.Test; public class MNReplicationIT extends ContextAwareTestCaseDataone { private static final String format_text_plain = "text/plain"; private static final long replicationDelay = 120000; //240000; //how many minutes? 2? 4? private Subject subject; private String originMNId = "DEMO1"; private String targetMNId = "DEMO2"; private String blockedMNId = "DEMO4"; MNode originMN; MNode targetMN; MNode blockedMN; Node originNode; Node targetNode; Node blockedNode; private InputStream textPlainSource; private SystemMetadata sysMeta; /** * Test replication from origin to at least one other target node */ @Test public void testReplicateOnCreateWithoutPreferredList() { // set up the common member variables setup(); // Ensure we have valid system metadata fields for replication // build a valid ReplicationPolicy ReplicationPolicy policy = new ReplicationPolicy(); policy.setReplicationAllowed(true); policy.setNumberReplicas(1); List preferredList = new ArrayList(); List blockedList = new ArrayList(); policy.setBlockedMemberNodeList(blockedList); policy.setPreferredMemberNodeList(preferredList); sysMeta.setReplicationPolicy(policy); // create the object on the origin node submit(); // check one of the randomly selected target nodes checkAnyTarget(); } /** * Test replication from origin to a preferred target node * */ @Test public void testReplicateOnCreateWithPreferredList() { int count = 1; //10; while (count > 0) { count--; // set up the common member variables setup(); // Ensure we have valid system metadata fields for this test // build a valid ReplicationPolicy ReplicationPolicy policy = new ReplicationPolicy(); policy.setReplicationAllowed(true); policy.setNumberReplicas(1); // the preferred list of targets List preferredList = new ArrayList(); preferredList.add(targetNode.getIdentifier()); List blockedList = new ArrayList(); policy.setBlockedMemberNodeList(blockedList); policy.setPreferredMemberNodeList(preferredList); sysMeta.setReplicationPolicy(policy); // create the object on the origin node submit(); // check the target node //checkTarget(); } } /** * Test replication from origin to any node other than blocked node * */ @Test public void testReplicateOnCreateWithBlockedList() { // set up the common member variables setup(); // Ensure we have valid system metadata fields for this test // build a valid ReplicationPolicy ReplicationPolicy policy = new ReplicationPolicy(); policy.setReplicationAllowed(true); policy.setNumberReplicas(1); // the blocked list of targets List preferredList = new ArrayList(); List blockedList = new ArrayList(); blockedList.add(blockedNode.getIdentifier()); policy.setBlockedMemberNodeList(blockedList); policy.setPreferredMemberNodeList(preferredList); sysMeta.setReplicationPolicy(policy); // create the object on the origin node submit(); // check the blocked node to make sure it is node there checkBlockedTarget(); } /** * Test replication from origin when no policy is given * */ //@Test public void testReplicateOnCreateNoPolicy() { // set up the common member variables setup(); // Ensure we have valid system metadata fields for this test sysMeta.setReplicationPolicy(null); // create the object on the origin node submit(); // check that there are NOT replicas checkAllTargets(false); } /** * Set up the usual scenario -- individual tests should mutate the * sysMeta object for their specific needs */ protected void setup() { // the subject nad certificate subject = setupClientSubject("testRightsHolder"); log.debug("Subject is: " + subject.getValue()); Iterator it = getMemberNodeIterator(); // iterate over the node list and set the source and target nodes while (it.hasNext()) { Node currentNode = it.next(); String currentNodeId = currentNode.getIdentifier().getValue(); if (currentNodeId.equals(originMNId)) { originNode = currentNode; } else if (currentNodeId.equals(targetMNId)) { targetNode = currentNode; } else if (currentNodeId.equals(blockedMNId)) { blockedNode = currentNode; } } // get callable MN objects try { originMN = D1Client.getMN(originNode.getIdentifier()); targetMN = D1Client.getMN(targetNode.getIdentifier()); blockedMN = D1Client.getMN(blockedNode.getIdentifier()); } catch (ServiceFailure e) { fail("Couldn't get origin or target node objects: " + e.getMessage()); } String identifierStr = ExampleUtilities.generateIdentifier(); Identifier guid = new Identifier(); guid.setValue("MNReplicationIT." + identifierStr); try { textPlainSource = new ByteArrayInputStream("Plain text source".getBytes("UTF-8")); log.debug("Data string is: " + IOUtils.toString(textPlainSource)); } catch (Exception e) { fail("Couldn't get an example input stream: " + e.getMessage()); } // build the system metadata object sysMeta = ExampleUtilities.generateSystemMetadata(guid, format_text_plain, textPlainSource, null); //ExampleUtilities.generateSystemMetadata(guid, "eml://ecoinformatics.org/eml-2.1.0", textPlainSource, null); // Ensure we have valid system metadata fields for tests // clear the Replica list sysMeta.clearReplicaList(); // build a valid access policy AccessPolicy accessPolicy = new AccessPolicy(); AccessRule allowRule = new AccessRule(); Subject publicSubject = new Subject(); publicSubject.setValue("public"); allowRule.addSubject(publicSubject); allowRule.addPermission(Permission.READ); accessPolicy.addAllow(allowRule); sysMeta.setAccessPolicy(accessPolicy); // update other critical fields for replication sysMeta.setAuthoritativeMemberNode(originNode.getIdentifier()); sysMeta.setOriginMemberNode(originNode.getIdentifier()); // set the submitter as the cert DN try { X509Certificate certificate = CertificateManager.getInstance().loadCertificate(); String ownerX500 = CertificateManager.getInstance().getSubjectDN(certificate); sysMeta.getRightsHolder().setValue(ownerX500); sysMeta.getSubmitter().setValue(ownerX500); } catch (Exception e) { // warn about this e.printStackTrace(); } } /** * save the object, then check the */ protected void submit() { // try the create try { log.debug("adding object to origin node " + originMNId); Identifier pid = originMN.create(null, sysMeta.getIdentifier(), textPlainSource, sysMeta); log.debug("Created object, pid=" + pid.getValue()); } catch (Exception e) { fail("Unexpected error: " + e.getMessage()); } } /** * check the target node after a time delay */ protected void checkTarget() { // look for it on the target try { // wait for replication log.debug("Waiting for replication, delay=" + replicationDelay); Thread.sleep(replicationDelay); // now check log.debug("checking target node for object, pid=" + sysMeta.getIdentifier().getValue()); InputStream returnedObject = targetMN.get(null, sysMeta.getIdentifier()); log.debug( "Returned data string is: " + IOUtils.toString(returnedObject)); assertTrue(IOUtils.contentEquals(textPlainSource, returnedObject)); } catch (Exception e) { fail("Unexpected error: " + e.getMessage()); } } /** * Check that a replica DID NOT end up on a blocked MN */ protected void checkBlockedTarget() { // look for it on the target try { // wait for replication log.debug("Waiting for replication, delay=" + replicationDelay); Thread.sleep(replicationDelay); // look at the CN's sysmeta int replicaCount = 1; log.debug("checking CN for system metadata, pid=" + sysMeta.getIdentifier().getValue()); SystemMetadata sysMetaCN = D1Client.getCN().getSystemMetadata(null, sysMeta.getIdentifier()); // check that there is no replica for the blocked node for (Replica replica: sysMetaCN.getReplicaList()) { // if it's on the blocked node, this is bad if (replica.getReplicaMemberNode().getValue().equals(blockedMNId)) { fail("Replica is reported by CN to be on blocked node, " + blockedMNId); } replicaCount++; } log.debug("CN reports replica count=" + replicaCount); // we have enough replicas assertTrue(replicaCount >= sysMeta.getReplicationPolicy().getNumberReplicas()); // now double check that the blocked node does not have the object log.debug("checking other target node for object, node=" + blockedMNId); try { InputStream returnedObject = blockedMN.get(null, sysMeta.getIdentifier()); fail("Replica found on blocked node, " + blockedMNId); } catch (NotFound e) { // this is what we want log.debug("Object not found on the blocked node"); } } catch (Exception e) { fail("Unexpected error: " + e.getMessage()); } } @Override protected String getTestDescription() { return "Test Case that runs through the Member Node Tier 4 (Replication) API methods"; } /** * check the CN node after a time delay * Finds the replicas and checks that one of them was successful */ protected void checkAnyTarget() { // look for it on the target try { // wait for replication log.debug("Waiting for replication, delay=" + replicationDelay); Thread.sleep(replicationDelay); // look at the CN's sysmeta int replicaCount = 1; NodeReference otherTarget = null; log.debug("checking CN for system metadata, pid=" + sysMeta.getIdentifier().getValue()); SystemMetadata sysMetaCN = D1Client.getCN().getSystemMetadata(null, sysMeta.getIdentifier()); for (Replica replica: sysMetaCN.getReplicaList()) { // if it's on another node besides the orign, increment the count if (!replica.getReplicaMemberNode().getValue().equals(originMNId)) { replicaCount++; otherTarget = replica.getReplicaMemberNode(); } } log.debug("CN reports replica count=" + replicaCount); // we have enough replicas assertTrue(replicaCount >= sysMeta.getReplicationPolicy().getNumberReplicas()); // now check that other node log.debug("checking other target node for object, node=" + otherTarget.getValue()); InputStream returnedObject = D1Client.getMN(otherTarget).get(null, sysMeta.getIdentifier()); log.debug("Returned data string is: " + IOUtils.toString(returnedObject)); assertTrue(IOUtils.contentEquals(textPlainSource, returnedObject)); } catch (Exception e) { fail("Unexpected error: " + e.getMessage()); } } /** * check the CN node after a time delay * Finds the replicas and checks that they all exist */ protected void checkAllTargets(boolean exists) { // look for it on the target try { // wait for replication log.debug("Waiting for replication, delay=" + replicationDelay); Thread.sleep(replicationDelay); // look at the CN's sysmeta int replicaCount = 1; NodeReference otherTarget = null; log.debug("checking CN for system metadata, pid=" + sysMeta.getIdentifier().getValue()); SystemMetadata sysMetaCN = D1Client.getCN().getSystemMetadata(null, sysMeta.getIdentifier()); for (Replica replica: sysMetaCN.getReplicaList()) { // if it's on another node besides the origin, increment the count if (!replica.getReplicaMemberNode().getValue().equals(originMNId)) { replicaCount++; otherTarget = replica.getReplicaMemberNode(); // now check that other node has it or not InputStream returnedObject = null; try { log.debug("checking other target node for object, node=" + otherTarget.getValue()); returnedObject = D1Client.getMN(otherTarget).get(null, sysMeta.getIdentifier()); log.debug("Returned data string is: " + IOUtils.toString(returnedObject)); } catch (NotFound nfe) { if (exists) { fail("Replica not found where it should be, node=" + otherTarget.getValue()); } else { // this passes for the node assertTrue(IOUtils.contentEquals(textPlainSource, returnedObject)); } } } } log.debug("CN reports replica count=" + replicaCount); // we have enough replicas, where applicable? if (sysMeta.getReplicationPolicy() != null) { assertTrue(replicaCount >= sysMeta.getReplicationPolicy().getNumberReplicas()); } } catch (Exception e) { fail("Unexpected error: " + e.getMessage()); } } }