#8897 ansible-freeipa automember test fails with `automember_add_condition: testgroup: 'objectclass'` due to ldap cache
Closed: fixed 2 years ago by rcritten. Opened 2 years ago by twoerner.

Issue

ansible-freeipa automember test https://github.com/freeipa/ansible-freeipa/blob/master/tests/automember/test_automember.yml fails with IPA 4.9.4.
The error message is automember_add_condition: testgroup: 'objectclass'.
The error is not happening if the ldap cache is turned off or is the module is not run within server context. The issue is also not reproducible on the server directly. It is only triggered using the ansible-freeipa module.

Steps to Reproduce

  1. Run ansible-freeipa automember test

Actual behavior

fatal: [ipaserver.test.local]: FAILED! => {"changed": false, "failed_when_result": true, "msg": "automember_add_condition: testgroup: 'objectclass'"}

Expected behavior

No failure

Version/Release/Distribution

$ rpm -q freeipa-server freeipa-client ipa-server ipa-client 389-ds-base pki-ca krb5-server
freeipa-server-4.9.4-1.fc34.x86_64
freeipa-client-4.9.4-1.fc34.x86_64
package ipa-server is not installed
package ipa-client is not installed
389-ds-base-2.0.5-1.fc34.x86_64
pki-ca-10.10.6-1.fc34.noarch
krb5-server-1.19.1-3.fc34.x86_64

Additional info:

This is the traceback:

