From 1dd9e1407361bdd6ed337c70dcb1d209ce034cb6 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Fri, 1 Jul 2011 15:32:31 -0400 Subject: Optionally wait for 389-ds postop plugins to complete Add a new command that lets you wait for an attribute to appear in a value. Using this you can do things like wait for a managed entry to be created, adding a new objectclass to the parent entry. This is controlled by a new booleon option, wait_for_attr, defaulting to False. https://fedorahosted.org/freeipa/ticket/1144 --- ipalib/plugins/baseldap.py | 53 +++++++++++++++++++++++++++++++++++++++++++++ ipalib/plugins/hostgroup.py | 8 +++++++ ipalib/plugins/user.py | 5 +++++ 3 files changed, 66 insertions(+) (limited to 'ipalib/plugins') diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py index 8d58422bd..1ff7a2a6d 100644 --- a/ipalib/plugins/baseldap.py +++ b/ipalib/plugins/baseldap.py @@ -194,6 +194,25 @@ def get_effective_rights(ldap, dn, attrs=None): return rdict +def entry_from_entry(entry, newentry): + """ + Python is more or less pass-by-value except for immutable objects. So if + you pass in a dict to a function you are free to change members of that + dict but you can't create a new dict in the function and expect to replace + what was passed in. + + In some post-op plugins that is exactly what we want to do, so here is a + clumsy way around the problem. + """ + + # Wipe out the current data + for e in entry.keys(): + del entry[e] + + # Re-populate it with new wentry + for e in newentry: + entry[e] = newentry[e] + def wait_for_memberof(keys, entry_start, completed, show_command, adding=True): """ When adding or removing reverse members we are faking an update to @@ -238,6 +257,40 @@ def wait_for_memberof(keys, entry_start, completed, show_command, adding=True): return entry_attrs +def wait_for_value(ldap, dn, attr, value): + """ + 389-ds postoperation plugins are executed after the data has been + returned to a client. This means that plugins that add data in a + postop are not included in data returned to the user. + + The downside of waiting is that this increases the time of the + command. + + The updated entry is returned. + """ + # Loop a few times to give the postop-plugin a chance to complete + # Don't sleep for more than 6 seconds. + x = 0 + while x < 20: + # sleep first because the first search, even on a quiet system, + # almost always fails. + time.sleep(.3) + x = x + 1 + + # FIXME: put a try/except around here? I think it is probably better + # to just let the exception filter up to the caller. + (dn, entry_attrs) = ldap.get_entry( dn, ['*']) + if attr in entry_attrs: + if isinstance(entry_attrs[attr], (list, tuple)): + values = map(lambda y:y.lower(), entry_attrs[attr]) + if value.lower() in values: + break + else: + if value.lower() == entry_attrs[attr].lower(): + break + + return entry_attrs + class LDAPObject(Object): """ Object representing a LDAP entry. diff --git a/ipalib/plugins/hostgroup.py b/ipalib/plugins/hostgroup.py index 11ed66376..ef44a8b9b 100644 --- a/ipalib/plugins/hostgroup.py +++ b/ipalib/plugins/hostgroup.py @@ -98,6 +98,14 @@ class hostgroup_add(LDAPCreate): msg_summary = _('Added hostgroup "%(value)s"') + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + if self.api.env.wait_for_attr: + newentry = wait_for_value(ldap, dn, 'objectclass', 'mepOriginEntry') + entry_from_entry(entry_attrs, newentry) + + return dn + + api.register(hostgroup_add) diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index e6060c0f3..3068c6291 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -402,6 +402,11 @@ class user_add(LDAPCreate): self.api.Command['user_mod'](keys[-1], **kw) except (errors.EmptyModlist, errors.NotFound): pass + else: + if self.api.env.wait_for_attr: + newentry = wait_for_value(ldap, dn, 'objectclass', 'mepOriginEntry') + entry_from_entry(entry_attrs, newentry) + return dn api.register(user_add) -- cgit