zrhoffman / rpms / 389-ds-base

Forked from rpms/389-ds-base 3 years ago
Clone

Blame SOURCES/0002-Ticket-50232-export-creates-not-importable-ldif-file.patch

26521d
From 70d1336481bd1a36d8b0bdef43a9364c7db58c26 Mon Sep 17 00:00:00 2001
26521d
From: Ludwig Krispenz <lkrispen@redhat.com>
26521d
Date: Wed, 20 Feb 2019 10:11:15 +0100
26521d
Subject: [PATCH] Ticket 50232 - export creates not importable ldif file
26521d
26521d
Bug: If the RUV entry hasa  smaller entryid than the suffix entry it will be
26521d
	exported before the suffix. If that ldif is used for import the RUV entry
26521d
	is skipped and a new one generated with a different database generation
26521d
26521d
Fix: Before exporting the RUV check that the suffix is alread exported, if not
26521d
	make the RUV entry pending and write it after all othere entries
26521d
26521d
Reviewed by: tbordaz, wbrown. Thanks
26521d
---
26521d
 dirsrvtests/tests/tickets/ticket50232_test.py | 163 ++++++++++++++++++
26521d
 ldap/servers/slapd/back-ldbm/ldif2ldbm.c      |  58 ++++++-
26521d
 2 files changed, 219 insertions(+), 2 deletions(-)
26521d
 create mode 100644 dirsrvtests/tests/tickets/ticket50232_test.py
