From 6205f87c065409851efb0275fb21b09fabc43e8a Mon Sep 17 00:00:00 2001
From: ugaston <ugaston@localhost>
Date: Mon, 14 Sep 2009 17:20:50 +0000
Subject: [PATCH] First set of External Changelog functional tests

---
 opendj-sdk/opends/tests/staf-tests/shared/functions/ldap.xml                                                      |  323 +++++
 opendj-sdk/opends/tests/staf-tests/shared/java/ldapjndi/modifyDn.java                                             |  169 +++
 opendj-sdk/opends/tests/staf-tests/functional-tests/testcases/replication/replication.xml                         |    1 
 opendj-sdk/opends/tests/staf-tests/shared/functions/utils.xml                                                     |  658 ++++++++++++
 opendj-sdk/opends/tests/staf-tests/functional-tests/testcases/replication/externalchangelog/externalchangelog.xml | 2039 +++++++++++++++++++++++++++++++++++++
 5 files changed, 3,185 insertions(+), 5 deletions(-)

diff --git a/opendj-sdk/opends/tests/staf-tests/functional-tests/testcases/replication/externalchangelog/externalchangelog.xml b/opendj-sdk/opends/tests/staf-tests/functional-tests/testcases/replication/externalchangelog/externalchangelog.xml
new file mode 100644
index 0000000..126a9a9
--- /dev/null
+++ b/opendj-sdk/opends/tests/staf-tests/functional-tests/testcases/replication/externalchangelog/externalchangelog.xml
@@ -0,0 +1,2039 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE stax SYSTEM "../../../../shared/stax.dtd">
+<!--
+ ! 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
+ !
+ !      Copyright 2008-2009 Sun Microsystems, Inc.
+ ! -->
+<stax>
+    
+  <defaultcall function="replication_externalchangelog"/>
+    
+  <function name="replication_externalchangelog">
+    <sequence>
+      <block name="'externalchangelog'">
+        <sequence>
+          <script>
+            if not CurrentTestPath.has_key('group'):
+              CurrentTestPath['group']='replication'
+            CurrentTestPath['suite']=STAXCurrentBlock
+          </script>
+            
+          <call function="'testSuite_Preamble'"/>
+            
+          <!--- Test Suite information
+          #@TestSuiteName       Replication External Changelog Tests
+          #@TestSuitePurpose    Test the event notification system provided by
+                                the External Changelog.
+          #@TestSuiteID         External Changelog Tests
+          #@TestSuiteGroup      External Changelog
+          #@TestGroup           Replication
+          #@TestScript          replication_externalchangelog.xml
+          #@TestHTMLLink        http://opends.dev.java.net/
+          -->
+          
+          <import machine="STAF_LOCAL_HOSTNAME"
+                  file="'%s/testcases/replication/replication_setup.xml' 
+                        % (TESTS_DIR)"/>
+          <call function="'replication_setup'">
+            { 'dataFile' : 'Example.ldif' }
+          </call>
+          
+          
+          <script>
+            # initialise the lastCookie value to that for the first ECL request
+            lastCookie = ';'
+            nextCookie = None
+            
+            addOperationalAttrs = """\* creatorsname createtimestamp entryuuid"""
+            modOperationalAttrs = """\* modifiersname modifytimestamp entryuuid"""
+            
+            class Entry:
+              def __init__(self, rdn):
+                self.userDn = '%s,ou=People,%s' \
+                              % (rdn, synchroSuffix)     
+                self.listAttr = []
+                self.listAttr.append('objectclass:top')
+                self.listAttr.append('objectclass:organizationalperson')
+                self.listAttr.append('objectclass:inetorgperson')
+                self.listAttr.append('objectclass:person')
+              def getDn(self):
+                return self.userDn              
+              def getAttrList(self):
+                return self.listAttr
+              def addAttr(self, attrType, attrValue):
+                self.listAttr.append('%s:%s' % (attrType, attrValue))
+          </script>         
+
+
+                        
+          <!--- Test Case information
+          #@TestMarker          Replication External Changelog Tests
+          #@TestName            Replication: External Changelog: Add_1
+          #@TestID              Add_1
+          #@TestPurpose         Verify add operation is recorded properly in the
+                                external changelog on each replication server
+          #@TestPreamble
+          #@TestSteps           Add entry to server A
+          #@TestSteps           Verify servers in sync
+          #@TestSteps           Read entry from ldap server
+          #@TestSteps           Read last changelog entry on each repl server
+          #@TestSteps           Check changelog entry against expected values
+          #@TestPostamble
+          #@TestResult          Success if the server synchronised and the
+                                changelog entry value check succeeds
+          -->               
+          <testcase name="getTestCaseName('Add_1')">
+            <sequence>            
+              <call function="'testCase_Preamble'"/>
+              <message>
+                'Replication: External Changelog: Add_1. \
+                Verify add operation is recorded properly in the external \
+                changelog on each replication server'
+              </message>
+
+              <!-- Add entry to "master" server -->
+              <script>
+                myEntry = Entry('cn=a')
+                myEntry.addAttr('sn', 'a')
+                myEntry.addAttr('description', '1')
+                myEntry.addAttr('description', '2')
+                myEntry.addAttr('uid', 'A')
+                
+                addedEntry = None
+                eclEntry   = None
+              </script>
+              <call function="'addAnEntry'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'DNToAdd'        : myEntry.getDn(),
+                  'listAttributes' : myEntry.getAttrList()
+                }
+              </call>
+              
+              <!-- Verify the synchronization of the trees among the servers in
+                the topology -->          
+              <call function="'verifyTrees'">
+                [ clientHost, clientPath, master, consumerList, synchroSuffix ]
+              </call>
+              
+              <!-- Retrieve entry information for checkChangelogEntry's sake -->
+              <call function="'ldapSearchWithScript'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'dsBaseDN'       : myEntry.getDn(),
+                  'dsFilter'       : 'objectclass=*',
+                  'dsAttributes'   : addOperationalAttrs
+                }                
+              </call>
+              <script>
+                searchRC = STAXResult[0][0]
+                searchResult = STAXResult[0][1]
+              </script>
+              
+              <if expr="searchRC == 0">
+                <sequence>
+                  <message>
+                    'This is the entry as read from server %s:%s: \n%s' % \
+                    (masterHost, master.getPort(), searchResult)
+                  </message>
+                  <call function="'parseLdifEntry'">
+                    { 'ldifEntry' : searchResult }
+                  </call>
+                  <script>
+                    addedEntry = STAXResult
+                    
+                    myTargetDN             = addedEntry['dn'][0]
+                    myChangeType           = 'add'
+                    myChangeTime           = addedEntry['createtimestamp'][0]
+                    myTargetEntryUUID      = addedEntry['entryuuid'][0]
+                    # myReplicationCSN     = ----> grab from where???
+                    # myReplicatIdentifier = ----> grab from ldap server
+                    myChanges              = {}
+                    
+                    for attr in addedEntry.keys():
+                      if (attr != 'dn') and (attr != 'changelogcookie') :
+                        myChanges[attr] = addedEntry[attr]
+                  </script>
+                </sequence>
+                <else>
+                  <message log="1" level="'Error'">
+                    'Could not read added entry %s from server %s:%s' % \
+                    (myEntry.getDn(), masterHost, master.getPort())
+                  </message>
+                </else>
+              </if>
+                                
+              <!-- Search changelog in the various replication servers -->                                                           
+              <iterate var="server" 
+                       in="_topologyServerList" 
+                       indexvar="i">
+                <sequence>                    
+                  <script>
+                    if globalSplitServers:
+                      replServer = _topologyReplServerList[i]
+                    else:
+                      replServer = server
+
+                    replServerPath = '%s/%s' % (replServer.getDir(), OPENDSNAME)
+                  </script>
+                  
+                  <message>
+                    'Reading changelog in replication server %s:%s from index: \
+                    %s' % \
+                    (replServer.getHostname(), replServer.getPort(), lastCookie)
+                  </message>
+                    
+                  <!-- Search for entry add -->
+                  <call function="'SearchExternalChangelog'">
+                    { 'location'        : replServer.getHostname(),
+                      'dsPath'          : replServerPath,
+                      'dsInstanceHost'  : replServer.getHostname(),
+                      'dsInstancePort'  : replServer.getPort(),
+                      'dsInstanceDn'    : replServer.getRootDn(),
+                      'dsInstancePswd'  : replServer.getRootPwd(),
+                      'dsScope'         : 'subordinate', 
+                      'dsBaseDN'        : 'cn=changelog',
+                      'dsFilter'        : '(objectclass=*)',
+                      'changelogCookie' : lastCookie
+                    }                
+                  </call>
+                  <script>
+                    searchRC = STAXResult[0][0]
+                    searchResult = STAXResult[0][1]
+                  </script>
+                  <if expr="searchRC == 0">
+                    <sequence>
+                    <message>
+                      'This is the ECL entry as read from server %s:%s: \n%s' %\
+                      (replServer.getHostname(), replServer.getPort(), 
+                       searchResult)
+                    </message>
+                      <call function="'parseLdifEntry'">
+                        { 'ldifEntry' : searchResult }
+                      </call>
+                      <script>
+                        eclEntry = STAXResult
+                        
+                        nextCookie = eclEntry['changelogcookie'][0]
+                      </script>
+                      <message>
+                        'Parsed changelog entry:  \n%s' % eclEntry
+                      </message>
+                      <message>
+                        'Cookie to use for the next ECL search:  %s' % \
+                        nextCookie
+                      </message>
+                    </sequence>
+                    <else>
+                      <message log="1" level="'Error'">
+                        'Could not read changelog on replication server %s:%s' \
+                        % (replServer.getHostname(), replServer.getPort())
+                      </message>
+                    </else>
+                  </if>
+                  
+                  <if expr="addedEntry and eclEntry">
+                    <call function="'checkChangelogEntry'">
+                      { 'location'        : masterHost,
+                        'dsPath'          : masterPath,
+                        'changelogEntry'  : eclEntry,
+                        'targetDN'        : myTargetDN,
+                        'changeType'      : myChangeType,
+                        'changeTime'      : myChangeTime,
+                        'targetEntryUUID' : myTargetEntryUUID,
+                        'changes'         : myChanges
+                      }
+                    </call>
+                    <else>
+                      <sequence>
+                        <message log="1" level="'Error'">
+                          'Unable to check external changelog entry (failed to \
+                          read entries %s and/or %s)' % \
+                          (myEntry.getDn(), eclEntry['dn'][0])
+                        </message>
+                        <call function="'testFailed'"/>
+                      </sequence>
+                    </else>
+                  </if>
+                  
+                </sequence>                  
+              </iterate>
+              
+              <script>
+                # set the lastCookie for the next ECL search
+                if nextCookie:
+                  lastCookie = nextCookie
+                
+                # reset variables, just in case
+                addedEntry = None
+                eclEntry   = None
+              </script>
+              
+              <call function="'testCase_Postamble'"/>
+            </sequence>
+          </testcase>
+                                
+
+
+          <!--- Test Case information
+          #@TestMarker          Replication External Changelog Tests
+          #@TestName            Replication: External Changelog: Modify_add
+          #@TestID              Modify_add
+          #@TestPurpose         Verify modify-add operation is recorded properly
+                                in the external changelog on each repl server
+          #@TestPreamble
+          #@TestSteps           Modify entry on server A
+          #@TestSteps           Verify servers in sync
+          #@TestSteps           Read entry from ldap server
+          #@TestSteps           Read last changelog entry on each repl server
+          #@TestSteps           Check changelog entry against expected values
+          #@TestPostamble
+          #@TestResult          Success if the server synchronised and the
+                                changelog entry value check succeeds
+          -->               
+          <testcase name="getTestCaseName('Modify_add')">
+            <sequence>            
+              <call function="'testCase_Preamble'"/>
+              <message>
+                'Replication: External Changelog: Modify_add. \
+                Verify modify-add operation is recorded properly in the \
+                external changelog on each repl server'
+              </message>
+
+              <script>
+                modEntry   = None
+                eclEntry   = None
+              </script>
+              
+              <!-- Modify entry on one of the servers -->
+              <call function="'modifyAnAttribute'">
+                { 'location'          : masterHost,
+                  'dsPath'            : masterPath,
+                  'dsInstanceHost'    : masterHost,
+                  'dsInstancePort'    : master.getPort(),
+                  'dsInstanceDn'      : master.getRootDn(),
+                  'dsInstancePswd'    : master.getRootPwd(),
+                  'DNToModify'        : myEntry.getDn(),
+                  'attributeName'     : 'description',
+                  'newAttributeValue' : '3',
+                  'changetype'        : 'add'
+                }
+              </call>
+              
+              <!-- Verify the synchronization of the trees among the servers in
+                the topology -->          
+              <call function="'verifyTrees'">
+                [ clientHost, clientPath, master, consumerList, synchroSuffix ]
+              </call>
+              
+              <!-- Retrieve entry information for checkChangelogEntry's sake -->
+              <call function="'ldapSearchWithScript'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'dsBaseDN'       : myEntry.getDn(),
+                  'dsFilter'       : 'objectclass=*',
+                  'dsAttributes'   : modOperationalAttrs
+                }                
+              </call>
+              <script>
+                searchRC = STAXResult[0][0]
+                searchResult = STAXResult[0][1]
+              </script>
+              
+              <if expr="searchRC == 0">
+                <sequence>
+                  <message>
+                    'This is the entry as read from server %s:%s: \n%s' % \
+                    (masterHost, master.getPort(), searchResult)
+                  </message>
+                  <call function="'parseLdifEntry'">
+                    { 'ldifEntry' : searchResult }
+                  </call>
+                  <script>
+                    modEntry = STAXResult
+                    
+                    myTargetDN             = modEntry['dn'][0]
+                    myChangeType           = 'modify'
+                    myChangeTime           = modEntry['modifytimestamp'][0]
+                    myTargetEntryUUID      = modEntry['entryuuid'][0]
+                    # myReplicationCSN     = ----> grab from where???
+                    # myReplicatIdentifier = ----> grab from ldap server
+                    myModifiersName        = modEntry['modifiersname'][0]
+                    myModifyTimestamp      = modEntry['modifytimestamp'][0]
+                    myChanges              = []
+                    myChanges.append(['add', 'description', '3'])
+                    myChanges.append(['replace', 'modifiersname', myModifiersName])
+                    myChanges.append(['replace', 'modifytimestamp', myModifyTimestamp])
+                  </script>
+                </sequence>
+                <else>
+                  <message log="1" level="'Error'">
+                    'Could not read modified entry %s from server %s:%s' % \
+                    (myEntry.getDn(), masterHost, master.getPort())
+                  </message>
+                </else>
+              </if>
+                                
+              <!-- Search changelog in the various replication servers -->                                                           
+              <iterate var="server" 
+                       in="_topologyServerList" 
+                       indexvar="i">
+                <sequence>                    
+                  <script>
+                    if globalSplitServers:
+                      replServer = _topologyReplServerList[i]
+                    else:
+                      replServer = server
+
+                    replServerPath = '%s/%s' % (replServer.getDir(), OPENDSNAME)
+                  </script>
+                  
+                  <message>
+                    'Reading changelog in replication server %s:%s from index: \
+                    %s' % \
+                    (replServer.getHostname(), replServer.getPort(), lastCookie)
+                  </message>
+                    
+                  <!-- Search for entry modify -->
+                  <call function="'SearchExternalChangelog'">
+                    { 'location'        : replServer.getHostname(),
+                      'dsPath'          : replServerPath,
+                      'dsInstanceHost'  : replServer.getHostname(),
+                      'dsInstancePort'  : replServer.getPort(),
+                      'dsInstanceDn'    : replServer.getRootDn(),
+                      'dsInstancePswd'  : replServer.getRootPwd(),
+                      'dsScope'         : 'subordinate',
+                      'dsBaseDN'        : 'cn=changelog',
+                      'dsFilter'        : '(objectclass=*)',
+                      'changelogCookie' : lastCookie
+                    }                
+                  </call>
+                  <script>
+                    searchRC = STAXResult[0][0]
+                    searchResult = STAXResult[0][1]
+                  </script>
+                  <if expr="searchRC == 0">
+                    <sequence>
+                      <message>
+                        'This is the ECL entry as read from server %s:%s: \n%s'\
+                         % (replServer.getHostname(), replServer.getPort(), 
+                            searchResult)
+                      </message>
+                      <call function="'parseLdifEntry'">
+                        { 'ldifEntry' : searchResult }
+                      </call>
+                      <script>
+                        eclEntry = STAXResult
+                        
+                        nextCookie = eclEntry['changelogcookie'][0]
+                      </script>
+                      <message>
+                        'Parsed changelog entry:  \n%s' % eclEntry
+                      </message>
+                      <message>
+                        'Cookie to use for the next ECL search:  %s' % \
+                        nextCookie
+                      </message>
+                    </sequence>
+                    <else>
+                      <message log="1" level="'Error'">
+                        'Could not read changelog on replication server %s:%s' \
+                        % (replServer.getHostname(), replServer.getPort())
+                      </message>
+                    </else>
+                  </if>
+                  
+                  <if expr="modEntry and eclEntry">
+                    <call function="'checkChangelogEntry'">
+                      { 'location'        : masterHost,
+                        'dsPath'          : masterPath,
+                        'changelogEntry'  : eclEntry,
+                        'targetDN'        : myTargetDN,
+                        'changeType'      : myChangeType,
+                        'changeTime'      : myChangeTime,
+                        'targetEntryUUID' : myTargetEntryUUID,
+                        'changes'         : myChanges
+                      }
+                    </call>
+                    <else>
+                      <sequence>
+                        <message log="1" level="'Error'">
+                          'Unable to check external changelog entry (failed to \
+                          read entries %s and/or %s)' % \
+                          (myEntry.getDn(), eclEntry['dn'][0])
+                        </message>
+                        <call function="'testFailed'"/>
+                      </sequence>
+                    </else>
+                  </if>
+                  
+                </sequence>                  
+              </iterate>
+
+              <script>
+                # set the lastCookie for the next ECL search
+                if nextCookie:
+                  lastCookie = nextCookie
+                
+                # reset variables, just in case
+                modEntry  = None
+                eclEntry  = None
+              </script>
+              
+              <call function="'testCase_Postamble'"/>
+            </sequence>
+          </testcase>
+
+
+          <!--- Test Case information
+          #@TestMarker          Replication External Changelog Tests
+          #@TestName            Replication: External Changelog: Modify_delete
+          #@TestID              Modify_delete
+          #@TestPurpose         Verify modify-del operation is recorded properly
+                                in the external changelog on each repl server
+          #@TestPreamble
+          #@TestSteps           Modify entry on server A
+          #@TestSteps           Verify servers in sync
+          #@TestSteps           Read entry from ldap server
+          #@TestSteps           Read last changelog entry on each repl server
+          #@TestSteps           Check changelog entry against expected values
+          #@TestPostamble
+          #@TestResult          Success if the server synchronised and the
+                                changelog entry value check succeeds
+          -->               
+          <testcase name="getTestCaseName('Modify_delete')">
+            <sequence>            
+              <call function="'testCase_Preamble'"/>
+              <message>
+                'Replication: External Changelog: Modify_delete. \
+                Verify modify-del operation is recorded properly in the \
+                external changelog on each repl server'
+              </message>
+
+              <script>
+                modEntry   = None
+                eclEntry   = None
+                
+                valuesToDelete = []
+                valuesToDelete.append('description:1')
+              </script>
+              
+              <!-- Modify entry on one of the servers -->
+              <call function="'modifyAnAttribute'">
+                { 'location'          : masterHost,
+                  'dsPath'            : masterPath,
+                  'dsInstanceHost'    : masterHost,
+                  'dsInstancePort'    : master.getPort(),
+                  'dsInstanceDn'      : master.getRootDn(),
+                  'dsInstancePswd'    : master.getRootPwd(),
+                  'DNToModify'        : myEntry.getDn(),
+                  'listAttributes'    : valuesToDelete,
+                  'changetype'        : 'delete'
+                }
+              </call>
+              
+              <!-- Verify the synchronization of the trees among the servers in
+                the topology -->          
+              <call function="'verifyTrees'">
+                [ clientHost, clientPath, master, consumerList, synchroSuffix ]
+              </call>
+              
+              <!-- Retrieve entry information for checkChangelogEntry's sake -->
+              <call function="'ldapSearchWithScript'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'dsBaseDN'       : myEntry.getDn(),
+                  'dsFilter'       : 'objectclass=*',
+                  'dsAttributes'   : modOperationalAttrs
+                }                
+              </call>
+              <script>
+                searchRC = STAXResult[0][0]
+                searchResult = STAXResult[0][1]
+              </script>
+              
+              <if expr="searchRC == 0">
+                <sequence>
+                  <message>
+                    'This is the entry as read from server %s:%s: \n%s' % \
+                    (masterHost, master.getPort(), searchResult)
+                  </message>
+                  <call function="'parseLdifEntry'">
+                    { 'ldifEntry' : searchResult }
+                  </call>
+                  <script>
+                    modEntry = STAXResult
+                    
+                    myTargetDN             = modEntry['dn'][0]
+                    myChangeType           = 'modify'
+                    myChangeTime           = modEntry['modifytimestamp'][0]
+                    myTargetEntryUUID      = modEntry['entryuuid'][0]
+                    # myReplicationCSN     = ----> grab from where???
+                    # myReplicatIdentifier = ----> grab from ldap server
+                    myModifiersName        = modEntry['modifiersname'][0]
+                    myModifyTimestamp      = modEntry['modifytimestamp'][0]
+                    myChanges              = []
+                    myChanges.append(['delete', 'description', '1'])
+                    myChanges.append(['replace', 'modifiersname', myModifiersName])
+                    myChanges.append(['replace', 'modifytimestamp', myModifyTimestamp])
+                  </script>
+                </sequence>
+                <else>
+                  <message log="1" level="'Error'">
+                    'Could not read modified entry %s from server %s:%s' % \
+                    (myEntry.getDn(), masterHost, master.getPort())
+                  </message>
+                </else>
+              </if>
+                                
+              <!-- Search changelog in the various replication servers -->                                                           
+              <iterate var="server" 
+                       in="_topologyServerList" 
+                       indexvar="i">
+                <sequence>                    
+                  <script>
+                    if globalSplitServers:
+                      replServer = _topologyReplServerList[i]
+                    else:
+                      replServer = server
+
+                    replServerPath = '%s/%s' % (replServer.getDir(), OPENDSNAME)
+                  </script>
+                  
+                  <message>
+                    'Reading changelog in replication server %s:%s from index: \
+                    %s' % \
+                    (replServer.getHostname(), replServer.getPort(), lastCookie)
+                  </message>
+                    
+                  <!-- Search for entry modify -->
+                  <call function="'SearchExternalChangelog'">
+                    { 'location'        : replServer.getHostname(),
+                      'dsPath'          : replServerPath,
+                      'dsInstanceHost'  : replServer.getHostname(),
+                      'dsInstancePort'  : replServer.getPort(),
+                      'dsInstanceDn'    : replServer.getRootDn(),
+                      'dsInstancePswd'  : replServer.getRootPwd(),
+                      'dsScope'         : 'subordinate',
+                      'dsBaseDN'        : 'cn=changelog',
+                      'dsFilter'        : '(objectclass=*)',
+                      'changelogCookie' : lastCookie
+                    }                
+                  </call>
+                  <script>
+                    searchRC = STAXResult[0][0]
+                    searchResult = STAXResult[0][1]
+                  </script>
+                  <if expr="searchRC == 0">
+                    <sequence>
+                      <message>
+                        'This is the ECL entry as read from server %s:%s: \n%s'\
+                         % (replServer.getHostname(), replServer.getPort(), 
+                            searchResult)
+                      </message>
+                      <call function="'parseLdifEntry'">
+                        { 'ldifEntry' : searchResult }
+                      </call>
+                      <script>
+                        eclEntry = STAXResult
+                        
+                        nextCookie = eclEntry['changelogcookie'][0]
+                      </script>
+                      <message>
+                        'Parsed changelog entry:  \n%s' % eclEntry
+                      </message>
+                      <message>
+                        'Cookie to use for the next ECL search:  %s' % \
+                        nextCookie
+                      </message>
+                    </sequence>
+                    <else>
+                      <message log="1" level="'Error'">
+                        'Could not read changelog on replication server %s:%s' \
+                        % (replServer.getHostname(), replServer.getPort())
+                      </message>
+                    </else>
+                  </if>
+                  
+                  <if expr="modEntry and eclEntry">
+                    <call function="'checkChangelogEntry'">
+                      { 'location'        : masterHost,
+                        'dsPath'          : masterPath,
+                        'changelogEntry'  : eclEntry,
+                        'targetDN'        : myTargetDN,
+                        'changeType'      : myChangeType,
+                        'changeTime'      : myChangeTime,
+                        'targetEntryUUID' : myTargetEntryUUID,
+                        'changes'         : myChanges
+                      }
+                    </call>
+                    <else>
+                      <sequence>
+                        <message log="1" level="'Error'">
+                          'Unable to check external changelog entry (failed to \
+                          read entries %s and/or %s)' % \
+                          (myEntry.getDn(), eclEntry['dn'][0])
+                        </message>
+                        <call function="'testFailed'"/>
+                      </sequence>
+                    </else>
+                  </if>
+                  
+                </sequence>                  
+              </iterate>
+
+              <script>
+                # set the lastCookie for the next ECL search
+                if nextCookie:
+                  lastCookie = nextCookie
+                
+                # reset variables, just in case
+                modEntry  = None
+                eclEntry  = None
+              </script>
+              
+              <call function="'testCase_Postamble'"/>
+            </sequence>
+          </testcase>
+                                                                                              
+
+          <!--- Test Case information
+          #@TestMarker          Replication External Changelog Tests
+          #@TestName            Replication: External Changelog: Modify_replace
+          #@TestID              Modify_replace
+          #@TestPurpose         Verify modify-replace operation is well recorded 
+                                in the external changelog on each repl server
+          #@TestPreamble
+          #@TestSteps           Modify entry on server A
+          #@TestSteps           Verify servers in sync
+          #@TestSteps           Read entry from ldap server
+          #@TestSteps           Read last changelog entry on each repl server
+          #@TestSteps           Check changelog entry against expected values
+          #@TestPostamble
+          #@TestResult          Success if the server synchronised and the
+                                changelog entry value check succeeds
+          -->               
+          <testcase name="getTestCaseName('Modify_replace')">
+            <sequence>            
+              <call function="'testCase_Preamble'"/>
+              <message>
+                'Replication: External Changelog: Modify_replace. \
+                Verify modify-replace operation is recorded properly in the \
+                external changelog on each repl server'
+              </message>
+
+              <script>
+                modEntry   = None
+                eclEntry   = None
+              </script>
+              
+              <!-- Modify entry on one of the servers -->
+              <call function="'modifyAnAttribute'">
+                { 'location'          : masterHost,
+                  'dsPath'            : masterPath,
+                  'dsInstanceHost'    : masterHost,
+                  'dsInstancePort'    : master.getPort(),
+                  'dsInstanceDn'      : master.getRootDn(),
+                  'dsInstancePswd'    : master.getRootPwd(),
+                  'DNToModify'        : myEntry.getDn(),
+                  'attributeName'     : 'description',
+                  'newAttributeValue' : 'new',
+                  'changetype'        : 'replace'
+                }
+              </call>
+              
+              <!-- Verify the synchronization of the trees among the servers in
+                the topology -->          
+              <call function="'verifyTrees'">
+                [ clientHost, clientPath, master, consumerList, synchroSuffix ]
+              </call>
+              
+              <!-- Retrieve entry information for checkChangelogEntry's sake -->
+              <call function="'ldapSearchWithScript'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'dsBaseDN'       : myEntry.getDn(),
+                  'dsFilter'       : 'objectclass=*',
+                  'dsAttributes'   : modOperationalAttrs
+                }                
+              </call>
+              <script>
+                searchRC = STAXResult[0][0]
+                searchResult = STAXResult[0][1]
+              </script>
+              
+              <if expr="searchRC == 0">
+                <sequence>
+                  <message>
+                    'This is the entry as read from server %s:%s: \n%s' % \
+                    (masterHost, master.getPort(), searchResult)
+                  </message>
+                  <call function="'parseLdifEntry'">
+                    { 'ldifEntry' : searchResult }
+                  </call>
+                  <script>
+                    modEntry = STAXResult
+                    
+                    myTargetDN             = modEntry['dn'][0]
+                    myChangeType           = 'modify'
+                    myChangeTime           = modEntry['modifytimestamp'][0]
+                    myTargetEntryUUID      = modEntry['entryuuid'][0]
+                    # myReplicationCSN     = ----> grab from where???
+                    # myReplicatIdentifier = ----> grab from ldap server
+                    myModifiersName        = modEntry['modifiersname'][0]
+                    myModifyTimestamp      = modEntry['modifytimestamp'][0]
+                    myChanges              = []
+                    myChanges.append(['replace', 'description', 'new'])
+                    myChanges.append(['replace', 'modifiersname', myModifiersName])
+                    myChanges.append(['replace', 'modifytimestamp', myModifyTimestamp])
+                  </script>
+                </sequence>
+                <else>
+                  <message log="1" level="'Error'">
+                    'Could not read modified entry %s from server %s:%s' % \
+                    (myEntry.getDn(), masterHost, master.getPort())
+                  </message>
+                </else>
+              </if>
+                                
+              <!-- Search changelog in the various replication servers -->                                                           
+              <iterate var="server" 
+                       in="_topologyServerList" 
+                       indexvar="i">
+                <sequence>                    
+                  <script>
+                    if globalSplitServers:
+                      replServer = _topologyReplServerList[i]
+                    else:
+                      replServer = server
+
+                    replServerPath = '%s/%s' % (replServer.getDir(), OPENDSNAME)
+                  </script>
+                  
+                  <message>
+                    'Reading changelog in replication server %s:%s from index: \
+                    %s' % \
+                    (replServer.getHostname(), replServer.getPort(), lastCookie)
+                  </message>
+                    
+                  <!-- Search for entry modify -->
+                  <call function="'SearchExternalChangelog'">
+                    { 'location'        : replServer.getHostname(),
+                      'dsPath'          : replServerPath,
+                      'dsInstanceHost'  : replServer.getHostname(),
+                      'dsInstancePort'  : replServer.getPort(),
+                      'dsInstanceDn'    : replServer.getRootDn(),
+                      'dsInstancePswd'  : replServer.getRootPwd(),
+                      'dsScope'         : 'subordinate',
+                      'dsBaseDN'        : 'cn=changelog',
+                      'dsFilter'        : '(objectclass=*)',
+                      'changelogCookie' : lastCookie
+                    }                
+                  </call>
+                  <script>
+                    searchRC = STAXResult[0][0]
+                    searchResult = STAXResult[0][1]
+                  </script>
+                  <if expr="searchRC == 0">
+                    <sequence>
+                      <message>
+                        'This is the ECL entry as read from server %s:%s: \n%s'\
+                         % (replServer.getHostname(), replServer.getPort(), 
+                            searchResult)
+                      </message>
+                      <call function="'parseLdifEntry'">
+                        { 'ldifEntry' : searchResult }
+                      </call>
+                      <script>
+                        eclEntry = STAXResult
+                        
+                        nextCookie = eclEntry['changelogcookie'][0]
+                      </script>
+                      <message>
+                        'Parsed changelog entry:  \n%s' % eclEntry
+                      </message>
+                      <message>
+                        'Cookie to use for the next ECL search:  %s' % \
+                        nextCookie
+                      </message>
+                    </sequence>
+                    <else>
+                      <message log="1" level="'Error'">
+                        'Could not read changelog on replication server %s:%s' \
+                        % (replServer.getHostname(), replServer.getPort())
+                      </message>
+                    </else>
+                  </if>
+                  
+                  <if expr="modEntry and eclEntry">
+                    <call function="'checkChangelogEntry'">
+                      { 'location'        : masterHost,
+                        'dsPath'          : masterPath,
+                        'changelogEntry'  : eclEntry,
+                        'targetDN'        : myTargetDN,
+                        'changeType'      : myChangeType,
+                        'changeTime'      : myChangeTime,
+                        'targetEntryUUID' : myTargetEntryUUID,
+                        'changes'         : myChanges
+                      }
+                    </call>
+                    <else>
+                      <sequence>
+                        <message log="1" level="'Error'">
+                          'Unable to check external changelog entry (failed to \
+                          read entries %s and/or %s)' % \
+                          (myEntry.getDn(), eclEntry['dn'][0])
+                        </message>
+                        <call function="'testFailed'"/>
+                      </sequence>
+                    </else>
+                  </if>
+                  
+                </sequence>                  
+              </iterate>
+
+              <script>
+                # set the lastCookie for the next ECL search
+                if nextCookie:
+                  lastCookie = nextCookie
+                
+                # reset variables, just in case
+                modEntry  = None
+                eclEntry  = None
+              </script>
+              
+              <call function="'testCase_Postamble'"/>
+            </sequence>
+          </testcase>
+
+
+          <!--- Test Case information
+          #@TestMarker          Replication External Changelog Tests
+          #@TestName            Replication: External Changelog: Modrdn_deleteoldrdn_true
+          #@TestID              Modrdn_deleteoldrdn_true
+          #@TestPurpose         Verify modrdn operation is recorded properly
+                                in the external changelog on each repl server
+          #@TestPreamble
+          #@TestSteps           Modrdn entry on server A
+          #@TestSteps           Verify servers in sync
+          #@TestSteps           Read entry from ldap server
+          #@TestSteps           Read last changelog entry on each repl server
+          #@TestSteps           Check changelog entry against expected values
+          #@TestPostamble
+          #@TestResult          Success if the server synchronised and the
+                                changelog entry value check succeeds
+          -->               
+          <testcase name="getTestCaseName('Modrdn_deleteoldrdn_true')">
+            <sequence>            
+              <call function="'testCase_Preamble'"/>
+              <message>
+                'Replication: External Changelog: Modrdn_deleteoldrdn_true. \
+                Verify modrdn operation is recorded properly in the \
+                external changelog on each repl server'
+              </message>
+
+              <script>
+                modEntry = None
+                eclEntry = None
+                
+                oldEntry = myEntry
+                newRdn   = 'cn=b'
+                newEntry = Entry(newRdn) 
+              </script>
+              
+              <!-- Modify entry on one of the servers -->
+              <call function="'modifyDn'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'DNToModify'     : oldEntry.getDn(),
+                  'newRDN'         : newRdn
+                }
+              </call>
+              
+              <!-- Verify the synchronization of the trees among the servers in
+                the topology -->          
+              <call function="'verifyTrees'">
+                [ clientHost, clientPath, master, consumerList, synchroSuffix ]
+              </call>
+              
+              <!-- Retrieve entry information for checkChangelogEntry's sake -->
+              <call function="'ldapSearchWithScript'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'dsBaseDN'       : newEntry.getDn(),
+                  'dsFilter'       : 'objectclass=*',
+                  'dsAttributes'   : modOperationalAttrs
+                }                
+              </call>
+              <script>
+                searchRC = STAXResult[0][0]
+                searchResult = STAXResult[0][1]
+              </script>
+              
+              <if expr="searchRC == 0">
+                <sequence>
+                  <message>
+                    'This is the entry as read from server %s:%s: \n%s' % \
+                    (masterHost, master.getPort(), searchResult)
+                  </message>
+                  <call function="'parseLdifEntry'">
+                    { 'ldifEntry' : searchResult }
+                  </call>
+                  <script>
+                    modEntry = STAXResult
+                    
+                    myTargetDN             = oldEntry.getDn()
+                    myChangeType           = 'modrdn'
+                    myChangeTime           = modEntry['modifytimestamp'][0]
+                    myTargetEntryUUID      = modEntry['entryuuid'][0]
+                    # myReplicationCSN     = ----> grab from where???
+                    # myReplicatIdentifier = ----> grab from ldap server
+                    myNewRDN               = newRdn
+                    myDeleteOldRDN         = 'true'
+                  </script>
+                </sequence>
+                <else>
+                  <message log="1" level="'Error'">
+                    'Could not read added entry %s from server %s:%s' % \
+                    (newEntry.getDn(), masterHost, master.getPort())
+                  </message>
+                </else>
+              </if>
+                                
+              <!-- Search changelog in the various replication servers -->                                                           
+              <iterate var="server" 
+                       in="_topologyServerList" 
+                       indexvar="i">
+                <sequence>                    
+                  <script>
+                    if globalSplitServers:
+                      replServer = _topologyReplServerList[i]
+                    else:
+                      replServer = server
+
+                    replServerPath = '%s/%s' % (replServer.getDir(), OPENDSNAME)
+                  </script>
+                  
+                  <message>
+                    'Reading changelog in replication server %s:%s from index: \
+                    %s' % \
+                    (replServer.getHostname(), replServer.getPort(), lastCookie)
+                  </message>
+                    
+                  <!-- Search for entry modify -->
+                  <call function="'SearchExternalChangelog'">
+                    { 'location'        : replServer.getHostname(),
+                      'dsPath'          : replServerPath,
+                      'dsInstanceHost'  : replServer.getHostname(),
+                      'dsInstancePort'  : replServer.getPort(),
+                      'dsInstanceDn'    : replServer.getRootDn(),
+                      'dsInstancePswd'  : replServer.getRootPwd(),
+                      'dsScope'         : 'subordinate',
+                      'dsBaseDN'        : 'cn=changelog',
+                      'dsFilter'        : '(objectclass=*)',
+                      'changelogCookie' : lastCookie
+                    }                
+                  </call>
+                  <script>
+                    searchRC = STAXResult[0][0]
+                    searchResult = STAXResult[0][1]
+                  </script>
+                  <if expr="searchRC == 0">
+                    <sequence>
+                      <message>
+                        'This is the ECL entry as read from server %s:%s: \n%s'\
+                         % (replServer.getHostname(), replServer.getPort(), 
+                            searchResult)
+                      </message>
+                      <call function="'parseLdifEntry'">
+                        { 'ldifEntry' : searchResult }
+                      </call>
+                      <script>
+                        eclEntry = STAXResult
+                        
+                        nextCookie = eclEntry['changelogcookie'][0]
+                      </script>
+                      <message>
+                        'Parsed changelog entry:  \n%s' % eclEntry
+                      </message>
+                      <message>
+                        'Cookie to use for the next ECL search:  %s' % \
+                        nextCookie
+                      </message>
+                    </sequence>
+                    <else>
+                      <message log="1" level="'Error'">
+                        'Could not read changelog on replication server %s:%s' \
+                        % (replServer.getHostname(), replServer.getPort())
+                      </message>
+                    </else>
+                  </if>
+                  
+                  <if expr="modEntry and eclEntry">
+                    <call function="'checkChangelogEntry'">
+                      { 'location'        : masterHost,
+                        'dsPath'          : masterPath,
+                        'changelogEntry'  : eclEntry,
+                        'targetDN'        : myTargetDN,
+                        'changeType'      : myChangeType,
+                        'changeTime'      : myChangeTime,
+                        'targetEntryUUID' : myTargetEntryUUID,
+                        'newRDN'          : myNewRDN,
+                        'deleteOldRDN'    : myDeleteOldRDN
+                      }
+                    </call>
+                    <else>
+                      <sequence>
+                        <message log="1" level="'Error'">
+                          'Unable to check external changelog entry (failed to \
+                          read entries %s and/or %s)' % \
+                          (newEntry.getDn(), eclEntry['dn'][0])
+                        </message>
+                        <call function="'testFailed'"/>
+                      </sequence>
+                    </else>
+                  </if>
+                  
+                </sequence>                  
+              </iterate>
+
+              <script>
+                # set the lastCookie for the next ECL search
+                if nextCookie:
+                  lastCookie = nextCookie
+                
+                # reset variables, just in case
+                modEntry  = None
+                eclEntry  = None
+              </script>
+              
+              <call function="'testCase_Postamble'"/>
+            </sequence>
+          </testcase>
+
+          
+
+          <!--- Test Case information
+          #@TestMarker          Replication External Changelog Tests
+          #@TestName            Replication: External Changelog: Modrdn_deleteoldrdn_false
+          #@TestID              Modrdn_deleteoldrdn_false
+          #@TestPurpose         Verify modrdn operation is recorded properly
+                                in the external changelog on each repl server
+          #@TestPreamble
+          #@TestSteps           Modrdn entry on server A
+          #@TestSteps           Verify servers in sync
+          #@TestSteps           Read entry from ldap server
+          #@TestSteps           Read last changelog entry on each repl server
+          #@TestSteps           Check changelog entry against expected values
+          #@TestPostamble
+          #@TestResult          Success if the server synchronised and the
+                                changelog entry value check succeeds
+          -->               
+          <testcase name="getTestCaseName('Modrdn_deleteoldrdn_false')">
+            <sequence>            
+              <call function="'testCase_Preamble'"/>
+              <message>
+                'Replication: External Changelog: Modrdn_deleteoldrdn_false. \
+                Verify modrdn operation is recorded properly in the \
+                external changelog on each repl server'
+              </message>
+
+              <script>
+                modEntry = None
+                eclEntry = None
+                
+                oldEntry = newEntry
+                newRdn   = 'cn=c'
+                newEntry = Entry(newRdn) 
+              </script>
+              
+              <!-- Modify entry on one of the servers -->
+              <call function="'modifyDn'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'DNToModify'     : oldEntry.getDn(),
+                  'newRDN'         : newRdn,
+                  'deleteOldRDN'   : 0
+                }
+              </call>
+              
+              <!-- Verify the synchronization of the trees among the servers in
+                the topology -->          
+              <call function="'verifyTrees'">
+                [ clientHost, clientPath, master, consumerList, synchroSuffix ]
+              </call>
+              
+              <!-- Retrieve entry information for checkChangelogEntry's sake -->
+              <call function="'ldapSearchWithScript'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'dsBaseDN'       : newEntry.getDn(),
+                  'dsFilter'       : 'objectclass=*',
+                  'dsAttributes'   : modOperationalAttrs
+                }                
+              </call>
+              <script>
+                searchRC = STAXResult[0][0]
+                searchResult = STAXResult[0][1]
+              </script>
+              
+              <if expr="searchRC == 0">
+                <sequence>
+                  <message>
+                    'This is the entry as read from server %s:%s: \n%s' % \
+                    (masterHost, master.getPort(), searchResult)
+                  </message>
+                  <call function="'parseLdifEntry'">
+                    { 'ldifEntry' : searchResult }
+                  </call>
+                  <script>
+                    modEntry = STAXResult
+                    
+                    myTargetDN             = oldEntry.getDn()
+                    myChangeType           = 'modrdn'
+                    myChangeTime           = modEntry['modifytimestamp'][0]
+                    myTargetEntryUUID      = modEntry['entryuuid'][0]
+                    # myReplicationCSN     = ----> grab from where???
+                    # myReplicatIdentifier = ----> grab from ldap server
+                    myNewRDN               = newRdn
+                    myDeleteOldRDN         = 'false'
+                  </script>
+                </sequence>
+                <else>
+                  <message log="1" level="'Error'">
+                    'Could not read added entry %s from server %s:%s' % \
+                    (newEntry.getDn(), masterHost, master.getPort())
+                  </message>
+                </else>
+              </if>
+                                
+              <!-- Search changelog in the various replication servers -->                                                           
+              <iterate var="server" 
+                       in="_topologyServerList" 
+                       indexvar="i">
+                <sequence>                    
+                  <script>
+                    if globalSplitServers:
+                      replServer = _topologyReplServerList[i]
+                    else:
+                      replServer = server
+
+                    replServerPath = '%s/%s' % (replServer.getDir(), OPENDSNAME)
+                  </script>
+                  
+                  <message>
+                    'Reading changelog in replication server %s:%s from index: \
+                    %s' % \
+                    (replServer.getHostname(), replServer.getPort(), lastCookie)
+                  </message>
+                    
+                  <!-- Search for entry modify -->
+                  <call function="'SearchExternalChangelog'">
+                    { 'location'        : replServer.getHostname(),
+                      'dsPath'          : replServerPath,
+                      'dsInstanceHost'  : replServer.getHostname(),
+                      'dsInstancePort'  : replServer.getPort(),
+                      'dsInstanceDn'    : replServer.getRootDn(),
+                      'dsInstancePswd'  : replServer.getRootPwd(),
+                      'dsScope'         : 'subordinate',
+                      'dsBaseDN'        : 'cn=changelog',
+                      'dsFilter'        : '(objectclass=*)',
+                      'changelogCookie' : lastCookie
+                    }                
+                  </call>
+                  <script>
+                    searchRC = STAXResult[0][0]
+                    searchResult = STAXResult[0][1]
+                  </script>
+                  <if expr="searchRC == 0">
+                    <sequence>
+                      <message>
+                        'This is the ECL entry as read from server %s:%s: \n%s'\
+                         % (replServer.getHostname(), replServer.getPort(), 
+                            searchResult)
+                      </message>
+                      <call function="'parseLdifEntry'">
+                        { 'ldifEntry' : searchResult }
+                      </call>
+                      <script>
+                        eclEntry = STAXResult
+                        
+                        nextCookie = eclEntry['changelogcookie'][0]
+                      </script>
+                      <message>
+                        'Parsed changelog entry:  \n%s' % eclEntry
+                      </message>
+                      <message>
+                        'Cookie to use for the next ECL search:  %s' % \
+                        nextCookie
+                      </message>
+                    </sequence>
+                    <else>
+                      <message log="1" level="'Error'">
+                        'Could not read changelog on replication server %s:%s' \
+                        % (replServer.getHostname(), replServer.getPort())
+                      </message>
+                    </else>
+                  </if>
+                  
+                  <if expr="modEntry and eclEntry">
+                    <call function="'checkChangelogEntry'">
+                      { 'location'        : masterHost,
+                        'dsPath'          : masterPath,
+                        'changelogEntry'  : eclEntry,
+                        'targetDN'        : myTargetDN,
+                        'changeType'      : myChangeType,
+                        'changeTime'      : myChangeTime,
+                        'targetEntryUUID' : myTargetEntryUUID,
+                        'newRDN'          : myNewRDN,
+                        'deleteOldRDN'    : myDeleteOldRDN
+                      }
+                    </call>
+                    <else>
+                      <sequence>
+                        <message log="1" level="'Error'">
+                          'Unable to check external changelog entry (failed to \
+                          read entries %s and/or %s)' % \
+                          (newEntry.getDn(), eclEntry['dn'][0])
+                        </message>
+                        <call function="'testFailed'"/>
+                      </sequence>
+                    </else>
+                  </if>
+                  
+                </sequence>                  
+              </iterate>
+
+              <script>
+                # set the lastCookie for the next ECL search
+                if nextCookie:
+                  lastCookie = nextCookie
+                
+                # reset variables, just in case
+                modEntry  = None
+                eclEntry  = None
+              </script>
+              
+              <call function="'testCase_Postamble'"/>
+            </sequence>
+          </testcase>
+          
+          
+          <!--- Test Case information
+          #@TestMarker          Replication External Changelog Tests
+          #@TestName            Replication: External Changelog: Delete_1
+          #@TestID              Delete_1
+          #@TestPurpose         Verify delete operation is recorded properly
+                                in the external changelog on each repl server
+          #@TestPreamble
+          #@TestSteps           Delete entry on server A
+          #@TestSteps           Verify servers in sync
+          #@TestSteps           Read entry from ldap server
+          #@TestSteps           Read last changelog entry on each repl server
+          #@TestSteps           Check changelog entry against expected values
+          #@TestPostamble
+          #@TestResult          Success if the server synchronised and the
+                                changelog entry value check succeeds
+          -->               
+          <testcase name="getTestCaseName('Delete_1')">
+            <sequence>            
+              <call function="'testCase_Preamble'"/>
+              <message>
+                'Replication: External Changelog: Delete_1. \
+                Verify delete operation is recorded properly in the \
+                external changelog on each repl server'
+              </message>
+
+              <script>
+                delEntry = None
+                eclEntry = None
+              </script>
+              
+              <!-- Retrieve entry information for checkChangelogEntry's sake -->
+              <call function="'ldapSearchWithScript'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'dsBaseDN'       : newEntry.getDn(),
+                  'dsFilter'       : 'objectclass=*',
+                  'dsAttributes'   : modOperationalAttrs
+                }                
+              </call>
+              <script>
+                searchRC = STAXResult[0][0]
+                searchResult = STAXResult[0][1]
+              </script>
+              
+              <if expr="searchRC == 0">
+                <sequence>
+                  <message>
+                    'This is the entry as read from server %s:%s: \n%s' % \
+                    (masterHost, master.getPort(), searchResult)
+                  </message>
+                  <call function="'parseLdifEntry'">
+                    { 'ldifEntry' : searchResult }
+                  </call>
+                  <script>
+                    delEntry = STAXResult
+                    
+                    myTargetDN             = delEntry['dn'][0]
+                    myChangeType           = 'delete'
+                    myTargetEntryUUID      = delEntry['entryuuid'][0]
+                    # myReplicationCSN     = ----> grab from where???
+                    # myReplicatIdentifier = ----> grab from ldap server
+                  </script>
+                </sequence>
+                <else>
+                  <message log="1" level="'Error'">
+                    'Could not read added entry %s from server %s:%s' % \
+                    (newEntry.getDn(), masterHost, master.getPort())
+                  </message>
+                </else>
+              </if>
+              
+              <!-- Delete entry on one of the servers -->
+              <call function="'DeleteEntry'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'dsBaseDN'       : newEntry.getDn()
+                }
+              </call>
+
+              <!-- Check delete worked on "master" server -->
+              <call function="'ldapSearchWithScript'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'dsBaseDN'       : newEntry.getDn(),
+                  'dsFilter'       : 'objectclass=*',
+                  'expectedRC'     : 32
+                }                  
+              </call>
+              
+              <!-- Verify the synchronization of the trees among the servers in
+                the topology -->          
+              <call function="'verifyTrees'">
+                [ clientHost, clientPath, master, consumerList, synchroSuffix ]
+              </call>
+                                
+              <!-- Search changelog in the various replication servers -->                                                           
+              <iterate var="server" 
+                       in="_topologyServerList" 
+                       indexvar="i">
+                <sequence>                    
+                  <script>
+                    if globalSplitServers:
+                      replServer = _topologyReplServerList[i]
+                    else:
+                      replServer = server
+
+                    replServerPath = '%s/%s' % (replServer.getDir(), OPENDSNAME)
+                  </script>
+                  
+                  <message>
+                    'Reading changelog in replication server %s:%s from index: \
+                    %s' % \
+                    (replServer.getHostname(), replServer.getPort(), lastCookie)
+                  </message>
+                    
+                  <!-- Search for entry modify -->
+                  <call function="'SearchExternalChangelog'">
+                    { 'location'        : replServer.getHostname(),
+                      'dsPath'          : replServerPath,
+                      'dsInstanceHost'  : replServer.getHostname(),
+                      'dsInstancePort'  : replServer.getPort(),
+                      'dsInstanceDn'    : replServer.getRootDn(),
+                      'dsInstancePswd'  : replServer.getRootPwd(),
+                      'dsScope'         : 'subordinate',
+                      'dsBaseDN'        : 'cn=changelog',
+                      'dsFilter'        : '(objectclass=*)',
+                      'changelogCookie' : lastCookie
+                    }                
+                  </call>
+                  <script>
+                    searchRC = STAXResult[0][0]
+                    searchResult = STAXResult[0][1]
+                  </script>
+                  <if expr="searchRC == 0">
+                    <sequence>
+                      <message>
+                        'This is the ECL entry as read from server %s:%s: \n%s'\
+                         % (replServer.getHostname(), replServer.getPort(), 
+                            searchResult)
+                      </message>
+                      <call function="'parseLdifEntry'">
+                        { 'ldifEntry' : searchResult }
+                      </call>
+                      <script>
+                        eclEntry = STAXResult
+                        
+                        nextCookie = eclEntry['changelogcookie'][0]
+                      </script>
+                      <message>
+                        'Parsed changelog entry:  \n%s' % eclEntry
+                      </message>
+                      <message>
+                        'Cookie to use for the next ECL search:  %s' % \
+                        nextCookie
+                      </message>
+                    </sequence>
+                    <else>
+                      <message log="1" level="'Error'">
+                        'Could not read changelog on replication server %s:%s' \
+                        % (replServer.getHostname(), replServer.getPort())
+                      </message>
+                    </else>
+                  </if>
+                  
+                  <if expr="delEntry and eclEntry">
+                    <call function="'checkChangelogEntry'">
+                      { 'location'        : masterHost,
+                        'dsPath'          : masterPath,
+                        'changelogEntry'  : eclEntry,
+                        'targetDN'        : myTargetDN,
+                        'changeType'      : myChangeType,
+                        'targetEntryUUID' : myTargetEntryUUID
+                      }
+                    </call>
+                    <else>
+                      <sequence>
+                        <message log="1" level="'Error'">
+                          'Unable to check external changelog entry (failed to \
+                          read entries %s and/or %s)' % \
+                          (newEntry.getDn(), eclEntry['dn'][0])
+                        </message>
+                        <call function="'testFailed'"/>
+                      </sequence>
+                    </else>
+                  </if>
+                  
+                </sequence>                  
+              </iterate>
+
+              <script>
+                # set the lastCookie for the next ECL search
+                if nextCookie:
+                  lastCookie = nextCookie
+                
+                # reset variables, just in case
+                delEntry  = None
+                eclEntry  = None
+              </script>
+              
+              <call function="'testCase_Postamble'"/>
+            </sequence>
+          </testcase>
+          
+          
+          <!--- Test Case information
+          #@TestMarker          Replication External Changelog Tests
+          #@TestName            Replication: External Changelog: Add_2
+          #@TestID              Add_2
+          #@TestPurpose         Verify add operation (of an entry with 
+                                userpassword) is recorded properly in the
+                                external changelog on each replication server
+          #@TestPreamble
+          #@TestSteps           Add entry to server A (with userpassword)
+          #@TestSteps           Verify servers in sync
+          #@TestSteps           Read entry from ldap server
+          #@TestSteps           Read last changelog entry on each repl server
+          #@TestSteps           Check changelog entry against expected values
+          #@TestPostamble
+          #@TestResult          Success if the server synchronised and the
+                                changelog entry value check succeeds
+          -->               
+          <testcase name="getTestCaseName('Add_2')">
+            <sequence>            
+              <call function="'testCase_Preamble'"/>
+              <message>
+                'Replication: External Changelog: Add_2. \
+                Verify add operation (of and entry with userpassword) is \
+                recorded properly in the external changelog on each \
+                replication server'
+              </message>
+
+              <!-- Add entry to "master" server -->
+              <script>
+                myEntry = Entry('cn=user')
+                myEntry.addAttr('sn', 'user')
+                myEntry.addAttr('userpassword', 'password')
+                
+                addedEntry = None
+                eclEntry   = None
+              </script>
+              <call function="'addAnEntry'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'DNToAdd'        : myEntry.getDn(),
+                  'listAttributes' : myEntry.getAttrList()
+                }
+              </call>
+              
+              <!-- Verify the synchronization of the trees among the servers in
+                the topology -->          
+              <call function="'verifyTrees'">
+                [ clientHost, clientPath, master, consumerList, synchroSuffix ]
+              </call>
+              
+              <!-- Retrieve entry information for checkChangelogEntry's sake -->
+              <call function="'ldapSearchWithScript'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'dsBaseDN'       : myEntry.getDn(),
+                  'dsFilter'       : 'objectclass=*',
+                  'dsAttributes'   : '%s pwdChangedTime' % addOperationalAttrs
+                }                
+              </call>
+              <script>
+                searchRC = STAXResult[0][0]
+                searchResult = STAXResult[0][1]
+              </script>
+              
+              <if expr="searchRC == 0">
+                <sequence>
+                  <message>
+                    'This is the entry as read from server %s:%s: \n%s' % \
+                    (masterHost, master.getPort(), searchResult)
+                  </message>
+                  <call function="'parseLdifEntry'">
+                    { 'ldifEntry' : searchResult }
+                  </call>
+                  <script>
+                    addedEntry = STAXResult
+                    
+                    myTargetDN             = addedEntry['dn'][0]
+                    myChangeType           = 'add'
+                    myChangeTime           = addedEntry['createtimestamp'][0]
+                    myTargetEntryUUID      = addedEntry['entryuuid'][0]
+                    # myReplicationCSN     = ----> grab from where???
+                    # myReplicatIdentifier = ----> grab from ldap server
+                    myChanges              = {}
+                    
+                    for attr in addedEntry.keys():
+                      if (attr != 'dn') and (attr != 'changelogcookie') :
+                        myChanges[attr] = addedEntry[attr]
+                  </script>
+                </sequence>
+                <else>
+                  <message log="1" level="'Error'">
+                    'Could not read added entry %s from server %s:%s' % \
+                    (myEntry.getDn(), masterHost, master.getPort())
+                  </message>
+                </else>
+              </if>
+                                
+              <!-- Search changelog in the various replication servers -->                                                           
+              <iterate var="server" 
+                       in="_topologyServerList" 
+                       indexvar="i">
+                <sequence>                    
+                  <script>
+                    if globalSplitServers:
+                      replServer = _topologyReplServerList[i]
+                    else:
+                      replServer = server
+
+                    replServerPath = '%s/%s' % (replServer.getDir(), OPENDSNAME)
+                  </script>
+                  
+                  <message>
+                    'Reading changelog in replication server %s:%s from index: \
+                    %s' % \
+                    (replServer.getHostname(), replServer.getPort(), lastCookie)
+                  </message>
+                    
+                  <!-- Search for entry add -->
+                  <call function="'SearchExternalChangelog'">
+                    { 'location'        : replServer.getHostname(),
+                      'dsPath'          : replServerPath,
+                      'dsInstanceHost'  : replServer.getHostname(),
+                      'dsInstancePort'  : replServer.getPort(),
+                      'dsInstanceDn'    : replServer.getRootDn(),
+                      'dsInstancePswd'  : replServer.getRootPwd(),
+                      'dsScope'         : 'subordinate', 
+                      'dsBaseDN'        : 'cn=changelog',
+                      'dsFilter'        : '(objectclass=*)',
+                      'changelogCookie' : lastCookie
+                    }                
+                  </call>
+                  <script>
+                    searchRC = STAXResult[0][0]
+                    searchResult = STAXResult[0][1]
+                  </script>
+                  <if expr="searchRC == 0">
+                    <sequence>
+                    <message>
+                      'This is the ECL entry as read from server %s:%s: \n%s' %\
+                      (replServer.getHostname(), replServer.getPort(), 
+                       searchResult)
+                    </message>
+                      <call function="'parseLdifEntry'">
+                        { 'ldifEntry' : searchResult }
+                      </call>
+                      <script>
+                        eclEntry = STAXResult
+                        
+                        nextCookie = eclEntry['changelogcookie'][0]
+                      </script>
+                      <message>
+                        'Parsed changelog entry:  \n%s' % eclEntry
+                      </message>
+                      <message>
+                        'Cookie to use for the next ECL search:  %s' % \
+                        nextCookie
+                      </message>
+                    </sequence>
+                    <else>
+                      <message log="1" level="'Error'">
+                        'Could not read changelog on replication server %s:%s' \
+                        % (replServer.getHostname(), replServer.getPort())
+                      </message>
+                    </else>
+                  </if>
+                  
+                  <if expr="addedEntry and eclEntry">
+                    <call function="'checkChangelogEntry'">
+                      { 'location'        : masterHost,
+                        'dsPath'          : masterPath,
+                        'changelogEntry'  : eclEntry,
+                        'targetDN'        : myTargetDN,
+                        'changeType'      : myChangeType,
+                        'changeTime'      : myChangeTime,
+                        'targetEntryUUID' : myTargetEntryUUID,
+                        'changes'         : myChanges
+                      }
+                    </call>
+                    <else>
+                      <sequence>
+                        <message log="1" level="'Error'">
+                          'Unable to check external changelog entry (failed to \
+                          read entries %s and/or %s)' % \
+                          (myEntry.getDn(), eclEntry['dn'][0])
+                        </message>
+                        <call function="'testFailed'"/>
+                      </sequence>
+                    </else>
+                  </if>
+                  
+                </sequence>                  
+              </iterate>
+              
+              <script>
+                # set the lastCookie for the next ECL search
+                if nextCookie:
+                  lastCookie = nextCookie
+                
+                # reset variables, just in case
+                addedEntry = None
+                eclEntry   = None
+              </script>
+              
+              <call function="'testCase_Postamble'"/>
+            </sequence>
+          </testcase>
+
+          
+          <!--- Test Case information
+          #@TestMarker          Replication External Changelog Tests
+          #@TestName            Replication: External Changelog: Modify_replace_2
+          #@TestID              Modify_replace_2
+          #@TestPurpose         Verify modify-replace operation as non directory 
+                                manager is well recorded in the external 
+                                changelog on each repl server
+          #@TestPreamble
+          #@TestSteps           Modify entry on server A (as non dir manager)
+          #@TestSteps           Verify servers in sync
+          #@TestSteps           Read entry from ldap server
+          #@TestSteps           Read last changelog entry on each repl server
+          #@TestSteps           Check changelog entry against expected values
+          #@TestPostamble
+          #@TestResult          Success if the server synchronised and the
+                                changelog entry value check succeeds
+          -->               
+          <testcase name="getTestCaseName('Modify_replace_2')">
+            <sequence>            
+              <call function="'testCase_Preamble'"/>
+              <message>
+                'Replication: External Changelog: Modify_replace_2. \
+                Verify modify-replace operation as non directory manager is \
+                recorded properly in the external changelog on each repl server'
+              </message>
+
+              <script>
+                modEntry   = None
+                eclEntry   = None
+              </script>
+              
+              <!-- Modify entry on one of the servers -->
+              <call function="'modifyAnAttribute'">
+                { 'location'          : masterHost,
+                  'dsPath'            : masterPath,
+                  'dsInstanceHost'    : masterHost,
+                  'dsInstancePort'    : master.getPort(),
+                  'dsInstanceDn'      : myEntry.getDn(),
+                  'dsInstancePswd'    : 'password',
+                  'DNToModify'        : myEntry.getDn(),
+                  'attributeName'     : 'userpassword',
+                  'newAttributeValue' : 'newpass',
+                  'changetype'        : 'replace'
+                }
+              </call>
+              
+              <!-- Verify the synchronization of the trees among the servers in
+                the topology -->          
+              <call function="'verifyTrees'">
+                [ clientHost, clientPath, master, consumerList, synchroSuffix ]
+              </call>
+              
+              <!-- Retrieve entry information for checkChangelogEntry's sake -->
+              <call function="'ldapSearchWithScript'">
+                { 'location'       : masterHost,
+                  'dsPath'         : masterPath,
+                  'dsInstanceHost' : masterHost,
+                  'dsInstancePort' : master.getPort(),
+                  'dsInstanceDn'   : master.getRootDn(),
+                  'dsInstancePswd' : master.getRootPwd(),
+                  'dsBaseDN'       : myEntry.getDn(),
+                  'dsFilter'       : 'objectclass=*',
+                  'dsAttributes'   : '%s pwdChangedTime' % modOperationalAttrs
+                }                
+              </call>
+              <script>
+                searchRC = STAXResult[0][0]
+                searchResult = STAXResult[0][1]
+              </script>
+              
+              <if expr="searchRC == 0">
+                <sequence>
+                  <message>
+                    'This is the entry as read from server %s:%s: \n%s' % \
+                    (masterHost, master.getPort(), searchResult)
+                  </message>
+                  <call function="'parseLdifEntry'">
+                    { 'ldifEntry' : searchResult }
+                  </call>
+                  <script>
+                    modEntry = STAXResult
+                    
+                    myTargetDN             = modEntry['dn'][0]
+                    myChangeType           = 'modify'
+                    myChangeTime           = modEntry['modifytimestamp'][0]
+                    myTargetEntryUUID      = modEntry['entryuuid'][0]
+                    # myReplicationCSN     = ----> grab from where???
+                    # myReplicatIdentifier = ----> grab from ldap server
+                    myModifiersName        = modEntry['modifiersname'][0]
+                    myModifyTimestamp      = modEntry['modifytimestamp'][0]
+                    myUserPassword         = modEntry['userpassword'][0]
+                    myPwdChangedTime       = modEntry['pwdchangedtime'][0]
+                    myChanges              = []
+                    myChanges.append(['replace', 'userpassword', myUserPassword])
+                    myChanges.append(['replace', 'pwdchangedtime', myPwdChangedTime])
+                    myChanges.append(['replace', 'modifiersname', myModifiersName])
+                    myChanges.append(['replace', 'modifytimestamp', myModifyTimestamp])
+                  </script>
+                </sequence>
+                <else>
+                  <message log="1" level="'Error'">
+                    'Could not read modified entry %s from server %s:%s' % \
+                    (myEntry.getDn(), masterHost, master.getPort())
+                  </message>
+                </else>
+              </if>
+                                
+              <!-- Search changelog in the various replication servers -->                                                           
+              <iterate var="server" 
+                       in="_topologyServerList" 
+                       indexvar="i">
+                <sequence>                    
+                  <script>
+                    if globalSplitServers:
+                      replServer = _topologyReplServerList[i]
+                    else:
+                      replServer = server
+
+                    replServerPath = '%s/%s' % (replServer.getDir(), OPENDSNAME)
+                  </script>
+                  
+                  <message>
+                    'Reading changelog in replication server %s:%s from index: \
+                    %s' % \
+                    (replServer.getHostname(), replServer.getPort(), lastCookie)
+                  </message>
+                    
+                  <!-- Search for entry modify -->
+                  <call function="'SearchExternalChangelog'">
+                    { 'location'        : replServer.getHostname(),
+                      'dsPath'          : replServerPath,
+                      'dsInstanceHost'  : replServer.getHostname(),
+                      'dsInstancePort'  : replServer.getPort(),
+                      'dsInstanceDn'    : replServer.getRootDn(),
+                      'dsInstancePswd'  : replServer.getRootPwd(),
+                      'dsScope'         : 'subordinate',
+                      'dsBaseDN'        : 'cn=changelog',
+                      'dsFilter'        : '(objectclass=*)',
+                      'changelogCookie' : lastCookie
+                    }                
+                  </call>
+                  <script>
+                    searchRC = STAXResult[0][0]
+                    searchResult = STAXResult[0][1]
+                  </script>
+                  <if expr="searchRC == 0">
+                    <sequence>
+                      <message>
+                        'This is the ECL entry as read from server %s:%s: \n%s'\
+                         % (replServer.getHostname(), replServer.getPort(), 
+                            searchResult)
+                      </message>
+                      <call function="'parseLdifEntry'">
+                        { 'ldifEntry' : searchResult }
+                      </call>
+                      <script>
+                        eclEntry = STAXResult
+                        
+                        nextCookie = eclEntry['changelogcookie'][0]
+                      </script>
+                      <message>
+                        'Parsed changelog entry:  \n%s' % eclEntry
+                      </message>
+                      <message>
+                        'Cookie to use for the next ECL search:  %s' % \
+                        nextCookie
+                      </message>
+                    </sequence>
+                    <else>
+                      <message log="1" level="'Error'">
+                        'Could not read changelog on replication server %s:%s' \
+                        % (replServer.getHostname(), replServer.getPort())
+                      </message>
+                    </else>
+                  </if>
+                  
+                  <if expr="modEntry and eclEntry">
+                    <call function="'checkChangelogEntry'">
+                      { 'location'        : masterHost,
+                        'dsPath'          : masterPath,
+                        'changelogEntry'  : eclEntry,
+                        'targetDN'        : myTargetDN,
+                        'changeType'      : myChangeType,
+                        'changeTime'      : myChangeTime,
+                        'targetEntryUUID' : myTargetEntryUUID,
+                        'changes'         : myChanges
+                      }
+                    </call>
+                    <else>
+                      <sequence>
+                        <message log="1" level="'Error'">
+                          'Unable to check external changelog entry (failed to \
+                          read entries %s and/or %s)' % \
+                          (myEntry.getDn(), eclEntry['dn'][0])
+                        </message>
+                        <call function="'testFailed'"/>
+                      </sequence>
+                    </else>
+                  </if>
+                  
+                </sequence>                  
+              </iterate>
+
+              <script>
+                # set the lastCookie for the next ECL search
+                if nextCookie:
+                  lastCookie = nextCookie
+                
+                # reset variables, just in case
+                modEntry  = None
+                eclEntry  = None
+              </script>
+              
+              <call function="'testCase_Postamble'"/>
+            </sequence>
+          </testcase>
+
+          
+          <import machine="STAF_LOCAL_HOSTNAME"
+                  file="'%s/testcases/replication/replication_cleanup.xml' 
+                        % (TESTS_DIR)"/>
+          <call function="'replication_cleanup'" />
+
+          <call function="'testSuite_Postamble'"/>
+        </sequence>
+      </block>
+    </sequence>
+  </function>
+</stax>
diff --git a/opendj-sdk/opends/tests/staf-tests/functional-tests/testcases/replication/replication.xml b/opendj-sdk/opends/tests/staf-tests/functional-tests/testcases/replication/replication.xml
index b64bac3..1e75ac9 100644
--- a/opendj-sdk/opends/tests/staf-tests/functional-tests/testcases/replication/replication.xml
+++ b/opendj-sdk/opends/tests/staf-tests/functional-tests/testcases/replication/replication.xml
@@ -58,6 +58,7 @@
             testList.append('encryption')
             testList.append('changelog')
             testList.append('conflict')
