Find orphan automember rules

If groups or hostgroups have been removed after automember rules have been
created using them, then automember-rebuild, automember-add, host-add and
more commands could fail.

A new command has been added to the ipa tool:

  ipa automember-find-orphans --type={hostgroup,group} [--remove]

This command retuns the list of orphan automember rules in the same way as
automember-find. With the --remove option the orphan rules are also removed.

The IPA API version has been increased and a test case has been added.

Using ideas from a patch by: Rob Crittenden <rcritten@redhat.com>

See: https://pagure.io/freeipa/issue/6476
Signed-off-by: Thomas Woerner <twoerner@redhat.com>
Reviewed-By: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
This commit is contained in:
Thomas Woerner 2018-10-01 11:58:26 +01:00 committed by Florence Blanc-Renaud
parent 6650d1b563
commit 7f507519d4
4 changed files with 124 additions and 2 deletions

15
API.txt
View File

@ -186,6 +186,20 @@ output: Output('count', type=[<type 'int'>])
output: ListOfEntries('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: Output('truncated', type=[<type 'bool'>])
command: automember_find_orphans/1
args: 1,7,4
arg: Str('criteria?')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('description?', autofill=False, cli_name='desc')
option: Flag('pkey_only?', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Flag('remove?', autofill=True, default=False)
option: StrEnum('type', values=[u'group', u'hostgroup'])
option: Str('version?')
output: Output('count', type=[<type 'int'>])
output: ListOfEntries('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: Output('truncated', type=[<type 'bool'>])
command: automember_mod/1
args: 1,9,3
arg: Str('cn', cli_name='automember_rule')
@ -6503,6 +6517,7 @@ default: automember_default_group_set/1
default: automember_default_group_show/1
default: automember_del/1
default: automember_find/1
default: automember_find_orphans/1
default: automember_mod/1
default: automember_rebuild/1
default: automember_remove_condition/1

View File

@ -83,8 +83,8 @@ define(IPA_DATA_VERSION, 20100614120000)
# #
########################################################
define(IPA_API_VERSION_MAJOR, 2)
define(IPA_API_VERSION_MINOR, 229)
# Last change: Added the Certificate parameter
define(IPA_API_VERSION_MINOR, 230)
# Last change: Added `automember-find-orphans' command
########################################################

View File

@ -116,6 +116,11 @@ EXAMPLES:
""") + _("""
Find all of the automember rules:
ipa automember-find
""") + _("""
Find all of the orphan automember rules:
ipa automember-find-orphans --type=hostgroup
Find all of the orphan automember rules and remove them:
ipa automember-find-orphans --type=hostgroup --remove
""") + _("""
Display a automember rule:
ipa automember-show --type=hostgroup webservers
@ -817,3 +822,57 @@ class automember_rebuild(Method):
result=result,
summary=unicode(summary),
value=pkey_to_value(None, options))
@register()
class automember_find_orphans(LDAPSearch):
__doc__ = _("""
Search for orphan automember rules. The command might need to be run as
a privileged user user to get all orphan rules.
""")
takes_options = group_type + (
Flag(
'remove?',
doc=_("Remove orphan automember rules"),
),
)
msg_summary = ngettext(
'%(count)d rules matched', '%(count)d rules matched', 0
)
def execute(self, *keys, **options):
results = super().execute(*keys, **options)
remove_option = options.get('remove')
pkey_only = options.get('pkey_only', False)
ldap = self.obj.backend
orphans = []
for entry in results["result"]:
am_dn_entry = entry['automembertargetgroup'][0]
# Make DN for --raw option
if not isinstance(am_dn_entry, DN):
am_dn_entry = DN(am_dn_entry)
try:
ldap.get_entry(am_dn_entry)
except errors.NotFound:
if pkey_only:
# For pkey_only remove automembertargetgroup
del(entry['automembertargetgroup'])
orphans.append(entry)
if remove_option:
ldap.delete_entry(entry['dn'])
results["result"][:] = orphans
results["count"] = len(orphans)
return results
def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args,
**options):
assert isinstance(base_dn, DN)
scope = ldap.SCOPE_SUBTREE
ndn = DN(('cn', options['type']), base_dn)
if options.get('pkey_only', False):
# For pkey_only add automembertargetgroup
attrs_list.append('automembertargetgroup')
return filters, ndn, scope

View File

@ -715,3 +715,51 @@ class TestMultipleAutomemberConditions(XMLRPC_test):
defaultgroup1.ensure_missing()
defaulthostgroup1.ensure_missing()
@pytest.mark.tier1
class TestAutomemberFindOrphans(XMLRPC_test):
def test_create_deps_for_find_orphans(self, hostgroup1, host1,
automember_hostgroup):
""" Create host, hostgroup, and automember tracker for this class
of tests. """
# Create hostgroup1 and automember rule with condition
hostgroup1.ensure_exists()
host1.ensure_exists()
# Manually create automember rule and condition, racker will try to
# remove the automember rule in the end, which is failing as the rule
# is already removed
api.Command['automember_add'](hostgroup1.cn, type=u'hostgroup')
api.Command['automember_add_condition'](
hostgroup1.cn,
key=u'fqdn', type=u'hostgroup',
automemberinclusiveregex=[hostgroup_include_regex]
)
hostgroup1.retrieve()
def test_find_orphan_automember_rules(self, hostgroup1):
""" Remove hostgroup1, find and remove obsolete automember rules. """
# Remove hostgroup1
hostgroup1.ensure_missing()
# Find obsolete automember rules
result = api.Command['automember_find_orphans'](type=u'hostgroup')
assert result['count'] == 1
# Find and remove obsolete automember rules
result = api.Command['automember_find_orphans'](type=u'hostgroup',
remove=True)
assert result['count'] == 1
# Find obsolete automember rules
result = api.Command['automember_find_orphans'](type=u'hostgroup')
assert result['count'] == 0
# Final cleanup of automember rule if it still exists
with raises_exact(errors.NotFound(
reason=u'%s: Automember rule not found' % hostgroup1.cn)):
api.Command['automember_del'](hostgroup1.cn, type=u'hostgroup')