These changes rovides the ability to repair
the consistency in the replication topology in the (hopefully) rare case
when hardware failure or software bugs could break it (issue 788 and 791)
There are several parts in this commit :
- a new log file called replication was added.
It's purpose is to store all the consistency errors detected by the replication
with enough information to allow the administrator to repair the problem.
This file is configured by default and contain only the replication errors,
To achieve the a new log severity keyword "None" has been created so that
the associated error log publisher does not print any error not related to
replication. (I will update the reference guide after this commit)
- a new control (the replication repair control) has been added.
When this control is used in a MODIFY, DELETE, ADD or MODDN operation
the operation is marked as a non-replicated replication operation.
This cause the following :
- The operation is allowed to modify attributes that are normally not
allowed to be modified or added (NO-USER-MODIFCATION) such as
entryuuid and ds-sync-hist
To achieve this I add to move those checks from the
AddOperationBasis.getObjectClasses() or AddOperationBasis.getUserAttributes()
to the LocalBackendWorkflowElement.processAdd() but this has already
been reviewed.
- no change number is associated to the operation.
- the operation is not published to the replication server and is therefore
a local only operation.
- the replication don't try to solve conflict or generate historical information
for this operation.
The intended usage of this control is that the administrator will check
for errors in the replication log, determine the entries that have inconsistent
values and use the control to repair them.
I will write some documentation explaining in more details how to do this.
- even though this is not related to replication repair I also took advantage of
this change to add the multimaster replication synchronization provider
in the default configuration.
This will make configuration of replication using dsconfig easier because
the user will now only need to configure the replication servers and
replication domains.
2 files added
14 files modified
| | |
| | | ds-cfg-rotation-policy-dn: cn=Size Limit Rotation Policy,cn=Log Rotation Policies,cn=config |
| | | ds-cfg-retention-policy-dn: cn=File Count Retention Policy,cn=Log Retention Policies,cn=config |
| | | |
| | | dn: cn=Replication Repair Logger,cn=Loggers,cn=config |
| | | objectClass: top |
| | | objectClass: ds-cfg-logger |
| | | objectClass: ds-cfg-error-logger |
| | | objectClass: ds-cfg-file-based-error-logger |
| | | cn: File-Based Error Logger |
| | | ds-cfg-logger-class: org.opends.server.loggers.TextErrorLogPublisher |
| | | ds-cfg-logger-enabled: true |
| | | ds-cfg-log-file: logs/replication |
| | | ds-cfg-log-file-mode: 640 |
| | | ds-cfg-default-severity: none |
| | | ds-cfg-override-severity: SYNC=INFO,MILD_ERROR,MILD_WARNING,NOTICE |
| | | ds-cfg-asynchronous-writes: false |
| | | ds-cfg-rotation-policy-dn: cn=7 Days Time Limit Rotation Policy,cn=Log Rotation Policies,cn=config |
| | | ds-cfg-rotation-policy-dn: cn=Size Limit Rotation Policy,cn=Log Rotation Policies,cn=config |
| | | ds-cfg-retention-policy-dn: cn=File Count Retention Policy,cn=Log Retention Policies,cn=config |
| | | |
| | | dn: cn=File-Based Debug Logger,cn=Loggers,cn=config |
| | | objectClass: top |
| | | objectClass: ds-cfg-logger |
| | |
| | | ds-cfg-num-worker-threads: 24 |
| | | ds-cfg-max-work-queue-capacity: 0 |
| | | |
| | | dn: cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config |
| | | objectClass: top |
| | | objectClass: ds-cfg-synchronization-provider |
| | | objectClass: ds-cfg-multimaster-synchronization-provider |
| | | cn: Multimaster Synchronization |
| | | ds-cfg-synchronization-provider-enabled: true |
| | | ds-cfg-synchronization-provider-class: org.opends.server.replication.plugin.MultimasterReplication |
| | | |
| | | dn: cn=domains,cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config |
| | | objectClass: top |
| | | objectClass: ds-cfg-branch |
| | | cn: domains |
| | |
| | | # cn=Replication Server, cn=Multimaster Synchronization, cn=Synchronization Providers, cn=config |
| | | # entry. |
| | | |
| | | |
| | | dn: cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config |
| | | objectClass: top |
| | | objectClass: ds-cfg-synchronization-provider |
| | | objectClass: ds-cfg-multimaster-synchronization-provider |
| | | cn: Multimaster Synchronization |
| | | ds-cfg-synchronization-provider-enabled: true |
| | | ds-cfg-synchronization-provider-class: org.opends.server.replication.plugin.MultimasterReplication |
| | | |
| | | dn: cn=domains,cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config |
| | | objectClass: top |
| | | objectClass: ds-cfg-branch |
| | | cn: domains |
| | | |
| | | dn: cn=example,cn=domains,cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config |
| | | objectClass: top |
| | | objectClass: ds-cfg-replication-domain-config |
| | |
| | | Messages of all severity levels will be logged. |
| | | </adm:synopsis> |
| | | </adm:value> |
| | | <adm:value name="none"> |
| | | <adm:synopsis> |
| | | No messages of any severity will be logged by default. |
| | | This value is intended to be used in conjunction with |
| | | the override-severity property to define an error |
| | | logger that will publish no error message beside the errors |
| | | of a given category. |
| | | </adm:synopsis> |
| | | </adm:value> |
| | | <adm:value name="fatal-error"> |
| | | <adm:synopsis> |
| | | The error log severity that will be used for messages that |
| | |
| | | # CDDL HEADER END |
| | | # |
| | | # Portions Copyright 2006-2007 Sun Microsystems, Inc. |
| | | |
| | | # |
| | | # |
| | | # This file contains the primary Directory Server configuration. It must not |
| | | # be directly edited while the server is online. The server configuration |
| | | # should only be managed using the administration utilities provided with the |
| | | # Directory Server. |
| | | |
| | | |
| | | # |
| | |
| | | MILD_ERR_COULD_NOT_BIND_CHANGELOG_6=Changelog failed to start : could not \ |
| | | bind to the changelog listen port : %d. Error : %s |
| | | MILD_ERR_UNKNOWN_TYPE_7=Unknown operation type : %s |
| | | MILD_ERR_ERROR_REPLAYING_OPERATION_8=Error %s when replaying operation with \ |
| | | changenumber %s %s : %s |
| | | MILD_ERR_OPERATION_NOT_FOUND_IN_PENDING_9=Internal Error : Operation %s \ |
| | | change number %s was not found in pending list |
| | | MILD_ERR_COULD_NOT_INITIALIZE_DB_10=Changelog failed to start because the \ |
| | |
| | | MILD_ERR_EXCEPTION_REPLAYING_OPERATION_12=An Exception was caught while \ |
| | | replaying operation %s : %s |
| | | MILD_ERR_NEED_CHANGELOG_PORT_13=The replication server port must be defined |
| | | MILD_ERR_ERROR_UPDATING_RUV_14=Error %s when updating server state %s : %s \ |
| | | DEBUG_ERROR_UPDATING_RUV_14=Error %s when updating server state %s : %s \ |
| | | base dn : %s |
| | | MILD_ERR_ERROR_SEARCHING_RUV_15=Error %s when searching for server state %s : \ |
| | | %s base dn : %s |
| | |
| | | server should be configured |
| | | NOTICE_EXCEPTION_STARTING_SESSION_20=Caught Exception during initial \ |
| | | communication with replication server : |
| | | NOTICE_CANNOT_RECOVER_CHANGES_21=Error when searching old changes from the \ |
| | | database |
| | | MILD_ERR_CANNOT_RECOVER_CHANGES_21=Error when searching old changes from the \ |
| | | database for base DN %s |
| | | NOTICE_COULD_NOT_FIND_CHANGELOG_WITH_MY_CHANGES_22=Could not find a \ |
| | | replication server that has seen all the local changes. Going to replay \ |
| | | changes |
| | |
| | | SEVERE_ERR_EXCEPTION_RECEIVING_REPLICATION_MESSAGE_34=An Exception was caught \ |
| | | while receiving replication message : %s |
| | | MILD_ERR_LOOP_REPLAYING_OPERATION_35=A loop was detected while replaying \ |
| | | operation: %s |
| | | operation: %s error %s |
| | | MILD_ERR_FILE_CHECK_CREATE_FAILED_36=An Exception was caught while testing \ |
| | | existence or trying to create the directory for the changelog database : %s |
| | | INFO_CHANGELOG_SERVER_ATTR_37=Specifies the list of replication servers to \ |
| | |
| | | have the same ServerId : %d |
| | | SEVERE_ERR_BAD_HISTORICAL_56=Entry %s was containing some unknown historical \ |
| | | information, This may cause some inconsistency for this entry |
| | | SEVERE_ERR_CANNOT_ADD_CONFLICT_ATTRIBUTE_57=A conflict was detected but the \ |
| | | MILD_ERR_CANNOT_ADD_CONFLICT_ATTRIBUTE_57=A conflict was detected but the \ |
| | | conflict information could not be added. Operation : |
| | | SEVERE_ERR_CANNOT_RENAME_CONFLICT_ENTRY_58=An error happened trying the \ |
| | | MILD_ERR_CANNOT_RENAME_CONFLICT_ENTRY_58=An error happened trying to \ |
| | | rename a conflicting entry : |
| | | SEVERE_ERR_EXCEPTION_RENAME_CONFLICT_ENTRY_59=An Exception happened when \ |
| | | trying the rename a conflicting entry : |
| | | MILD_ERR_EXCEPTION_RENAME_CONFLICT_ENTRY_59=An Exception happened when \ |
| | | trying to rename a conflicting entry : |
| | | SEVERE_ERR_CHANGELOG_UNSUPPORTED_UTF8_ENCODING_60=The JVM does not support \ |
| | | UTF-8. This is required to be able to encode the changes in the database. \ |
| | | This replication server will now shutdown |
| | |
| | | and reopened |
| | | SEVERE_ERR_CHANGELOG_ERROR_SENDING_MSG_66=An unexpected error occurred while \ |
| | | sending a Message to %s. This connection is going to be closed and reopened |
| | | MILD_ERR_ERROR_REPLAYING_OPERATION_67=Could not replay operation %s with \ |
| | | ChangeNumber %s error %s %s |
| | | MILD_ERR_UNKNOWN_ATTRIBUTE_IN_HISTORICAL_68=The entry %s has historical \ |
| | | information for attribute %s which is not defined in the schema. This \ |
| | | information will be ignored |
| | |
| | | defaultSeverities.add(Severity.SEVERE_ERROR); |
| | | defaultSeverities.add(Severity.SEVERE_WARNING); |
| | | } |
| | | else if (defSev.toString().equalsIgnoreCase(LOG_SEVERITY_NONE)) |
| | | { |
| | | // don't add any severity |
| | | } |
| | | else |
| | | { |
| | | Severity errorSeverity = |
| | |
| | | defaultSeverities.add(Severity.SEVERE_ERROR); |
| | | defaultSeverities.add(Severity.SEVERE_WARNING); |
| | | } |
| | | else if (defSev.toString().equalsIgnoreCase(LOG_SEVERITY_NONE)) |
| | | { |
| | | // don't add any severity |
| | | } |
| | | else |
| | | { |
| | | Severity errorSeverity = |
| | |
| | | * |
| | | * @param mod A modification that must be adde to the list of modifications |
| | | * included in this fake operation. |
| | | * @throws Exception when the addition of this type of modification |
| | | * is not valid for this FakeOperation. |
| | | */ |
| | | abstract public void addModification(Modification mod) throws Exception; |
| | | abstract public void addModification(Modification mod); |
| | | |
| | | } |
| | |
| | | * This attribute is unknown from the schema |
| | | * Just skip it, the modification will be processed but no |
| | | * historical information is going to be kept. |
| | | * TODO : REPAIR tool should deal with this, add some logging. |
| | | * Log information for the repair tool. |
| | | */ |
| | | Message message = ERR_UNKNOWN_ATTRIBUTE_IN_HISTORICAL.get( |
| | | entry.getDN().toNormalizedString(), histVal.getAttrString()); |
| | | logError(message); |
| | | continue; |
| | | } |
| | | |
| | |
| | | |
| | | if (fakeOperation != null) |
| | | { |
| | | try |
| | | { |
| | | fakeOperation.addModification(mod); |
| | | } catch (Exception e) |
| | | { |
| | | /* |
| | | * TODO : REPAIR : This Exception shows that there are some |
| | | * inconsistency in the historical information. |
| | | * This method can't fix the problem. |
| | | * This should be logged and somehow the repair |
| | | * service should get called to fix the problem. |
| | | */ |
| | | } |
| | | } |
| | | else |
| | | { |
| | |
| | | |
| | | import org.opends.server.types.BackupConfig; |
| | | import org.opends.server.types.ConfigChangeResult; |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.Entry; |
| | |
| | | import org.opends.server.types.operation.PreOperationModifyOperation; |
| | | import org.opends.messages.Message; |
| | | |
| | | import static org.opends.server.replication.plugin. |
| | | ReplicationRepairRequestControl.*; |
| | | |
| | | /** |
| | | * This class is used to load the Replication code inside the JVM |
| | |
| | | * Finds the domain for a given DN. |
| | | * |
| | | * @param dn The DN for which the domain must be returned. |
| | | * @param op An optional operation for which the check is done. |
| | | * @param pluginOp An optional operation for which the check is done. |
| | | * Can be null is the request has no associated operation. |
| | | * @return The domain for this DN. |
| | | */ |
| | | public static ReplicationDomain findDomain(DN dn, PluginOperation op) |
| | | public static ReplicationDomain findDomain(DN dn, PluginOperation pluginOp) |
| | | { |
| | | /* |
| | | * Don't run the special replication code on Operation that are |
| | | * specifically marked as don't synchronize. |
| | | */ |
| | | if ((op != null) && (op instanceof Operation) && |
| | | (((Operation) op).dontSynchronize())) |
| | | if ((pluginOp != null) && (pluginOp instanceof Operation)) |
| | | { |
| | | Operation op = ((Operation) pluginOp); |
| | | |
| | | if (op.dontSynchronize()) |
| | | return null; |
| | | |
| | | /* |
| | | * Check if the provided operation is a repair operation and set |
| | | * the synchronization flags if necessary. |
| | | * The repair operations are tagged as synchronization operations |
| | | * so that the core server let the operation modify the entryuuid |
| | | * and ds-sync-hist attributes. |
| | | * They are also tagged as dontSynchronize so that the replication |
| | | * code running later do not generate ChnageNumber, solve conflicts |
| | | * and forward the operation to the replication server. |
| | | */ |
| | | for (Control c : op.getRequestControls()) |
| | | { |
| | | if (c.getOID().equals(OID_REPLICATION_REPAIR_CONTROL)) |
| | | { |
| | | op.setSynchronizationOperation(true); |
| | | op.setDontSynchronize(true); |
| | | // remove this control from the list of controls since |
| | | // it has now been processed and the local backend will |
| | | // fail if it finds a control that it does not know about and |
| | | // that is marked as critical. |
| | | List<Control> controls = op.getRequestControls(); |
| | | controls.remove(c); |
| | | return null; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | ReplicationDomain domain = null; |
| | | DN temp = dn; |
| | | do |
| | |
| | | DirectoryServer.registerRestoreTaskListener(this); |
| | | DirectoryServer.registerExportTaskListener(this); |
| | | DirectoryServer.registerImportTaskListener(this); |
| | | |
| | | DirectoryServer.registerSupportedControl( |
| | | ReplicationRepairRequestControl.OID_REPLICATION_REPAIR_CONTROL); |
| | | } |
| | | |
| | | /** |
| | |
| | | op.run(); |
| | | |
| | | ResultCode result = op.getResultCode(); |
| | | if (result != ResultCode.SUCCESS) |
| | | if ((result != ResultCode.SUCCESS) && |
| | | ((result != ResultCode.NO_SUCH_OBJECT))) |
| | | { |
| | | Message message = ERR_ERROR_UPDATING_RUV.get( |
| | | Message message = DEBUG_ERROR_UPDATING_RUV.get( |
| | | op.getResultCode().getResultCodeName().toString(), |
| | | op.toString(), |
| | | op.getErrorMessage().toString(), |
| | |
| | | * An error happened trying to search for the updates |
| | | * This server will start acepting again new updates but |
| | | * some inconsistencies will stay between servers. |
| | | * TODO : REPAIR : log an error for the repair tool |
| | | * Log an error for the repair tool |
| | | * that will need to resynchronize the servers. |
| | | */ |
| | | Message message = NOTE_CANNOT_RECOVER_CHANGES.get(); |
| | | Message message = ERR_CANNOT_RECOVER_CHANGES.get( |
| | | baseDn.toNormalizedString()); |
| | | logError(message); |
| | | } |
| | | else |
| | |
| | | { |
| | | // Continue with the next change but the servers could now become |
| | | // inconsistent. |
| | | // TODO : REPAIR : Should let the repair tool know about this |
| | | Message message = ERR_LOOP_REPLAYING_OPERATION.get(op.toString()); |
| | | // Let the repair tool know about this. |
| | | Message message = ERR_LOOP_REPLAYING_OPERATION.get(op.toString(), |
| | | op.getErrorMessage().toString()); |
| | | logError(message); |
| | | numUnresolvedNamingConflicts.incrementAndGet(); |
| | | |
| | |
| | | * An Exception happened during the replay process. |
| | | * Continue with the next change but the servers will now start |
| | | * to be inconsistent. |
| | | * TODO : REPAIR : Should let the repair tool know about this |
| | | * Let the repair tool know about this. |
| | | */ |
| | | Message message = ERR_EXCEPTION_REPLAYING_OPERATION.get( |
| | | stackTraceToSingleLineString(e), op.toString()); |
| | |
| | | else |
| | | { |
| | | // The other type of errors can not be caused by naming conflicts. |
| | | // TODO log a message for the repair tool. |
| | | // Log a message for the repair tool. |
| | | Message message = ERR_ERROR_REPLAYING_OPERATION.get( |
| | | op.toString(), ctx.getChangeNumber().toString(), |
| | | result.toString(), op.getErrorMessage().toString()); |
| | | logError(message); |
| | | return true; |
| | | } |
| | | } |
| | |
| | | else |
| | | { |
| | | // The other type of errors can not be caused by naming conflicts. |
| | | // TODO log a message for the repair tool. |
| | | // Log a message for the repair tool. |
| | | Message message = ERR_ERROR_REPLAYING_OPERATION.get( |
| | | op.toString(), ctx.getChangeNumber().toString(), |
| | | result.toString(), op.getErrorMessage().toString()); |
| | | logError(message); |
| | | return true; |
| | | } |
| | | } |
| | |
| | | else |
| | | { |
| | | // The other type of errors can not be caused by naming conflicts. |
| | | // TODO log a message for the repair tool. |
| | | // Log a message for the repair tool. |
| | | Message message = ERR_ERROR_REPLAYING_OPERATION.get( |
| | | op.toString(), ctx.getChangeNumber().toString(), |
| | | result.toString(), op.getErrorMessage().toString()); |
| | | logError(message); |
| | | return true; |
| | | } |
| | | } |
| | |
| | | else |
| | | { |
| | | // The other type of errors can not be caused by naming conflicts. |
| | | // TODO log a message for the repair tool. |
| | | // log a message for the repair tool. |
| | | Message message = ERR_ERROR_REPLAYING_OPERATION.get( |
| | | op.toString(), ctx.getChangeNumber().toString(), |
| | | result.toString(), op.getErrorMessage().toString()); |
| | | logError(message); |
| | | return true; |
| | | } |
| | | } |
| | |
| | | } |
| | | else |
| | | { |
| | | // log error and information for the REPAIR tool. |
| | | MessageBuilder mb = new MessageBuilder(); |
| | | mb.append(ERR_CANNOT_RENAME_CONFLICT_ENTRY.get()); |
| | | mb.append(String.valueOf(entryDN)); |
| | |
| | | mb.append(" "); |
| | | mb.append(String.valueOf(op.getResultCode())); |
| | | logError(mb.toMessage()); |
| | | // TODO : log error and information for the REPAIR tool. |
| | | } |
| | | } catch (DirectoryException e) |
| | | { |
| | | // log errror and information for the REPAIR tool. |
| | | MessageBuilder mb = new MessageBuilder(); |
| | | mb.append(ERR_EXCEPTION_RENAME_CONFLICT_ENTRY.get()); |
| | | mb.append(String.valueOf(entryDN)); |
| | |
| | | mb.append(" "); |
| | | mb.append(e.getLocalizedMessage()); |
| | | logError(mb.toMessage()); |
| | | // TODO log errror and information for the REPAIR tool. |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | if (newOp.getResultCode() != ResultCode.SUCCESS) |
| | | { |
| | | // log information for the repair tool. |
| | | MessageBuilder mb = new MessageBuilder(); |
| | | mb.append(ERR_CANNOT_RENAME_CONFLICT_ENTRY.get()); |
| | | mb.append(String.valueOf(dn)); |
| | |
| | | mb.append(" "); |
| | | mb.append(String.valueOf(newOp.getResultCode())); |
| | | logError(mb.toMessage()); |
| | | /* |
| | | * TODO : REPAIR should log information for the repair tool. |
| | | */ |
| | | } |
| | | } |
| | | |
| | |
| | | ModifyOperation newOp = conn.processModify(currentDN, mods); |
| | | if (newOp.getResultCode() != ResultCode.SUCCESS) |
| | | { |
| | | // Log information for the repair tool. |
| | | MessageBuilder mb = new MessageBuilder(); |
| | | mb.append(ERR_CANNOT_ADD_CONFLICT_ATTRIBUTE.get()); |
| | | mb.append(String.valueOf(op)); |
| | | mb.append(" "); |
| | | mb.append(String.valueOf(newOp.getResultCode())); |
| | | logError(mb.toMessage()); |
| | | /* |
| | | * TODO : REPAIR should log information for the repair tool. |
| | | */ |
| | | } |
| | | } |
| | | |
| 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.replication.plugin; |
| | | |
| | | |
| | | import org.opends.server.types.Control; |
| | | import org.opends.server.types.LDAPException; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class implements the Sun-defined replication repair control. |
| | | * This control can be used to modify the content of a replicated database |
| | | * on a single server without impacting the other servers that are replicated |
| | | * with this server. |
| | | * It also allows to modify attributes like entryuuid and ds-sync-hist that |
| | | * are normally not modifiable from an external connection. |
| | | */ |
| | | public class ReplicationRepairRequestControl |
| | | extends Control |
| | | { |
| | | /** |
| | | * The OID of the Replication repair Control. |
| | | */ |
| | | public static final String |
| | | OID_REPLICATION_REPAIR_CONTROL = "1.3.6.1.4.1.26027.1.5.2"; |
| | | |
| | | |
| | | /** |
| | | * Creates a new instance of the replication repair request control with the |
| | | * default settings. |
| | | */ |
| | | public ReplicationRepairRequestControl() |
| | | { |
| | | super(OID_REPLICATION_REPAIR_CONTROL, false); |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new instance of the replication repair control with the |
| | | * provided information. |
| | | * |
| | | * @param oid The OID to use for this control. |
| | | * @param isCritical Indicates whether support for this control should be |
| | | * considered a critical part of the client processing. |
| | | */ |
| | | public ReplicationRepairRequestControl(String oid, boolean isCritical) |
| | | { |
| | | super(oid, isCritical); |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new replication repair request control from the contents of the |
| | | * provided control. |
| | | * |
| | | * @param control The generic control containing the information to use to |
| | | * create this replication repair request control. |
| | | * |
| | | * @return The replication repair request control decoded from the provided |
| | | * control. |
| | | * |
| | | * @throws LDAPException If this control cannot be decoded as a valid |
| | | * replication repair request control. |
| | | */ |
| | | public static ReplicationRepairRequestControl decodeControl(Control control) |
| | | throws LDAPException |
| | | { |
| | | return new ReplicationRepairRequestControl(control.getOID(), |
| | | control.isCritical()); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves a string representation of this replication repair request |
| | | * control. |
| | | * |
| | | * @return A string representation of this replication repair request |
| | | * control. |
| | | */ |
| | | public String toString() |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | | toString(buffer); |
| | | return buffer.toString(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Appends a string representation of this replication repair request control |
| | | * to the provided buffer. |
| | | * |
| | | * @param buffer The buffer to which the information should be appended. |
| | | */ |
| | | public void toString(StringBuilder buffer) |
| | | { |
| | | buffer.append("ReplicationRepairRequestControl()"); |
| | | } |
| | | } |
| | | |
| | |
| | | */ |
| | | public static final String LOG_SEVERITY_ALL = "all"; |
| | | |
| | | /** |
| | | * The English name for the basic none log severity used to log |
| | | * no error message beside some specific category. |
| | | */ |
| | | public static final String LOG_SEVERITY_NONE = "none"; |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | |
| | | } |
| | | } |
| | | |
| | | // If the attribute type is marked "NO-USER-MODIFICATION" then fail |
| | | // unless this is an internal operation or is related to |
| | | // synchronization in some way. |
| | | // This must be done before running the password policy code |
| | | // and any other code that may add attributes marked as |
| | | // "NO-USER-MODIFICATION" |
| | | // |
| | | // Note that doing this checks at this time |
| | | // of the processing does not make it possible for pre-parse plugins |
| | | // to add NO-USER-MODIFICATION attributes to the entry. |
| | | for (AttributeType at : userAttributes.keySet()) |
| | | { |
| | | if (at.isNoUserModification()) |
| | | { |
| | | if (! (localOp.isInternalOperation() || |
| | | localOp.isSynchronizationOperation())) |
| | | { |
| | | localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | localOp.appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get( |
| | | String.valueOf(entryDN), at.getNameOrOID())); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | for (AttributeType at : operationalAttributes.keySet()) |
| | | { |
| | | if (at.isNoUserModification()) |
| | | { |
| | | if (! (localOp.isInternalOperation() || |
| | | localOp.isSynchronizationOperation())) |
| | | { |
| | | localOp.setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | localOp.appendErrorMessage(ERR_ADD_ATTR_IS_NO_USER_MOD.get( |
| | | String.valueOf(entryDN), at.getNameOrOID())); |
| | | |
| | | break addProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Check to see if the entry already exists. We do this before |
| | | // checking whether the parent exists to ensure a referral entry |
| | |
| | | */ |
| | | protected void configureReplication() throws Exception |
| | | { |
| | | // Add the Multimaster replication plugin |
| | | String synchroPluginLdif = "dn: " + synchroPluginStringDN + "\n" |
| | | + "objectClass: top\n" |
| | | + "objectClass: ds-cfg-synchronization-provider\n" |
| | | + "objectClass: ds-cfg-multimaster-synchronization-provider\n" |
| | | + "ds-cfg-synchronization-provider-enabled: true\n" |
| | | + "ds-cfg-synchronization-provider-class: " + |
| | | "org.opends.server.replication.plugin.MultimasterReplication\n"; |
| | | Entry synchroPluginEntry = TestCaseUtils.entryFromLdifString(synchroPluginLdif); |
| | | DirectoryServer.getConfigHandler().addEntry(synchroPluginEntry, null); |
| | | configEntryList.add(synchroPluginEntry.getDN()); |
| | | assertNotNull(DirectoryServer.getConfigEntry(DN |
| | | .decode(synchroPluginStringDN)), |
| | | "Unable to add the Multimaster replication plugin"); |
| | | |
| | | // domains container entry. |
| | | String domainsLdif = "dn: " |
| | | + "cn=domains," + synchroPluginStringDN + "\n" |
| | | + "objectClass: top\n" |
| | | + "objectClass: ds-cfg-branch\n"; |
| | | Entry domainsEntry = TestCaseUtils.entryFromLdifString(domainsLdif); |
| | | DirectoryServer.getConfigHandler().addEntry(domainsEntry, null); |
| | | configEntryList.add(domainsEntry.getDN()); |
| | | assertNotNull(DirectoryServer.getConfigEntry( |
| | | DN.decode(synchroPluginStringDN)), |
| | | "Unable to add the Multimaster replication plugin"); |
| | | |
| | | if (replServerEntry != null) |
| | | { |
| | | // Add the replication server |
| 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.replication.plugin; |
| | | |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.tools.LDAPModify; |
| | | import org.testng.annotations.Test; |
| | | import static org.testng.Assert.*; |
| | | |
| | | public class ReplicationRepairControlTest extends ReplicationTestCase |
| | | { |
| | | @Test() |
| | | public void testRepairControl() |
| | | throws Exception |
| | | { |
| | | TestCaseUtils.startServer(); |
| | | |
| | | TestCaseUtils.initializeTestBackend(true); |
| | | |
| | | // Test that we can't add an entry with the entryuuid attribute |
| | | // without specifying the replication repair control. |
| | | String path = TestCaseUtils.createTempFile( |
| | | "dn: uid=test.repair,o=test\n" + |
| | | "changetype: add\n" + |
| | | "objectClass: top\n" + |
| | | "objectClass: person" + |
| | | "objectClass: organizationalPerson" + |
| | | "objectClass: inetOrgPerson" + |
| | | "uid: test.repair" + |
| | | "givenName: Test" + |
| | | "sn: User" + |
| | | "cn: Test User" + |
| | | "userPassword: password" + |
| | | "entryuuid: d5b910d8-47cb-4ac0-9e5f-0f4a77de58d4"); |
| | | |
| | | String[] args = |
| | | { |
| | | "-h", "127.0.0.1", |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-D", "cn=Directory Manager", |
| | | "-w", "password", |
| | | "-a", |
| | | "-f", path |
| | | }; |
| | | |
| | | assertEquals(LDAPModify.mainModify(args, false, null, null), 65); |
| | | |
| | | // Test that we can't add an entry with the ds-sync-hist attribute |
| | | // without specifying the replication repair control. |
| | | String path1 = TestCaseUtils.createTempFile( |
| | | "dn: uid=test.repair,o=test\n" + |
| | | "changetype: add\n" + |
| | | "objectClass: top\n" + |
| | | "objectClass: person" + |
| | | "objectClass: organizationalPerson" + |
| | | "objectClass: inetOrgPerson" + |
| | | "uid: test.repair" + |
| | | "givenName: Test" + |
| | | "sn: User" + |
| | | "cn: Test User" + |
| | | "userPassword: password" + |
| | | "ds-sync-hist: description:00000108b3a6cbb800000001:del:deleted_value"); |
| | | |
| | | String[] args1 = |
| | | { |
| | | "-h", "127.0.0.1", |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-D", "cn=Directory Manager", |
| | | "-w", "password", |
| | | "-a", |
| | | "-f", path1 |
| | | }; |
| | | |
| | | |
| | | assertEquals(LDAPModify.mainModify(args1, false, null, null), 65); |
| | | |
| | | // Now Test specifying the replication repair control makes |
| | | // possible to add an entry with the entryuuid and ds-sync-hist attributes |
| | | // (notice the -J repairControlOid in the ldapmodify arguments) |
| | | String path2 = TestCaseUtils.createTempFile( |
| | | "dn: uid=test.repair,o=test\n" + |
| | | "changetype: add\n" + |
| | | "objectClass: top\n" + |
| | | "objectClass: person" + |
| | | "objectClass: organizationalPerson" + |
| | | "objectClass: inetOrgPerson" + |
| | | "uid: test.repair" + |
| | | "givenName: Test" + |
| | | "sn: User" + |
| | | "cn: Test User" + |
| | | "userPassword: password" + |
| | | "ds-sync-hist: description:00000108b3a6cbb800000001:del:deleted_value" + |
| | | "entryuuid: d5b910d8-47cb-4ac0-9e5f-0f4a77de58d4"); |
| | | |
| | | String[] args2 = |
| | | { |
| | | "-h", "127.0.0.1", |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-D", "cn=Directory Manager", |
| | | "-w", "password", |
| | | "-J", "1.3.6.1.4.1.26027.1.5.2", |
| | | "-a", |
| | | "-f", path2 |
| | | }; |
| | | |
| | | assertEquals(LDAPModify.mainModify(args2, false, null, null), 0); |
| | | } |
| | | } |