+            testList.append('externalchangelog')
             
             globalAssuredReplication = None
             
diff --git a/opendj-sdk/opends/tests/staf-tests/shared/functions/ldap.xml b/opendj-sdk/opends/tests/staf-tests/shared/functions/ldap.xml
index 4c803e5..f7081b6 100755
--- a/opendj-sdk/opends/tests/staf-tests/shared/functions/ldap.xml
+++ b/opendj-sdk/opends/tests/staf-tests/shared/functions/ldap.xml
@@ -3083,6 +3083,151 @@
     </sequence>
   </function>
   
+  
+  <!-- Rename an entry -->
+  <function name="modifyDn">
+    <function-prolog>
+      This function renames an entry, either by a modRDN or by a modDN
+    </function-prolog>
+    <function-map-args>
+      <function-arg-def name="location" 
+                        type="optional" 
+                        default="STAF_REMOTE_HOSTNAME">
+        <function-arg-description>
+          Location of target host
+        </function-arg-description>
+        <function-arg-property name="type" value="hostname"/>
+      </function-arg-def>
+      <function-arg-def name="dsPath" 
+                        type="optional" 
+                        default="'%s/%s' % (DIRECTORY_INSTANCE_DIR,OPENDSNAME)">
+        <function-arg-description>
+          Pathname to installation root
+        </function-arg-description>
+        <function-arg-property name="type" value="filepath"/>
+      </function-arg-def>
+      <function-arg-def name="dsInstanceHost" 
+                        type="optional" 
+                        default="STAF_REMOTE_HOSTNAME">
+        <function-arg-description>
+          Directory server hostname or IP address
+        </function-arg-description>
+        <function-arg-property name="type" value="hostname"/>
+      </function-arg-def>
+      <function-arg-def name="dsInstancePort" type="required">
+        <function-arg-description>
+          Directory server port number
+        </function-arg-description>
+        <function-arg-property name="type" value="Port number"/>
+      </function-arg-def>
+      <function-arg-def name="dsInstanceDn" type="required">
+        <function-arg-description>
+          Bind DN
+        </function-arg-description>
+        <function-arg-property name="type" value="DN"/>
+      </function-arg-def>
+      <function-arg-def name="dsInstancePswd" type="required">
+        <function-arg-description>
+          Bind password
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="DNToModify" type="required">
+        <function-arg-description>
+          DN to modify
+        </function-arg-description>
+        <function-arg-property name="type" value="DN"/>
+      </function-arg-def>
+      <function-arg-def name="newRDN" 
+                        type="optional"
+                        default="None">
+        <function-arg-description>
+          New rdn name : ie uid=jdoe_bis. If not passed, the newRDN will be the
+          same old rdn
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="deleteOldRDN" 
+                        type="optional"
+                        default="None">
+        <function-arg-description>
+          1 : delete old rdn, 0 do not delete old rdn, default value is 1
+        </function-arg-description>
+        <function-arg-property name="type" value="integer"/>
+      </function-arg-def>
+      <function-arg-def name="newSuperior" 
+                        type="optional"
+                        default="None">
+        <function-arg-description>
+          New parent entry for the entry
+        </function-arg-description>
+        <function-arg-property name="type" value="DN"/>
+      </function-arg-def>
+      <function-arg-def name="expectedRC" type="optional" default="0">
+        <function-arg-description>
+          Expected return code value. Default value is 0
+          Wildcard 'noCheck' to not check the RC
+        </function-arg-description>
+        <function-arg-property name="type" value="integer"/>
+      </function-arg-def>
+      <function-arg-def name="knownIssue" type="optional" default="None">
+        <function-arg-description>
+          Known issue. Corresponds to an issue number.
+        </function-arg-description>
+        <function-arg-property name="type" value="string" />
+      </function-arg-def>
+    </function-map-args>
+    <sequence>
+    
+      <!-- Local variables -->
+      <script>
+        mylocation=location
+        #Build the Command
+        STAFCmdParamsList=[]
+        STAFCmdParams=''
+        STAFCmd=''
+      </script>
+      
+      <!-- Set common ldap arguments -->      
+      <call function="'_ldapCommonArgs'" />
+      <script>
+        if DNToModify:
+          STAFCmdParamsList.append('-d "%s"' % DNToModify)
+        
+        if newRDN:
+          STAFCmdParamsList.append('-e "%s"' % newRDN)
+        
+        if deleteOldRDN == 0:
+          STAFCmdParamsList.append('-f "false"')
+        elif deleteOldRDN == 1:
+          STAFCmdParamsList.append('-f "true"')
+
+        if newSuperior:
+          STAFCmdParamsList.append('-g "%s"' % newSuperior)
+
+        STAFCmdParams=' '.join(STAFCmdParamsList)
+        
+        STAFCmd='modifyDn' 
+      </script>
+      <call function="'runCommand'" >
+        { 'name'      : 'Modify DN',
+          'command'   : '%s/bin/java' % JAVA_HOME,
+          'arguments' : '%s %s' % (STAFCmd,STAFCmdParams),
+          'location'  : location,
+          'envCmd'    : ['CLASSPATH=%s/../%s/ldapjndi' % (dsPath,remote.reljavadir)],
+          'path'      : '%s/../%s/ldapjndi' % (dsPath,remote.reljavadir),
+          'expectedRC': expectedRC,
+          'knownIssue': knownIssue
+        }
+      </call>
+      <return>
+        STAXResult
+      </return>
+    </sequence>
+  </function>
+  
+  
+  
   <!-- Modify an attribute -->
   <function name="saslSearch">
     <function-prolog>