26521d
26521d
diff --git a/dirsrvtests/tests/tickets/ticket50232_test.py b/dirsrvtests/tests/tickets/ticket50232_test.py
26521d
new file mode 100644
26521d
index 000000000..133ed0dfe
26521d
--- /dev/null
26521d
+++ b/dirsrvtests/tests/tickets/ticket50232_test.py
26521d
@@ -0,0 +1,163 @@
26521d
+# --- BEGIN COPYRIGHT BLOCK ---
26521d
+# Copyright (C) 2016 Red Hat, Inc.
26521d
+# All rights reserved.
26521d
+#
26521d
+# License: GPL (version 3 or any later version).
26521d
+# See LICENSE for details.
26521d
+# --- END COPYRIGHT BLOCK ---
26521d
+#
26521d
+import logging
26521d
+
26521d
+import pytest
26521d
+# from lib389.tasks import *
26521d
+# from lib389.utils import *
26521d
+from lib389.topologies import topology_st
26521d
+from lib389.replica import ReplicationManager,Replicas
26521d
+
26521d
+from lib389._constants import DEFAULT_SUFFIX, BACKEND_NAME
26521d
+
26521d
+from lib389.idm.user import UserAccounts
26521d
+from lib389.idm.organization import Organization
26521d
+from lib389.idm.organizationalunit import OrganizationalUnit
26521d
+
26521d
+log = logging.getLogger(__name__)
26521d
+
26521d
+NORMAL_SUFFIX = 'o=normal'
26521d
+NORMAL_BACKEND_NAME = 'normal'
26521d
+REVERSE_SUFFIX = 'o=reverse'
26521d
+REVERSE_BACKEND_NAME = 'reverse'
26521d
+
26521d
+def _enable_replica(instance, suffix):
26521d
+
26521d
+    repl = ReplicationManager(DEFAULT_SUFFIX)
26521d
+    repl._ensure_changelog(instance)
26521d
+    replicas = Replicas(instance)
26521d
+    replicas.create(properties={
26521d
+        'cn': 'replica',
26521d
+        'nsDS5ReplicaRoot': suffix,
26521d
+        'nsDS5ReplicaId': '1',
26521d
+        'nsDS5Flags': '1',
26521d
+        'nsDS5ReplicaType': '3'
26521d
+        })
26521d
+
26521d
+def _populate_suffix(instance, suffixname):
26521d
+
26521d
+    o = Organization(instance, 'o={}'.format(suffixname))
26521d
+    o.create(properties={
26521d
+        'o': suffixname,
26521d
+        'description': 'test'
26521d
+    })
26521d
+    ou = OrganizationalUnit(instance, 'ou=people,o={}'.format(suffixname))
26521d
+    ou.create(properties={
26521d
+        'ou': 'people'
26521d
+    })
26521d
+
26521d
+def _get_replica_generation(instance, suffix):
26521d
+
26521d
+    replicas = Replicas(instance)
26521d
+    replica = replicas.get(suffix)
26521d
+    ruv = replica.get_ruv()
26521d
+    return ruv._data_generation
26521d
+
26521d
+def _test_export_import(instance, suffix, backend):
26521d
+
26521d
+    before_generation = _get_replica_generation(instance, suffix)
26521d
+
26521d
+    instance.stop()
26521d
+    instance.db2ldif(
26521d
+        bename=backend,
26521d
+        suffixes=[suffix],
26521d
+        excludeSuffixes=[],
26521d
+        encrypt=False,
26521d
+        repl_data=True,
26521d
+        outputfile="/tmp/output_file",
26521d
+    )
26521d
+    instance.ldif2db(
26521d
+        bename=None,
26521d
+        excludeSuffixes=None,
26521d
+        encrypt=False,
26521d
+        suffixes=[suffix],
26521d
+        import_file="/tmp/output_file",
26521d
+    )
26521d
+    instance.start()
26521d
+    after_generation = _get_replica_generation(instance, suffix)
26521d
+
26521d
+    assert (before_generation == after_generation)
26521d
+
26521d
+def test_ticket50232_normal(topology_st):
26521d
+    """
26521d
+    The fix for ticket 50232
26521d
+
26521d
+
26521d
+    The test sequence is:
26521d
+    - create suffix
26521d
+    - add suffix entry and some child entries
26521d
+    - "normally" done after populating suffix: enable replication
26521d
+    - get RUV and database generation
26521d
+    - export -r
26521d
+    - import
26521d
+    - get RUV and database generation
26521d
+    - assert database generation has not changed
26521d
+    """
26521d
+
26521d
+    log.info('Testing Ticket 50232 - export creates not imprtable ldif file, normal creation order')
26521d
+
26521d
+    topology_st.standalone.backend.create(NORMAL_SUFFIX, {BACKEND_NAME: NORMAL_BACKEND_NAME})
26521d
+    topology_st.standalone.mappingtree.create(NORMAL_SUFFIX, bename=NORMAL_BACKEND_NAME, parent=None)
26521d
+
26521d
+    _populate_suffix(topology_st.standalone, NORMAL_BACKEND_NAME)
26521d
+
26521d
+    repl = ReplicationManager(DEFAULT_SUFFIX)
26521d
+    repl._ensure_changelog(topology_st.standalone)
26521d
+    replicas = Replicas(topology_st.standalone)
26521d
+    replicas.create(properties={
26521d
+        'cn': 'replica',
26521d
+        'nsDS5ReplicaRoot': NORMAL_SUFFIX,
26521d
+        'nsDS5ReplicaId': '1',
26521d
+        'nsDS5Flags': '1',
26521d
+        'nsDS5ReplicaType': '3'
26521d
+        })
26521d
+
26521d
+    _test_export_import(topology_st.standalone, NORMAL_SUFFIX, NORMAL_BACKEND_NAME)
26521d
+
26521d
+def test_ticket50232_reverse(topology_st):
26521d
+    """
26521d
+    The fix for ticket 50232
26521d
+
26521d
+
26521d
+    The test sequence is:
26521d
+    - create suffix
26521d
+    - enable replication before suffix enztry is added
26521d
+    - add suffix entry and some child entries
26521d
+    - get RUV and database generation
26521d
+    - export -r
26521d
+    - import
26521d
+    - get RUV and database generation
26521d
+    - assert database generation has not changed
26521d
+    """
26521d
+
26521d
+    log.info('Testing Ticket 50232 - export creates not imprtable ldif file, normal creation order')
26521d
+
26521d
+    #
26521d
+    # Setup Replication
26521d
+    #
26521d
+    log.info('Setting up replication...')
26521d
+    repl = ReplicationManager(DEFAULT_SUFFIX)
26521d
+    # repl.create_first_master(topology_st.standalone)
26521d
+    #
26521d
+    # enable dynamic plugins, memberof and retro cl plugin
26521d
+    #
26521d
+    topology_st.standalone.backend.create(REVERSE_SUFFIX, {BACKEND_NAME: REVERSE_BACKEND_NAME})
26521d
+    topology_st.standalone.mappingtree.create(REVERSE_SUFFIX, bename=REVERSE_BACKEND_NAME, parent=None)
26521d
+
26521d
+    _enable_replica(topology_st.standalone, REVERSE_SUFFIX)
26521d
+
26521d
+    _populate_suffix(topology_st.standalone, REVERSE_BACKEND_NAME)
26521d
+
26521d
+    _test_export_import(topology_st.standalone, REVERSE_SUFFIX, REVERSE_BACKEND_NAME)
26521d
+
26521d
+if __name__ == '__main__':
26521d
+    # Run isolated
26521d
+    # -s for DEBUG mode
26521d
+    CURRENT_FILE = os.path.realpath(__file__)
26521d
+    pytest.main("-s %s" % CURRENT_FILE)
26521d
diff --git a/ldap/servers/slapd/back-ldbm/ldif2ldbm.c b/ldap/servers/slapd/back-ldbm/ldif2ldbm.c
26521d
index 11c020af0..49fe7cd5d 100644
26521d
--- a/ldap/servers/slapd/back-ldbm/ldif2ldbm.c
26521d
+++ b/ldap/servers/slapd/back-ldbm/ldif2ldbm.c
26521d
@@ -1103,6 +1103,7 @@ bail:
26521d
  * (reunified at last)
