diff --git a/plugins/doc_fragments/ipamodule_base_docs.py b/plugins/doc_fragments/ipamodule_base_docs.py index 22f2a1d1c8fbc2240326fb533b61cc8b48eedd66..4e8c126835ba7bb32c503fbf3508d38863c4ffb0 100644 --- a/plugins/doc_fragments/ipamodule_base_docs.py +++ b/plugins/doc_fragments/ipamodule_base_docs.py @@ -30,4 +30,11 @@ options: ipaadmin_password: description: The admin password. required: false + ipaapi_context: + description: | + The context in which the module will execute. Executing in a + server context is preferred. If not provided context will be + determined by the execution environment. + choices: ["server", "client"] + required: false """ diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py index fede14a5ee4744c923da233a08ecc2e029af1b0a..fe5268046829ca4690146c5dd76884e2e5d51184 100644 --- a/plugins/module_utils/ansible_freeipa_module.py +++ b/plugins/module_utils/ansible_freeipa_module.py @@ -107,6 +107,34 @@ else: except ImportError: from collections import Mapping # pylint: disable=deprecated-class + # Try to import is_ipa_configured or use a fallback implementation. + try: + from ipalib.facts import is_ipa_configured + except ImportError: + try: + from ipaserver.install.installutils import is_ipa_configured + except ImportError: + from ipalib.install import sysrestore + + def is_ipa_configured(): + sstore = sysrestore.StateFile(paths.SYSRESTORE) + + if sstore.has_state('installation'): + return sstore.get_state('installation', 'complete') + + fstore = sysrestore.FileStore(paths.SYSRESTORE) + + IPA_MODULES = [ # pylint: disable=invalid-name + 'httpd', 'kadmin', 'dirsrv', 'pki-tomcatd', 'install', + 'krb5kdc', 'ntpd', 'named' + ] + + for module in IPA_MODULES: + if sstore.has_state(module): + return True + + return fstore.has_files() + if six.PY3: unicode = str @@ -179,18 +207,25 @@ else: `context` can be any of: * `server` (default) - * `ansible-freeipa` - * `cli_installer` + * `client` """ env = Env() env._bootstrap() env._finalize_core(**dict(DEFAULT_CONFIG)) - # available contexts are 'server', 'ansible-freeipa' and - # 'cli_installer' - + # If not set, context will be based on current API context. if context is None: - context = 'server' + context = "server" if is_ipa_configured() else "client" + + # Available contexts are 'server' and 'client'. + if context not in ["server", "client"]: + raise ValueError("Invalid execution context: %s" % (context)) + + # IPA uses 'cli' for a 'client' context, but 'client' + # provides a better user interface. Here we map the + # value if needed. + if context == "client": + context = "cli" api.bootstrap(context=context, debug=env.debug, log=None) api.finalize() @@ -577,6 +612,9 @@ else: ipa_module_base_spec = dict( ipaadmin_principal=dict(type="str", default="admin"), ipaadmin_password=dict(type="str", required=False, no_log=True), + ipaapi_context=dict( + type="str", required=False, choices=["server", "client"], + ), ) def __init__(self, *args, **kwargs): @@ -604,6 +642,8 @@ else: # ipaadmin vars ipaadmin_principal = self.params_get("ipaadmin_principal") ipaadmin_password = self.params_get("ipaadmin_password") + if context is None: + context = self.params_get("ipaapi_context") ccache_dir = None ccache_name = None @@ -1071,7 +1111,8 @@ else: def ipa_run(self): """Execute module actions.""" - with self.ipa_connect(): + ipaapi_context = self.ipa_params.get("ipaapi_context") + with self.ipa_connect(context=ipaapi_context): self.check_ipa_params() self.define_ipa_commands() self._run_ipa_commands()