@@ -3298,5 +3443,183 @@
     </sequence>
     
   </function>  
+
+
+
+  <function name="SearchExternalChangelog">
+    <function-prolog>
+      This function searches the external changelog
+    </function-prolog>
+    <function-map-args>
+      <function-arg-def name="location" type="optional" default="STAF_REMOTE_HOSTNAME">
+        <function-arg-description>
+          Location of target host
+        </function-arg-description>
+        <function-arg-property name="type" value="hostname"/>
+      </function-arg-def>
+      <function-arg-def name="dsPath" type="optional" default="'%s/%s' % (DIRECTORY_INSTANCE_BIN,OPENDSNAME)">
+        <function-arg-description>
+          Pathname to installation root
+        </function-arg-description>
+        <function-arg-property name="type" value="pathname"/>
+      </function-arg-def>
+      <function-arg-def name="dsInstanceHost" type="optional">
+        <function-arg-description>
+          Directory Server Host Name
+        </function-arg-description>
+        <function-arg-property name="type" value="hostname"/>
+      </function-arg-def>
+      <function-arg-def name="dsInstancePort" type="optional">
+        <function-arg-description>
+          Directory Server Port Number
+        </function-arg-description>
+        <function-arg-property name="type" value="integer"/>
+      </function-arg-def>                
+      <function-arg-def name="dsInstanceDn" type="optional">
+        <function-arg-description>
+          Directory Server Manager DN
+        </function-arg-description>
+        <function-arg-property name="type" value="dn"/>
+      </function-arg-def>     
+      <function-arg-def name="dsInstancePswd" type="optional">
+        <function-arg-description>
+          Directory Server Manager Password
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="dsScope" type="optional">
+        <function-arg-description>
+          The scope of the search operation
+        </function-arg-description>
+        <function-arg-property name="type" value="dn"/>
+      </function-arg-def>        
+      <function-arg-def name="dsBaseDN" type="optional">
+        <function-arg-description>
+          The baseDN for the search operation
+        </function-arg-description>
+        <function-arg-property name="type" value="dn"/>
+      </function-arg-def>
+      <function-arg-def name="dsFilter" type="required">
+        <function-arg-description>
+          The filter for the search operation
+        </function-arg-description>
+        <function-arg-property name="type" value="filter"/>
+      </function-arg-def>
+      <function-arg-def name="extraParams" type="optional">
+        <function-arg-description>
+          Optional extra parameters for specific test cases
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="attributes" type="optional">
+        <function-arg-description>
+          Optional Attributes to return
+        </function-arg-description>
+        <function-arg-property name="type" value="attributes"/>
+      </function-arg-def>
+      <function-arg-def name="changelogCookie" 
+                        type="optional" 
+                        default="None">
+        <function-arg-description>
+          Changelog cookie of the last change retrieved.
+          (OpenDS external changelog => non draft-compatible mode)
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>      
+      </function-arg-def>
+      <function-arg-def name="changeNumberFilter" 
+                        type="optional" 
+                        default="None">
+        <function-arg-description>
+          Changenumber corresponding to the last change retrieved.
+          (Changelog draft-compatible mode)
+        </function-arg-description>
+        <function-arg-property name="type" value="filter"/>      
+      </function-arg-def>
+      <function-arg-def name="expectedRC" type="optional" default="0">
+        <function-arg-description>
+          Expected return code value. Default value is 0.
+          Wildcard 'noCheck' to not check the RC
+        </function-arg-description>
+        <function-arg-property name="type" value="integer"/>      
+      </function-arg-def>    
+      <function-arg-def name="knownIssue" type="optional" default="None">
+        <function-arg-description>
+          Known issue. Corresponds to an issue number.
+        </function-arg-description>
+        <function-arg-property name="type" value="string" />
+      </function-arg-def>
+    </function-map-args>
+    <sequence>
+    
+      <!-- Local variables -->
+      <script>
+        mylocation=location
+      </script>
+
+      <!-- Build the Command -->
+      <script>
+        STAFCmdParamsList=[]
+        STAFCmdParams=''
+
+        if dsPath:
+          dsBinPath='%s/%s' % (dsPath,fileFolder) 
+          STAFCmd='%s/ldapsearch%s' % (dsBinPath,fileExt)
+
+      </script>
+
+      <!-- Set common ldap arguments -->      
+      <call function="'_ldapCommonArgs'"/>
+      
+      <script>
+        if changelogCookie:
+          control = '1.3.6.1.4.1.26027.1.5.4:false:%s' % changelogCookie
+          STAFCmdParamsList.append('--control "%s"' % control)
+        
+        if dsScope:          
+          STAFCmdParamsList.append('-s %s' % dsScope)
+          
+        if extraParams:
+          STAFCmdParamsList.append('%s' % extraParams)
+          
+        if dsBaseDN:
+          STAFCmdParamsList.append('-b "%s"' % dsBaseDN)          
+          
+        if dsFilter:
+          if changeNumberFilter:
+            searchFilter = '&amp;%s%s' % (changeNumberFilter, dsFilter)
+          else:
+            searchFilter = dsFilter
+          STAFCmdParamsList.append('"%s"' % searchFilter)
+        else:
+          if changeNumberFilter:
+            STAFCmdParamsList.append('"%s"' % changeNumberFilter)
+          
+        if attributes:
+          STAFCmdParamsList.append('%s' % attributes)
+        else:
+          STAFCmdParamsList.append('@changeLogEntry')  
+ 
+        STAFCmdParams=' '.join(STAFCmdParamsList)
+      </script>
+      
+      <message>
+        '%s %s' % (STAFCmd, STAFCmdParams)
+      </message>
+      
+      <call function="'runCommand'">
+        { 'command'   : STAFCmd,
+          'arguments' : STAFCmdParams,
+          'location'  : location,
+          'name'      : 'ldapsearch',
+          'expectedRC': expectedRC,
+          'knownIssue': knownIssue
+        }
+      </call>
+      <return>
+        STAXResult
+      </return>
+    </sequence>
+
+  </function>
   
 </stax>
