AssuredReplicationPluginTest.java revision 5fbe2f7032d3113bff70dd775555967c992964e5
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2008-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2015 ForgeRock AS
*/
/**
* Test the client part (plugin) of the assured feature in both safe data and
* safe read modes. We use a fake RS and control the behaviour of the client
* DS (timeout, wait for acks, error handling...)
* Also check for monitoring values for assured replication
*/
@SuppressWarnings("javadoc")
public class AssuredReplicationPluginTest extends ReplicationTestCase
{
public class MonitorAssertions
{
{
"assured-sr-sent-updates", "assured-sr-acknowledged-updates",
"assured-sr-not-acknowledged-updates", "assured-sr-timeout-updates",
"assured-sr-wrong-status-updates", "assured-sr-replay-error-updates",
"assured-sr-received-updates", "assured-sr-received-updates-acked",
"assured-sr-received-updates-not-acked", "assured-sd-sent-updates",
"assured-sd-acknowledged-updates", "assured-sd-timeout-updates");
{
}
}
{
return this;
}
public void assertRemainingValuesAreZero()
{
{
}
}
}
/** The port of the replicationServer. */
private int replServerPort;
private final int RS_SERVER_ID = 90;
/** Sleep time of the RS, before sending an ack. */
private static final long NO_TIMEOUT_RS_SLEEP_TIME = 2000;
/**
* Create two distinct base dns, one for safe data replication, the other one
* for safe read replication.
*/
private Entry safeDataDomainCfgEntry;
private Entry safeReadDomainCfgEntry;
private Entry notAssuredDomainCfgEntry;
/** The fake replication server. */
private FakeReplicationServer replicationServer;
/** Definitions for the scenario the RS supports. */
private static final int NOT_ASSURED_SCENARIO = 1;
private static final int TIMEOUT_SCENARIO = 2;
private static final int NO_TIMEOUT_SCENARIO = 3;
private static final int SAFE_READ_MANY_ERRORS = 4;
private static final int SAFE_DATA_MANY_ERRORS = 5;
private static final int NO_READ = 6;
/** The tracer object for the debug logger. */
{
if (logger.isTraceEnabled())
{
}
}
/** Before starting the tests configure some stuff. */
{
super.setUp();
// Create base dns for each tested modes
"objectClass: organizationalUnit\n";
"objectClass: organizationalUnit\n";
"objectClass: organizationalUnit\n";
}
/** Add an entry in the database. */
{
}
/**
* Creates a domain using the passed assured settings.
* Returns the matching config entry added to the config backend.
*/
long assuredTimeout) throws Exception
{
switch (assuredMode)
{
case SAFE_READ_MODE:
break;
case SAFE_DATA_MODE:
break;
default:
fail("Unexpected assured level.");
}
// Create an assured config entry ldif, matching passed assured settings
"ds-cfg-server-id: 1\n" + "ds-cfg-receive-status: true\n" +
// heartbeat = 10 min so no need to emulate heartbeat in fake RS: session
// not closed by client
"ds-cfg-heartbeat-interval: 600000ms\n" +
// heartbeat = 10 min so no need to emulate heartbeat in fake RS: session
// not closed by client
"ds-cfg-changetime-heartbeat-interval: 0ms\n";
switch (assuredMode)
{
case SAFE_READ_MODE:
break;
case SAFE_DATA_MODE:
break;
default:
fail("Unexpected assured level.");
}
// Add the config entry to create the replicated domain
"Unable to add the domain config entry: " + configEntryLdif);
return domainCfgEntry;
}
/**
* Creates a domain without assured mode
* Returns the matching config entry added to the config backend.
*/
{
// Create a not assured config entry ldif
"ds-cfg-server-id: 1\n" + "ds-cfg-receive-status: true\n" +
// heartbeat = 10 min so no need to emulate heartbeat in fake RS: session
// not closed by client
"ds-cfg-heartbeat-interval: 600000ms\n" +
"ds-cfg-changetime-heartbeat-interval: 0ms\n";
// Add the config entry to create the replicated domain
"Unable to add the domain config entry: " + configEntryLdif);
return domainCfgEntry;
}
/**
* The fake replication server used to emulate RS behaviour the way we want
* for assured features test.
* This fake replication server is able to receive a DS connection only.
* According to the configured scenario, it will answer to updates with acks
* as the scenario is requesting.
*/
private class FakeReplicationServer extends Thread
{
private ServerSocket listenSocket;
private boolean shutdown;
/** Parameters given at constructor time. */
private final int port;
private int serverId = -1;
private boolean isAssured;
private byte safeDataLevel = 1;
/** Predefined config parameters. */
private final int degradedStatusThreshold = 5000;
/** Parameters set with received server start message. */
private long generationId = -1L;
private ServerState serverState;
private int windowSize = -1;
private byte groupId = -1;
private boolean sslEncryption;
/** The scenario this RS is expecting. */
private int scenario = -1;
/** Parameters at handshake are ok. */
private boolean handshakeOk;
/**
* Signal that the current scenario the RS must execute reached the point
* where the main code can perform test assertion.
*/
private boolean scenarioExecuted;
private CSNGenerator gen;
/**
* Constructor for RS receiving updates in SR assured mode or not assured
* The assured boolean means:
* - true: SR mode
* - false: not assured
*/
{
if (assured)
{
this.isAssured = true;
}
}
/** Constructor for RS receiving updates in SD assured mode. */
{
this.isAssured = true;
this.safeDataLevel = (byte) safeDataLevel;
}
public void setAssured(boolean assured)
{
}
/** Starts the fake RS, expecting and testing the passed scenario. */
{
// Store expected test case
// Start listening
start();
}
/** Wait for DS connections. */
public void run()
{
// Create server socket
try
{
listenSocket = new ServerSocket();
} catch (IOException e)
{
}
// Loop waiting for DS connections
while (!shutdown)
{
try
{
newSocket.setTcpNoDelay(true);
newSocket.setKeepAlive(true);
// Create client session
{
continue;
}
// Ok, now call connection handling code + special code for the
// configured test
} catch (Exception e)
{
// The socket has probably been closed as part of the
// shutdown
}
}
}
/** Shutdown the Replication Server service and all its connections. */
public void shutdown()
{
if (shutdown)
{
return;
}
shutdown = true;
// Shutdown the listener thread and any current client handling code
try
{
join();
} catch (InterruptedException ie)
{
}
}
/**
* Handle the handshake processing with the connecting DS
* returns true if handshake was performed without errors.
*/
private boolean performHandshake() throws Exception
{
{
// Receive server start
// Send replication server start
if (!sslEncryption)
{
}
// Disconnection of DS looking for best server
return false;
}
// Sanity checking for assured parameters
if (isAssured)
{
}
debugInfo("Received start session assured parameters are ok.");
// Send topo view
}
return true;
}
/**
* Tells if the received handshake parameters regarding assured config were
* ok and handshake phase is terminated.
*/
public boolean isHandshakeOk()
{
return handshakeOk;
}
/**
* Tells the main code that the fake RS executed enough of the expected
* scenario and can perform test assertion.
*/
public boolean isScenarioExecuted()
{
return scenarioExecuted;
}
/** Handle client connection then call code specific to configured test. */
private void handleClientConnection() throws Exception
{
// Handle DS connection
if (!performHandshake())
{
return;
}
// If we come here, assured parameters sent by DS are as expected and
// handshake phase is terminated
handshakeOk = true;
// Now execute the requested scenario
switch (scenario)
{
case NOT_ASSURED_SCENARIO:
break;
case TIMEOUT_SCENARIO:
break;
case NO_TIMEOUT_SCENARIO:
break;
case SAFE_READ_MANY_ERRORS:
break;
case SAFE_DATA_MANY_ERRORS:
break;
case NO_READ:
// Nothing to execute, just let session opne. This scenario used to
// send updates from the RS to the DS (reply test cases)
while (!shutdown)
{
try
{
sleep(5000);
} catch (InterruptedException ex)
{
// Going shutdown ?
break;
}
}
break;
default:
}
}
/**
* Make the RS send an add message with the passed entry and return the ack
* message it receives from the DS.
*/
{
// Send add message in assured mode
}
/** Read the coming update and check parameters are not assured. */
private void executeNotAssuredScenario() throws Exception
{
scenarioExecuted = true;
}
/** Read the coming update and make the client time out by not sending back the ack. */
private void executeTimeoutScenario() throws Exception
{
scenarioExecuted = true;
// We do not send back an ack and the client code is expected to be
// blocked at least for the programmed timeout time.
}
/** Read the coming update, sleep some time then send back an ack. */
private void executeNoTimeoutScenario() throws Exception
{
// Sleep before sending back the ack
// Send the ack without errors
scenarioExecuted = true;
}
/**
* Receives an {@link UpdateMsg} and checks that received update assured
* parameters are as defined at RS start.
*/
{
if (isAssured)
{
}
debugInfo("Received update assured parameters are ok.");
return updateMsg;
}
/** Read the coming safe read mode updates and send back acks with errors. */
private void executeSafeReadManyErrorsScenario() throws Exception
{
// Read first update
// Sleep before sending back the ack
// Send an ack with errors:
// - replay error
// - server 10 error, server 20 error
// Read second update
// Sleep before sending back the ack
// Send an ack with errors:
// - timeout error
// - wrong status error
// - replay error
// - server 10 error, server 20 error, server 30 error
// Read third update
// let timeout occur
scenarioExecuted = true;
}
/** Read the coming safe data mode updates and send back acks with errors. */
private void executeSafeDataManyErrorsScenario() throws Exception
{
// Read first update
// Sleep before sending back the ack
// Send an ack with errors:
// - timeout error
// - server 10 error
// Read second update
// Sleep before sending back the ack
// Send an ack with errors:
// - timeout error
// - server 10 error, server 20 error
// Read third update
// let timeout occur
scenarioExecuted = true;
}
}
/** Return various group id values. */
private Object[][] rsGroupIdProvider()
{
return new Object[][]
{
{ (byte)1 },
{ (byte)2 }
};
}
/**
* Tests that a DS performing a modification in safe data mode waits for
* the ack of the RS for the configured timeout time, then times out.
* If the RS group id is not the same as the DS one, this must not time out
* and return immediately.
*/
{
int TIMEOUT = 5000;
try
{
// Create and start a RS expecting clients in safe data assured mode with
// safe data level 2
1, testcase);
if (rsGroupId != 1)
{
replicationServer.setAssured(false);
}
long startTime;
// Create a safe data assured domain
if (rsGroupId == 1)
{
// Wait for connection of domain to RS
// Make an LDAP update (add an entry)
"objectClass: top\n" +
"objectClass: organizationalUnit\n";
}
else
{
// Wait for connection of domain to RS
// Make an LDAP update (add an entry)
"objectClass: top\n" +
"objectClass: organizationalUnit\n";
}
if (rsGroupId == 1)
{
// RS has same group id as DS
// Check monitoring values
new MonitorAssertions(baseDN)
}
else
{
// RS has a different group id, addEntry should have returned quickly
// No error should be seen in monitoring and update should have not been
// sent in assured mode
}
} finally
{
}
}
{
}
{
}
{
}
/**
* Tests that a DS performing a modification in safe read mode waits for
* the ack of the RS for the configured timeout time, then times out.
* If the RS group id is not the same as the DS one, this must not time out
* and return immediately.
*/
{
int TIMEOUT = 5000;
try
{
// Create and start a RS expecting clients in safe read assured mode
true, testcase);
if (rsGroupId != 1)
{
replicationServer.setAssured(false);
}
long startTime;
// Create a safe data assured domain
if (rsGroupId == 1)
{
// Create a safe read assured domain
// Wait for connection of domain to RS
// Make an LDAP update (add an entry)
"objectClass: top\n" +
"objectClass: organizationalUnit\n";
}
else
{
// Wait for connection of domain to RS
// Make an LDAP update (add an entry)
"objectClass: top\n" +
"objectClass: organizationalUnit\n";
}
if (rsGroupId == 1)
{
// Check monitoring values
new MonitorAssertions(baseDN)
}
else
{
// RS has a different group id, addEntry should have returned quickly
// No error should be seen in monitoring and update should have not been
// sent in assured mode
}
} finally
{
}
}
/** Tests parameters sent in session handshake and updates, when not using assured replication. */
@Test
public void testNotAssuredSession() throws Exception
{
try
{
// Create and start a RS expecting not assured clients
false, testcase);
// Create a not assured domain
// Wait for connection of domain to RS
// Make an LDAP update (add an entry)
"objectClass: top\n" +
"objectClass: organizationalUnit\n";
// Wait for entry received by RS
// No more test to do here
} finally
{
}
}
/**
* Wait for connection to the fake replication server or times out with error
* after some seconds.
*/
{
int nsec = -1;
do
{
nsec++;
if (nsec == 10)
{
}
} while (!rs.isHandshakeOk());
}
/**
* Wait for the scenario to be executed by the fake replication server or
* times out with error after some seconds.
*/
private void waitForScenarioExecutedOnRs(String testCase, FakeReplicationServer rs) throws Exception
{
int nsec = -1;
do
{
nsec++;
if (nsec == 10)
{
fail(testCase + ": timeout waiting for scenario to be exectued on fake RS after " + nsec + " seconds.");
}
} while (!rs.isScenarioExecuted());
}
{
if (replicationServer != null)
{
}
}
/**
* Tests that a DS performing a modification in safe data mode receives the RS
* ack and does not return before returning it.
*/
@Test
public void testSafeDataModeAck() throws Exception
{
int TIMEOUT = 5000;
try
{
// Create and start a RS expecting clients in safe data assured mode with
// safe data level 2
2, testcase);
// Create a safe data assured domain
// Wait for connection of domain to RS
// Make an LDAP update (add an entry)
"objectClass: top\n" +
"objectClass: organizationalUnit\n";
// Check monitoring values
new MonitorAssertions(baseDN)
} finally
{
}
}
/**
* Tests that a DS performing a modification in safe read mode receives the RS
* ack and does not return before returning it.
*/
@Test
public void testSafeReadModeAck() throws Exception
{
int TIMEOUT = 5000;
try
{
// Create and start a RS expecting clients in safe read assured mode
true, testcase);
// Create a safe read assured domain
// Wait for connection of domain to RS
// Make an LDAP update (add an entry)
"objectClass: top\n" +
"objectClass: organizationalUnit\n";
// Check monitoring values
new MonitorAssertions(baseDN)
} finally
{
}
}
/**
* Tests that a DS receiving an update from a RS in safe read mode effectively
* sends an ack back (with or without error).
*/
{
int TIMEOUT = 5000;
try
{
// Create and start a RS expecting clients in safe read assured mode
true, testcase);
// Create a safe read assured domain
// Wait for connection of domain to RS
/* Send an update from the RS and get the ack */
// Make the RS send an assured add message
"objectClass: top\n" +
"objectClass: organizationalUnit\n";
try {
if (rsGroupId == 2)
{
fail("Should only go here for RS with same group id as DS");
}
// Ack received, replay has occurred
// Check that DS replied an ack without errors anyway
// Check for monitoring data
new MonitorAssertions(baseDN)
}
catch (SocketTimeoutException e)
{
// Expected
if (rsGroupId == 1)
{
fail("Should only go here for RS with group id different from DS one");
}
return;
}
/* Send an update with error from the RS and get the ack with error */
// Make the RS send a not possible assured add message
// TODO: make the domain return an error: use a plugin ?
// The resolution code does not generate any error so we need to find a
// way to have the replay not working to test this...
// Check that DS replied an ack with errors
// assertFalse(ackMsg.hasTimeout());
// assertTrue(ackMsg.hasReplayError());
// assertFalse(ackMsg.hasWrongStatus());
// List<Integer> failedServers = ackMsg.getFailedServers();
// assertEquals(failedServers.size(), 1);
// assertEquals((integer)failedServers.get(0), (integer)1);
} finally
{
}
}
/**
* Tests that a DS receiving an update from a RS in safe data mode does not
* send back and ack (only safe read is taken into account in DS replay).
*/
{
int TIMEOUT = 5000;
try
{
// Create and start a RS expecting clients in safe data assured mode
4, testcase);
// Create a safe data assured domain
// Wait for connection of domain to RS
// Make the RS send an assured add message: we expect a read timeout as
// safe data should be ignored by DS
"objectClass: top\n" +
"objectClass: organizationalUnit\n";
}
catch (SocketTimeoutException expected)
{
return;
}
finally
{
}
}
/**
* DS performs many successive modifications in safe data mode and receives RS
* acks with various errors. Check for monitoring right errors
*/
public void testSafeDataManyErrors() throws Exception
{
int TIMEOUT = 5000;
try
{
// Create and start a RS expecting clients in safe data assured mode with
// safe data level 3
3, testcase);
// Create a safe data assured domain
// Wait for connection of domain to RS
// Make a first LDAP update (add an entry)
"objectClass: top\n" +
"objectClass: organizationalUnit\n";
// Check monitoring values
// The expected ack for the first update is:
// - timeout error
// - server 10 error
new MonitorAssertions(baseDN)
// Make a second LDAP update (delete the entry)
// Check monitoring values
// The expected ack for the second update is:
// - timeout error
// - server 10 error, server 20 error
new MonitorAssertions(baseDN)
// Make a third LDAP update (re-add the entry)
// Check monitoring values
// No ack should have comen back, so timeout incremented (flag and error for rs)
new MonitorAssertions(baseDN)
} finally
{
}
}
/**
* In this scenario, the fake RS will not send back an ack so we expect the
* add entry code (LDAP client code emulation) to be blocked for the timeout
* value at least. If the time we have slept is lower, timeout handling code
* is not working...
*/
{
}
/**
* In this scenario, the fake RS will send back an ack after
* (LDAP client code emulation) to be blocked for more than
* NO_TIMEOUT_RS_SLEEP_TIME seconds but no more than the timeout value.
*/
{
}
/**
* DS performs many successive modifications in safe read mode and receives RS
* acks with various errors. Check for monitoring right errors
*/
public void testSafeReadManyErrors() throws Exception
{
int TIMEOUT = 5000;
try
{
// Create and start a RS expecting clients in safe read assured mode
true, testcase);
// Create a safe read assured domain
// Wait for connection of domain to RS
// Make a first LDAP update (add an entry)
"objectClass: top\n" +
"objectClass: organizationalUnit\n";
// Check monitoring values
// The expected ack for the first update is:
// - replay error
// - server 10 error, server 20 error
new MonitorAssertions(baseDN)
// Make a second LDAP update (delete the entry)
// Check monitoring values
// The expected ack for the second update is:
// - timeout error
// - wrong status error
// - replay error
// - server 10 error, server 20 error, server 30 error
new MonitorAssertions(baseDN)
// Make a third LDAP update (re-add the entry)
// Check monitoring values
// No ack should have comen back, so timeout incremented (flag and error for rs)
new MonitorAssertions(baseDN)
} finally
{
}
}
/** Delete an entry from the database. */
{
}
/**
* Get the errors by servers from cn=monitor, according to the requested base dn
* and the requested mode
* This corresponds to the values for multi valued attributes:
* - assured-sr-server-not-acknowledged-updates in SR mode
* - assured-sd-server-timeout-updates in SD mode
*/
{
// Find monitoring entry for requested base DN
int count = 0;
do
{
if (count++>0)
{
}
}
/* Find the multi valued attribute matching the requested assured mode */
switch(assuredMode)
{
case SAFE_READ_MODE:
assuredAttr = "assured-sr-server-not-acknowledged-updates";
break;
case SAFE_DATA_MODE:
assuredAttr = "assured-sd-server-timeout-updates";
break;
default:
throw new Exception("Unknown assured type");
}
{
return Collections.emptyMap();
}
// Parse and store values
{
}
}
}
return resultMap;
}
{
int ii=0;
{
ii++;
if (ii>10)
{
}
}
}
}