Traceback (most recent call last):
   File "/tmp/ansible_ipaautomember_payload_za0tz354/ansible_ipaautomember_payload.zip/ansible/modules/ipaautomember.py", line 366, in main
   File "/tmp/ansible_ipaautomember_payload_za0tz354/ansible_ipaautomember_payload.zip/ansible/module_utils/ansible_freeipa_module.py", line 207, in api_command
     return api.Command[command](name, **args)
   File "/usr/lib/python3.9/site-packages/ipalib/frontend.py", line 471, in __call__
     return self.__do_call(*args,**options)
   File "/usr/lib/python3.9/site-packages/ipalib/frontend.py", line 499, in __do_call
     ret = self.run(*args, **options)
   File "/usr/lib/python3.9/site-packages/ipalib/frontend.py", line 821, in run
     return self.execute(*args, **options)
   File "/usr/lib/python3.9/site-packages/ipaserver/plugins/automember.py", line 417, in execute
     result = super(automember_add_condition, self).execute(*keys, **options)
   File "/usr/lib/python3.9/site-packages/ipaserver/plugins/baseldap.py", line 1511, in execute
     entry_attrs.dn = callback(
   File "/usr/lib/python3.9/site-packages/ipaserver/plugins/automember.py", line 389, in pre_callback
     if not isinstance(entry_attrs[regex], (list, tuple)):
   File "/usr/lib/python3.9/site-packages/ipapython/ipaldap.py", line 516, in __getitem__
     return self._get_nice(name)
   File "/usr/lib/python3.9/site-packages/ipapython/ipaldap.py", line 483, in _get_nice
     name = self._get_attr_name(name)
   File "/usr/lib/python3.9/site-packages/ipapython/ipaldap.py", line 479, in _get_attr_name
     name = self._names[name]
   File "/usr/lib/python3.9/site-packages/ipapython/ipautil.py", line 656, in __getitem__
     return super(CIDict,self).__getitem__(key.lower()) KeyError: 'objectclass'

Here is a shorter version of the test playbook to trigger the issue:

---
- name: Test automember with ldap cache
  hosts: ipaserver
  become: true

  tasks:

  # CLEANUP TEST ITEMS

  - name: Ensure group testgroup is absent
    ipagroup:
      ipaadmin_password: SomeADMINpassword
      name: testgroup
      state: absent

  - name: Ensure group automember rule testgroup is absent
    ipaautomember:
      ipaadmin_password: SomeADMINpassword
      name: testgroup
      state: absent
      automember_type: group

  # CREATE TEST ITEMS

  # TESTS
  - name: Ensure testgroup group is present
    ipagroup:
      ipaadmin_password: SomeADMINpassword
      name: testgroup

  - name: Ensure testgroup group automember rule is present
    ipaautomember:
      ipaadmin_password: SomeADMINpassword
      name: testgroup
      description: testgroup automember rule.
      automember_type: group
    register: result
    failed_when: not result.changed or result.failed

  - name: Change testgroup group automember rule description
    ipaautomember:
      ipaadmin_password: SomeADMINpassword
      name: testgroup
      description: testgroup automember rule description.
      automember_type: group
    register: result
    failed_when: not result.changed or result.failed

  - name: Ensure testgroup group automember rule has conditions
    ipaautomember:
      ipaadmin_password: SomeADMINpassword
      name: testgroup
      automember_type: group
      inclusive:
        - key: 'uid'
          expression: 'uid'
        - key: 'uidnumber'
          expression: 'uidnumber'
      exclusive:
        - key: 'uid'
          expression: 'uid'
    register: result
    failed_when: not result.changed or result.failed

  - name: Add testgroup group automember rule member condition
    ipaautomember:
      ipaadmin_password: SomeADMINpassword
      name: testgroup
      automember_type: group
      action: member
      inclusive:
        - key: 'manager'
          expression: 'uid=mscott'
    register: result
    failed_when: not result.changed or result.failed

  - name: Ensure testgroup group automember rule has conditions
    ipaautomember:
      ipaadmin_password: SomeADMINpassword
      name: testgroup
      automember_type: group
      inclusive:
        - key: 'uid'
          expression: 'uid'
        - key: 'uidnumber'
          expression: 'uidnumber'
        - key: 'manager'
          expression: 'uid=mscott'
      exclusive:
        - key: 'uid'
          expression: 'uid'
    register: result
    failed_when: result.changed or result.failed

  - name: Remove testgroup group automember rule member condition
    ipaautomember:
      ipaadmin_password: SomeADMINpassword
      name: testgroup
      automember_type: group
      action: member
      state: absent
      inclusive:
        - key: 'manager'
          expression: 'uid=mscott'
    register: result
    failed_when: not result.changed or result.failed

  # CLEANUP TEST ITEMS

  - name: Ensure group testgroup is absent
    ipagroup:
      ipaadmin_password: SomeADMINpassword
      name: testgroup
      state: absent

  - name: Ensure group automember rule testgroup is absent
    ipaautomember:
      ipaadmin_password: SomeADMINpassword
      automember_type: group
      name: testgroup
      state: absent

Metadata Update from @rcritten:
- Issue assigned to rcritten

2 years ago

I think it might be a strange request pattern but it seems to be related to '*' handling in the cache.

Still early, but it looks like the cache thinks that '*' was originally requested when it wasn't so we end up with a cache HIT when all attributes are not available, in this case objectclass.

I figured it out, not sure of the fix yet.

The entry_attrs value has the typical set of user-provided attributes set. In this case the regex option passed in. It looks like:

LDAPEntry(ipapython.dn.DN('cn=testgroup,cn=group,cn=automember,cn=etc,dc=example,dc=test'), {'automemberinclusiveregex': ['uid=uid']}

So then the code pulls the old_entry and looks to see if this is already set somewhere via:

for regex in old_entry.keys():

This is a bit much and breaks the cache because it doesn't have things like objectclass and cn.
And it doesn't need them either. This happens because we have a cached value of the entry, so old_entry contains all the attributes, almost none of which are in the temporary new entry.

So the code can be pretty easily fixed by only examining values we care about. And it would be better that way too, perhaps microscopically faster.

But the cache really shouldn't blow up either. It's just not an obvious fix.

If the attribute requested is not there a KeyError is returned. That seems right. But diagnosing this, particularly with Ansible, was not at all easy.

There is a ton of code that relies on this KeyError behavior so this would be non-trivial to change.

I'm going to submit the automember fix for now.

The issue was that the cache returned the entire cached entry regardless of what attributes were requested. So this gave more for the caller to dig through which led to the error.

So instead the cache should create a new entry to return that just contains those attributes that were requested if all the requested attributes exist within the cached entry.

Metadata Update from @rcritten:
- Custom field rhbz adjusted to https://bugzilla.redhat.com/show_bug.cgi?id=1976286

2 years ago

master:

  • 19d5b3b Return a copy of cached entries, only with requested attributes

ipa-4-9:

  • ae4478d Return a copy of cached entries, only with requested attributes

Metadata Update from @rcritten:
- Issue close_status updated to: fixed
- Issue status updated to: Closed (was: Open)

2 years ago

Login to comment on this ticket.

Metadata