|
|
ad1545 |
From b78abe934c6c0038f74dd9e52309f61854d86469 Mon Sep 17 00:00:00 2001
|
|
|
ad1545 |
From: Thomas Woerner <twoerner@redhat.com>
|
|
|
ad1545 |
Date: Mon, 1 Oct 2018 11:58:26 +0100
|
|
|
ad1545 |
Subject: [PATCH] Find orphan automember rules
|
|
|
ad1545 |
|
|
|
ad1545 |
If groups or hostgroups have been removed after automember rules have been
|
|
|
ad1545 |
created using them, then automember-rebuild, automember-add, host-add and
|
|
|
ad1545 |
more commands could fail.
|
|
|
ad1545 |
|
|
|
ad1545 |
A new command has been added to the ipa tool:
|
|
|
ad1545 |
|
|
|
ad1545 |
ipa automember-find-orphans --type={hostgroup,group} [--remove]
|
|
|
ad1545 |
|
|
|
ad1545 |
This command retuns the list of orphan automember rules in the same way as
|
|
|
ad1545 |
automember-find. With the --remove option the orphan rules are also removed.
|
|
|
ad1545 |
|
|
|
ad1545 |
The IPA API version has been increased and a test case has been added.
|
|
|
ad1545 |
|
|
|
ad1545 |
Using ideas from a patch by: Rob Crittenden <rcritten@redhat.com>
|
|
|
ad1545 |
|
|
|
ad1545 |
See: https://pagure.io/freeipa/issue/6476
|
|
|
ad1545 |
Signed-off-by: Thomas Woerner <twoerner@redhat.com>
|
|
|
ad1545 |
Reviewed-By: Christian Heimes <cheimes@redhat.com>
|
|
|
ad1545 |
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
|
ad1545 |
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
|
ad1545 |
---
|
|
|
ad1545 |
API.txt | 15 +++++
|
|
|
ad1545 |
VERSION.m4 | 4 +-
|
|
|
ad1545 |
ipaserver/plugins/automember.py | 60 +++++++++++++++++++
|
|
|
ad1545 |
.../test_xmlrpc/test_automember_plugin.py | 48 +++++++++++++++
|
|
|
ad1545 |
4 files changed, 125 insertions(+), 2 deletions(-)
|
|
|
ad1545 |
|
|
|
ad1545 |
diff --git a/API.txt b/API.txt
|
|
|
ad1545 |
index 0e09e58a6ecaa4f724fb0c92b4faaf64df9fab5a..b9dc35fb5752ce04f58aa8c4c3e89c7299f34cd7 100644
|
|
|
ad1545 |
--- a/API.txt
|
|
|
ad1545 |
+++ b/API.txt
|
|
|
ad1545 |
@@ -186,6 +186,20 @@ output: Output('count', type=[<type 'int'>])
|
|
|
ad1545 |
output: ListOfEntries('result')
|
|
|
ad1545 |
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
|
|
ad1545 |
output: Output('truncated', type=[<type 'bool'>])
|
|
|
ad1545 |
+command: automember_find_orphans/1
|
|
|
ad1545 |
+args: 1,7,4
|
|
|
ad1545 |
+arg: Str('criteria?')
|
|
|
ad1545 |
+option: Flag('all', autofill=True, cli_name='all', default=False)
|
|
|
ad1545 |
+option: Str('description?', autofill=False, cli_name='desc')
|
|
|
ad1545 |
+option: Flag('pkey_only?', autofill=True, default=False)
|
|
|
ad1545 |
+option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
|
|
ad1545 |
+option: Flag('remove?', autofill=True, default=False)
|
|
|
ad1545 |
+option: StrEnum('type', values=[u'group', u'hostgroup'])
|
|
|
ad1545 |
+option: Str('version?')
|
|
|
ad1545 |
+output: Output('count', type=[<type 'int'>])
|
|
|
ad1545 |
+output: ListOfEntries('result')
|
|
|
ad1545 |
+output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
|
|
ad1545 |
+output: Output('truncated', type=[<type 'bool'>])
|
|
|
ad1545 |
command: automember_mod/1
|
|
|
ad1545 |
args: 1,9,3
|
|
|
ad1545 |
arg: Str('cn', cli_name='automember_rule')
|
|
|
ad1545 |
@@ -6498,6 +6512,7 @@ default: automember_default_group_set/1
|
|
|
ad1545 |
default: automember_default_group_show/1
|
|
|
ad1545 |
default: automember_del/1
|
|
|
ad1545 |
default: automember_find/1
|
|
|
ad1545 |
+default: automember_find_orphans/1
|
|
|
ad1545 |
default: automember_mod/1
|
|
|
ad1545 |
default: automember_rebuild/1
|
|
|
ad1545 |
default: automember_remove_condition/1
|
|
|
ad1545 |
diff --git a/VERSION.m4 b/VERSION.m4
|
|
|
ad1545 |
index 81e671ed60f2ada0766b06db879c706cf7c4c77a..7ebf3410c8a688577f1fabc37d65b128e47418a6 100644
|
|
|
ad1545 |
--- a/VERSION.m4
|
|
|
ad1545 |
+++ b/VERSION.m4
|
|
|
ad1545 |
@@ -82,8 +82,8 @@ define(IPA_DATA_VERSION, 20100614120000)
|
|
|
ad1545 |
# #
|
|
|
ad1545 |
########################################################
|
|
|
ad1545 |
define(IPA_API_VERSION_MAJOR, 2)
|
|
|
ad1545 |
-define(IPA_API_VERSION_MINOR, 229)
|
|
|
ad1545 |
-# Last change: Added the Certificate parameter
|
|
|
ad1545 |
+define(IPA_API_VERSION_MINOR, 230)
|
|
|
ad1545 |
+# Last change: Added `automember-find-orphans' command
|
|
|
ad1545 |
|
|
|
ad1545 |
|
|
|
ad1545 |
########################################################
|
|
|
ad1545 |
diff --git a/ipaserver/plugins/automember.py b/ipaserver/plugins/automember.py
|
|
|
ad1545 |
index 1e29f365784695c2cf1947f62351d99d7da0515d..3f48769f588f8db03caf65e7bc1206047796f63e 100644
|
|
|
ad1545 |
--- a/ipaserver/plugins/automember.py
|
|
|
ad1545 |
+++ b/ipaserver/plugins/automember.py
|
|
|
ad1545 |
@@ -116,6 +116,11 @@ EXAMPLES:
|
|
|
ad1545 |
""") + _("""
|
|
|
ad1545 |
Find all of the automember rules:
|
|
|
ad1545 |
ipa automember-find
|
|
|
ad1545 |
+""") + _("""
|
|
|
ad1545 |
+ Find all of the orphan automember rules:
|
|
|
ad1545 |
+ ipa automember-find-orphans --type=hostgroup
|
|
|
ad1545 |
+ Find all of the orphan automember rules and remove them:
|
|
|
ad1545 |
+ ipa automember-find-orphans --type=hostgroup --remove
|
|
|
ad1545 |
""") + _("""
|
|
|
ad1545 |
Display a automember rule:
|
|
|
ad1545 |
ipa automember-show --type=hostgroup webservers
|
|
|
ad1545 |
@@ -820,3 +825,58 @@ class automember_rebuild(Method):
|
|
|
ad1545 |
result=result,
|
|
|
ad1545 |
summary=unicode(summary),
|
|
|
ad1545 |
value=pkey_to_value(None, options))
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+@register()
|
|
|
ad1545 |
+class automember_find_orphans(LDAPSearch):
|
|
|
ad1545 |
+ __doc__ = _("""
|
|
|
ad1545 |
+ Search for orphan automember rules. The command might need to be run as
|
|
|
ad1545 |
+ a privileged user user to get all orphan rules.
|
|
|
ad1545 |
+ """)
|
|
|
ad1545 |
+ takes_options = group_type + (
|
|
|
ad1545 |
+ Flag(
|
|
|
ad1545 |
+ 'remove?',
|
|
|
ad1545 |
+ doc=_("Remove orphan automember rules"),
|
|
|
ad1545 |
+ ),
|
|
|
ad1545 |
+ )
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ msg_summary = ngettext(
|
|
|
ad1545 |
+ '%(count)d rules matched', '%(count)d rules matched', 0
|
|
|
ad1545 |
+ )
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ def execute(self, *keys, **options):
|
|
|
ad1545 |
+ results = super(automember_find_orphans, self).execute(*keys,
|
|
|
ad1545 |
+ **options)
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ remove_option = options.get('remove')
|
|
|
ad1545 |
+ pkey_only = options.get('pkey_only', False)
|
|
|
ad1545 |
+ ldap = self.obj.backend
|
|
|
ad1545 |
+ orphans = []
|
|
|
ad1545 |
+ for entry in results["result"]:
|
|
|
ad1545 |
+ am_dn_entry = entry['automembertargetgroup'][0]
|
|
|
ad1545 |
+ # Make DN for --raw option
|
|
|
ad1545 |
+ if not isinstance(am_dn_entry, DN):
|
|
|
ad1545 |
+ am_dn_entry = DN(am_dn_entry)
|
|
|
ad1545 |
+ try:
|
|
|
ad1545 |
+ ldap.get_entry(am_dn_entry)
|
|
|
ad1545 |
+ except errors.NotFound:
|
|
|
ad1545 |
+ if pkey_only:
|
|
|
ad1545 |
+ # For pkey_only remove automembertargetgroup
|
|
|
ad1545 |
+ del(entry['automembertargetgroup'])
|
|
|
ad1545 |
+ orphans.append(entry)
|
|
|
ad1545 |
+ if remove_option:
|
|
|
ad1545 |
+ ldap.delete_entry(entry['dn'])
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ results["result"][:] = orphans
|
|
|
ad1545 |
+ results["count"] = len(orphans)
|
|
|
ad1545 |
+ return results
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args,
|
|
|
ad1545 |
+ **options):
|
|
|
ad1545 |
+ assert isinstance(base_dn, DN)
|
|
|
ad1545 |
+ scope = ldap.SCOPE_SUBTREE
|
|
|
ad1545 |
+ ndn = DN(('cn', options['type']), base_dn)
|
|
|
ad1545 |
+ if options.get('pkey_only', False):
|
|
|
ad1545 |
+ # For pkey_only add automembertargetgroup
|
|
|
ad1545 |
+ attrs_list.append('automembertargetgroup')
|
|
|
ad1545 |
+ return filters, ndn, scope
|
|
|
ad1545 |
diff --git a/ipatests/test_xmlrpc/test_automember_plugin.py b/ipatests/test_xmlrpc/test_automember_plugin.py
|
|
|
ad1545 |
index ffbc91104ab504a98099babb024f9edab114ac5b..c83e11ac9410ce07a431f818bda79a34fcc3b180 100644
|
|
|
ad1545 |
--- a/ipatests/test_xmlrpc/test_automember_plugin.py
|
|
|
ad1545 |
+++ b/ipatests/test_xmlrpc/test_automember_plugin.py
|
|
|
ad1545 |
@@ -715,3 +715,51 @@ class TestMultipleAutomemberConditions(XMLRPC_test):
|
|
|
ad1545 |
|
|
|
ad1545 |
defaultgroup1.ensure_missing()
|
|
|
ad1545 |
defaulthostgroup1.ensure_missing()
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+@pytest.mark.tier1
|
|
|
ad1545 |
+class TestAutomemberFindOrphans(XMLRPC_test):
|
|
|
ad1545 |
+ def test_create_deps_for_find_orphans(self, hostgroup1, host1,
|
|
|
ad1545 |
+ automember_hostgroup):
|
|
|
ad1545 |
+ """ Create host, hostgroup, and automember tracker for this class
|
|
|
ad1545 |
+ of tests. """
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ # Create hostgroup1 and automember rule with condition
|
|
|
ad1545 |
+ hostgroup1.ensure_exists()
|
|
|
ad1545 |
+ host1.ensure_exists()
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ # Manually create automember rule and condition, racker will try to
|
|
|
ad1545 |
+ # remove the automember rule in the end, which is failing as the rule
|
|
|
ad1545 |
+ # is already removed
|
|
|
ad1545 |
+ api.Command['automember_add'](hostgroup1.cn, type=u'hostgroup')
|
|
|
ad1545 |
+ api.Command['automember_add_condition'](
|
|
|
ad1545 |
+ hostgroup1.cn,
|
|
|
ad1545 |
+ key=u'fqdn', type=u'hostgroup',
|
|
|
ad1545 |
+ automemberinclusiveregex=[hostgroup_include_regex]
|
|
|
ad1545 |
+ )
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ hostgroup1.retrieve()
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ def test_find_orphan_automember_rules(self, hostgroup1):
|
|
|
ad1545 |
+ """ Remove hostgroup1, find and remove obsolete automember rules. """
|
|
|
ad1545 |
+ # Remove hostgroup1
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ hostgroup1.ensure_missing()
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ # Find obsolete automember rules
|
|
|
ad1545 |
+ result = api.Command['automember_find_orphans'](type=u'hostgroup')
|
|
|
ad1545 |
+ assert result['count'] == 1
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ # Find and remove obsolete automember rules
|
|
|
ad1545 |
+ result = api.Command['automember_find_orphans'](type=u'hostgroup',
|
|
|
ad1545 |
+ remove=True)
|
|
|
ad1545 |
+ assert result['count'] == 1
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ # Find obsolete automember rules
|
|
|
ad1545 |
+ result = api.Command['automember_find_orphans'](type=u'hostgroup')
|
|
|
ad1545 |
+ assert result['count'] == 0
|
|
|
ad1545 |
+
|
|
|
ad1545 |
+ # Final cleanup of automember rule if it still exists
|
|
|
ad1545 |
+ with raises_exact(errors.NotFound(
|
|
|
ad1545 |
+ reason=u'%s: Automember rule not found' % hostgroup1.cn)):
|
|
|
ad1545 |
+ api.Command['automember_del'](hostgroup1.cn, type=u'hostgroup')
|
|
|
ad1545 |
--
|
|
|
ad1545 |
2.17.2
|
|
|
ad1545 |
|