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