26521d
  */
26521d
 #define LDBM2LDIF_BUSY (-2)
26521d
+#define RUVRDN SLAPI_ATTR_UNIQUEID "=" RUV_STORAGE_ENTRY_UNIQUEID
26521d
 int
26521d
 ldbm_back_ldbm2ldif(Slapi_PBlock *pb)
26521d
 {
26521d
@@ -1111,6 +1112,7 @@ ldbm_back_ldbm2ldif(Slapi_PBlock *pb)
26521d
     DB *db = NULL;
26521d
     DBC *dbc = NULL;
26521d
     struct backentry *ep;
26521d
+    struct backentry *pending_ruv = NULL;
26521d
     DBT key = {0};
26521d
     DBT data = {0};
26521d
     char *fname = NULL;
26521d
@@ -1146,6 +1148,8 @@ ldbm_back_ldbm2ldif(Slapi_PBlock *pb)
26521d
     static int load_dse = 1; /* We'd like to load dse just once. */
26521d
     int server_running;
26521d
     export_args eargs = {0};
26521d
+    int32_t suffix_written = 0;
26521d
+    int32_t skip_ruv = 0;
26521d
 
26521d
     slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_ldbm2ldif", "=>\n");
26521d
 
26521d
@@ -1463,8 +1467,25 @@ ldbm_back_ldbm2ldif(Slapi_PBlock *pb)
26521d
                 }
26521d
             }
26521d
 
26521d
-            if (0 != return_value)
26521d
+            if (DB_NOTFOUND == return_value) {
26521d
+                /* reached the end of the database,
26521d
+                 * check if ruv is pending and write it
26521d
+                 */
26521d
+                if (pending_ruv) {
26521d
+                    eargs.ep = pending_ruv;
26521d
+                    eargs.idindex = idindex;
26521d
+                    eargs.cnt = &cn;;
26521d
+                    eargs.lastcnt = &lastcnt;
26521d
+                    rc = export_one_entry(li, inst, &eargs);
26521d
+                    backentry_free(&pending_ruv);
26521d
+                }
26521d
+                break;
26521d
+            }
26521d
+
26521d
+            if (0 != return_value) {
26521d
+                /* error reading database */
26521d
                 break;
26521d
+            }
26521d
 
26521d
             /* back to internal format */
26521d
             temp_id = id_stored_to_internal((char *)key.data);
26521d
@@ -1501,7 +1522,30 @@ ldbm_back_ldbm2ldif(Slapi_PBlock *pb)
26521d
                 rc = get_value_from_string((const char *)data.dptr,
26521d
                                            LDBM_PARENTID_STR, &pid_str);
26521d
                 if (rc) {
26521d
-                    rc = 0; /* assume this is a suffix */
26521d
+                    /* this could be a suffix or the RUV entry.
26521d
+                     * If it is the ruv and the suffix is not written
26521d
+                     * keep the ruv and export as last entry.
26521d
+                     *
26521d
+                     * The reason for this is that if the RUV entry is in the
26521d
+                     * ldif before the suffix entry then at an attempt to import
26521d
+                     * that ldif the RUV entry would be skipped because the parent
26521d
+                     * does not exist. Later a new RUV would be generated with
26521d
+                     * a different database generation and replication is broken
26521d
+                     */
26521d
+                    if (suffix_written) {
26521d
+                        /* this must be the RUV, just continue and write it */
26521d
+                        rc = 0;
26521d
+                    } else if (0 == strcasecmp(rdn, RUVRDN)) {
26521d
+                        /* this is the RUV and the suffix is not yet written
26521d
+                         * make it pending and continue with next entry
26521d
+                         */
26521d
+                        skip_ruv = 1;
26521d
+                        rc = 0;
26521d
+                    } else {
26521d
+                        /* this has to be the suffix */
26521d
+                        suffix_written = 1;
26521d
+                        rc = 0;
26521d
+                    }
26521d
                 } else {
26521d
                     pid = (ID)strtol(pid_str, (char **)NULL, 10);
26521d
                     slapi_ch_free_string(&pid_str);
26521d
@@ -1614,6 +1658,16 @@ ldbm_back_ldbm2ldif(Slapi_PBlock *pb)
26521d
             continue;
26521d
         }
26521d
 
26521d
+        if (skip_ruv) {
26521d
+            /* now we keep a copy of the ruv entry
26521d
+             * and continue with the next entry
26521d
+             */
26521d
+            pending_ruv = ep;
26521d
+            ep = NULL;
26521d
+            skip_ruv = 0;
26521d
+            continue;
26521d
+        }
26521d
+
26521d
         eargs.ep = ep;
26521d
         eargs.idindex = idindex;
26521d
         eargs.cnt = &cn;;
26521d
-- 
26521d
2.17.2
26521d