Merge pull request #918 from t-woerner/fix_plugins_for_ansible_fake_execution_test

Fix plugins for ansible fake execution test
This commit is contained in:
Rafael Guterres Jeffman
2022-09-27 10:56:42 -03:00
committed by GitHub
4 changed files with 1111 additions and 1060 deletions

View File

@@ -4,7 +4,7 @@
# Sergio Oliveira Campos <seocam@redhat.com> # Sergio Oliveira Campos <seocam@redhat.com>
# Thomas Woerner <twoerner@redhat.com> # Thomas Woerner <twoerner@redhat.com>
# #
# Copyright (C) 2019 Red Hat # Copyright (C) 2019-2022 Red Hat
# see file 'COPYING' for use and warranty information # see file 'COPYING' for use and warranty information
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@@ -31,29 +31,26 @@ __all__ = ["gssapi", "netaddr", "api", "ipalib_errors", "Env",
"paths", "tasks", "get_credentials_if_valid", "Encoding", "paths", "tasks", "get_credentials_if_valid", "Encoding",
"load_pem_x509_certificate", "DNSName", "getargspec"] "load_pem_x509_certificate", "DNSName", "getargspec"]
import os
import sys import sys
import operator
import tempfile
import shutil
import socket
import base64
from datetime import datetime
from contextlib import contextmanager
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_text
from ansible.module_utils.common.text.converters import jsonify
from ansible.module_utils import six
from ansible.module_utils.common._collections_compat import Mapping
# HACK: workaround for Ansible 2.9 # Import getargspec from inspect or provide own getargspec for
# https://github.com/ansible/ansible/issues/68361 # Python 2 compatibility with Python 3.11+.
if 'ansible.executor' in sys.modules: try:
for attr in __all__:
setattr(sys.modules[__name__], attr, None)
else:
import operator
import os
import uuid
import tempfile
import shutil
import netaddr
import gssapi
from datetime import datetime
from contextlib import contextmanager
# Import getargspec from inspect or provide own getargspec for
# Python 2 compatibility with Python 3.11+.
try:
from inspect import getargspec from inspect import getargspec
except ImportError: except ImportError:
from collections import namedtuple from collections import namedtuple
from inspect import getfullargspec from inspect import getfullargspec
@@ -71,8 +68,11 @@ else:
", use inspect.signature() API which can support them") ", use inspect.signature() API which can support them")
return ArgSpec(args, varargs, varkw, defaults) return ArgSpec(args, varargs, varkw, defaults)
# ansible-freeipa requires locale to be C, IPA requires utf-8.
os.environ["LANGUAGE"] = "C" try:
import uuid
import netaddr
import gssapi
from ipalib import api from ipalib import api
from ipalib import errors as ipalib_errors # noqa from ipalib import errors as ipalib_errors # noqa
@@ -91,9 +91,6 @@ else:
from ipalib.krb_utils import get_credentials_if_valid from ipalib.krb_utils import get_credentials_if_valid
from ipapython.dnsutil import DNSName from ipapython.dnsutil import DNSName
from ipapython import kerberos from ipapython import kerberos
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_text
from ansible.module_utils.common.text.converters import jsonify
try: try:
from ipalib.x509 import Encoding from ipalib.x509 import Encoding
@@ -106,15 +103,6 @@ else:
from ipalib.x509 import load_certificate from ipalib.x509 import load_certificate
load_pem_x509_certificate = None load_pem_x509_certificate = None
import socket
import base64
from ansible.module_utils import six
try:
from collections.abc import Mapping # noqa
except ImportError:
from collections import Mapping # pylint: disable=deprecated-class
# Try to import is_ipa_configured or use a fallback implementation. # Try to import is_ipa_configured or use a fallback implementation.
try: try:
from ipalib.facts import is_ipa_configured from ipalib.facts import is_ipa_configured
@@ -150,10 +138,30 @@ else:
except ImportError: except ImportError:
_dcerpc_bindings_installed = False # pylint: disable=invalid-name _dcerpc_bindings_installed = False # pylint: disable=invalid-name
if six.PY3: except ImportError as _err:
ANSIBLE_FREEIPA_MODULE_IMPORT_ERROR = str(_err)
for attr in __all__:
setattr(sys.modules[__name__], attr, None)
uuid = None
netaddr = None
is_ipa_configured = None
load_certificate = None
kerberos = None
ipaserver = None # pylint: disable=C0103
else:
ANSIBLE_FREEIPA_MODULE_IMPORT_ERROR = None
# ansible-freeipa requires locale to be C, IPA requires utf-8.
os.environ["LANGUAGE"] = "C"
if six.PY3:
unicode = str unicode = str
def valid_creds(module, principal): # noqa
def valid_creds(module, principal): # noqa
"""Get valid credentials matching the princial, try GSSAPI first.""" """Get valid credentials matching the princial, try GSSAPI first."""
if "KRB5CCNAME" in os.environ: if "KRB5CCNAME" in os.environ:
ccache = os.environ["KRB5CCNAME"] ccache = os.environ["KRB5CCNAME"]
@@ -190,7 +198,8 @@ else:
return True return True
return False return False
def temp_kinit(principal, password):
def temp_kinit(principal, password):
"""Kinit with password using a temporary ccache.""" """Kinit with password using a temporary ccache."""
if not password: if not password:
raise RuntimeError("The password is not set") raise RuntimeError("The password is not set")
@@ -203,12 +212,13 @@ else:
try: try:
kinit_password(principal, password, ccache_name) kinit_password(principal, password, ccache_name)
except RuntimeError as e: except RuntimeError as e:
raise RuntimeError("Kerberos authentication failed: {}".format(e)) raise RuntimeError("Kerberos authentication failed: %s" % str(e))
os.environ["KRB5CCNAME"] = ccache_name os.environ["KRB5CCNAME"] = ccache_name
return ccache_dir, ccache_name return ccache_dir, ccache_name
def temp_kdestroy(ccache_dir, ccache_name):
def temp_kdestroy(ccache_dir, ccache_name):
"""Destroy temporary ticket and remove temporary ccache.""" """Destroy temporary ticket and remove temporary ccache."""
if ccache_name is not None: if ccache_name is not None:
run([paths.KDESTROY, '-c', ccache_name], raiseonerr=False) run([paths.KDESTROY, '-c', ccache_name], raiseonerr=False)
@@ -216,7 +226,8 @@ else:
if ccache_dir is not None: if ccache_dir is not None:
shutil.rmtree(ccache_dir, ignore_errors=True) shutil.rmtree(ccache_dir, ignore_errors=True)
def api_connect(context=None, **overrides):
def api_connect(context=None, **overrides):
""" """
Initialize IPA API with the provided configuration. Initialize IPA API with the provided configuration.
@@ -270,23 +281,28 @@ else:
if not backend.isconnected(): if not backend.isconnected():
backend.connect(ccache=os.environ.get('KRB5CCNAME', None)) backend.connect(ccache=os.environ.get('KRB5CCNAME', None))
def api_command(_module, command, name, args):
def api_command(_module, command, name, args):
"""Call ipa.Command.""" """Call ipa.Command."""
return api.Command[command](name, **args) return api.Command[command](name, **args)
def api_command_no_name(_module, command, args):
def api_command_no_name(_module, command, args):
"""Call ipa.Command without a name.""" """Call ipa.Command without a name."""
return api.Command[command](**args) return api.Command[command](**args)
def api_check_command(command):
def api_check_command(command):
"""Return if command exists in command list.""" """Return if command exists in command list."""
return command in api.Command return command in api.Command
def api_check_param(command, name):
def api_check_param(command, name):
"""Check if param exists in command param list.""" """Check if param exists in command param list."""
return name in api.Command[command].params return name in api.Command[command].params
def api_check_ipa_version(oper, requested_version):
def api_check_ipa_version(oper, requested_version):
""" """
Compare the installed IPA version against a requested version. Compare the installed IPA version against a requested version.
@@ -306,7 +322,8 @@ else:
return operation(tasks.parse_ipa_version(VERSION), return operation(tasks.parse_ipa_version(VERSION),
tasks.parse_ipa_version(requested_version)) tasks.parse_ipa_version(requested_version))
def date_format(value):
def date_format(value):
accepted_date_formats = [ accepted_date_formats = [
LDAP_GENERALIZED_TIME_FORMAT, # generalized time LDAP_GENERALIZED_TIME_FORMAT, # generalized time
'%Y-%m-%dT%H:%M:%SZ', # ISO 8601, second precision '%Y-%m-%dT%H:%M:%SZ', # ISO 8601, second precision
@@ -323,7 +340,8 @@ else:
pass pass
raise ValueError("Invalid date '%s'" % value) raise ValueError("Invalid date '%s'" % value)
def compare_args_ipa(module, args, ipa, ignore=None): # noqa
def compare_args_ipa(module, args, ipa, ignore=None): # noqa
"""Compare IPA object attributes against command arguments. """Compare IPA object attributes against command arguments.
This function compares 'ipa' attributes with the 'args' the module This function compares 'ipa' attributes with the 'args' the module
@@ -427,7 +445,8 @@ else:
return False return False
return True return True
def _afm_convert(value):
def _afm_convert(value):
if value is not None: if value is not None:
if isinstance(value, list): if isinstance(value, list):
return [_afm_convert(x) for x in value] return [_afm_convert(x) for x in value]
@@ -439,7 +458,8 @@ else:
return value return value
def module_params_get(module, name, allow_empty_string=False):
def module_params_get(module, name, allow_empty_string=False):
value = _afm_convert(module.params.get(name)) value = _afm_convert(module.params.get(name))
# Fail on empty strings in the list or if allow_empty_string is True # Fail on empty strings in the list or if allow_empty_string is True
@@ -463,7 +483,8 @@ else:
return value return value
def module_params_get_lowercase(module, name, allow_empty_string=False):
def module_params_get_lowercase(module, name, allow_empty_string=False):
value = module_params_get(module, name, allow_empty_string) value = module_params_get(module, name, allow_empty_string)
if isinstance(value, list): if isinstance(value, list):
value = [v.lower() for v in value] value = [v.lower() for v in value]
@@ -471,21 +492,26 @@ else:
value = value.lower() value = value.lower()
return value return value
def api_get_domain():
def api_get_domain():
return api.env.domain return api.env.domain
def ensure_fqdn(name, domain):
def ensure_fqdn(name, domain):
if "." not in name: if "." not in name:
return "%s.%s" % (name, domain) return "%s.%s" % (name, domain)
return name return name
def api_get_realm():
def api_get_realm():
return api.env.realm return api.env.realm
def api_get_basedn():
def api_get_basedn():
return api.env.basedn return api.env.basedn
def gen_add_del_lists(user_list, res_list):
def gen_add_del_lists(user_list, res_list):
""" """
Generate the lists for the addition and removal of members. Generate the lists for the addition and removal of members.
@@ -505,7 +531,8 @@ else:
return add_list, del_list return add_list, del_list
def gen_add_list(user_list, res_list):
def gen_add_list(user_list, res_list):
""" """
Generate add list for addition of new members. Generate add list for addition of new members.
@@ -521,7 +548,8 @@ else:
return list(set(user_list or []) - set(res_list or [])) return list(set(user_list or []) - set(res_list or []))
def gen_intersection_list(user_list, res_list):
def gen_intersection_list(user_list, res_list):
""" """
Generate the intersection list for removal of existing members. Generate the intersection list for removal of existing members.
@@ -537,7 +565,8 @@ else:
return list(set(res_list or []).intersection(set(user_list or []))) return list(set(res_list or []).intersection(set(user_list or [])))
def encode_certificate(cert):
def encode_certificate(cert):
""" """
Encode a certificate using base64. Encode a certificate using base64.
@@ -551,7 +580,8 @@ else:
encoded = encoded.decode('ascii') encoded = encoded.decode('ascii')
return encoded return encoded
def load_cert_from_str(cert):
def load_cert_from_str(cert):
cert = cert.strip() cert = cert.strip()
if not cert.startswith("-----BEGIN CERTIFICATE-----"): if not cert.startswith("-----BEGIN CERTIFICATE-----"):
cert = "-----BEGIN CERTIFICATE-----\n" + cert cert = "-----BEGIN CERTIFICATE-----\n" + cert
@@ -564,7 +594,8 @@ else:
cert = load_certificate(cert.encode('utf-8')) cert = load_certificate(cert.encode('utf-8'))
return cert return cert
def DN_x500_text(text): # pylint: disable=invalid-name
def DN_x500_text(text): # pylint: disable=invalid-name
if hasattr(DN, "x500_text"): if hasattr(DN, "x500_text"):
return DN(text).x500_text() return DN(text).x500_text()
# Emulate x500_text # Emulate x500_text
@@ -572,7 +603,8 @@ else:
dn.rdns = reversed(dn.rdns) dn.rdns = reversed(dn.rdns)
return str(dn) return str(dn)
def is_valid_port(port):
def is_valid_port(port):
if not isinstance(port, int): if not isinstance(port, int):
return False return False
@@ -581,7 +613,8 @@ else:
return False return False
def is_ip_address(ipaddr):
def is_ip_address(ipaddr):
"""Test if given IP address is a valid IPv4 or IPv6 address.""" """Test if given IP address is a valid IPv4 or IPv6 address."""
try: try:
netaddr.IPAddress(str(ipaddr)) netaddr.IPAddress(str(ipaddr))
@@ -589,7 +622,8 @@ else:
return False return False
return True return True
def is_ip_network_address(ipaddr):
def is_ip_network_address(ipaddr):
"""Test if given IP address is a valid IPv4 or IPv6 address.""" """Test if given IP address is a valid IPv4 or IPv6 address."""
try: try:
netaddr.IPNetwork(str(ipaddr)) netaddr.IPNetwork(str(ipaddr))
@@ -597,7 +631,8 @@ else:
return False return False
return True return True
def is_ipv4_addr(ipaddr):
def is_ipv4_addr(ipaddr):
"""Test if given IP address is a valid IPv4 address.""" """Test if given IP address is a valid IPv4 address."""
try: try:
socket.inet_pton(socket.AF_INET, ipaddr) socket.inet_pton(socket.AF_INET, ipaddr)
@@ -605,7 +640,8 @@ else:
return False return False
return True return True
def is_ipv6_addr(ipaddr):
def is_ipv6_addr(ipaddr):
"""Test if given IP address is a valid IPv6 address.""" """Test if given IP address is a valid IPv6 address."""
try: try:
socket.inet_pton(socket.AF_INET6, ipaddr) socket.inet_pton(socket.AF_INET6, ipaddr)
@@ -613,7 +649,8 @@ else:
return False return False
return True return True
def servicedelegation_normalize_principals(module, principal,
def servicedelegation_normalize_principals(module, principal,
check_exists=False): check_exists=False):
""" """
Normalize servicedelegation principals. Normalize servicedelegation principals.
@@ -696,7 +733,8 @@ else:
return _principal return _principal
def exit_raw_json(module, **kwargs):
def exit_raw_json(module, **kwargs):
""" """
Print the raw parameters in JSON format, without masking. Print the raw parameters in JSON format, without masking.
@@ -715,7 +753,8 @@ else:
print(jsonify(kwargs)) print(jsonify(kwargs))
sys.exit(0) sys.exit(0)
def __get_domain_validator():
def __get_domain_validator():
if not _dcerpc_bindings_installed: if not _dcerpc_bindings_installed:
raise ipalib_errors.NotFound( raise ipalib_errors.NotFound(
reason=( reason=(
@@ -739,7 +778,8 @@ else:
return domain_validator return domain_validator
def get_trusted_domain_sid_from_name(dom_name):
def get_trusted_domain_sid_from_name(dom_name):
""" """
Given a trust domain name, returns the domain SID. Given a trust domain name, returns the domain SID.
@@ -751,7 +791,8 @@ else:
return unicode(sid) if sid is not None else None return unicode(sid) if sid is not None else None
class IPAParamMapping(Mapping):
class IPAParamMapping(Mapping):
""" """
Provides IPA API mapping to playbook parameters or computed values. Provides IPA API mapping to playbook parameters or computed values.
@@ -874,7 +915,8 @@ else:
return args return args
class IPAAnsibleModule(AnsibleModule):
class IPAAnsibleModule(AnsibleModule):
""" """
IPA Ansible Module. IPA Ansible Module.
@@ -953,6 +995,9 @@ else:
# pylint: disable=super-with-arguments # pylint: disable=super-with-arguments
super(IPAAnsibleModule, self).__init__(*args, **kwargs) super(IPAAnsibleModule, self).__init__(*args, **kwargs)
if ANSIBLE_FREEIPA_MODULE_IMPORT_ERROR is not None:
self.fail_json(msg=ANSIBLE_FREEIPA_MODULE_IMPORT_ERROR)
@contextmanager @contextmanager
def ipa_connect(self, context=None): def ipa_connect(self, context=None):
""" """

View File

@@ -866,8 +866,13 @@ RETURN = """
from ansible.module_utils._text import to_text from ansible.module_utils._text import to_text
from ansible.module_utils.ansible_freeipa_module import \ from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, is_ipv4_addr, is_ipv6_addr, ipalib_errors IPAAnsibleModule, is_ipv4_addr, is_ipv6_addr, ipalib_errors
import dns.reversename try:
import dns.resolver import dns.reversename
import dns.resolver
except ImportError as _err:
MODULE_IMPORT_ERROR = str(_err)
else:
MODULE_IMPORT_ERROR = None
from ansible.module_utils import six from ansible.module_utils import six
@@ -1131,6 +1136,9 @@ def configure_module():
ansible_module._ansible_debug = True ansible_module._ansible_debug = True
if MODULE_IMPORT_ERROR is not None:
ansible_module.fail_json(msg=MODULE_IMPORT_ERROR)
return ansible_module return ansible_module

View File

@@ -201,7 +201,6 @@ dnszone:
returned: always returned: always
""" """
from ipapython.dnsutil import DNSName # noqa: E402
from ansible.module_utils.ansible_freeipa_module import ( from ansible.module_utils.ansible_freeipa_module import (
IPAAnsibleModule, IPAAnsibleModule,
is_ip_address, is_ip_address,
@@ -210,8 +209,9 @@ from ansible.module_utils.ansible_freeipa_module import (
ipalib_errors, ipalib_errors,
compare_args_ipa, compare_args_ipa,
IPAParamMapping, IPAParamMapping,
DNSName,
netaddr
) # noqa: E402 ) # noqa: E402
import netaddr
from ansible.module_utils import six from ansible.module_utils import six

View File

@@ -1,8 +1,6 @@
plugins/module_utils/ansible_freeipa_module.py compile-2.6!skip plugins/module_utils/ansible_freeipa_module.py compile-2.6!skip
plugins/module_utils/ansible_freeipa_module.py import-2.6!skip plugins/module_utils/ansible_freeipa_module.py import-2.6!skip
plugins/module_utils/ansible_freeipa_module.py pylint:ansible-bad-import-from
plugins/module_utils/ansible_freeipa_module.py pylint:ansible-bad-function plugins/module_utils/ansible_freeipa_module.py pylint:ansible-bad-function
plugins/module_utils/ansible_freeipa_module.py pylint:ansible-format-automatic-specification
plugins/modules/ipaclient_get_facts.py compile-2.6!skip plugins/modules/ipaclient_get_facts.py compile-2.6!skip
plugins/modules/ipaclient_get_facts.py import-2.6!skip plugins/modules/ipaclient_get_facts.py import-2.6!skip
plugins/modules/ipaclient_api.py pylint:ansible-format-automatic-specification plugins/modules/ipaclient_api.py pylint:ansible-format-automatic-specification