diff --git a/opendj-sdk/opends/tests/staf-tests/shared/functions/utils.xml b/opendj-sdk/opends/tests/staf-tests/shared/functions/utils.xml
index 3a211e6..68a2e67 100755
--- a/opendj-sdk/opends/tests/staf-tests/shared/functions/utils.xml
+++ b/opendj-sdk/opends/tests/staf-tests/shared/functions/utils.xml
@@ -242,6 +242,15 @@
         </function-arg-description>
         <function-arg-property name="type" value="string"/>
       </function-arg-def>
+      <function-arg-def name="searchType" 
+                        type="optional" 
+                        default="'substring'">
+        <function-arg-description>
+          the type of the search: substring, exact-case-insensitive or
+          exact-case-sensitive
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
       <function-arg-def name="knownIssue" type="optional" default="None">
         <function-arg-description>
           Known issue. Corresponds to an issue number.
@@ -279,13 +288,22 @@
           <return>[myRC, myReason]</return>
         </sequence>
       </if>
+      
+      <script>
+        if searchType == 'substring':
+          searchResult = (re.search(searchre, returnString) != None)
+        elif searchType == 'exact-case-sensitive':
+          searchResult = (expectedString == returnString)
+        elif searchType == 'exact-case-insensitive':
+          searchResult = (expectedString.lower() == returnString.lower())
+      </script>
 
       <!-- Search for the expectedString -->
