Add LDAPEntry method generate_modlist.

Use LDAPEntry.generate_modlist instead of LDAPClient._generate_modlist and
remove LDAPClient._generate_modlist.

https://fedorahosted.org/freeipa/ticket/3488
This commit is contained in:
Jan Cholasta
2013-12-10 11:45:10 +01:00
committed by Petr Viktorin
parent 9d4bcb63de
commit 61887ac392
3 changed files with 41 additions and 48 deletions

View File

@@ -992,6 +992,44 @@ class LDAPEntry(collections.MutableMapping):
return NotImplemented
return other is not self
def generate_modlist(self):
modlist = []
names = set(self.iterkeys())
names.update(self._orig.iterkeys())
for name in names:
new = self.raw.get(name)
old = self._orig.raw.get(name)
if old and not new:
modlist.append((ldap.MOD_DELETE, name, None))
continue
if not old:
modlist.append((ldap.MOD_REPLACE, name, new))
continue
# We used to convert to sets and use difference to calculate
# the changes but this did not preserve order which is important
# particularly for schema
adds = [value for value in new if value not in old]
dels = [value for value in old if value not in new]
if adds and self.conn.get_single_value(name):
if len(adds) > 1:
raise errors.OnlyOneValueAllowed(attr=name)
modlist.append((ldap.MOD_REPLACE, name, adds))
else:
if adds:
modlist.append((ldap.MOD_ADD, name, adds))
if dels:
modlist.append((ldap.MOD_DELETE, name, dels))
# Usually the modlist order does not matter.
# However, for schema updates, we want 'attributetypes' before
# 'objectclasses'.
# A simple sort will ensure this.
modlist.sort(key=lambda m: m[1].lower() != 'attributetypes')
return modlist
# FIXME: Remove when python-ldap tuple compatibility is dropped
def __iter__(self):
yield self._dn
@@ -1587,51 +1625,6 @@ class LDAPClient(object):
self.conn.rename_s(dn, new_rdn, delold=int(del_old))
time.sleep(.3) # Give memberOf plugin a chance to work
def _generate_modlist(self, dn, entry_attrs):
assert isinstance(dn, DN)
# generate modlist
# for multi value attributes: no MOD_REPLACE to handle simultaneous
# updates better
# for single value attribute: always MOD_REPLACE
modlist = []
for (k, v) in entry_attrs.raw.iteritems():
if not v and k in entry_attrs.orig_data:
modlist.append((ldap.MOD_DELETE, k, None))
else:
v = set(v)
old_v = set(entry_attrs.orig_data.raw.get(k, []))
adds = list(v.difference(old_v))
rems = list(old_v.difference(v))
is_single_value = self.get_single_value(k)
value_count = len(old_v) + len(adds) - len(rems)
if is_single_value and value_count > 1:
raise errors.OnlyOneValueAllowed(attr=k)
force_replace = False
if len(v) > 0 and len(v.intersection(old_v)) == 0:
force_replace = True
if adds:
if force_replace:
modlist.append((ldap.MOD_REPLACE, k, adds))
else:
modlist.append((ldap.MOD_ADD, k, adds))
if rems:
if not force_replace:
modlist.append((ldap.MOD_DELETE, k, rems))
# Usually the modlist order does not matter.
# However, for schema updates, we want 'attributetypes' before
# 'objectclasses'.
# A simple sort will ensure this.
modlist.sort(key=lambda m: m[1].lower())
return modlist
def update_entry(self, entry, entry_attrs=None):
"""Update entry's attributes.
@@ -1645,7 +1638,7 @@ class LDAPClient(object):
entry.update(entry_attrs)
# generate modlist
modlist = self._generate_modlist(entry.dn, entry)
modlist = entry.generate_modlist()
if not modlist:
raise errors.EmptyModlist()

View File

@@ -718,7 +718,7 @@ class LDAPUpdate:
else:
# Update LDAP
try:
changes = self.conn._generate_modlist(entry.dn, entry)
changes = entry.generate_modlist()
if len(changes) >= 1:
updated = True
safe_changes = []

View File

@@ -114,7 +114,7 @@ def update_schema(schema_files, ldapi=False, dm_password=None, live_run=True):
# FIXME: We should have a better way to display the modlist,
# for now display raw output of our internal routine
modlist = conn._generate_modlist(schema_entry.dn, schema_entry)
modlist = schema_entry.generate_modlist()
log.debug("Complete schema modlist:\n%s", pprint.pformat(modlist))
if modified and live_run: