opends/src/ads/org/opends/admin/ads/ReplicaDescriptor.java
@@ -41,6 +41,8 @@ private ServerDescriptor server; private Set<String> replicationServers = new HashSet<String>(); private int replicationId = -1; private int missingChanges = -1; private int ageOfOldestMissingChange = -1; /** * Returns the number of entries contained in the replica. @@ -160,4 +162,40 @@ { this.replicationId = replicationId; } /** * Returns the age of the oldest missing change. * @return the age of the oldest missing change. */ public int getAgeOfOldestMissingChange() { return ageOfOldestMissingChange; } /** * Sets the age of the oldest missing change. * @param ageOfOldestMissingChange the age of the oldest missing change. */ public void setAgeOfOldestMissingChange(int ageOfOldestMissingChange) { this.ageOfOldestMissingChange = ageOfOldestMissingChange; } /** * Returns the number of missing changes. * @return the number of missing changes. */ public int getMissingChanges() { return missingChanges; } /** * Sets the number of missing changes. * @param missingChanges the number of missing changes. */ public void setMissingChanges(int missingChanges) { this.missingChanges = missingChanges; } } opends/src/ads/org/opends/admin/ads/ServerDescriptor.java
@@ -44,6 +44,7 @@ import javax.naming.ldap.Rdn; import org.opends.admin.ads.util.ConnectionUtils; import org.opends.quicksetup.util.Utils; /** * The object of this class represent an OpenDS server. @@ -688,6 +689,7 @@ InitialLdapContext ctx) throws NamingException { boolean replicationEnabled = false; boolean oneDomainReplicated = false; SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.OBJECT_SCOPE); ctls.setReturningAttributes( @@ -747,6 +749,7 @@ Set<String> replicationServers = getValues(sr, "ds-cfg-replication-server"); Set<String> dns = getValues(sr, "ds-cfg-replication-dn"); oneDomainReplicated = dns.size() > 0; for (String dn : dns) { for (ReplicaDescriptor replica : desc.getReplicas()) @@ -818,6 +821,58 @@ { /* ignore */ } ctls = new SearchControls(); ctls.setSearchScope(SearchControls.ONELEVEL_SCOPE); ctls.setReturningAttributes( new String[] { "approximate-delay", "waiting-changes", "base-dn" }); filter = "(approximate-delay=*)"; jndiName = new LdapName("cn=monitor"); if (oneDomainReplicated) { try { NamingEnumeration monitorEntries = ctx.search(jndiName, filter, ctls); while(monitorEntries.hasMore()) { SearchResult sr = (SearchResult)monitorEntries.next(); String dn = getFirstValue(sr, "base-dn"); for (ReplicaDescriptor replica: desc.getReplicas()) { if (Utils.areDnsEqual(dn, replica.getSuffix().getDN()) && replica.isReplicated()) { try { replica.setAgeOfOldestMissingChange( new Integer(getFirstValue(sr, "approximate-delay"))); } catch (Throwable t) { } try { replica.setMissingChanges( new Integer(getFirstValue(sr, "waiting-changes"))); } catch (Throwable t) { } } } } } catch (NameNotFoundException nse) { } } } /** opends/src/guitools/org/opends/guitools/replicationcli/ReplicationCliArgumentParser.java
@@ -59,6 +59,7 @@ private SubCommand enableReplicationSubCmd; private SubCommand disableReplicationSubCmd; private SubCommand initializeReplicationSubCmd; private SubCommand statusReplicationSubCmd; private BooleanArgument noPromptArg; @@ -218,6 +219,11 @@ private BooleanArgument quietArg; /** * The 'scriptFriendly' argument. */ private BooleanArgument scriptFriendlyArg; /** * The text of the enable replication subcommand. */ public static final String ENABLE_REPLICATION_SUBCMD_NAME = "enable"; @@ -233,6 +239,11 @@ public static final String INITIALIZE_REPLICATION_SUBCMD_NAME = "initialize"; /** * The text of the status replication subcommand. */ public static final String STATUS_REPLICATION_SUBCMD_NAME = "status"; /** * Creates a new instance of this argument parser with no arguments. * * @param mainClassName @@ -265,6 +276,7 @@ createEnableReplicationSubCommand(); createDisableReplicationSubCommand(); createInitializeReplicationSubCommand(); createStatusReplicationSubCommand(); } /** @@ -300,7 +312,7 @@ if (!isInteractive()) { // Check that we have the required data if (!baseDNsArg.isPresent()) if (!baseDNsArg.isPresent() && !isStatusReplicationSubcommand()) { errors.add(ERR_REPLICATION_NO_BASE_DN_PROVIDED.get()); } @@ -598,6 +610,31 @@ } /** * Creates the status replication subcommand and all the specific options * for the subcommand. Note: this method assumes that * initializeGlobalArguments has already been called and that hostNameArg, * portArg, startTLSArg and useSSLArg have been created. */ private void createStatusReplicationSubCommand() throws ArgumentException { statusReplicationSubCmd = new SubCommand(this, STATUS_REPLICATION_SUBCMD_NAME, INFO_DESCRIPTION_SUBCMD_STATUS_REPLICATION.get()); scriptFriendlyArg = new BooleanArgument( "script-friendly", 's', "script-friendly", INFO_DESCRIPTION_SCRIPT_FRIENDLY.get()); Argument[] argsToAdd = { secureArgsList.hostNameArg, secureArgsList.portArg, secureArgsList.useSSLArg, secureArgsList.useStartTLSArg, scriptFriendlyArg }; for (int i=0; i<argsToAdd.length; i++) { statusReplicationSubCmd.addArgument(argsToAdd[i]); } } /** * Tells whether the user specified to have an interactive operation or not. * This method must be called after calling parseArguments. * @return <CODE>true</CODE> if the user specified to have an interactive @@ -620,6 +657,17 @@ } /** * Tells whether the user specified to have a script-friendly output or not. * This method must be called after calling parseArguments. * @return <CODE>true</CODE> if the user specified to have a script-friendly * output and <CODE>false</CODE> otherwise. */ public boolean isScriptFriendly() { return scriptFriendlyArg.isPresent(); } /** * Get the password which has to be used for the command to connect to the * first server without prompting the user in the enable replication * subcommand. If no password was specified return null. @@ -852,6 +900,30 @@ } /** * Indicate if the SSL mode is required for the server in the status * replication subcommand. * * @return <CODE>true</CODE> if SSL mode is required for the server in the * status replication subcommand and <CODE>false</CODE> otherwise. */ public boolean useSSLToStatus() { return secureArgsList.useSSLArg.isPresent(); } /** * Indicate if the SSL mode is required for the server in the status * replication subcommand. * * @return <CODE>true</CODE> if StartTLS mode is required for the server in * the status replication subcommand and <CODE>false</CODE> otherwise. */ public boolean useStartTLSToStatus() { return secureArgsList.useStartTLSArg.isPresent(); } /** * Returns the Administrator UID explicitly provided in the command-line. * @return the Administrator UID explicitly provided in the command-line. */ @@ -1112,6 +1184,26 @@ } /** * Returns the host name explicitly provided in the status replication * subcommand. * @return the host name explicitly provided in the status replication * subcommand. */ public String getHostNameToStatus() { return getValue(secureArgsList.hostNameArg); } /** * Returns the host name default value in the status replication subcommand. * @return the host name default value in the status replication subcommand. */ public String getDefaultHostNameToStatus() { return getDefaultValue(secureArgsList.hostNameArg); } /** * Returns the source host name explicitly provided in the initialize * replication subcommand. * @return the source host name explicitly provided in the initialize @@ -1222,6 +1314,26 @@ } /** * Returns the server port explicitly provided in the status replication * subcommand. * @return the server port explicitly provided in the status replication * subcommand. Returns -1 if no port was explicitly provided. */ public int getPortToStatus() { return getValue(secureArgsList.portArg); } /** * Returns the server port default value in the status replication subcommand. * @return the server port default value in the status replication subcommand. */ public int getDefaultPortToStatus() { return getDefaultValue(secureArgsList.portArg); } /** * Returns the list of base DNs provided by the user. * @return the list of base DNs provided by the user. */ @@ -1321,6 +1433,10 @@ { validateDisableReplicationOptions(buf); } else if (isStatusReplicationSubcommand()) { validateStatusReplicationOptions(buf); } else if (isInitializeReplicationSubcommand()) { validateInitializeReplicationOptions(buf); @@ -1357,6 +1473,17 @@ } /** * Returns whether the user provided subcommand is the status replication * or not. * @return <CODE>true</CODE> if the user provided subcommand is the * status replication and <CODE>false</CODE> otherwise. */ public boolean isStatusReplicationSubcommand() { return isSubcommand(STATUS_REPLICATION_SUBCMD_NAME); } /** * Returns whether the user provided subcommand is the initialize replication * or not. * @return <CODE>true</CODE> if the user provided subcommand is the @@ -1443,8 +1570,7 @@ { Argument[][] conflictingPairs = { {useStartTLSSourceArg, useSSLSourceArg}, {useStartTLSDestinationArg, useSSLDestinationArg}, {secureArgsList.useStartTLSArg, secureArgsList.useSSLArg}, {adminUidArg, secureArgsList.bindDnArg} }; @@ -1462,6 +1588,43 @@ } /** * Checks the status replication subcommand options and updates the provided * MessageBuilder with the errors that were encountered with the subcommand * options. * * This method assumes that the method parseArguments for the parser has * already been called. * @param buf the MessageBuilder object where we add the error messages * describing the errors encountered. */ private void validateStatusReplicationOptions(MessageBuilder buf) { Argument[][] conflictingPairs = { {secureArgsList.useStartTLSArg, secureArgsList.useSSLArg} }; for (int i=0; i< conflictingPairs.length; i++) { Argument arg1 = conflictingPairs[i][0]; Argument arg2 = conflictingPairs[i][1]; if (arg1.isPresent() && arg2.isPresent()) { Message message = ERR_TOOL_CONFLICTING_ARGS.get( arg1.getLongIdentifier(), arg2.getLongIdentifier()); addMessage(buf, message); } } if (quietArg.isPresent()) { Message message = ERR_REPLICATION_STATUS_QUIET.get( STATUS_REPLICATION_SUBCMD_NAME, quietArg.getLongIdentifier()); addMessage(buf, message); } } /** * Checks the initialize replication subcommand options and updates the * provided MessageBuilder with the errors that were encountered with the * subcommand options. opends/src/guitools/org/opends/guitools/replicationcli/ReplicationCliMain.java
@@ -97,7 +97,8 @@ /** * This class provides a tool that can be used to enable and disable replication * and also to initialize the contents of a replicated suffix with the contents * of another suffix. * of another suffix. It also allows to display the replicated status of the * different base DNs of the servers that are registered in the ADS. */ public class ReplicationCliMain extends CliApplicationHelper { @@ -312,6 +313,10 @@ { returnValue = initializeReplication(); } else if (argParser.isStatusReplicationSubcommand()) { returnValue = statusReplication(); } else { err.println(wrapText(ERR_REPLICATION_VALID_SUBCOMMAND_NOT_FOUND.get(), @@ -390,6 +395,35 @@ } /** * Based on the data provided in the command-line it displays replication * status. * @return the error code if the operation failed and SUCCESSFUL if it was * successful. */ private ReplicationCliReturnCode statusReplication() { ReplicationCliReturnCode returnValue = SUCCESSFUL_NOP; StatusReplicationUserData uData = new StatusReplicationUserData(); if (argParser.isInteractive()) { if (promptIfRequired(uData)) { returnValue = statusReplication(uData); } else { returnValue = USER_CANCELLED; } } else { initializeWithArgParser(uData); returnValue = statusReplication(uData); } return returnValue; } /** * Based on the data provided in the command-line it initializes replication * between two servers. * @return the error code if the operation failed and SUCCESSFUL if it was @@ -1126,6 +1160,148 @@ } /** * Updates the contents of the provided StatusReplicationUserData object * with the information provided in the command-line. If some information * is missing, ask the user to provide valid data. * We assume that if this method is called we are in interactive mode. * @param uData the object to be updated. * @return <CODE>true</CODE> if the object was successfully updated and * <CODE>false</CODE> if the user cancelled the operation. */ private boolean promptIfRequired(StatusReplicationUserData uData) { boolean cancelled = false; String adminPwd = argParser.getBindPasswordAdmin(); String adminUid = argParser.getAdministratorUID(); String host = argParser.getHostNameToStatus(); if (host == null) { host = promptForString( INFO_REPLICATION_STATUS_HOSTNAME_PROMPT.get(), argParser.getDefaultHostNameToStatus(), false); } int port = argParser.getPortToStatus(); if (port == -1) { port = promptForPort( INFO_REPLICATION_STATUS_PORT_PROMPT.get(), argParser.getDefaultPortToStatus(), false); } boolean useSSL = argParser.useSSLToStatus(); boolean useStartTLS = argParser.useStartTLSToStatus(); if (!useSSL && !useStartTLS) { useSSL = confirm(INFO_CLI_USESSL_PROMPT.get(), false); if (!useSSL) { useStartTLS = confirm(INFO_CLI_USESTARTTLS_PROMPT.get(), false); } } if (adminUid == null) { adminUid = askForAdministratorUID(argParser.getDefaultAdministratorUID()); } if (adminPwd == null) { adminPwd = askForAdministratorPwd(); } /* * Try to connect to the server. */ InitialLdapContext ctx = null; while ((ctx == null) && !cancelled) { try { ctx = createContext(host, port, useSSL, useStartTLS, ADSContext.getAdministratorDN(adminUid), adminPwd, getTrustManager()); } catch (NamingException ne) { LOG.log(Level.WARNING, "Error connecting to "+host+":"+port, ne); if (Utils.isCertificateException(ne)) { String usedUrl = ConnectionUtils.getLDAPUrl(host, port, useSSL); if (!promptForCertificateConfirmation(ne, getTrustManager(), usedUrl, getTrustManager())) { cancelled = true; } } else { printLineBreak(); printErrorMessage(ERR_ERROR_CONNECTING_TO_SERVER_PROMPT_AGAIN.get( host+":"+port, ne.toString())); printLineBreak(); host = promptForString( INFO_REPLICATION_STATUS_HOSTNAME_PROMPT.get(), getValue(host, argParser.getDefaultHostNameToStatus()), false); port = promptForPort( INFO_REPLICATION_STATUS_PORT_PROMPT.get(), getValue(port, argParser.getDefaultPortToStatus()), false); useSSL = confirm(INFO_CLI_USESSL_PROMPT.get(), useSSL); if (!useSSL) { useStartTLS = confirm(INFO_CLI_USESTARTTLS_PROMPT.get(), useStartTLS); } adminUid = askForAdministratorUID(adminUid); adminPwd = askForAdministratorPwd(); } } } if (!cancelled) { uData.setHostName(host); uData.setPort(port); uData.setUseSSL(useSSL); uData.setUseStartTLS(useStartTLS); uData.setAdminUid(adminUid); uData.setAdminPwd(adminPwd); uData.setScriptFriendly(argParser.isScriptFriendly()); } if (ctx != null) { // If the server contains an ADS, try to load it and only load it: if // there are issues with the ADS they will be encountered in the // statusReplication(StatusReplicationUserData) method. Here we have // to load the ADS to ask the user to accept the certificates and // eventually admin authentication data. InitialLdapContext[] aux = new InitialLdapContext[] {ctx}; cancelled = !loadADSAndAcceptCertificates(aux, uData, false); ctx = aux[0]; } if (!cancelled) { LinkedList<String> suffixes = argParser.getBaseDNs(); uData.setBaseDNs(suffixes); } if (ctx != null) { try { ctx.close(); } catch (Throwable t) { } } return !cancelled; } /** * Updates the contents of the provided InitializeReplicationUserData object * with the information provided in the command-line. If some information * is missing, ask the user to provide valid data. @@ -1652,6 +1828,34 @@ uData.setUseStartTLS(argParser.useStartTLSToDisable()); } /** * Initializes the contents of the provided status replication user data * object with what was provided in the command-line without prompting to the * user. * @param uData the disable replication user data object to be initialized. */ private void initializeWithArgParser(StatusReplicationUserData uData) { uData.setBaseDNs(new LinkedList<String>(argParser.getBaseDNs())); String adminUid = getValue(argParser.getAdministratorUID(), argParser.getDefaultAdministratorUID()); uData.setAdminUid(adminUid); String adminPwd = argParser.getBindPasswordAdmin(); uData.setAdminPwd(adminPwd); String hostName = getValue(argParser.getHostNameToStatus(), argParser.getDefaultHostNameToStatus()); uData.setHostName(hostName); int port = getValue(argParser.getPortToStatus(), argParser.getDefaultPortToStatus()); uData.setPort(port); uData.setUseSSL(argParser.useSSLToStatus()); uData.setUseStartTLS(argParser.useStartTLSToStatus()); uData.setScriptFriendly(argParser.isScriptFriendly()); } /** * Tells whether the server to which the LdapContext is connected has a * replication port or not. @@ -1866,6 +2070,16 @@ } if ((exceptionMsgs.size() > 0) && !cancelled) { if (uData instanceof StatusReplicationUserData) { printWarningMessage( ERR_REPLICATION_STATUS_READING_REGISTERED_SERVERS.get( Utils.getMessageFromCollection(exceptionMsgs, Constants.LINE_SEPARATOR).toString())); printLineBreak(); } else { cancelled = !confirm( ERR_REPLICATION_READING_REGISTERED_SERVERS_CONFIRM_UPDATE_REMOTE. get(Utils.getMessageFromCollection(exceptionMsgs, @@ -1873,6 +2087,7 @@ } } } } catch (ADSContextException ace) { printLineBreak(); @@ -2335,6 +2550,69 @@ } /** * Displays the replication status of the baseDNs specified in the * StatusReplicationUserData object. This method does not prompt * to the user for information if something is missing. * @param uData the StatusReplicationUserData object. * @return ReplicationCliReturnCode.SUCCESSFUL if the operation was * successful. * and the replication could be enabled and an error code otherwise. */ private ReplicationCliReturnCode statusReplication( StatusReplicationUserData uData) { ReplicationCliReturnCode returnValue = SUCCESSFUL_NOP; InitialLdapContext ctx = null; try { ctx = createContext(uData.getHostName(), uData.getPort(), uData.useSSL(), uData.useStartTLS(), ADSContext.getAdministratorDN(uData.getAdminUid()), uData.getAdminPwd(), getTrustManager()); } catch (NamingException ne) { String hostPort = uData.getHostName()+":"+uData.getPort(); printLineBreak(); printErrorMessage(getMessageForException(ne, hostPort)); LOG.log(Level.SEVERE, "Complete error stack:", ne); } if (ctx != null) { uData.setBaseDNs(uData.getBaseDNs()); try { displayStatus(ctx, uData); returnValue = SUCCESSFUL; } catch (ReplicationCliException rce) { returnValue = rce.getErrorCode(); printLineBreak(); printErrorMessage(rce.getMessageObject()); LOG.log(Level.SEVERE, "Complete error stack:", rce); } } else { returnValue = ERROR_CONNECTING; } if (ctx != null) { try { ctx.close(); } catch (Throwable t) { } } return returnValue; } /** * Initializes the contents of one server with the contents of the other * using the parameters in the provided InitializeReplicationUserData. * This method does not prompt to the user for information if something is @@ -2577,8 +2855,10 @@ printErrorMessage(ERR_NO_SUFFIXES_SELECTED_TO_REPLICATE.get()); for (String dn : availableSuffixes) { if (!Utils.areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn) && !Utils.areDnsEqual(Constants.SCHEMA_DN, dn)) if (!Utils.areDnsEqual(dn, ADSContext.getAdministrationSuffixDN()) && !Utils.areDnsEqual(dn, Constants.SCHEMA_DN) && !Utils.areDnsEqual(dn, Constants.REPLICATION_CHANGES_DN)) { if (confirm(INFO_REPLICATION_ENABLE_SUFFIX_PROMPT.get(dn))) { @@ -2732,8 +3012,10 @@ printErrorMessage(ERR_NO_SUFFIXES_SELECTED_TO_DISABLE.get()); for (String dn : availableSuffixes) { if (!Utils.areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn) && !Utils.areDnsEqual(Constants.SCHEMA_DN, dn)) if (!Utils.areDnsEqual(dn, ADSContext.getAdministrationSuffixDN()) && !Utils.areDnsEqual(dn, Constants.SCHEMA_DN) && !Utils.areDnsEqual(dn, Constants.REPLICATION_CHANGES_DN)) { if (confirm(INFO_REPLICATION_DISABLE_SUFFIX_PROMPT.get(dn))) { @@ -2829,8 +3111,10 @@ for (String dn : availableSuffixes) { if (!Utils.areDnsEqual(ADSContext.getAdministrationSuffixDN(), dn) && !Utils.areDnsEqual(Constants.SCHEMA_DN, dn)) if (!Utils.areDnsEqual(dn, ADSContext.getAdministrationSuffixDN()) && !Utils.areDnsEqual(dn, Constants.SCHEMA_DN) && !Utils.areDnsEqual(dn, Constants.REPLICATION_CHANGES_DN)) { if (confirm(INFO_REPLICATION_INITIALIZE_SUFFIX_PROMPT.get(dn))) { @@ -2850,7 +3134,7 @@ * @param ctx1 the connection to the first server. * @param ctx2 the connection to the second server. * @param uData the EnableReplicationUserData object containing the required * parameters to update the confgiuration. * parameters to update the configuration. * @throws ReplicationCliException if there is an error. */ private void updateConfiguration(InitialLdapContext ctx1, @@ -3099,7 +3383,7 @@ if (uData.replicateSchema()) { baseDNs = uData.getBaseDNs(); baseDNs.add("cn=schema"); baseDNs.add(Constants.SCHEMA_DN); uData.setBaseDNs(baseDNs); } TopologyCache cache1 = null; @@ -3318,7 +3602,7 @@ * they are referenced) to disable replication. * @param ctx the connection to the server. * @param uData the DisableReplicationUserData object containing the required * parameters to update the confgiuration. * parameters to update the configuration. * @throws ReplicationCliException if there is an error. */ private void updateConfiguration(InitialLdapContext ctx, @@ -3421,6 +3705,406 @@ } /** * Displays the replication status of the different base DNs in the servers * registered in the ADS. * @param ctx the connection to the server. * @param uData the StatusReplicationUserData object containing the required * parameters to update the configuration. * @throws ReplicationCliException if there is an error. */ private void displayStatus(InitialLdapContext ctx, StatusReplicationUserData uData) throws ReplicationCliException { ADSContext adsCtx = new ADSContext(ctx); TopologyCache cache = null; try { if (adsCtx.hasAdminData()) { cache = new TopologyCache(adsCtx, getTrustManager()); cache.reloadTopology(); } } catch (ADSContextException adce) { throw new ReplicationCliException( ERR_REPLICATION_READING_ADS.get(adce.getMessage()), ERROR_READING_ADS, adce); } catch (TopologyCacheException tce) { throw new ReplicationCliException( ERR_REPLICATION_READING_ADS.get(tce.getMessage()), ERROR_READING_TOPOLOGY_CACHE, tce); } if (!argParser.isInteractive()) { // Inform the user of the potential errors that we found. LinkedHashSet<Message> messages = new LinkedHashSet<Message>(); if (cache != null) { messages.addAll(getErrorMessages(cache)); } if (!messages.isEmpty()) { Message msg = ERR_REPLICATION_STATUS_READING_REGISTERED_SERVERS.get( Utils.getMessageFromCollection(messages, Constants.LINE_SEPARATOR).toString()); printWarningMessage(msg); } } LinkedList<String> userBaseDNs = uData.getBaseDNs(); LinkedList<Set<ReplicaDescriptor>> replicaLists = new LinkedList<Set<ReplicaDescriptor>>(); boolean oneReplicated = false; for (SuffixDescriptor suffix : cache.getSuffixes()) { String dn = suffix.getDN(); // If no base DNs where specified display all the base DNs but the schema // and cn=admin data. boolean found = userBaseDNs.isEmpty() && !Utils.areDnsEqual(dn, ADSContext.getAdministrationSuffixDN()) && !Utils.areDnsEqual(dn, Constants.SCHEMA_DN) && !Utils.areDnsEqual(dn, Constants.REPLICATION_CHANGES_DN); for (String baseDN : userBaseDNs) { found = Utils.areDnsEqual(baseDN, dn); if (found) { break; } } if (found) { boolean replicated = false; for (ReplicaDescriptor replica : suffix.getReplicas()) { if (replica.isReplicated()) { replicated = true; break; } } if (replicated) { oneReplicated = true; replicaLists.add(suffix.getReplicas()); } else { // Check if there are already some non replicated base DNs. found = false; for (Set<ReplicaDescriptor> replicas : replicaLists) { ReplicaDescriptor replica = replicas.iterator().next(); if (!replica.isReplicated() && Utils.areDnsEqual(dn, replica.getSuffix().getDN())) { replicas.addAll(suffix.getReplicas()); found = true; break; } } if (!found) { replicaLists.add(suffix.getReplicas()); } } } } if (replicaLists.isEmpty()) { printProgressMessage(INFO_REPLICATION_STATUS_NO_BASEDNS.get()); printProgressLineBreak(); } else { LinkedList<Set<ReplicaDescriptor>> orderedReplicaLists = new LinkedList<Set<ReplicaDescriptor>>(); for (Set<ReplicaDescriptor> replicas1 : replicaLists) { String dn1 = replicas1.iterator().next().getSuffix().getDN(); boolean inserted = false; for (int i=0; i<orderedReplicaLists.size() && !inserted; i++) { String dn2 = orderedReplicaLists.get(i).iterator().next().getSuffix().getDN(); if (dn1.compareTo(dn2) < 0) { orderedReplicaLists.add(i, replicas1); inserted = true; } } if (!inserted) { orderedReplicaLists.add(replicas1); } } for (Set<ReplicaDescriptor> replicas : orderedReplicaLists) { printProgressLineBreak(); displayStatus(replicas, uData.isScriptFriendly()); } if (oneReplicated && !uData.isScriptFriendly()) { printProgressLineBreak(); printProgressMessage(INFO_REPLICATION_STATUS_REPLICATED_LEGEND.get()); printProgressLineBreak(); } } } /** * Displays the replication status of the replicas provided. The code assumes * that all the replicas have the same baseDN and that if they are replicated * all the replicas are replicated with each other. * @param replicas the list of replicas that we are trying to display. * @param scriptFriendly wheter to display it on script-friendly mode or not. */ private void displayStatus(Set<ReplicaDescriptor> replicas, boolean scriptFriendly) { boolean isReplicated = false; Set<ReplicaDescriptor> orderedReplicas = new LinkedHashSet<ReplicaDescriptor>(); Set<String> hostPorts = new TreeSet<String>(); for (ReplicaDescriptor replica : replicas) { if (replica.isReplicated()) { isReplicated = true; } hostPorts.add(replica.getServer().getHostPort(true)); } for (String hostPort : hostPorts) { for (ReplicaDescriptor replica : replicas) { if (replica.getServer().getHostPort(true).equals(hostPort)) { orderedReplicas.add(replica); } } } int nCols; final int SERVERPORT = 0; final int NUMBER_ENTRIES = 1; final int MISSING_CHANGES = 2; final int AGE_OF_OLDEST_MISSING_CHANGE = 3; Message[] headers; if (scriptFriendly) { if (isReplicated) { nCols = 4; headers = new Message[] { INFO_REPLICATION_STATUS_LABEL_SERVERPORT.get(), INFO_REPLICATION_STATUS_LABEL_NUMBER_ENTRIES.get(), INFO_REPLICATION_STATUS_LABEL_MISSING_CHANGES.get(), INFO_REPLICATION_STATUS_LABEL_AGE_OF_OLDEST_MISSING_CHANGE.get() }; } else { nCols = 2; headers = new Message[] { INFO_REPLICATION_STATUS_LABEL_SERVERPORT.get(), INFO_REPLICATION_STATUS_LABEL_NUMBER_ENTRIES.get() }; } } else { if (isReplicated) { nCols = 4; headers = new Message[] { INFO_REPLICATION_STATUS_HEADER_SERVERPORT.get(), INFO_REPLICATION_STATUS_HEADER_NUMBER_ENTRIES.get(), INFO_REPLICATION_STATUS_HEADER_MISSING_CHANGES.get(), INFO_REPLICATION_STATUS_HEADER_AGE_OF_OLDEST_MISSING_CHANGE.get() }; } else { nCols = 2; headers = new Message[] { INFO_REPLICATION_STATUS_HEADER_SERVERPORT.get(), INFO_REPLICATION_STATUS_HEADER_NUMBER_ENTRIES.get() }; } } Message[][] values = new Message[orderedReplicas.size()][nCols]; int[] maxWidths = new int[nCols]; int i; for (i=0; i<maxWidths.length; i++) { maxWidths[i] = Message.toString(headers[i]).length(); } i = 0; for (ReplicaDescriptor replica : orderedReplicas) { Message v; for (int j=0; j<nCols; j++) { switch (j) { case SERVERPORT: v = Message.raw(replica.getServer().getHostPort(true)); break; case NUMBER_ENTRIES: int nEntries = replica.getEntries(); if (nEntries >= 0) { v = Message.raw(String.valueOf(nEntries)); } else { v = INFO_NOT_AVAILABLE_LABEL.get(); } break; case MISSING_CHANGES: int missingChanges = replica.getMissingChanges(); if (missingChanges >= 0) { v = Message.raw(String.valueOf(missingChanges)); } else { v = INFO_NOT_AVAILABLE_LABEL.get(); } break; case AGE_OF_OLDEST_MISSING_CHANGE: int ageOfOldestMissingChange = replica.getAgeOfOldestMissingChange(); if (ageOfOldestMissingChange >= 0) { v = Message.raw(String.valueOf(ageOfOldestMissingChange)); } else { v = INFO_NOT_AVAILABLE_LABEL.get(); } break; default: throw new IllegalStateException("Unknown index: "+j); } values[i][j] = v; maxWidths[j] = Math.max(maxWidths[j], v.toString().length()); } i++; } int totalWidth = 0; for (i=0; i<maxWidths.length; i++) { if (i < maxWidths.length - 1) { maxWidths[i] += 5; } totalWidth += maxWidths[i]; } String dn = replicas.iterator().next().getSuffix().getDN(); if (scriptFriendly) { Message[] labels = { INFO_REPLICATION_STATUS_BASEDN.get(), INFO_REPLICATION_STATUS_IS_REPLICATED.get() }; Message[] vs = { Message.raw(dn), isReplicated ? INFO_BASEDN_REPLICATED_LABEL.get() : INFO_BASEDN_NOT_REPLICATED_LABEL.get() }; for (i=0; i<labels.length; i++) { printProgressMessage(labels[i]+": "+vs[i]); printProgressLineBreak(); } for (i=0; i<values.length; i++) { printProgressMessage("-"); printProgressLineBreak(); for (int j=0; j<values[i].length; j++) { printProgressMessage(headers[j]+": "+values[i][j]); printProgressLineBreak(); } } } else { if (isReplicated) { printProgressMessage( INFO_REPLICATION_STATUS_REPLICATED.get(dn)); printProgressLineBreak(); } else { printProgressMessage( INFO_REPLICATION_STATUS_NOT_REPLICATED.get(dn)); printProgressLineBreak(); } MessageBuilder headerLine = new MessageBuilder(); for (i=0; i<maxWidths.length; i++) { String header = headers[i].toString(); headerLine.append(header); int extra = maxWidths[i] - header.length(); for (int j=0; j<extra; j++) { headerLine.append(" "); } } StringBuilder builder = new StringBuilder(); for (i=0; i<headerLine.length(); i++) { builder.append("="); } printProgressMessage(builder.toString()); printProgressLineBreak(); printProgressMessage(headerLine.toMessage()); printProgressLineBreak(); builder = new StringBuilder(); for (i=0; i<headerLine.length(); i++) { builder.append("-"); } printProgressMessage(builder.toString()); printProgressLineBreak(); for (i=0; i<values.length; i++) { MessageBuilder line = new MessageBuilder(); for (int j=0; j<values[i].length; j++) { int extra = maxWidths[j]; line.append(values[i][j]); extra -= values[i][j].length(); for (int k=0; k<extra; k++) { line.append(" "); } } printProgressMessage(line.toMessage()); printProgressLineBreak(); } } } /** * Retrieves all the replication servers for a given baseDN. The * ServerDescriptor is used to identify the server where the suffix is * defined and it cannot be null. The TopologyCache is used to retrieve opends/src/guitools/org/opends/guitools/replicationcli/StatusReplicationUserData.java
New file @@ -0,0 +1,139 @@ /* * 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.guitools.replicationcli; /** * This class is used to store the information provided by the user to * show replication configuration. It is required because when we are in * interactive mode the ReplicationCliArgumentParser is not enough. * */ class StatusReplicationUserData extends ReplicationUserData { private String hostName; private int port; private boolean useStartTLS; private boolean useSSL; private boolean scriptFriendly; /** * Returns the host name of the server. * @return the host name of the server. */ String getHostName() { return hostName; } /** * Sets the host name of the server. * @param hostName the host name of the server. */ void setHostName(String hostName) { this.hostName = hostName; } /** * Returns the port of the server. * @return the port of the server. */ int getPort() { return port; } /** * Sets the port of the server. * @param port the port of the server. */ void setPort(int port) { this.port = port; } /** * Returns <CODE>true</CODE> if we must use SSL to connect to the server and * <CODE>false</CODE> otherwise. * @return <CODE>true</CODE> if we must use SSL to connect to the server and * <CODE>false</CODE> otherwise. */ boolean useSSL() { return useSSL; } /** * Sets whether we must use SSL to connect to the server or not. * @param useSSL whether we must use SSL to connect to the server or not. */ void setUseSSL(boolean useSSL) { this.useSSL = useSSL; } /** * Returns <CODE>true</CODE> if we must use StartTLS to connect to the server * and <CODE>false</CODE> otherwise. * @return <CODE>true</CODE> if we must use StartTLS to connect to the server * and <CODE>false</CODE> otherwise. */ boolean useStartTLS() { return useStartTLS; } /** * Sets whether we must use StartTLS to connect to the server or not. * @param useStartTLS whether we must use SSL to connect to the server or not. */ void setUseStartTLS(boolean useStartTLS) { this.useStartTLS = useStartTLS; } /** * Whether we must display information in a script-friendly mode or not. * @return <CODE>true</CODE> if we must display the information in a * script-friendly mode and <CODE>false</CODE> otherwise. */ public boolean isScriptFriendly() { return scriptFriendly; } /** * Sets whether we must display information in a script-friendly mode or not. * @param scriptFriendly whether we must display information in a * script-friendly mode or not. */ public void setScriptFriendly(boolean scriptFriendly) { this.scriptFriendly = scriptFriendly; } } opends/src/guitools/org/opends/guitools/statuspanel/StatusCli.java
@@ -1125,7 +1125,7 @@ labelWidth = Math.max(labelWidth, labels[i].length()); } Message replicatedLabel = INFO_SUFFIX_REPLICATED_LABEL.get(); Message replicatedLabel = INFO_BASEDN_REPLICATED_LABEL.get(); for (int i=0; i<tableModel.getRowCount(); i++) { if (i > 0) opends/src/guitools/org/opends/guitools/statuspanel/ui/DatabasesTableModel.java
@@ -560,11 +560,11 @@ Message s; if (rep.getType() == BaseDNDescriptor.Type.REPLICATED) { s = INFO_SUFFIX_REPLICATED_LABEL.get(); s = INFO_BASEDN_REPLICATED_LABEL.get(); } else { s = INFO_SUFFIX_NOT_REPLICATED_LABEL.get(); s = INFO_BASEDN_NOT_REPLICATED_LABEL.get(); } return s; } opends/src/messages/messages/admin_tool.properties
@@ -306,8 +306,8 @@ INFO_STATUSPANEL_DIALOG_TITLE=OpenDS Status Panel INFO_STOP_BUTTON_LABEL=Stop INFO_STOP_BUTTON_TOOLTIP=Stops the Directory Server INFO_SUFFIX_NOT_REPLICATED_LABEL=Disabled INFO_SUFFIX_REPLICATED_LABEL=Enabled INFO_BASEDN_NOT_REPLICATED_LABEL=Disabled INFO_BASEDN_REPLICATED_LABEL=Enabled INFO_SUMMARY_DELETING_EXTERNAL_DB_FILES=Deleting Database Files outside the \ Installation Path... INFO_SUMMARY_DELETING_EXTERNAL_LOG_FILES=Deleting Log Files outside the \ @@ -496,6 +496,10 @@ INFO_DESCRIPTION_SUBCMD_DISABLE_REPLICATION=Disables replication on the \ specified server for the provided Base DN and removes references in the other \ servers with which it is replicating data. INFO_DESCRIPTION_SUBCMD_STATUS_REPLICATION=Displays a list with the basic \ replication configuration of the base DNs of the servers defined in the \ registration information. If no base DNs are specified as parameter the \ information for all base DNs is displayed. SEVERE_ERR_REPLICATION_NO_BASE_DN_PROVIDED=You must provide at least one base \ DN in no interactive mode. SEVERE_ERR_REPLICATION_NO_ADMINISTRATOR_PASSWORD_PROVIDED=You must provide the \ @@ -516,6 +520,8 @@ replication port (%s) for two servers located on the same machine (%s). SEVERE_ERR_REPLICATION_VALID_SUBCOMMAND_NOT_FOUND=Could not find a valid \ subcommand. SEVERE_ERR_REPLICATION_STATUS_QUIET=The {%s} subcommand is not compatible with \ the {%s} argument. INFO_REPLICATION_SUCCESSFUL=The operation has been successfully completed INFO_REPLICATION_SUCCESSFUL_NOP=The operation has been successfully completed, \ but no action was required @@ -567,6 +573,8 @@ INFO_REPLICATION_DISABLE_BINDDN_PROMPT=Global Administrator User ID (or bind \ DN if no Global Administrator is defined) INFO_REPLICATION_DISABLE_PASSWORD_PROMPT=Password for %s: INFO_REPLICATION_STATUS_HOSTNAME_PROMPT=Host name of the server INFO_REPLICATION_STATUS_PORT_PROMPT=LDAP port of the server INFO_REPLICATION_INITIALIZE_HOSTNAMEDESTINATION_PROMPT=Host name of the \ destination server INFO_REPLICATION_INITIALIZE_PORTDESTINATION_PROMPT=LDAP port of the \ @@ -608,10 +616,13 @@ INFO_REPLICATION_DISABLE_SUFFIX_PROMPT=Disable replication on base DN %s? MILD_ERR_REPLICATION_READING_REGISTERED_SERVERS_CONFIRM_UPDATE_REMOTE=The \ following errors were encountered reading the configuration of the existing \ servers:\n%s\nIf you continue the replication tool will to try to update the \ configuration of all the servers in a best-effort mode. However it cannot \ guarantee that the servers that are generating errors will be updated. Do \ you want to continue? servers:%n%s%n%nIf you continue the replication tool will to try to update \ the configuration of all the servers in a best-effort mode. However it \ cannot guarantee that the servers that are generating errors will be \ updated. Do you want to continue? MILD_ERR_REPLICATION_STATUS_READING_REGISTERED_SERVERS=The displayed \ information might not be complete because the following errors were \ encountered reading the configuration of the existing servers:%n%s MILD_ERR_NOT_ADMINISTRATIVE_USER=You do not have access rights to the \ configuration of the server. INFO_REPLICATION_CONFIRM_DISABLE_ADS=You chose to disable replication on \ @@ -650,7 +661,7 @@ INFO_ENABLE_REPLICATION_INITIALIZING_ADS=Initializing Registration information \ on server %s with the contents of server %s SEVERE_ERR_REPLICATION_ENABLE_SEEDING_TRUSTSTORE=An unexpected error occurred \ seeding the truststore contentes. Details: %s seeding the truststore contents. Details: %s SEVERE_ERR_INITIALIZING_REPLICATIONID_NOT_FOUND=Error initializing. Could not \ find replication ID in the server %s for base DN %s. SEVERE_ERR_REPLICATION_INITIALIZING_TRIES_COMPLETED=Error initializing. Could \ @@ -672,3 +683,22 @@ %s of server %s INFO_REPLICATION_DISABLING_BASEDN=Disabling replication on base DN %s of \ server %s INFO_REPLICATION_STATUS_NO_BASEDNS=No base DNs found. INFO_REPLICATION_STATUS_BASEDN=Base DN INFO_REPLICATION_STATUS_IS_REPLICATED=Replication INFO_REPLICATION_STATUS_REPLICATED=%s - Replication Enabled INFO_REPLICATION_STATUS_NOT_REPLICATED=%s - Replication Disabled INFO_REPLICATION_STATUS_HEADER_SERVERPORT=Server:port INFO_REPLICATION_STATUS_HEADER_NUMBER_ENTRIES=Entries INFO_REPLICATION_STATUS_HEADER_MISSING_CHANGES=Missing Changes (1) INFO_REPLICATION_STATUS_HEADER_AGE_OF_OLDEST_MISSING_CHANGE=A.O.M.C. (2) INFO_REPLICATION_STATUS_REPLICATED_LEGEND=[1] The number of changes that are \ still missing on this server (and that have been at least applied to one of \ the other servers).%n[2] Age of oldest missing change: the age (in \ seconds) of the oldest change that has not arrived to this server. INFO_REPLICATION_STATUS_LABEL_SERVERPORT=Server INFO_REPLICATION_STATUS_LABEL_NUMBER_ENTRIES=Entries INFO_REPLICATION_STATUS_LABEL_MISSING_CHANGES=Missing Changes INFO_REPLICATION_STATUS_LABEL_AGE_OF_OLDEST_MISSING_CHANGE=Age of oldest \ missing change