-      <if expr='re.search(searchre, returnString) != None'>
+      <if expr='searchResult'>
         <sequence>
           <message log="1">
-            'Found substring, %s, in the return string' \
-            % (expectedString)
+            'Search type: %s  Found substring, %s, in the return string' \
+            % (searchType, expectedString)
           </message>
           <script>
             myRC = 0
@@ -295,8 +313,8 @@
         <else>
           <sequence>
             <message log="1">
-              'Did not find substring, %s, in the return string, %s' \
-              % (expectedString, returnString)
+              'Search type: %s  Did not find substring, %s, in the return \
+              string, %s' % (searchType, expectedString, returnString)
             </message>
             <script>
               myRC = 1
@@ -2938,5 +2956,635 @@
 
     </sequence>
   </function>
+  
+  
+  <!-- This function parses an ldif entry -->
+  <function name="parseLdifEntry">
+    <function-prolog>
+      This function parses an ldif entry and returns a dictionary, e.g.:
+      {'dn':['o=example'],'objectclass':['top','organization'],'o':['example']} 
+    </function-prolog>
 
+    <function-map-args>
+      <function-arg-def name="ldifEntry" type="required">
+        <function-arg-description>
+          Ldif entry to parse (single string).
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+    </function-map-args>
+    <sequence>
+      <script>
+        parsedEntry = {}
+        prevAttr    = None
+        prevVal     = None
+        
+        for line in ldifEntry.splitlines():
+          notBlank = (len(line.strip()) != 0)
+          if notBlank and (not line.startswith(' ')):
+            # line corresponds to an attr:val
+            attr = line[:line.find(':')].strip().lower()
+            val  = line[line.find(':') + 1:].lstrip()
+            if val.startswith(':'):
+              val = val[1:].lstrip()
+            if attr == 'objectclass':
+              val = val.lower()
+        
+            if not (attr in parsedEntry.keys()):
+              # This is the first occurrence of this attr
+              parsedEntry[attr] = [val]
+            else:
+              # There is already some value for this attr
+              parsedEntry[attr].append(val)
+            
+            prevAttr = attr
+            prevVal  = val
+          elif notBlank:
+            # line corresponds to a trailing value
+            parsedEntry[prevAttr].remove(prevVal)
+            val = prevVal + line.lstrip()
+            parsedEntry[prevAttr].append(val)
+            prevVal = val
+      </script>
+
+      <return> parsedEntry </return>      
+    </sequence>
+  </function>
+
+  
+
+  <!-- This function parses an ldif change -->
+  <function name="parseLdifChange">
+    <function-prolog>
+      This function parses an ldif change and returns a list, e.g.:
+      [ ['replace','l','London'], ['add','description','This is a test'] ] 
+    </function-prolog>
+
+    <function-map-args>
+      <function-arg-def name="ldifChange" type="required">
+        <function-arg-description>
+          Ldif change to parse (single string).
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+    </function-map-args>
+    <sequence>
+      <script>
+        parsedChange = []
+        mod          = []
+        prevAttr     = None
+        prevVal      = None
+        
+        for line in ldifChange.splitlines():
+          notBlank = (len(line.strip()) != 0)
+          if notBlank and (not line.startswith(' ')) and (not line.startswith('-')):
+            # line corresponds to an attr:val
+            attr = line[:line.find(':')].strip().lower()
+            val  = line[line.find(':') + 1:].lstrip()
+
+            if val.startswith(':'):
+              val = val[1:].lstrip()
+                             
+            if attr == 'objectclass':
+              val = val.lower()
+        
+            if (prevVal != None) and (attr == prevVal.lower()):
+              # attr represents indeed an attribute type, so we may assume the
+              # mod already has [mod_type,attr_type]
+              mod.append(val)
+            else:
+              # attr represents the mod_type, and val the attr_type
+              mod.append(attr)
+              mod.append(val.lower())
+            
+            prevAttr = attr
+            prevVal  = val
+          elif notBlank and line.startswith(' '):
+            # line corresponds to a trailing value
+            mod.remove(prevVal)
+            val = prevVal + line.lstrip()
+            mod.append(val)
+            prevVal = val
+          else:
+            # line is empty or line starts with '-'; this means that
+            # the mod is complete, so we can add it to the parsedChange list
+            parsedChange.append(mod)
+            mod = []
+        
+        if len(mod) != 0:
+          # add the trailing mod to the parsedChange list
+          parsedChange.append(mod)
+      </script>
+
+      <return> parsedChange </return>      
+    </sequence>
+  </function>
+  
+  <!-- This function checks the content of an external changelog entry -->
+  <function name="checkChangelogEntry">
+    <function-prolog>
+      This function checks the content of an external changelog entry
+    </function-prolog>
+
+    <function-map-args>
+      <function-arg-def name="location" 
+                        type="optional" 
+                        default="STAF_REMOTE_HOSTNAME">
+        <function-arg-description>
+          Location of target host
+        </function-arg-description>
+        <function-arg-property name="type" value="hostname"/>
+      </function-arg-def>
+      <function-arg-def name="dsPath" 
+                        type="optional" 
+                        default="'%s/%s' % (DIRECTORY_INSTANCE_BIN,OPENDSNAME)">
+        <function-arg-description>
+          Pathname to installation root
+        </function-arg-description>
+        <function-arg-property name="type" value="filepath"/>
+      </function-arg-def>
+      <function-arg-def name="changelogEntry" type="required">
+        <function-arg-description>
+          External changelog entry (as an output of parseLdifEntry)
+        </function-arg-description>
+        <function-arg-property name="type" value="dictionary"/>
+      </function-arg-def>
+      <function-arg-def name="targetDN" type="required">
+        <function-arg-description>
+          DN of the target entry for the change
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="changeType" type="required">
+        <function-arg-description>
+          Change type (e.g. add, delete, modify)
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="changeTime" 
+                        type="optional"
+                        default="None">
+        <function-arg-description>
+          Change time
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="changeNumber" 
+                        type="optional"
+                        default="None">
+        <function-arg-description>
+          Changenumber (only for changelog draft-compatible mode)
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="replicationCSN" 
+                        type="optional"
+                        default="None">
+        <function-arg-description>
+          Replication CSN
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="targetEntryUUID" 
+                        type="optional"
+                        default="None">
+        <function-arg-description>
+          Target entry uuid
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="replicaIdentifier" 
+                        type="optional"
+                        default="None">
+        <function-arg-description>
+          Replica Identifier
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="newRDN" 
+                        type="optional"
+                        default="None">
+        <function-arg-description>
+          NewRDN
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="deleteOldRDN" 
+                        type="optional"
+                        default="None">
+        <function-arg-description>
+          DeleteOldRDN
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="newSuperior" 
+                        type="optional"
+                        default="None">
+        <function-arg-description>
+          NewSuperior
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="changes" 
+                        type="optional"
+                        default="None">
+        <function-arg-description>
+          Changes. If changetype == add, changes should be a map, e.g.:
+          {'dn':['o=example'],'objectclass':['top','organization'],'o':['example']} 
+          If changetype == modify, changes should be a list, e.g.:
+          [ ['replace','l','London'], ['add','description','This is a test'] ]
+        </function-arg-description>
+        <function-arg-property name="type" value="string"/>
+      </function-arg-def>
+      <function-arg-def name="knownIssue" type="optional" default="None">
+        <function-arg-description>
+          Known issue. Corresponds to an issue number.
+        </function-arg-description>
+        <function-arg-property name="type" value="string" />
+      </function-arg-def>
+    </function-map-args>
+    
+    <sequence>
+      <script>
+        myLocation            = location
+        myPath                = dsPath
+        
+        # Mandatory attributes in a changeLogEntry
+        ecl_DN                = changelogEntry['dn'][0]
+        ecl_targetDN          = changelogEntry['targetdn'][0]
+        ecl_changeType        = changelogEntry['changetype'][0]
+        ecl_changeTime        = changelogEntry['changetime'][0]
+        ecl_changeNumber      = changelogEntry['changenumber'][0]
+        
+        # Optional attributes
+        ecl_replicationCSN    = None
+        ecl_replicaIdentifier = None
+        ecl_targetEntryUUID   = None
+        ecl_newRDN            = None
+        ecl_deleteOldRDN      = None
+        ecl_newSuperior       = None
+        ecl_changes           = None         
+                
+        if 'replicationcsn' in changelogEntry.keys():
+          ecl_replicationCSN = changelogEntry['replicationcsn'][0]
+        if 'replicaidentifier' in changelogEntry.keys():
+          ecl_replicaIdentifier = changelogEntry['replicaidentifier'][0]
+        if 'targetentryuuid' in changelogEntry.keys():
+          ecl_targetEntryUUID = changelogEntry['targetentryuuid'][0]
+        if 'newrdn' in changelogEntry.keys():
+          ecl_newRDN = changelogEntry['newrdn'][0]
+        if 'deleteoldrdn' in changelogEntry.keys():
+          ecl_deleteOldRDN = changelogEntry['deleteoldrdn'][0]
+        if 'newsuperior' in changelogEntry.keys():
+          ecl_newSuperior = changelogEntry['newsuperior'][0]
+        if 'changes' in changelogEntry.keys():
+          ecl_changes = changelogEntry['changes'][0]
+      </script>
+      
+      <message>
+        'checkChangelogEntry: Checking changelog entry %s against expected \
+        values' % ecl_DN
+      </message>
+
+      <message>
+        'checkChangelogEntry: Checking targetDN'
+      </message>
+      <call function="'searchString'">
+        { 'returnString'   : ecl_targetDN,
+          'expectedString' : targetDN,
+          'searchType'     : 'exact-case-insensitive'
+        }
+      </call>
+
+      <message>
+        'checkChangelogEntry: Checking changeType'
+      </message>
+      <call function="'searchString'">
+        { 'returnString'   : ecl_changeType,
+          'expectedString' : changeType,
+          'searchType'     : 'exact-case-insensitive'
+        }
+      </call>
+
+      <if expr="changeTime">
+        <sequence>
+          <message>
+            'checkChangelogEntry: Checking changeTime'
+          </message>
+          <call function="'searchString'">
+            { 'returnString'   : ecl_changeTime,
+              'expectedString' : changeTime,
+              'searchType'     : 'exact-case-insensitive'
+            }
+          </call>
+        </sequence>
+      </if>
+
+      <if expr="changeNumber">
+        <sequence>
+          <message>
+            'checkChangelogEntry: Checking changeNumber'
+          </message>
+          <call function="'searchString'">
+            { 'returnString'   : ecl_changeNumber,
+              'expectedString' : changeNumber,
+              'searchType'     : 'exact-case-sensitive'
+            }
+          </call>
+        </sequence>
+      </if>
+      
+      <if expr="replicationCSN">
+        <if expr="ecl_replicationCSN">
+          <sequence>
+            <message>
+              'checkChangelogEntry: Checking replicationCSN'
+            </message>
+            <call function="'searchString'">
+              { 'returnString'   : ecl_replicationCSN,
+                'expectedString' : replicationCSN,
+                'searchType'     : 'exact-case-insensitive'
+              }
+            </call>
+          </sequence>
+          <else>
+            <sequence>
+              <message log="1" level="'Error'">
+                'No replicationCSN could be found in the changelog entry' 
+              </message>
+              <call function="'testFailed'"/>
+            </sequence>
+          </else>
+        </if>
+      </if>
+
+      <if expr="replicaIdentifier">
+        <if expr="ecl_replicaIdentifier">
+          <sequence>
+            <message>
+              'checkChangelogEntry: Checking replicaIdentifier'
+            </message>
+            <call function="'searchString'">
+              { 'returnString'   : ecl_replicaIdentifier,
+                'expectedString' : replicaIdentifier,
+                'searchType'     : 'exact-case-sensitive'
+              }
+            </call>
+          </sequence>
+          <else>
+            <sequence>
+              <message log="1" level="'Error'">
+                'No replicaIdentifier could be found in the changelog entry' 
+              </message>
+              <call function="'testFailed'"/>
+            </sequence>
+          </else>
+        </if>
+      </if>
+      
+      <if expr="targetEntryUUID">
+        <if expr="ecl_targetEntryUUID">
+          <sequence>
+            <message>
+              'checkChangelogEntry: Checking targetEntryUUID'
+            </message>
+            <call function="'searchString'">
+              { 'returnString'   : ecl_targetEntryUUID,
+                'expectedString' : targetEntryUUID,
+                'searchType'     : 'exact-case-insensitive'
+              }
+            </call>
+          </sequence>
+          <else>
+            <sequence>
+              <message log="1" level="'Error'">
+                'No targetEntryUUID could be found in the changelog entry' 
+              </message>
+              <call function="'testFailed'"/>
+            </sequence>
+          </else>
+        </if>
+      </if>
+      
+      <if expr="newRDN">
+        <if expr="ecl_newRDN">
+          <sequence>
+            <message>
+              'checkChangelogEntry: Checking newRDN'
+            </message>
+            <call function="'searchString'">
+              { 'returnString'   : ecl_newRDN,
+                'expectedString' : newRDN,
+                'searchType'     : 'exact-case-insensitive'
+              }
+            </call>
+          </sequence>
+          <else>
+            <sequence>
+              <message log="1" level="'Error'">
+                'No newRDN could be found in the changelog entry' 
+              </message>
+              <call function="'testFailed'"/>
+            </sequence>
+          </else>
+        </if>
+      </if>
+
+      <if expr="deleteOldRDN">
+        <if expr="ecl_deleteOldRDN">
+          <sequence>
+            <message>
+              'checkChangelogEntry: Checking deleteOldRDN'
+            </message>
+            <call function="'searchString'">
+              { 'returnString'   : ecl_deleteOldRDN,
+                'expectedString' : deleteOldRDN,
+                'searchType'     : 'exact-case-sensitive'
+              }
+            </call>
+          </sequence>
+          <else>
+            <sequence>
+              <message log="1" level="'Error'">
+                'No deleteOldRDN could be found in the changelog entry' 
+              </message>
+              <call function="'testFailed'"/>
+            </sequence>
+          </else>
+        </if>
+      </if>
+      
+      <if expr="newSuperior">
+        <if expr="ecl_newSuperior">
+          <sequence>
+            <message>
+              'checkChangelogEntry: Checking newSuperior'
+            </message>
+            <call function="'searchString'">
+              { 'returnString'   : ecl_newSuperior,
+                'expectedString' : newSuperior,
+                'searchType'     : 'exact-case-insensitive'
+              }
+            </call>
+          </sequence>
+          <else>
+            <sequence>
+              <message log="1" level="'Error'">
+                'No newSuperior could be found in the changelog entry' 
+              </message>
+              <call function="'testFailed'"/>
+            </sequence>
+          </else>
+        </if>
+      </if>
+
+      <if expr="changes">
+        <if expr="ecl_changes">
+          <sequence>
+            <!-- Decode the changes that are encoded in base64 -->
+            <message>
+              'checkChangelogEntry: Decode external changelog entry changes'
+            </message>
+            <call function="'Base64WithScript'">
+              { 'location'    : myLocation,
+                'dsPath'      : myPath,
+                'subcommand'  : 'decode',
+                'encodedData' : ecl_changes
+              }
+            </call>
+            <!-- STAXResult is not always a list-->
+            <script>
+              try:
+                decodeRC, decodedChanges = STAXResult[0]
+              except AttributeError, details:
+                decodedChanges = 'AttributeError: can not parse STAXResult %s' \
+                                 % details
+                decodeRC = '1'                
+            </script>
+            <message>
+              'checkChangelogEntry: Decoded changes:\n%s' % decodedChanges
+            </message>
+            
+            <message>
+              'checkChangelogEntry: Checking changes'
+            </message>
+            <if expr="decodeRC == 0 and changeType == 'add'">
+              <!-- If changetype:add, the changes look like a sequence of
+               !   attribute:value, so we may parse them as an ldif entry -->
+              <sequence>
+                <call function="'parseLdifEntry'">
+                  { 'ldifEntry' : decodedChanges }
+                </call>
+                <script>
+                  ecl_changesMap = STAXResult
+                </script>
+                <message>
+                  'Parsed changelog entry changes:  \n%s' % ecl_changesMap
+                </message>
+                <iterate var="attr" in="changes.keys()">
+                  <sequence>
+                    <script>
+                      valueList     = changes[attr]
+                      ecl_valueList = None
+                      
+                      if attr in ecl_changesMap.keys():
+                        ecl_valueList = ecl_changesMap[attr]
+                        ecl_valueList.sort()
+                        valueList.sort()
+                    </script>
+                    <if expr="ecl_valueList != None">
+                      <sequence>
+                        <message>
+                          'checkChangelogEntry: Checking changes: %s' % attr
+                        </message>
+                        <if expr="valueList == ecl_valueList">
+                          <message>
+                            'Found expected values in changes: %s' % valueList
+                          </message>
+                          <else>
+                            <sequence>
+                              <message log="1" level="'Error'">
+                                'Expected values %s could not be found in %s' \
+                                 % (valueList, ecl_valueList) 
+                              </message>
+                              <call function="'testFailed'"/>
+                            </sequence>
+                          </else>
+                        </if>
+                      </sequence>
+                      <else>
+                        <sequence>
+                          <message log="1" level="'Error'">
+                            'No %s could be found in the changes' % attr 
+                          </message>
+                          <call function="'testFailed'"/>
+                        </sequence>
+                      </else>
+                    </if>                    
+                  </sequence>
+                </iterate>
+              </sequence>
+              
+              <elseif expr="decodeRC == 0">
+                <!-- If changetype:modify, the changes look like a sequence of
+                 !     mod_type:attribute
+                 !     attribute:value
+                 !   so we need to treat them differently -->
+                <sequence>
+                  <call function="'parseLdifChange'">
+                    { 'ldifChange' : decodedChanges }
+                  </call>
+                  <script>
+                    ecl_changesList = STAXResult
+                  </script>
+                  <message>
+                    'Parsed changelog entry changes:  \n%s' % ecl_changesList
+                  </message>
+                  <iterate var="mod" in="changes">
+                    <sequence>
+                      <script>
+                        mod_type = mod[0]
+                        mod_attr = mod[1]
+                        mod_val  = None
+                        if len(mod) == 3:
+                          mod_val = mod[2]
+                      </script>
+                      <message>
+                        'checkChangelogEntry: Checking changes: %s' % mod
+                      </message>
+                      <if expr="mod in ecl_changesList">
+                        <message>
+                          'Found expected change:\n %s: %s\n %s: %s\n' \
+                           % (mod_type, mod_attr, mod_attr, mod_val)
+                        </message>
+                        <else>
+                          <sequence>
+                            <message log="1" level="'Error'">
+                              'Expected change %s could not be found in %s'\
+                               % (mod, ecl_changesList) 
+                            </message>
+                            <call function="'testFailed'"/>
+                          </sequence>
+                        </else>
+                      </if>
+                    </sequence>
+                  </iterate>
+                </sequence>
+              </elseif>
+            </if>
+            
+          </sequence>
+          <else>
+            <sequence>
+              <message log="1" level="'Error'">
+                'No changes could be found in the changelog entry' 
+              </message>
+              <call function="'testFailed'"/>
+            </sequence>
+          </else>
+        </if>
+      </if>
+      
+    </sequence>
+  </function>
 </stax>
