Skip to content
Snippets Groups Projects
Unverified Commit 8ba32bfc authored by Rafael Guterres Jeffman's avatar Rafael Guterres Jeffman Committed by GitHub
Browse files

Merge pull request #1237 from t-woerner/fix_idempotency_issues_ipahost

ipahost: Fix idempotency issues 
parents 2626715d 15e9201d
No related branches found
No related tags found
No related merge requests found
...@@ -31,7 +31,7 @@ __all__ = ["gssapi", "netaddr", "api", "ipalib_errors", "Env", ...@@ -31,7 +31,7 @@ __all__ = ["gssapi", "netaddr", "api", "ipalib_errors", "Env",
"paths", "tasks", "get_credentials_if_valid", "Encoding", "paths", "tasks", "get_credentials_if_valid", "Encoding",
"DNSName", "getargspec", "certificate_loader", "DNSName", "getargspec", "certificate_loader",
"write_certificate_list", "boolean", "template_str", "write_certificate_list", "boolean", "template_str",
"urlparse"] "urlparse", "normalize_sshpubkey"]
import os import os
# ansible-freeipa requires locale to be C, IPA requires utf-8. # ansible-freeipa requires locale to be C, IPA requires utf-8.
...@@ -157,6 +157,8 @@ try: ...@@ -157,6 +157,8 @@ try:
except ImportError: except ImportError:
from ansible.module_utils.six.moves.urllib.parse import urlparse from ansible.module_utils.six.moves.urllib.parse import urlparse
from ipalib.util import normalize_sshpubkey
except ImportError as _err: except ImportError as _err:
ANSIBLE_FREEIPA_MODULE_IMPORT_ERROR = str(_err) ANSIBLE_FREEIPA_MODULE_IMPORT_ERROR = str(_err)
......
...@@ -509,7 +509,8 @@ host: ...@@ -509,7 +509,8 @@ host:
from ansible.module_utils.ansible_freeipa_module import \ from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \ IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
encode_certificate, is_ipv4_addr, is_ipv6_addr, ipalib_errors encode_certificate, is_ipv4_addr, is_ipv6_addr, ipalib_errors, \
gen_add_list, gen_intersection_list, normalize_sshpubkey
from ansible.module_utils import six from ansible.module_utils import six
if six.PY3: if six.PY3:
unicode = str unicode = str
...@@ -533,6 +534,11 @@ def find_host(module, name): ...@@ -533,6 +534,11 @@ def find_host(module, name):
if certs is not None: if certs is not None:
_res["usercertificate"] = [encode_certificate(cert) for _res["usercertificate"] = [encode_certificate(cert) for
cert in certs] cert in certs]
# krbprincipalname is returned as ipapython.kerberos.Principal, convert
# to string
principals = _res.get("krbprincipalname")
if principals is not None:
_res["krbprincipalname"] = [str(princ) for princ in principals]
return _res return _res
...@@ -676,8 +682,15 @@ def check_authind(module, auth_ind): ...@@ -676,8 +682,15 @@ def check_authind(module, auth_ind):
"by your IPA version" % "','".join(_invalid)) "by your IPA version" % "','".join(_invalid))
def convert_certificate(certificate):
if certificate is None:
return None
return [cert.strip() for cert in certificate]
# pylint: disable=unused-argument # pylint: disable=unused-argument
def result_handler(module, result, command, name, args, errors, exit_args, def result_handler(module, result, command, name, args, exit_args,
single_host): single_host):
if "random" in args and command in ["host_add", "host_mod"] \ if "random" in args and command in ["host_add", "host_mod"] \
and "randompassword" in result["result"]: and "randompassword" in result["result"]:
...@@ -688,41 +701,6 @@ def result_handler(module, result, command, name, args, errors, exit_args, ...@@ -688,41 +701,6 @@ def result_handler(module, result, command, name, args, errors, exit_args,
exit_args.setdefault(name, {})["randompassword"] = \ exit_args.setdefault(name, {})["randompassword"] = \
result["result"]["randompassword"] result["result"]["randompassword"]
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
if "failed" in result and len(result["failed"]) > 0:
for item in result["failed"]:
failed_item = result["failed"][item]
for member_type in failed_item:
for member, failure in failed_item[member_type]:
if "already a member" in failure \
or "not a member" in failure:
continue
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
# pylint: disable=unused-argument
def exception_handler(module, ex, errors, exit_args, single_host):
msg = str(ex)
if "already contains" in msg \
or "does not contain" in msg:
return True
# The canonical principal name may not be removed
if "equal to the canonical principal name must" in msg:
return True
# Host is already disabled, ignore error
if "This entry is already disabled" in msg:
return True
# Ignore no modification error.
if "no modifications to be performed" in msg:
return True
return False
def main(): def main():
host_spec = dict( host_spec = dict(
...@@ -916,6 +894,11 @@ def main(): ...@@ -916,6 +894,11 @@ def main():
auth_ind, requires_pre_auth, ok_as_delegate, ok_to_auth_as_delegate, auth_ind, requires_pre_auth, ok_as_delegate, ok_to_auth_as_delegate,
force, reverse, ip_address, update_dns, update_password) force, reverse, ip_address, update_dns, update_password)
certificate = convert_certificate(certificate)
if sshpubkey is not None:
sshpubkey = [str(normalize_sshpubkey(key)) for key in sshpubkey]
# Use hosts if names is None # Use hosts if names is None
if hosts is not None: if hosts is not None:
names = hosts names = hosts
...@@ -999,6 +982,12 @@ def main(): ...@@ -999,6 +982,12 @@ def main():
ok_to_auth_as_delegate, force, reverse, ip_address, ok_to_auth_as_delegate, force, reverse, ip_address,
update_dns, update_password) update_dns, update_password)
certificate = convert_certificate(certificate)
if sshpubkey is not None:
sshpubkey = [str(normalize_sshpubkey(key)) for
key in sshpubkey]
elif isinstance(host, (str, unicode)): elif isinstance(host, (str, unicode)):
name = host name = host
else: else:
...@@ -1074,6 +1063,17 @@ def main(): ...@@ -1074,6 +1063,17 @@ def main():
args["krbprincipalauthind"] == ['']: args["krbprincipalauthind"] == ['']:
del args["krbprincipalauthind"] del args["krbprincipalauthind"]
# Ignore sshpubkey if it is empty (for resetting)
# and not set for the host
if "ipasshpubkey" not in res_find and \
"ipasshpubkey" in args and \
args["ipasshpubkey"] == []:
del args["ipasshpubkey"]
# Ignore updatedns if it is the only arg
if "updatedns" in args and len(args) == 1:
del args["updatedns"]
# For all settings is args, check if there are # For all settings is args, check if there are
# different settings in the find result. # different settings in the find result.
# If yes: modify # If yes: modify
...@@ -1106,7 +1106,7 @@ def main(): ...@@ -1106,7 +1106,7 @@ def main():
gen_add_del_lists(managedby_host, gen_add_del_lists(managedby_host,
res_find.get("managedby_host")) res_find.get("managedby_host"))
principal_add, principal_del = gen_add_del_lists( principal_add, principal_del = gen_add_del_lists(
principal, res_find.get("principal")) principal, res_find.get("krbprincipalname"))
# Principals are not returned as utf8 for IPA using # Principals are not returned as utf8 for IPA using
# python2 using host_show, therefore we need to # python2 using host_show, therefore we need to
# convert the principals that we should remove. # convert the principals that we should remove.
...@@ -1174,13 +1174,7 @@ def main(): ...@@ -1174,13 +1174,7 @@ def main():
gen_add_del_lists( gen_add_del_lists(
dnsrecord_args.get("aaaarecord"), dnsrecord_args.get("aaaarecord"),
_dnsrec.get("aaaarecord")) _dnsrec.get("aaaarecord"))
else: else:
if res_find is None:
ansible_module.fail_json(
msg="No host '%s'" % name)
if action != "host" or (action == "host" and res_find is None):
certificate_add = certificate or [] certificate_add = certificate or []
certificate_del = [] certificate_del = []
managedby_host_add = managedby_host or [] managedby_host_add = managedby_host or []
...@@ -1211,13 +1205,84 @@ def main(): ...@@ -1211,13 +1205,84 @@ def main():
allow_retrieve_keytab_hostgroup_add = \ allow_retrieve_keytab_hostgroup_add = \
allow_retrieve_keytab_hostgroup or [] allow_retrieve_keytab_hostgroup or []
allow_retrieve_keytab_hostgroup_del = [] allow_retrieve_keytab_hostgroup_del = []
dnsrecord_a_add = dnsrecord_args.get("arecord") or [] _dnsrec = res_find_dnsrecord or {}
dnsrecord_a_add = gen_add_list(
dnsrecord_args.get("arecord"),
_dnsrec.get("arecord"))
dnsrecord_a_del = []
dnsrecord_aaaa_add = gen_add_list(
dnsrecord_args.get("aaaarecord"),
_dnsrec.get("aaaarecord"))
dnsrecord_aaaa_del = []
else:
# action member
if res_find is None:
ansible_module.fail_json(
msg="No host '%s'" % name)
certificate_add = gen_add_list(
certificate, res_find.get("usercertificate"))
certificate_del = []
managedby_host_add = gen_add_list(
managedby_host, res_find.get("managedby_host"))
managedby_host_del = []
principal_add = gen_add_list(
principal, res_find.get("krbprincipalname"))
principal_del = []
allow_create_keytab_user_add = gen_add_list(
allow_create_keytab_user,
res_find.get(
"ipaallowedtoperform_write_keys_user"))
allow_create_keytab_user_del = []
allow_create_keytab_group_add = gen_add_list(
allow_create_keytab_group,
res_find.get(
"ipaallowedtoperform_write_keys_group"))
allow_create_keytab_group_del = []
allow_create_keytab_host_add = gen_add_list(
allow_create_keytab_host,
res_find.get(
"ipaallowedtoperform_write_keys_host"))
allow_create_keytab_host_del = []
allow_create_keytab_hostgroup_add = gen_add_list(
allow_create_keytab_hostgroup,
res_find.get(
"ipaallowedtoperform_write_keys_hostgroup"))
allow_create_keytab_hostgroup_del = []
allow_retrieve_keytab_user_add = gen_add_list(
allow_retrieve_keytab_user,
res_find.get(
"ipaallowedtoperform_read_keys_user"))
allow_retrieve_keytab_user_del = []
allow_retrieve_keytab_group_add = gen_add_list(
allow_retrieve_keytab_group,
res_find.get(
"ipaallowedtoperform_read_keys_group"))
allow_retrieve_keytab_group_del = []
allow_retrieve_keytab_host_add = gen_add_list(
allow_retrieve_keytab_host,
res_find.get(
"ipaallowedtoperform_read_keys_host"))
allow_retrieve_keytab_host_del = []
allow_retrieve_keytab_hostgroup_add = gen_add_list(
allow_retrieve_keytab_hostgroup,
res_find.get(
"ipaallowedtoperform_read_keys_hostgroup"))
allow_retrieve_keytab_hostgroup_del = []
_dnsrec = res_find_dnsrecord or {}
dnsrecord_a_add = gen_add_list(
dnsrecord_args.get("arecord"),
_dnsrec.get("arecord"))
dnsrecord_a_del = [] dnsrecord_a_del = []
dnsrecord_aaaa_add = dnsrecord_args.get("aaaarecord") or [] dnsrecord_aaaa_add = gen_add_list(
dnsrecord_args.get("aaaarecord"),
_dnsrec.get("aaaarecord"))
dnsrecord_aaaa_del = [] dnsrecord_aaaa_del = []
# Remove canonical principal from principal_del # Remove canonical principal from principal_del
canonical_principal = "host/" + name + "@" + server_realm canonical_principal = "host/" + name + "@" + server_realm
# canonical_principal is also in find_res["krbcanonicalname"]
if canonical_principal in principal_del and \ if canonical_principal in principal_del and \
action == "host" and (principal is not None or action == "host" and (principal is not None or
canonical_principal not in principal): canonical_principal not in principal):
...@@ -1398,8 +1463,10 @@ def main(): ...@@ -1398,8 +1463,10 @@ def main():
# the removal of non-existing entries. # the removal of non-existing entries.
# Remove certificates # Remove certificates
if certificate is not None: certificate_del = gen_intersection_list(
for _certificate in certificate: certificate, res_find.get("usercertificate"))
if certificate_del is not None:
for _certificate in certificate_del:
commands.append([name, "host_remove_cert", commands.append([name, "host_remove_cert",
{ {
"usercertificate": "usercertificate":
...@@ -1412,8 +1479,10 @@ def main(): ...@@ -1412,8 +1479,10 @@ def main():
# the removal of non-existing entries. # the removal of non-existing entries.
# Remove managedby_hosts # Remove managedby_hosts
if managedby_host is not None: managedby_host_del = gen_intersection_list(
for _managedby_host in managedby_host: managedby_host, res_find.get("managedby_host"))
if managedby_host_del is not None:
for _managedby_host in managedby_host_del:
commands.append([name, "host_remove_managedby", commands.append([name, "host_remove_managedby",
{ {
"host": "host":
...@@ -1426,8 +1495,10 @@ def main(): ...@@ -1426,8 +1495,10 @@ def main():
# the removal of non-existing entries. # the removal of non-existing entries.
# Remove principals # Remove principals
if principal is not None: principal_del = gen_intersection_list(
for _principal in principal: principal, res_find.get("krbprincipalname"))
if principal_del is not None:
for _principal in principal_del:
commands.append([name, "host_remove_principal", commands.append([name, "host_remove_principal",
{ {
"krbprincipalname": "krbprincipalname":
...@@ -1435,51 +1506,77 @@ def main(): ...@@ -1435,51 +1506,77 @@ def main():
}]) }])
# Disallow create keytab # Disallow create keytab
if allow_create_keytab_user is not None or \ allow_create_keytab_user_del = gen_intersection_list(
allow_create_keytab_group is not None or \ allow_create_keytab_user,
allow_create_keytab_host is not None or \ res_find.get("ipaallowedtoperform_write_keys_user"))
allow_create_keytab_hostgroup is not None: allow_create_keytab_group_del = gen_intersection_list(
allow_create_keytab_group,
res_find.get("ipaallowedtoperform_write_keys_group"))
allow_create_keytab_host_del = gen_intersection_list(
allow_create_keytab_host,
res_find.get("ipaallowedtoperform_write_keys_host"))
allow_create_keytab_hostgroup_del = gen_intersection_list(
allow_create_keytab_hostgroup,
res_find.get(
"ipaallowedtoperform_write_keys_hostgroup"))
if len(allow_create_keytab_user_del) > 0 or \
len(allow_create_keytab_group_del) > 0 or \
len(allow_create_keytab_host_del) > 0 or \
len(allow_create_keytab_hostgroup_del) > 0:
commands.append( commands.append(
[name, "host_disallow_create_keytab", [name, "host_disallow_create_keytab",
{ {
"user": allow_create_keytab_user, "user": allow_create_keytab_user_del,
"group": allow_create_keytab_group, "group": allow_create_keytab_group_del,
"host": allow_create_keytab_host, "host": allow_create_keytab_host_del,
"hostgroup": allow_create_keytab_hostgroup, "hostgroup":
allow_create_keytab_hostgroup_del,
}]) }])
# Disallow retrieve keytab # Disallow retrieve keytab
if allow_retrieve_keytab_user is not None or \ allow_retrieve_keytab_user_del = gen_intersection_list(
allow_retrieve_keytab_group is not None or \ allow_retrieve_keytab_user,
allow_retrieve_keytab_host is not None or \ res_find.get("ipaallowedtoperform_read_keys_user"))
allow_retrieve_keytab_hostgroup is not None: allow_retrieve_keytab_group_del = gen_intersection_list(
allow_retrieve_keytab_group,
res_find.get("ipaallowedtoperform_read_keys_group"))
allow_retrieve_keytab_host_del = gen_intersection_list(
allow_retrieve_keytab_host,
res_find.get("ipaallowedtoperform_read_keys_host"))
allow_retrieve_keytab_hostgroup_del = \
gen_intersection_list(
allow_retrieve_keytab_hostgroup,
res_find.get(
"ipaallowedtoperform_read_keys_hostgroup"))
if len(allow_retrieve_keytab_user_del) > 0 or \
len(allow_retrieve_keytab_group_del) > 0 or \
len(allow_retrieve_keytab_host_del) > 0 or \
len(allow_retrieve_keytab_hostgroup_del) > 0:
commands.append( commands.append(
[name, "host_disallow_retrieve_keytab", [name, "host_disallow_retrieve_keytab",
{ {
"user": allow_retrieve_keytab_user, "user": allow_retrieve_keytab_user_del,
"group": allow_retrieve_keytab_group, "group": allow_retrieve_keytab_group_del,
"host": allow_retrieve_keytab_host, "host": allow_retrieve_keytab_host_del,
"hostgroup": allow_retrieve_keytab_hostgroup, "hostgroup":
allow_retrieve_keytab_hostgroup_del,
}]) }])
dnsrecord_args = gen_dnsrecord_args(ansible_module, if res_find_dnsrecord is not None:
ip_address, reverse) dnsrecord_args = gen_dnsrecord_args(
ansible_module, ip_address, reverse)
# Remove arecord and aaaarecord from dnsrecord_args
# if the record does not exits in res_find_dnsrecord # Only keep a and aaaa recrords that are part
# to prevent "DNS resource record not found" error # of res_find_dnsrecord.
if "arecord" in dnsrecord_args \ for _type in ["arecord", "aaaarecord"]:
and dnsrecord_args["arecord"] is not None \ if _type in dnsrecord_args:
and len(dnsrecord_args["arecord"]) > 0 \ recs = gen_intersection_list(
and (res_find_dnsrecord is None dnsrecord_args[_type],
or "arecord" not in res_find_dnsrecord): res_find_dnsrecord.get(_type))
del dnsrecord_args["arecord"] if len(recs) > 0:
if "aaaarecord" in dnsrecord_args \ dnsrecord_args[_type] = recs
and dnsrecord_args["aaaarecord"] is not None \ else:
and len(dnsrecord_args["aaaarecord"]) > 0 \ del dnsrecord_args[_type]
and (res_find_dnsrecord is None
or "aaaarecord" not in res_find_dnsrecord):
del dnsrecord_args["aaaarecord"]
if "arecord" in dnsrecord_args or \ if "arecord" in dnsrecord_args or \
"aaaarecord" in dnsrecord_args: "aaaarecord" in dnsrecord_args:
...@@ -1504,7 +1601,7 @@ def main(): ...@@ -1504,7 +1601,7 @@ def main():
# Execute commands # Execute commands
changed = ansible_module.execute_ipa_commands( changed = ansible_module.execute_ipa_commands(
commands, result_handler, exception_handler, commands, result_handler,
exit_args=exit_args, single_host=hosts is None) exit_args=exit_args, single_host=hosts is None)
# Done # Done
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment