|
|
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 |
|