diff --git a/opendj-sdk/opends/tests/staf-tests/shared/java/ldapjndi/modifyDn.java b/opendj-sdk/opends/tests/staf-tests/shared/java/ldapjndi/modifyDn.java
new file mode 100644
index 0000000..3078615
--- /dev/null
+++ b/opendj-sdk/opends/tests/staf-tests/shared/java/ldapjndi/modifyDn.java
@@ -0,0 +1,169 @@
+/*
+ * 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
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ */
+
+import java.util.Hashtable;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.CommunicationException;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ *  Modify the DN of an entry.
+ *  The operation can be a modDN (tree move) or a modRDN.
+ *  The function returns the ldap error code
+ */
+public class modifyDn {
+
+  /**
+   * Main.
+   *
+   * @param args arguments
+   */
+  public static void main(String[] args) {
+
+    String hostname=null;
+    String ldapPort=null;
+    String principal=null;
+    String credential=null;
+    String dnToModify=null;
+    String newRDN=null;
+    String deleteOldRDN=null;
+    String newSuperior=null;
+    String errorCode=null;
+    String errorMessage=null;
+
+    Hashtable envLdap  = new Hashtable();
+    LdapContext ctx;
+    LdapName nameToModify;
+    LdapName targetName;
+    String targetDn;
+
+
+    for (int k=0; k< args.length; k++) {
+      String opt1 = args[k];
+      String val1 = args[k+1];
+
+      if (opt1.equals("-h")) {
+        hostname = val1;
+      }
+      if (opt1.equals("-p")) {
+        ldapPort = val1;
+      }
+      if (opt1.equals("-D")) {
+        principal = val1;
+      }
+      if (opt1.equals("-w")) {
+        credential = val1;
+      }
+      if (opt1.equals("-d")) {
+        dnToModify = val1;
+      }
+      if (opt1.equals("-e")) {
+        newRDN = val1;
+      }
+      if (opt1.equals("-f")) {
+        deleteOldRDN = val1;
+      }
+      if (opt1.equals("-g")) {
+        newSuperior = val1;
+      }
+      k++;
+    }
+
+
+    String provider = "ldap://"  + hostname + ":" + ldapPort  + "/";
+
+    envLdap.put("java.naming.factory.initial",
+                "com.sun.jndi.ldap.LdapCtxFactory");
+    envLdap.put(Context.SECURITY_AUTHENTICATION, "simple");
+    envLdap.put(Context.SECURITY_PRINCIPAL,  principal);
+    envLdap.put(Context.SECURITY_CREDENTIALS, credential);
+    envLdap.put(Context.PROVIDER_URL, provider);
+
+    // Whether the old RDN attribute values are to be retained 
+    // as attributes of the entry, or deleted from the entry.
+    // Default is 'true'
+    if (deleteOldRDN != null) {
+      envLdap.put("java.naming.ldap.deleteRDN", deleteOldRDN);
+    }
+
+    try {
+      System.out.println("Modify the entry " + dnToModify);
+      
+      if ( newRDN == null ) {
+        // newRDN = same as old rdn
+        nameToModify = new LdapName(dnToModify);
+        newRDN = (String) nameToModify.remove(nameToModify.size() - 1);
+      }
+      
+      if ( newSuperior != null ) {
+        // modDN operation => new parent = newSuperior	
+        targetName = new LdapName(newSuperior);
+      } else { 
+        // modRDN operation => new parent = old parent
+        targetName = new LdapName(dnToModify);
+        targetName.remove(targetName.size() - 1);
+      }
+      targetName.add(newRDN);
+      targetDn = targetName.toString();
+
+
+      // connect to server
+      ctx = new InitialLdapContext(envLdap, null);
+
+      // rename the entry
+      ctx.rename(dnToModify, targetDn);
+
+      ctx.close();
+    } catch (CommunicationException e1) {
+      errorMessage = e1.getMessage();
+    } catch (NamingException e2) {
+      errorMessage = e2.getMessage();
+    } catch (Exception e3) {
+      errorMessage= e3.getMessage();
+    }
+
+    // No error, the modify is success
+    if ( errorMessage == null ) {
+      errorCode="0";
+    } else {
+      System.out.println(errorMessage);
+      int ind=errorMessage.indexOf("-");
+      if ( ind > 0 ) {
+        errorCode=errorMessage.substring(18, ind-1);
+      } else errorCode="0";
+    }
+
+    int RC = Integer.parseInt(errorCode);
+    System.exit(RC);
+  }
+
+}

--
Gitblit v1.10.0