This is an automated email from the git hooks/post-receive script.
firstyear pushed a commit to branch master
in repository lib389.
commit 6fb2933fff9c0a9ea1efe032b6c85c37064877b2
Author: Ilias Stamatis <stamatis.iliass(a)gmail.com>
Date: Mon Jul 17 04:12:55 2017 +0300
Issue 48 - Add support for USN plugin
Description: Add dsconf support for configuring the USN plugin from the
command line, the cleanup task for removing tombstone entries, and functional
tests for testing its functionality.
https://pagure.io/lib389/issue/48
Author: Ilias95
Review by: wibrown (Great work, thanks!)
---
cli/dsconf | 2 +
lib389/cli_conf/plugin.py | 14 ++
lib389/cli_conf/plugins/memberof.py | 20 +--
lib389/cli_conf/plugins/usn.py | 56 +++++++
lib389/plugins.py | 41 +++++
lib389/tasks.py | 16 +-
lib389/tests/cli/conf_plugins/usn_test.py | 53 +++++++
lib389/tests/plugins/usn_test.py | 240 ++++++++++++++++++++++++++++++
8 files changed, 423 insertions(+), 19 deletions(-)
diff --git a/cli/dsconf b/cli/dsconf
index 168ecdf..6a5578d 100755
--- a/cli/dsconf
+++ b/cli/dsconf
@@ -23,6 +23,7 @@ from lib389.cli_conf import plugin as cli_plugin
from lib389.cli_conf import schema as cli_schema
from lib389.cli_conf import health as cli_health
from lib389.cli_conf.plugins import memberof as cli_memberof
+from lib389.cli_conf.plugins import usn as cli_usn
from lib389.cli_base import disconnect_instance, connect_instance
from lib389.cli_base.dsrc import dsrc_to_ldap, dsrc_arg_concat
@@ -65,6 +66,7 @@ if __name__ == '__main__':
cli_health.create_parser(subparsers)
cli_plugin.create_parser(subparsers)
cli_memberof.create_parser(subparsers)
+ cli_usn.create_parser(subparsers)
args = parser.parse_args()
diff --git a/lib389/cli_conf/plugin.py b/lib389/cli_conf/plugin.py
index 70b75a0..b856786 100644
--- a/lib389/cli_conf/plugin.py
+++ b/lib389/cli_conf/plugin.py
@@ -83,6 +83,20 @@ def generic_status(inst, basedn, log, args):
else:
log.info("%s is disabled", plugin.rdn)
+def add_generic_plugin_parsers(subparser, plugin_cls):
+ show_parser = subparser.add_parser('show', help='display plugin
configuration')
+ show_parser.set_defaults(func=generic_show, plugin_cls=plugin_cls)
+
+ enable_parser = subparser.add_parser('enable', help='enable plugin')
+ enable_parser.set_defaults(func=generic_enable, plugin_cls=plugin_cls)
+
+ disable_parser = subparser.add_parser('disable', help='disable
plugin')
+ disable_parser.set_defaults(func=generic_disable, plugin_cls=plugin_cls)
+
+ status_parser = subparser.add_parser('status', help='display plugin
status')
+ status_parser.set_defaults(func=generic_status, plugin_cls=plugin_cls)
+
+
def create_parser(subparsers):
plugin_parser = subparsers.add_parser('plugin', help="Manage plugins
available on the server")
diff --git a/lib389/cli_conf/plugins/memberof.py b/lib389/cli_conf/plugins/memberof.py
index 74c13ab..74287f9 100644
--- a/lib389/cli_conf/plugins/memberof.py
+++ b/lib389/cli_conf/plugins/memberof.py
@@ -9,8 +9,7 @@
import ldap
from lib389.plugins import MemberOfPlugin
-from lib389.cli_conf.plugin import (
- generic_enable, generic_disable, generic_status, generic_show)
+from lib389.cli_conf.plugin import add_generic_plugin_parsers
def manage_attr(inst, basedn, log, args):
@@ -207,17 +206,7 @@ def create_parser(subparsers):
subcommands = memberof_parser.add_subparsers(help='action')
- show_parser = subcommands.add_parser('show', help='display memberof
plugin configuration')
- show_parser.set_defaults(func=generic_show, plugin_cls=MemberOfPlugin)
-
- enable_parser = subcommands.add_parser('enable', help='enable memberof
plugin')
- enable_parser.set_defaults(func=generic_enable, plugin_cls=MemberOfPlugin)
-
- disable_parser = subcommands.add_parser('disable', help='disable memberof
plugin')
- disable_parser.set_defaults(func=generic_disable, plugin_cls=MemberOfPlugin)
-
- status_parser = subcommands.add_parser('status', help='display memberof
plugin status')
- status_parser.set_defaults(func=generic_status, plugin_cls=MemberOfPlugin)
+ add_generic_plugin_parsers(subcommands, MemberOfPlugin)
attr_parser = subcommands.add_parser('attr', help='get or set
memberofattr')
attr_parser.set_defaults(func=manage_attr)
@@ -225,7 +214,6 @@ def create_parser(subparsers):
groupattr_parser = subcommands.add_parser('groupattr', help='get or
manage memberofgroupattr')
groupattr_parser.set_defaults(func=display_groupattr)
- # argparse doesn't support optional subparsers in python2!
groupattr_subcommands = groupattr_parser.add_subparsers(help='action')
add_groupattr_parser = groupattr_subcommands.add_parser('add', help='add
memberofgroupattr value')
add_groupattr_parser.set_defaults(func=add_groupattr)
@@ -236,7 +224,6 @@ def create_parser(subparsers):
allbackends_parser = subcommands.add_parser('allbackends', help='get or
manage memberofallbackends')
allbackends_parser.set_defaults(func=display_allbackends)
- # argparse doesn't support optional subparsers in python2!
allbackends_subcommands = allbackends_parser.add_subparsers(help='action')
on_allbackends_parser = allbackends_subcommands.add_parser('on',
help='enable all backends for memberof')
on_allbackends_parser.set_defaults(func=enable_allbackends)
@@ -245,7 +232,6 @@ def create_parser(subparsers):
skipnested_parser = subcommands.add_parser('skipnested', help='get or
manage memberofskipnested')
skipnested_parser.set_defaults(func=display_skipnested)
- # argparse doesn't support optional subparsers in python2!
skipnested_subcommands = skipnested_parser.add_subparsers(help='action')
on_skipnested_parser = skipnested_subcommands.add_parser('on', help='skip
nested groups for memberof')
on_skipnested_parser.set_defaults(func=enable_skipnested)
@@ -259,7 +245,6 @@ def create_parser(subparsers):
scope_parser = subcommands.add_parser('scope', help='get or manage
memberofentryscope')
scope_parser.set_defaults(func=display_scope)
- # argparse doesn't support optional subparsers in python2!
scope_subcommands = scope_parser.add_subparsers(help='action')
add_scope_parser = scope_subcommands.add_parser('add', help='add
memberofentryscope value')
add_scope_parser.set_defaults(func=add_scope)
@@ -272,7 +257,6 @@ def create_parser(subparsers):
exclude_parser = subcommands.add_parser('exclude', help='get or manage
memberofentryscopeexcludesubtree')
exclude_parser.set_defaults(func=display_excludescope)
- # argparse doesn't support optional subparsers in python2!
exclude_subcommands = exclude_parser.add_subparsers(help='action')
add_exclude_parser = exclude_subcommands.add_parser('add', help='add
memberofentryscopeexcludesubtree value')
add_exclude_parser.set_defaults(func=add_excludescope)
diff --git a/lib389/cli_conf/plugins/usn.py b/lib389/cli_conf/plugins/usn.py
new file mode 100644
index 0000000..05e34dd
--- /dev/null
+++ b/lib389/cli_conf/plugins/usn.py
@@ -0,0 +1,56 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2016-2017 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
+from lib389.plugins import USNPlugin
+from lib389.cli_conf.plugin import add_generic_plugin_parsers
+
+
+def display_usn_mode(inst, basedn, log, args):
+ plugin = USNPlugin(inst)
+ if plugin.is_global_mode_set():
+ log.info("USN global mode is enabled")
+ else:
+ log.info("USN global mode is disabled")
+
+def enable_global_mode(inst, basedn, log, args):
+ plugin = USNPlugin(inst)
+ plugin.enable_global_mode()
+ log.info("USN global mode enabled")
+
+def disable_global_mode(inst, basedn, log, args):
+ plugin = USNPlugin(inst)
+ plugin.disable_global_mode()
+ log.info("USN global mode disabled")
+
+def tombstone_cleanup(inst, basedn, log, args):
+ plugin = USNPlugin(inst)
+ log.info('Attempting to add task entry... This will fail if replication is
enabled or if USN plug-in is disabled.')
+ task = plugin.cleanup(args.suffix, args.backend, args.maxusn)
+ log.info('Successfully added task entry ' + task.dn)
+
+def create_parser(subparsers):
+ usn_parser = subparsers.add_parser('usn', help='Manage and configure USN
plugin')
+
+ subcommands = usn_parser.add_subparsers(help='action')
+
+ add_generic_plugin_parsers(subcommands, USNPlugin)
+
+ global_mode_parser = subcommands.add_parser('global', help='get or manage
global usn mode')
+ global_mode_parser.set_defaults(func=display_usn_mode)
+ global_mode_subcommands = global_mode_parser.add_subparsers(help='action')
+ on_global_mode_parser = global_mode_subcommands.add_parser('on',
help='enable usn global mode')
+ on_global_mode_parser.set_defaults(func=enable_global_mode)
+ off_global_mode_parser = global_mode_subcommands.add_parser('off',
help='disable usn global mode')
+ off_global_mode_parser.set_defaults(func=disable_global_mode)
+
+ cleanup_parser = subcommands.add_parser('cleanup', help='run the USN
tombstone cleanup task')
+ cleanup_parser.set_defaults(func=tombstone_cleanup)
+ cleanup_group = cleanup_parser.add_mutually_exclusive_group(required=True)
+ cleanup_group.add_argument('-s', '--suffix', help="suffix where
USN tombstone entries are cleaned up")
+ cleanup_group.add_argument('-n', '--backend', help="backend
instance in which USN tombstone entries are cleaned up (alternative to suffix)")
+ cleanup_parser.add_argument('-m', '--maxusn', type=int,
help="USN tombstone entries are deleted up to the entry with maxusn")
diff --git a/lib389/plugins.py b/lib389/plugins.py
index e497df2..bccc233 100644
--- a/lib389/plugins.py
+++ b/lib389/plugins.py
@@ -11,6 +11,7 @@ import copy
from lib389 import tasks
from lib389._mapped_object import DSLdapObjects, DSLdapObject
+from lib389.exceptions import Error
from lib389._constants import DN_PLUGIN
from lib389.properties import (
PLUGINS_OBJECTCLASS_VALUE, PLUGIN_PROPNAME_TO_ATTRNAME,
@@ -313,8 +314,48 @@ class PassThroughAuthenticationPlugin(Plugin):
super(PassThroughAuthenticationPlugin, self).__init__(instance, dn, batch)
class USNPlugin(Plugin):
+ _plugin_properties = {
+ 'cn' : 'USN',
+ 'nsslapd-pluginEnabled': 'off',
+ 'nsslapd-pluginPath': 'libusn-plugin',
+ 'nsslapd-pluginInitfunc': 'usn_init',
+ 'nsslapd-pluginType': 'object',
+ 'nsslapd-pluginbetxn': 'on',
+ 'nsslapd-plugin-depends-on-type': 'database',
+ 'nsslapd-pluginId': 'USN',
+ 'nsslapd-pluginVendor': '389 Project',
+ 'nsslapd-pluginVersion': '1.3.7.0',
+ 'nsslapd-pluginDescription': 'USN (Update Sequence Number)
plugin',
+ }
+
def __init__(self, instance, dn="cn=USN,cn=plugins,cn=config",
batch=False):
super(USNPlugin, self).__init__(instance, dn, batch)
+ self._create_objectclasses.extend(['extensibleObject'])
+
+ def is_global_mode_set(self):
+ """Return True if global mode is enabled, else
False."""
+ return self._instance.config.get_attr_val_utf8('nsslapd-entryusn-global')
== 'on'
+
+ def enable_global_mode(self):
+ self._instance.config.set('nsslapd-entryusn-global', 'on')
+
+ def disable_global_mode(self):
+ self._instance.config.set('nsslapd-entryusn-global', 'off')
+
+ def cleanup(self, suffix=None, backend=None, max_usn=None):
+ task = tasks.USNTombstoneCleanupTask(self._instance)
+ task_properties = {}
+
+ if suffix is not None:
+ task_properties['suffix'] = suffix
+ if backend is not None:
+ task_properties['backend'] = backend
+ if max_usn is not None:
+ task_properties['maxusn_to_delete'] = str(max_usn)
+
+ task.create(properties=task_properties)
+
+ return task
class WhoamiPlugin(Plugin):
_plugin_properties = {
diff --git a/lib389/tasks.py b/lib389/tasks.py
index b7e7357..2c62dce 100644
--- a/lib389/tasks.py
+++ b/lib389/tasks.py
@@ -14,10 +14,11 @@ from datetime import datetime
from lib389 import Entry
from lib389._mapped_object import DSLdapObject
+from lib389.exceptions import Error
from lib389._constants import (
DEFAULT_SUFFIX, DEFAULT_BENAME, DN_EXPORT_TASK, DN_BACKUP_TASK,
DN_IMPORT_TASK, DN_RESTORE_TASK, DN_INDEX_TASK, DN_MBO_TASK,
- DN_TOMB_FIXUP_TASK
+ DN_TOMB_FIXUP_TASK, DN_TASKS
)
from lib389.properties import (
TASK_WAIT, EXPORT_REPL_INFO, MT_PROPNAME_TO_ATTRNAME, MT_SUFFIX,
@@ -76,6 +77,19 @@ class MemberOfFixupTask(Task):
self._must_attributes.extend(['basedn'])
+class USNTombstoneCleanupTask(Task):
+ def __init__(self, instance, dn=None, batch=False):
+ self.cn = 'usn_cleanup_' + Task._get_task_date()
+ dn = "cn=" + self.cn + ",cn=USN tombstone cleanup task," +
DN_TASKS
+
+ super(USNTombstoneCleanupTask, self).__init__(instance, dn, batch)
+
+ def _validate(self, rdn, properties, basedn):
+ if not 'suffix' in properties and not 'backend' in properties:
+ raise Error("Either suffix or backend must be specified for cleanup
task.")
+
+ return super(USNTombstoneCleanupTask, self)._validate(rdn, properties, basedn)
+
class Tasks(object):
proxied_methods = 'search_s getEntry'.split()
diff --git a/lib389/tests/cli/conf_plugins/usn_test.py
b/lib389/tests/cli/conf_plugins/usn_test.py
new file mode 100644
index 0000000..fcfc95f
--- /dev/null
+++ b/lib389/tests/cli/conf_plugins/usn_test.py
@@ -0,0 +1,53 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2016-2017 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
+import pytest
+
+from lib389.tests.cli import topology as default_topology
+from lib389.cli_base import LogCapture, FakeArgs
+from lib389.plugins import USNPlugin
+from lib389.cli_conf.plugins import usn as usn_cli
+
+
+(a)pytest.fixture(scope="module")
+def topology(request):
+ topology = default_topology(request)
+
+ plugin = USNPlugin(topology.standalone)
+ if not plugin.exists():
+ plugin.create()
+
+ # we need to restart the server after enabling the plugin
+ plugin.enable()
+ topology.standalone.restart()
+ topology.logcap.flush()
+
+ return topology
+
+
+def test_enable_global_mode(topology):
+ args = FakeArgs()
+
+ usn_cli.enable_global_mode(topology.standalone, None, topology.logcap.log, args)
+ assert topology.logcap.contains("USN global mode enabled")
+ topology.logcap.flush()
+
+ usn_cli.display_usn_mode(topology.standalone, None, topology.logcap.log, args)
+ assert topology.logcap.contains("USN global mode is enabled")
+ topology.logcap.flush()
+
+def test_disable_global_mode(topology):
+ args = FakeArgs()
+
+ usn_cli.disable_global_mode(topology.standalone, None, topology.logcap.log, args)
+ assert topology.logcap.contains("USN global mode disabled")
+ topology.logcap.flush()
+
+ usn_cli.display_usn_mode(topology.standalone, None, topology.logcap.log, args)
+ assert topology.logcap.contains("USN global mode is disabled")
+ topology.logcap.flush()
diff --git a/lib389/tests/plugins/usn_test.py b/lib389/tests/plugins/usn_test.py
new file mode 100644
index 0000000..186de98
--- /dev/null
+++ b/lib389/tests/plugins/usn_test.py
@@ -0,0 +1,240 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2016-2017 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+#
+
+import pytest
+
+from lib389.topologies import topology_st
+from lib389.plugins import USNPlugin
+from lib389.rootdse import RootDSE
+from lib389._mapped_object import DSLdapObjects
+from lib389.backend import Backend
+from lib389.tests.plugins.utils import create_test_user, create_test_ou, delete_objects
+from lib389._constants import DEFAULT_SUFFIX, DN_LDBM
+from lib389.properties import BACKEND_NAME, BACKEND_SUFFIX
+
+
+def reset_USN(plugin, objects):
+ """
+ Delete objects containing an entryusn attr, clean USN tombstone entries
+ and restart the instance. This makes USN to reset lastusn back to -1.
+ """
+ delete_objects(objects)
+ task = plugin.cleanup(suffix=DEFAULT_SUFFIX)
+ task.wait()
+ plugin._instance.restart()
+
+
+(a)pytest.fixture(scope="module")
+def plugin(request):
+ topology = topology_st(request)
+ plugin = USNPlugin(topology.standalone)
+ return plugin
+
+
+def test_usn_enable_disable(plugin):
+ """
+ Test that the plugin doesn't do anything while disabled, but stores
+ entryusn values properly when enabled.
+
+ NOTICE: This test case leaves the plugin enabled for the following tests.
+ """
+ # assert plugin is disabled (by default)
+ assert plugin.status() == False
+
+ root_dse = RootDSE(plugin._instance)
+ # create a function for returning lastusn every time is called
+ lastusn = lambda : root_dse.get_attr_val_int("lastusn;userroot")
+
+ user1 = create_test_user(plugin._instance)
+
+ # assert no entryusn,lastusn values were created while the plugin was disabled
+ assert not "entryusn" in user1.get_all_attrs()
+ assert not "lastusn;userroot" in root_dse.get_all_attrs()
+
+ # enable the plugin and restart the server for the action to take effect
+ plugin.enable()
+ plugin._instance.restart()
+ assert plugin.status() == True
+
+ assert lastusn() == -1
+ user2 = create_test_user(plugin._instance)
+ # assert that a new entry now contains the entryusn value
+ assert user2.get_attr_val_int("entryusn") == lastusn() == 0
+
+ # assert that USNs are properly assigned after any write operation
+ # write operations include add, modify, modrdn and delete operations
+ user3 = create_test_user(plugin._instance)
+ assert user3.get_attr_val_int("entryusn") == lastusn() == 1
+ user2.delete()
+ assert lastusn() == 2
+ user3.replace('sn', 'another surname')
+ assert user3.get_attr_val_int("entryusn") == lastusn() == 3
+
+ # reset USN for subsequent test cases
+ reset_USN(plugin, [user1, user3])
+
+def test_usn_local_mode(plugin):
+ """
+ Test that when USN is operating in local mode, each backend has an instance
+ of the USN Plug-in with a USN counter specific to that backend database.
+ """
+ # assert local mode
+ assert not plugin.is_global_mode_set()
+
+ ou_value = "People2"
+ ou_suffix = DEFAULT_SUFFIX
+
+ # create a new backend
+ plugin._instance.backends.create(
+ None, properties={
+ BACKEND_NAME: "People2Data",
+ BACKEND_SUFFIX: "ou=" + ou_value + "," + ou_suffix,
+ })
+
+ # create a new sub-suffix stored in the new backend
+ ou2 = create_test_ou(plugin._instance, ou=ou_value, suffix=ou_suffix)
+
+ root_dse = RootDSE(plugin._instance)
+ lastusn_b1 = lambda : root_dse.get_attr_val_int("lastusn;userroot")
+ lastusn_b2 = lambda : root_dse.get_attr_val_int("lastusn;people2data")
+
+ assert lastusn_b1() == lastusn_b2() == -1
+
+ # add a user in the default backend; trigger the plugin
+ user_b1 = create_test_user(plugin._instance)
+ user_b1.replace('sn', 'surname2')
+ user_b1.replace('sn', 'surname3')
+
+ # add a user in the new backend
+ user_b2 = create_test_user(plugin._instance, suffix=ou2.dn)
+
+ # assert the USN counter is different for each backend
+ assert user_b1.get_attr_val_int("entryusn") == lastusn_b1() == 2
+ assert user_b2.get_attr_val_int("entryusn") == lastusn_b2() == 0
+
+ # reset USN for subsequent test cases
+ b = Backend(plugin._instance, dn="cn=people2data," + DN_LDBM)
+ reset_USN(plugin, [user_b1, user_b2, ou2, b])
+
+def test_tombstone_cleanup(plugin):
+ """
+ Assert that the USN plugin removes tombstone entries when the cleanup task
+ is run. Test removal for a specific backend, a specific suffix, and up to
+ a given USN number.
+ """
+ # create a new backend and a new sub-suffix stored in the new backend
+ ou_value = "People3"
+ ou_suffix = DEFAULT_SUFFIX
+ plugin._instance.backends.create(
+ None, properties={
+ BACKEND_NAME: "People3Data",
+ BACKEND_SUFFIX: "ou=" + ou_value + "," + ou_suffix,
+ })
+
+ ou2 = create_test_ou(plugin._instance, ou=ou_value, suffix=ou_suffix)
+
+ tombstones_b1 = DSLdapObjects(plugin._instance)
+ tombstones_b1._basedn = "ou=People," + DEFAULT_SUFFIX
+ tombstones_b1._objectclasses = ['nsTombstone']
+
+ tombstones_b2 = DSLdapObjects(plugin._instance)
+ tombstones_b2._basedn = ou2.dn
+ tombstones_b2._objectclasses = ['nsTombstone']
+
+ root_dse = RootDSE(plugin._instance)
+ lastusn_b1 = lambda : root_dse.get_attr_val_int("lastusn;userroot")
+ assert lastusn_b1() == -1
+
+ user1_b1 = create_test_user(plugin._instance)
+ user2_b1 = create_test_user(plugin._instance)
+ user3_b1 = create_test_user(plugin._instance)
+
+ user1_b2 = create_test_user(plugin._instance, suffix=ou2.dn)
+ user2_b2 = create_test_user(plugin._instance, suffix=ou2.dn)
+
+ # assert no tombstones exist at this point
+ assert not tombstones_b1.list()
+ assert not tombstones_b2.list()
+
+ # create 3 tombstone entries on default backend
+ user1_b1.delete()
+ user2_b1.delete()
+ user3_b1.delete()
+
+ # assert there are 3 tombstone entries indeed on default backend
+ assert len(tombstones_b1.list()) == 3
+ assert not tombstones_b2.list()
+
+ assert lastusn_b1() == 5
+
+ # remove all tombstone entries from default backend, with a USN value up to 4
+ task = plugin.cleanup(suffix=DEFAULT_SUFFIX, max_usn=lastusn_b1()-1)
+ task.wait()
+
+ # assert all tombstone entries were deleted but the last one on default backend
+ assert len(tombstones_b1.list()) == 1
+ assert not tombstones_b2.list()
+
+ # create 2 tombstone entries on new backend
+ user1_b2.delete()
+ user2_b2.delete()
+
+ # assert there are 2 tombstone entries indeed on new backend
+ assert len(tombstones_b2.list()) == 2
+ assert len(tombstones_b1.list()) == 1
+
+ # remove all tombstone entries from ou2 suffix
+ task = plugin.cleanup(suffix=ou2.dn)
+ task.wait()
+
+ # assert there are no tombstone entries stored on ou2 suffix
+ assert not tombstones_b2.list()
+ assert len(tombstones_b1.list()) == 1
+
+ # reset USN for subsequent test cases
+ b = Backend(plugin._instance, dn="cn=people3data," + DN_LDBM)
+ reset_USN(plugin, [ou2, b])
+
+def test_usn_global_mode(plugin):
+ """
+ Test that when USN is operating in global mode, there is a global instance
+ of the USN Plug-in with a global USN counter that applies to changes made
+ to the entire directory (all backends).
+ """
+ plugin.enable_global_mode()
+ plugin._instance.restart()
+ assert plugin.is_global_mode_set()
+
+ # create a new backend and a new sub-suffix stored in the new backend
+ ou_value = "People4"
+ ou_suffix = DEFAULT_SUFFIX
+ plugin._instance.backends.create(
+ None, properties={
+ BACKEND_NAME: "People4Data",
+ BACKEND_SUFFIX: "ou=" + ou_value + "," + ou_suffix,
+ })
+
+ root_dse = RootDSE(plugin._instance)
+ assert "lastusn" in root_dse.get_all_attrs()
+
+ ou2 = create_test_ou(plugin._instance, ou=ou_value, suffix=ou_suffix)
+ assert ou2.get_attr_val_int("entryusn") == 0
+
+ # add a user in the default backend
+ user_b1 = create_test_user(plugin._instance)
+ # add a user in the new backend
+ user_b2 = create_test_user(plugin._instance, suffix=ou2.dn)
+
+ # assert that a global USN counter is used for all backends
+ assert user_b1.get_attr_val_int("entryusn") == 1
+ assert user_b2.get_attr_val_int("entryusn") == 2
+
+ # reset USN for subsequent test cases
+ b = Backend(plugin._instance, dn="cn=people4data," + DN_LDBM)
+ reset_USN(plugin, [user_b1, user_b2, ou2, b])
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.