mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

jvergara
25.45.2007 67f22097f2e23feed40d54ffad407672a73b7ce8
Fix for issue  2261 (dsreplication should provide a view of the replicated baseDNs in a topology).

The following changes are targetted to provide a CLI view on the replication topology and on replication monitoring.

A script-frindly option has been added to be able to parse more easily the output of this subcommand.
1 files added
7 files modified
1171 ■■■■■ changed files
opends/src/ads/org/opends/admin/ads/ReplicaDescriptor.java 38 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/ServerDescriptor.java 55 ●●●●● patch | view | raw | blame | history
opends/src/guitools/org/opends/guitools/replicationcli/ReplicationCliArgumentParser.java 169 ●●●●● patch | view | raw | blame | history
opends/src/guitools/org/opends/guitools/replicationcli/ReplicationCliMain.java 710 ●●●●● patch | view | raw | blame | history
opends/src/guitools/org/opends/guitools/replicationcli/StatusReplicationUserData.java 139 ●●●●● patch | view | raw | blame | history
opends/src/guitools/org/opends/guitools/statuspanel/StatusCli.java 2 ●●● patch | view | raw | blame | history
opends/src/guitools/org/opends/guitools/statuspanel/ui/DatabasesTableModel.java 4 ●●●● patch | view | raw | blame | history
opends/src/messages/messages/admin_tool.properties 54 ●●●● patch | view | raw | blame | history
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,10 +2070,21 @@
        }
        if ((exceptionMsgs.size() > 0) && !cancelled)
        {
          cancelled = !confirm(
              ERR_REPLICATION_READING_REGISTERED_SERVERS_CONFIRM_UPDATE_REMOTE.
              get(Utils.getMessageFromCollection(exceptionMsgs,
          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,
                    Constants.LINE_SEPARATOR).toString()));
          }
        }
      }
    }
@@ -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 \
@@ -664,11 +675,30 @@
SEVERE_ERR_REPLICATION_READING_ADS=Error reading Registration information.  \
 Details: %s
SEVERE_ERR_REPLICATION_ADS_MERGE_NOT_SUPPORTED=The registry information found \
in servers %s and %s is different.  This tool does not allow to handle this \
scenario.
 in servers %s and %s is different.  This tool does not allow to handle this \
 scenario.
SEVERE_ERR_REPLICATION_ERROR_READING_CONFIGURATION=Error reading replication \
configuration of server %s.  Details: %s
 configuration of server %s.  Details: %s
INFO_REPLICATION_REMOVING_REFERENCES_ON_REMOTE=Removing references on base DN \
%s of server %s
 %s of server %s
INFO_REPLICATION_DISABLING_BASEDN=Disabling replication on base DN %s of \
server %s
 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