| New file |
| | |
| | | /* |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | * or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | * 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 |
| | | * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 |
| | | * |
| | | * |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.synchronization; |
| | | |
| | | import static org.opends.server.config.ConfigConstants.ATTR_TASK_COMPLETION_TIME; |
| | | import static org.opends.server.config.ConfigConstants.ATTR_TASK_STATE; |
| | | import static org.testng.Assert.assertEquals; |
| | | import static org.testng.Assert.fail; |
| | | |
| | | import java.io.File; |
| | | import java.net.ServerSocket; |
| | | import java.util.UUID; |
| | | |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.backends.task.TaskState; |
| | | import org.opends.server.core.AddOperation; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.protocols.internal.InternalSearchOperation; |
| | | import org.opends.server.schema.DirectoryStringSyntax; |
| | | import org.opends.server.synchronization.plugin.MultimasterSynchronization; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchScope; |
| | | import org.testng.annotations.BeforeClass; |
| | | import org.testng.annotations.Test; |
| | | |
| | | /** |
| | | * Test re-synchronization after after backup/restore and LDIF import. |
| | | */ |
| | | public class ReSyncTest extends SynchronizationTestCase |
| | | { |
| | | /** |
| | | * Set up the environment for performing the tests in this Class. |
| | | * |
| | | * @throws Exception |
| | | * If the environment could not be set up. |
| | | */ |
| | | @BeforeClass |
| | | public void setup() throws Exception |
| | | { |
| | | /* |
| | | * - Start a server and a changelog server, configure synchronization |
| | | * - Do some changes. |
| | | */ |
| | | TestCaseUtils.startServer(); |
| | | |
| | | // find a free port for the changelog server |
| | | ServerSocket socket = TestCaseUtils.bindFreePort(); |
| | | int changelogPort = socket.getLocalPort(); |
| | | socket.close(); |
| | | |
| | | // Create an internal connection |
| | | connection = InternalClientConnection.getRootConnection(); |
| | | |
| | | // Create backend top level entries |
| | | addEntry("dn: dc=example,dc=com\n" + "objectClass: top\n" |
| | | + "objectClass: domain\n"); |
| | | |
| | | // top level synchro provider |
| | | String synchroStringDN = "cn=Synchronization Providers,cn=config"; |
| | | |
| | | // Multimaster Synchro plugin |
| | | synchroPluginStringDN = "cn=Multimaster Synchronization, " |
| | | + synchroStringDN; |
| | | String synchroPluginLdif = "dn: " |
| | | + synchroPluginStringDN |
| | | + "\n" |
| | | + "objectClass: top\n" |
| | | + "objectClass: ds-cfg-synchronization-provider\n" |
| | | + "ds-cfg-synchronization-provider-enabled: true\n" |
| | | + "ds-cfg-synchronization-provider-class: org.opends.server.synchronization.MultimasterSynchronization\n"; |
| | | synchroPluginEntry = TestCaseUtils.entryFromLdifString(synchroPluginLdif); |
| | | |
| | | // Change log |
| | | String changeLogStringDN = "cn=Changelog Server, " + synchroPluginStringDN; |
| | | String changeLogLdif = "dn: " + changeLogStringDN + "\n" |
| | | + "objectClass: top\n" |
| | | + "objectClass: ds-cfg-synchronization-changelog-server-config\n" |
| | | + "cn: Changelog Server\n" |
| | | + "ds-cfg-changelog-port:" + changelogPort + "\n" |
| | | + "ds-cfg-changelog-server-id: 1\n"; |
| | | changeLogEntry = TestCaseUtils.entryFromLdifString(changeLogLdif); |
| | | |
| | | // suffix synchronized |
| | | String synchroServerLdif = "dn: cn=example, " + synchroPluginStringDN + "\n" |
| | | + "objectClass: top\n" |
| | | + "objectClass: ds-cfg-synchronization-provider-config\n" |
| | | + "cn: example\n" |
| | | + "ds-cfg-synchronization-dn: dc=example,dc=com\n" |
| | | + "ds-cfg-changelog-server: localhost:"+ changelogPort + "\n" |
| | | + "ds-cfg-directory-server-id: 1\n"; |
| | | synchroServerEntry = TestCaseUtils.entryFromLdifString(synchroServerLdif); |
| | | |
| | | configureSynchronization(); |
| | | |
| | | // Create a dummy entry |
| | | addEntry("dn: dc=dummy, dc=example,dc=com\n" |
| | | + "objectClass: top\n" + "objectClass: domain\n"); |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Utility function. Can be used to create and add and entry |
| | | * in the local DS from its ldif description. |
| | | * |
| | | * @param entryString The entry in ldif from. |
| | | * @return The ResultCode of the operation. |
| | | * @throws Exception If something went wrong. |
| | | */ |
| | | private ResultCode addEntry(String entryString) throws Exception |
| | | { |
| | | Entry entry; |
| | | AddOperation addOp; |
| | | entry = TestCaseUtils.entryFromLdifString(entryString); |
| | | addOp = new AddOperation(connection, |
| | | InternalClientConnection.nextOperationID(), InternalClientConnection |
| | | .nextMessageID(), null, entry.getDN(), entry.getObjectClasses(), |
| | | entry.getUserAttributes(), entry.getOperationalAttributes()); |
| | | addOp.setInternalOperation(true); |
| | | addOp.run(); |
| | | entryList.add(entry.getDN()); |
| | | return addOp.getResultCode(); |
| | | } |
| | | |
| | | /** |
| | | * Test re-synchronization after after backup/restore |
| | | */ |
| | | @Test() |
| | | public void testResyncAfterRestore() throws Exception |
| | | { |
| | | /* |
| | | * - Backup the server |
| | | * - ADD an entry |
| | | * - Restore the backup taken previously |
| | | * - Check that entry has been added again in the LDAP server. |
| | | */ |
| | | |
| | | // Delete the entry we are going to use to make sure that |
| | | // we do test something. |
| | | connection.processDelete(DN.decode("dc=foo, dc=example,dc=com")); |
| | | |
| | | MultimasterSynchronization.notificationBackupStart( |
| | | DN.decode("dc=example,dc=com")); // TEMPORARY UNTIL core server |
| | | // sends a notification |
| | | |
| | | task("dn: ds-task-id=" + UUID.randomUUID() |
| | | + ",cn=Scheduled Tasks,cn=Tasks\n" |
| | | + "objectclass: top\n" |
| | | + "objectclass: ds-task\n" |
| | | + "objectclass: ds-task-backup\n" |
| | | + "ds-task-class-name: org.opends.server.tasks.BackupTask\n" |
| | | + "ds-backup-directory-path: bak\n" |
| | | + "ds-task-backup-all: TRUE\n"); |
| | | |
| | | addEntry("dn: dc=foo, dc=example,dc=com\n" |
| | | + "objectClass: top\n" + "objectClass: domain\n"); |
| | | |
| | | MultimasterSynchronization.notificationRestoreStart( |
| | | DN.decode("dc=example,dc=com"));// TEMPORARY UNTIL core server |
| | | // sends a notification |
| | | task("dn: ds-task-id=" + UUID.randomUUID() |
| | | + ",cn=Scheduled Tasks,cn=Tasks\n" |
| | | + "objectclass: top\n" |
| | | + "objectclass: ds-task\n" |
| | | + "objectclass: ds-task-restore\n" |
| | | + "ds-task-class-name: org.opends.server.tasks.RestoreTask\n" |
| | | + "ds-backup-directory-path: bak" + File.separator |
| | | + "userRoot\n"); |
| | | |
| | | MultimasterSynchronization.notificationRestoreEnd( |
| | | DN.decode("dc=example,dc=com"));// TEMPORARY UNTIL core server |
| | | // sends a notification |
| | | |
| | | if (getEntry(DN.decode("dc=foo, dc=example,dc=com"), 30000, true) == null) |
| | | fail("The Directory has not been resynchronized after the restore."); |
| | | |
| | | connection.processDelete(DN.decode("dc=foo, dc=example,dc=com")); |
| | | } |
| | | |
| | | /** |
| | | * Test re-synchronization after after backup/restore |
| | | */ |
| | | @Test() |
| | | public void testResyncAfterImport() throws Exception |
| | | { |
| | | /* |
| | | * - Do an export to a LDIF file |
| | | * - Add an entry |
| | | * - Import LDIF file generated above. |
| | | * - Check that entry has been added again in the LDAP server. |
| | | */ |
| | | |
| | | // delete the entry we are going to use to make sure that |
| | | // we do test something. |
| | | connection.processDelete(DN.decode("dc=foo, dc=example,dc=com")); |
| | | |
| | | MultimasterSynchronization.notificationBackupStart( |
| | | DN.decode("dc=example,dc=com")); // TEMPORARY UNTIL core server |
| | | // sends a notification |
| | | |
| | | String buildRoot = System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT); |
| | | String path = buildRoot + File.separator + "ReSynchTest"; |
| | | |
| | | task("dn: ds-task-id=" + UUID.randomUUID() |
| | | + ",cn=Scheduled Tasks,cn=Tasks\n" |
| | | + "objectclass: top\n" |
| | | + "objectclass: ds-task\n" |
| | | + "objectclass: ds-task-export\n" |
| | | + "ds-task-class-name: org.opends.server.tasks.ExportTask\n" |
| | | + "ds-task-export-backend-id: userRoot\n" |
| | | + "ds-task-export-ldif-file: " + path + "\n"); |
| | | |
| | | addEntry("dn: dc=foo, dc=example,dc=com\n" |
| | | + "objectClass: top\n" + "objectClass: domain\n"); |
| | | |
| | | MultimasterSynchronization.notificationRestoreStart( |
| | | DN.decode("dc=example,dc=com"));// TEMPORARY UNTIL core server |
| | | // sends a notification |
| | | task("dn: ds-task-id=" + UUID.randomUUID() |
| | | + ",cn=Scheduled Tasks,cn=Tasks\n" |
| | | + "objectclass: top\n" |
| | | + "objectclass: ds-task\n" |
| | | + "objectclass: ds-task-import\n" |
| | | + "ds-task-class-name: org.opends.server.tasks.ImportTask\n" |
| | | + "ds-task-import-backend-id: userRoot\n" |
| | | + "ds-task-import-ldif-file: " + path + "\n" |
| | | + "ds-task-import-reject-file: " + path + "reject\n"); |
| | | |
| | | MultimasterSynchronization.notificationRestoreEnd( |
| | | DN.decode("dc=example,dc=com"));// TEMPORARY UNTIL core server |
| | | // sends a notification |
| | | |
| | | if (getEntry(DN.decode("dc=foo, dc=example,dc=com"), 30000, true) == null) |
| | | fail("The Directory has not been resynchronized after the restore."); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Utility method to create, run a task and check its result. |
| | | */ |
| | | private void task(String task) throws Exception |
| | | { |
| | | Entry taskEntry = TestCaseUtils.makeEntry(task); |
| | | |
| | | InternalClientConnection connection = |
| | | InternalClientConnection.getRootConnection(); |
| | | |
| | | // Add the task. |
| | | AddOperation addOperation = |
| | | connection.processAdd(taskEntry.getDN(), |
| | | taskEntry.getObjectClasses(), |
| | | taskEntry.getUserAttributes(), |
| | | taskEntry.getOperationalAttributes()); |
| | | assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS, |
| | | "Add of the task definition was not successful"); |
| | | |
| | | // Wait until the task completes. |
| | | AttributeType completionTimeType = DirectoryServer.getAttributeType( |
| | | ATTR_TASK_COMPLETION_TIME.toLowerCase()); |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(objectclass=*)"); |
| | | Entry resultEntry = null; |
| | | String completionTime = null; |
| | | long startMillisecs = System.currentTimeMillis(); |
| | | do |
| | | { |
| | | InternalSearchOperation searchOperation = |
| | | connection.processSearch(taskEntry.getDN(), |
| | | SearchScope.BASE_OBJECT, |
| | | filter); |
| | | try |
| | | { |
| | | resultEntry = searchOperation.getSearchEntries().getFirst(); |
| | | } catch (Exception e) |
| | | { |
| | | continue; |
| | | } |
| | | completionTime = |
| | | resultEntry.getAttributeValue(completionTimeType, |
| | | DirectoryStringSyntax.DECODER); |
| | | |
| | | if (completionTime == null) |
| | | { |
| | | if (System.currentTimeMillis() - startMillisecs > 1000*30) |
| | | { |
| | | break; |
| | | } |
| | | Thread.sleep(10); |
| | | } |
| | | } while (completionTime == null); |
| | | |
| | | if (completionTime == null) |
| | | { |
| | | fail("The task has not completed after 30 seconds."); |
| | | } |
| | | |
| | | // Check that the task state is as expected. |
| | | AttributeType taskStateType = |
| | | DirectoryServer.getAttributeType(ATTR_TASK_STATE.toLowerCase()); |
| | | String stateString = |
| | | resultEntry.getAttributeValue(taskStateType, |
| | | DirectoryStringSyntax.DECODER); |
| | | TaskState taskState = TaskState.fromString(stateString); |
| | | assertEquals(taskState, TaskState.COMPLETED_SUCCESSFULLY, |
| | | "The task completed in an unexpected state"); |
| | | } |
| | | } |