This is an automated email from the git hooks/post-receive script.
firstyear pushed a commit to branch master
in repository lib389.
commit de3d6444c28c41001eab4c6c3f5f3ff8ca14cd94
Author: Ankit yadav <ankit(a)localhost.localdomain>
Date: Wed Apr 5 04:09:56 2017 +0530
Ticket 1 - cn=config comparison
Bug Description: Once we have enough plugin and index code,
we should be able to use dsconf to compare the state of two
instances. This will let us check for differing plugin, index,
configuration values between two servers.
Fix Description: This patch has a working compare function and
tests for comparing user objects.
https://pagure.io/lib389/issue/1
Author: ankity10
Review by: wibrown (Thanks Ankity!)
---
.gitignore | 2 +-
lib389/_mapped_object.py | 62 ++++++++++++++++++++++-
lib389/idm/user.py | 3 ++
lib389/tests/idm/user_compare_test.py | 92 +++++++++++++++++++++++++++++++++++
requirements.txt | 2 +-
5 files changed, 158 insertions(+), 3 deletions(-)
diff --git a/.gitignore b/.gitignore
index ccc1a64..941126e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,4 @@ logs/
*.patch
.cache
.tox
-*.egg-info
+*.egg-info
\ No newline at end of file
diff --git a/lib389/_mapped_object.py b/lib389/_mapped_object.py
index 93aad50..9ccf29d 100644
--- a/lib389/_mapped_object.py
+++ b/lib389/_mapped_object.py
@@ -76,6 +76,7 @@ class DSLogging(object):
class DSLdapObject(DSLogging):
+
# TODO: Automatically create objects when they are requested to have properties
added
def __init__(self, instance, dn=None, batch=False):
"""
@@ -93,6 +94,8 @@ class DSLdapObject(DSLogging):
self._create_objectclasses = []
self._rdn_attribute = None
self._must_attributes = None
+ # attributes, we don't want to compare
+ self._compare_exclude = []
def __unicode__(self):
val = self._dn
@@ -225,6 +228,63 @@ class DSLdapObject(DSLogging):
mod_list.append((action, key, value))
return self._instance.modify_s(self._dn, mod_list)
+ @classmethod
+ def compare(cls, obj1, obj2):
+ """
+ Compare if two RDN objects have same attributes and values.
+ This comparison is a loose comparison, not a strict one i.e. "this object
*is* this other object"
+ It will just check if the attributes are same.
+ 'nsUniqueId' attribute is not checked intentionally because we want to
compare arbitrary objects
+ i.e they may have different 'nsUniqueId' but same attributes.
+ Example:
+ cn=user1,ou=a
+ cn=user1,ou=b
+ Comparision of these two objects should result in same, even though their
'nsUniqueId' attribute differs.
+ This function returns 'True' if objects have same attributes else returns
'False'
+ """
+ # ensuring both the objects are RDN objects
+ if not issubclass(type(obj1), DSLdapObject) or not issubclass(type(obj2),
DSLdapObject):
+ raise ValueError("Invalid arguments: Expecting object types that
inherits 'DSLdapObject' class")
+ # check if RDN of objects is same
+ if obj1.rdn != obj2.rdn:
+ return False
+ obj1_attrs = obj1.get_compare_attrs()
+ obj2_attrs = obj2.get_compare_attrs()
+ # Bail fast if the keys don't match
+ if set(obj1_attrs.keys()) != set(obj2_attrs.keys()):
+ return False
+ # Check the values of each key
+ # using obj1_attrs.keys() because obj1_attrs.iterkleys() is not supported in
python3
+ for key in obj1_attrs.keys():
+ if set(obj1_attrs[key]) != set(obj2_attrs[key]):
+ return False
+ return True
+
+ def get_compare_attrs(self):
+ """
+ Get a dictionary having attributes to be compared i.e. excluding
self._compare_exclude
+ """
+ self._log.debug("%s get_compare_attrs" % (self._dn))
+ all_attrs_dict = self.get_all_attrs()
+ # removing _compate_exclude attrs from all attrs
+ compare_attrs = set(all_attrs_dict.keys()) - set(self._compare_exclude)
+ compare_attrs_dict = {attr:all_attrs_dict[attr] for attr in compare_attrs}
+ return compare_attrs_dict
+
+ def get_all_attrs(self):
+ """
+ Get a dictionary having all the attributes i.e. real attributes + operational
attributes
+ """
+ self._log.debug("%s get_all_attrs" % (self._dn))
+ if self._instance.state != DIRSRV_STATE_ONLINE:
+ raise ValueError("Invalid state. Cannot get properties on instance that
is not ONLINE")
+ else:
+ # retrieving real(*) and operational attributes(+)
+ attrs_entry = self._instance.getEntry(self._dn, ldap.SCOPE_BASE,
"(objectclass=*)", ["*", "+"])
+ # getting dict from 'entry' object
+ attrs_dict = attrs_entry.data
+ return attrs_dict
+
def get_attrs_vals(self, keys):
self._log.debug("%s get_attrs_vals(%r)" % (self._dn, keys))
if self._instance.state != DIRSRV_STATE_ONLINE:
@@ -498,7 +558,7 @@ class DSLdapObjects(DSLogging):
# Create the object
# Should we inject the rdn to properties?
# This may not work in all cases, especially when we consider plugins.
- #
+ #
co = self._entry_to_instance(dn=None, entry=None)
# Make the rdn naming attr avaliable
self._rdn_attribute = co._rdn_attribute
diff --git a/lib389/idm/user.py b/lib389/idm/user.py
index d422ed9..aef0ce0 100644
--- a/lib389/idm/user.py
+++ b/lib389/idm/user.py
@@ -18,7 +18,9 @@ MUST_ATTRIBUTES = [
]
RDN = 'uid'
+
class UserAccount(DSLdapObject):
+
def __init__(self, instance, dn=None, batch=False):
super(UserAccount, self).__init__(instance, dn, batch)
self._rdn_attribute = RDN
@@ -36,6 +38,7 @@ class UserAccount(DSLdapObject):
# Can we get this into core?
# 'ldapPublicKey',
]
+ self._compare_exclude = ['nsuniqueid']
self._protected = False
def _validate(self, rdn, properties, basedn):
diff --git a/lib389/tests/idm/user_compare_test.py
b/lib389/tests/idm/user_compare_test.py
new file mode 100644
index 0000000..900d980
--- /dev/null
+++ b/lib389/tests/idm/user_compare_test.py
@@ -0,0 +1,92 @@
+import os
+import sys
+import time
+import ldap
+import logging
+import pytest
+from lib389._constants import *
+from lib389.properties import *
+from lib389.tasks import *
+from lib389.utils import *
+
+from lib389.idm.group import Groups
+from lib389.idm.user import UserAccounts, UserAccount
+
+from lib389.topologies import topology_st as topology
+
+DEBUGGING = os.getenv('DEBUGGING', False)
+
+if DEBUGGING is not False:
+ DEBUGGING = True
+
+if DEBUGGING:
+ logging.getLogger(__name__).setLevel(logging.DEBUG)
+else:
+ logging.getLogger(__name__).setLevel(logging.INFO)
+
+log = logging.getLogger(__name__)
+
+
+def test_user_compare(topology):
+ """
+ Testing compare function
+ 1. Testing comparison of two different users.
+ 2. Testing comparison of 'str' object with itself, should raise
'ValueError'.
+ 3. Testing comparison of user with similar user (different object id).
+ 4. Testing comparison of user with group.
+ """
+ if DEBUGGING:
+ # Add debugging steps(if any)...
+ pass
+
+ users = UserAccounts(topology.standalone, DEFAULT_SUFFIX)
+ groups = Groups(topology.standalone, DEFAULT_SUFFIX)
+ # Create 1st user
+ user1_properties = {
+ 'uid': 'testuser1',
+ 'cn': 'testuser1',
+ 'sn': 'user',
+ 'uidNumber': '1000',
+ 'gidNumber': '2000',
+ 'homeDirectory': '/home/testuser1'
+ }
+
+ users.create(properties=user1_properties)
+ testuser1 = users.get('testuser1')
+ # Create 2nd user
+ user2_properties = {
+ 'uid': 'testuser2',
+ 'cn': 'testuser2',
+ 'sn': 'user',
+ 'uidNumber': '1001',
+ 'gidNumber': '2002',
+ 'homeDirectory': '/home/testuser2'
+ }
+
+ users.create(properties=user2_properties)
+ testuser2 = users.get('testuser2')
+ # create group
+ group_properties = {
+ 'cn' : 'group1',
+ 'description' : 'testgroup'
+ }
+
+ testuser1_copy = users.get("testuser1")
+ group = groups.create(properties=group_properties)
+
+ assert(UserAccount.compare(testuser1, testuser2) == False)
+
+ with pytest.raises(ValueError):
+ UserAccount.compare("test_str_object","test_str_object")
+
+ assert(UserAccount.compare(testuser1, testuser1_copy) == True)
+ assert(UserAccount.compare(testuser1, group) == False)
+
+ log.info("Test PASSED")
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
diff --git a/requirements.txt b/requirements.txt
index ff6b228..1f58b78 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,4 +5,4 @@ pyasn1
pyasn1-modules
six
pytest
-python-dateutil
+python-dateutil
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.